Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extending ActiveRecord::Base

I'm trying to add some custom methods to ActiveRecord. I want to add a *_after and *_before scopes for every date field of a model so I can do something like this:

User.created_at_after(DateTime.now - 3.days).created_at_before(DateTime.now)

I've followed the solution explained here Rails extending ActiveRecord::Base but when I execute the rails console and try to call the methods I get an undefined method error.

Here's my code:

# config/initializers/active_record_date_extension.rb
require "active_record_date_extension"

# lib/active_record_date_extension.rb
module ActiveRecordDateExtension
  extend ActiveSupport::Concern

  included do |base|
    base.columns_hash.each do |column_name,column|
      if ["datetime","date"].include? column.type
        base.define_method("#{column_name}_after") do |date|
          where("#{column_name} > ?", date)
        end
        base.define_method("#{column_name}_before") do |date|
          where("#{column_name} < ?", date)
        end
      end
    end
  end
end
Rails.application.eager_load!

ActiveRecord::Base.descendants.each do |model|
  model.send(:include, ActiveRecordDateExtension)
end

What am I doing wrong?

like image 234
Simon Soriano Avatar asked Aug 20 '15 18:08

Simon Soriano


People also ask

What does ActiveRecord base mean?

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

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.


1 Answers

Using Rails 4.1.9 and Ruby 2.2.1, I noticed a few issues with the code above.

  1. You are comparing column.type with strings, and Rails returns symbols for that attribute.
  2. base.define_method is trying to call a private method, you can get around that with send

This is the tweaked code

module ActiveRecordDateExtension
  extend ActiveSupport::Concern

  included do |base|
    base.columns_hash.each do |column_name,column|      
      if [:datetime, :date].include? column.type              
        base.class.send(:define_method, "#{column_name}_after") do |date|
          where("#{column_name} > ?", date)
        end
        base.class.send(:define_method, "#{column_name}_before") do |date|
          where("#{column_name} < ?", date)
        end
      end
    end
  end
end
like image 184
yez Avatar answered Sep 20 '22 18:09

yez