Overriding CE Models and Controllers: The Easy Way

11 views
Skip to first unread message

bobics

unread,
Nov 5, 2008, 1:27:11 AM11/5/08
to CommunityEngine
If you're like me and use CE as a plugin, and want customize CE
without modifying the original source, there's a pretty simple way
with the "require_dependency" statement. This method works for both
Models and Controllers. Just create a blank Model or Controller file,
and call it like this:

require_dependency "#{RAILS_ROOT}/vendor/plugins/community_engine/
app/models/user.rb"

class User < ActiveRecord::Base
...
(override only the methods you want)
...
end

The reason I use this for Controllers (and not only Models) instead of
just copying the *_controller.rb file, is because the Controller is
loaded twice and you'll end up getting a "NoMethodError (undefined
method `controller_name' for nil:NilClass):" error message on
production. This is due to filters being loaded twice when you
duplicate the controller. To put it simply, the suggested way of
overriding a Controller via copying the file WILL give you problems!
Further reading below.

-bobics

http://groups.google.com/group/communityengine/browse_thread/thread/b84154e5228bf9f3
http://rails-engines.org/development/common-issues-when-overloading-code-from-plugins/
http://sites.google.com/a/curbly.com/communityengine/customizing-ce-1/overriding-ce-models
(Bruno, you may want to update this)

Bruno Bornsztein

unread,
Nov 5, 2008, 11:05:14 AM11/5/08
to communi...@googlegroups.com
Thanks for the tip, I'll add this to the section on overriding models. I'm not sure I agree with you re: controllers, I've never encountered the NoMethodErrors you mention, despite overriding tons of controllers.

Maybe with a little more info, we can track down the exact problem.
Thanks!
bruno

bobics

unread,
Nov 6, 2008, 6:37:52 PM11/6/08
to CommunityEngine
Thanks Bruno,

Unfortunately the nil:NilClass problem was still happening, so I spent
a *bunch* of time investigating the problem yesterday. It turns out
using "require" in general is a very bad idea except for gems and core
ruby libraries. The two symptoms I was seeing was A) the nil:NilClass
error on production and B) the error "Help: A copy of <SomeClass> has
been removed from the module tree but is still active!". Originally I
had thought these two problems to be unrelated, since the first
happened on production, and the latter in dev mode.

In my setup I used "require" to _extend_ the User controller in two
places, and "require_dependency" to _override_ ther User controller in
another. When I changed everything to use "require_dependency"
instead, both errors went away.

This was a HUGE stumbling block for me, so hopefully by posting this
on the CE group this will save someone else some time and suffering.
Unfortunately, require_dependency is very poorly documented in RoR...
even though this is what you should be using! Reference below.

-bobics

http://spacevatican.org/2008/9/28/required-or-not
per the article:
" * Just don't require stuff. If you lets Rails' automagic loading
do its work none of this will happen
* If you do need to require stuff explicity, use
require_dependency. This means that Rails is kept in the loop"

Levi Rosol

unread,
Nov 14, 2008, 11:53:48 PM11/14/08
to CommunityEngine
i need help understanding this. If i'm reading it correctly, if i want
to add a has_many to the CE user.rb, i would do something like the
following in my user.rb file:

require_dependency "#{RAILS_ROOT}/vendor/plugins/community_engine/app/
models/user.rb"
class User < ActiveRecord::Base

has_many :myobject, :through => :user_myobject

end

this would allow me to keep all of the functionality in the CE
user.rb, but add my stuff. What about when i want to remove or change
functionality in the CE user.rb?

I'm trying to stay away from modifying the CE code, however, I'm not
sure it makes a difference whether i change it or override it. for
example, I've changed the shared/_admin_nav.html.haml partial to add
some new functionality. I did this by copy/pasting the view into my
project, then modifying it. This means that when CE has a new release,
and something is new in that file, I need to merge the diffs into my
new file. If that's the case, why not just modify the CE view, keeping
with the DRY idea.

based on the documentation for overriding the model, neither of the
two options there get around this. that assumes i understand them
completely too :-)

i hope this doesn't sound like to newbish of a question. I'm just
learning ruby / ror so bare with me.

thanks!

Levi


There are several of other ways to mix in your code into CE's models.
Taking User.rb as an example, you could:

1. Subclass the model, creating something like MyUser.rb in your
models directory.
2. Mix your code into User.rb, by writing a plugin or initializer
that adds your methods to the User.rb model dynamically (meta-
programming)
3. Other ways? (Suggestions welcome)

If you use the first method, you should check out the Rails
documentation for the 'becomes' method, which lets your switch objects
back and forth between classes. This is useful for taking the
current_user object (which is a User), and turning it into a MyUser
object.

Bruno Bornsztein

unread,
Nov 17, 2008, 10:42:41 AM11/17/08
to communi...@googlegroups.com
i need help understanding this. If i'm reading it correctly, if i want
to add a has_many to the CE user.rb, i would do something like the
following in my user.rb file:

require_dependency "#{RAILS_ROOT}/vendor/plugins/community_engine/app/
models/user.rb"
class User < ActiveRecord::Base

 has_many :myobject, :through => :user_myobject

end
This is not the strategy I use. Instead, I subclass the User model and make my additions there (i.e. class Player < User). Removing functionality is a little tricky; you can remove ActiveRecord validations pretty easily (just add your own validation callback and remove the errors), but removing other callbacks might not be so simple.

I'm trying to stay away from modifying the CE code, however, I'm not
sure it makes a difference whether i change it or override it. for
example, I've changed the shared/_admin_nav.html.haml partial to add
some new functionality. I did this by copy/pasting the view into my
project, then modifying it. This means that when CE has a new release,
and something is new in that file, I need to merge the diffs into my
new file. If that's the case, why not just modify the CE view, keeping
with the DRY idea.
That's certainly an option. On the other hand, you may not want to keep in sync with CE for certain views (application.html.haml comes to mind), so overriding makes sense in those cases.




SimianLogic

unread,
Nov 23, 2008, 4:55:09 AM11/23/08
to CommunityEngine

> This is not the strategy I use. Instead, I subclass the User model and make
> my additions there (i.e. class Player < User). Removing functionality is a
> little tricky; you can remove ActiveRecord validations pretty easily (just
> add your own validation callback and remove the errors), but removing other
> callbacks might not be so simple.

Doesn't this mean that you have to replace every instance of User in
your controllers and models with Player? It seems like a solution
where you need to replace the minimum number of files possible would
be best (though I'm at a loss for what that solution would be...).
Reply all
Reply to author
Forward
0 new messages