How to share code across models? (Rails 2.3)
I have a couple of methods I'd like to share between models rather than copy开发者_开发百科ing and pasting them. In a controller, I can do this by putting the methods in application_controller.rb which automatically gets included in every controller. Is there a similar thing for models?
Thanks!
You can create a file called functionality.rb
or something similar in the lib
directory of your Rails app. Make the file named after the module to autoload it when Rails starts. For example, if I wanted to add flagging to multiple models, I'd create a file called lib/flagging.rb
, and it would look like this:
module Flagging
# Flags an object for further review
def flag!
self.update_attribute(:flagged, true)
end
# Clears all flags on an object
def deflag!
self.update_attribute(:flagged, false)
end
end
In every model I wanted to add this functionality to, I'd do the following:
class Foo < ActiveRecord::Base
include Flagging
end
I'd then be able to do something like:
foo = Foo.create
foo.flag!
The top solution above worked for me. In Rails 3 I found I also had to update the application.rb file with: config.autoload_paths += Dir["#{config.root}/lib/**/"]
as answered here: Best way to load module/class from lib folder in Rails 3?
You can either a) do something to similar to application_controller and create a model class from which others can subclass or b) use a module.
There are a number of ways you can share methods between 2 model classes.
- Use inheritance if the models are related to each other, e.g. a shared parent class that contains the methods, or one subclasses the other that contains the methods.
- Use modules/mixins that can be shared among multiple models
To add to the answer by Josh, sometimes you might want to share some code defining a polymorphic association. You can't just put the has_many
method call in your module, because you will get an error, for example in a module called Votable:
undefined method `has_many' for Voteable:Module (NoMethodError)
So instead, you need to use the self.included(base)
method and base.instance_eval
. Here's an example with my Voteable
module:
module Voteable
def self.included(base)
base.instance_eval do
has_many :votes, as: :voteable
has_many :voters, through: :votes
end
end
def voted_up_by?(user)
user and votes.exists?(value: 1, voter_id: user)
end
def voted_down_by?(user)
user and votes.exists?(value: -1, voter_id: user)
end
end
Now you can include Voteable
in models that will have that behavior. This will execute the self.included(base)
method and it will define the association on the including class.
精彩评论