Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails extending ActiveRecord::Base

People also ask

What is ActiveRecord in Ruby on Rails?

Active Record is the M in MVC - the model - which is the layer of the system responsible for representing business data and logic. Active Record facilitates the creation and use of business objects whose data requires persistent storage to a database.

What is ActiveRecord :: Base in Rails?

ActiveRecord::Base indicates that the ActiveRecord class or module has a static inner class called Base that you're extending.


There are several approaches :

Using ActiveSupport::Concern (Preferred)

Read the ActiveSupport::Concern documentation for more details.

Create a file called active_record_extension.rb in the lib directory.

require 'active_support/concern'

module ActiveRecordExtension

  extend ActiveSupport::Concern

  # add your instance methods here
  def foo
     "foo"
  end

  # add your static(class) methods here
  class_methods do
    #E.g: Order.top_ten        
    def top_ten
      limit(10)
    end
  end
end

# include the extension 
ActiveRecord::Base.send(:include, ActiveRecordExtension)

Create a file in the config/initializers directory called extensions.rb and add the following line to the file:

require "active_record_extension"

Inheritance (Preferred)

Refer to Toby's answer.

Monkey patching (Should be avoided)

Create a file in the config/initializers directory called active_record_monkey_patch.rb.

class ActiveRecord::Base     
  #instance method, E.g: Order.new.foo       
  def foo
   "foo"
  end

  #class method, E.g: Order.top_ten        
  def self.top_ten
    limit(10)
  end
end

The famous quote about Regular expressions by Jamie Zawinski can be re-purposed to illustrate the problems associated with monkey-patching.

Some people, when confronted with a problem, think “I know, I'll use monkey patching.” Now they have two problems.

Monkey patching is easy and quick. But, the time and effort saved is always extracted back sometime in the future; with compound interest. These days I limit monkey patching to quickly prototype a solution in the rails console.


You can just extend the class and simply use inheritance.

class AbstractModel < ActiveRecord::Base  
  self.abstract_class = true
end

class Foo < AbstractModel
end

class Bar < AbstractModel
end

You can also use ActiveSupport::Concern and be more Rails core idiomatic like:

module MyExtension
  extend ActiveSupport::Concern

  def foo
  end

  module ClassMethods
    def bar
    end
  end
end

ActiveRecord::Base.send(:include, MyExtension)

[Edit] following the comment from @daniel

Then all your models will have the method foo included as an instance method and the methods in ClassMethods included as class methods. E.g. on a FooBar < ActiveRecord::Base you will have: FooBar.bar and FooBar#foo

http://api.rubyonrails.org/classes/ActiveSupport/Concern.html


With Rails 4, the concept of using concerns to modularize and DRY up your models has been in highlights.

Concerns basically allow you to group similar code of a model or across multiple models in a single module and then use this module in the models. Here is a example:

Consider a Article model, a Event model and a Comment Model. A article or A event has many comments. A comment belongs to either article or event.

Traditionally, the models may look like this:

Comment Model:

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end

Article Model:

class Article < ActiveRecord::Base
  has_many :comments, as: :commentable 

  def find_first_comment
    comments.first(created_at DESC)
  end

  def self.least_commented
   #return the article with least number of comments
  end
end

Event Model

class Event < ActiveRecord::Base
  has_many :comments, as: :commentable 

  def find_first_comment
    comments.first(created_at DESC)
  end

  def self.least_commented
   #returns the event with least number of comments
  end
end

As we can notice, there is a significant piece of code common to both Event and Article Model. Using concerns we can extract this common code in a separate module Commentable.

For this create a commentable.rb file in app/model/concerns.

module Commentable
    extend ActiveSupport::Concern

    included do 
        has_many :comments, as: :commentable 
    end

    # for the given article/event returns the first comment
    def find_first_comment
        comments.first(created_at DESC)
    end

    module ClassMethods     
        def least_commented
           #returns the article/event which has the least number of comments
        end
    end 
end

And Now your models look like this :

Comment Model:

    class Comment < ActiveRecord::Base
      belongs_to :commentable, polymorphic: true
    end

Article Model:

class Article < ActiveRecord::Base
    include Commentable
end

Event Model

class Event < ActiveRecord::Base    
    include Commentable
end

One point I will like to highlight while using Concerns is that Concerns should be used for 'domain based' grouping rather than 'technical' grouping. For example, a domain grouping is like 'Commentable', 'Taggable' etc. A technical based grouping will be like 'FinderMethods', 'ValidationMethods'.

Here is a link to a post that I found very useful for understanding concerns in Models.

Hope the writeup helps :)