13

I have a couple of methods I'd like to share between models rather than copying 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!

Yuval Karmi
  • 26,277
  • 39
  • 124
  • 175

5 Answers5

16

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!
Josh Delsman
  • 3,022
  • 1
  • 18
  • 28
  • 1
    dude. It's all about the lib dir. I've started to put more code there and it is just convenient place to organize things. – JP Silvashy Mar 01 '10 at 06:12
4

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?

Community
  • 1
  • 1
Ben Morris
  • 377
  • 2
  • 12
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.

2

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
marklai
  • 2,030
  • 1
  • 14
  • 14
  • `Turn those methods into helper methods that both models have access to` - I thought models didn't have access to helper methods. How is that possible (seems like the cleanest method for what I'm trying to do) – Yuval Karmi Feb 28 '10 at 23:30
  • You're right. I was thinking utility methods, not "helper methods" in the rails sense. So the mixin approach would be more useful than utility methods. – marklai Mar 01 '10 at 00:16
0

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.

Matt
  • 5,328
  • 2
  • 30
  • 43
  • In Rails 3 this is even easier—you can replace `def self.included(base) base.instance_eval` with `extend ActiveSupport::Concern included`. – Josh Coady Oct 17 '13 at 23:42