ruby-on-railsrubyruby-on-rails-3metaprogrammingmeta-search

How to dynamically add class methods to Rails Models using module


I have this code on model class to add search methods to be used by meta_search gem:

class Model

  self.def created_at_lteq_at_end_of_day(date)
     where("created_at <= ?", DateTime.strptime(date, '%d/%m/%Y').end_of_day)
  end

  search_methods :created_at_lteq_at_end_of_day
end

This code add search methods to date field. Now, is needed to add this search method to other classes and with others fields. To achieve this, this module was created:

lib/meta_search/extended_search_methods.rb

module MetaSearch
  module ExtendedSearchMethods
    def self.included(base)
      base.extend ClassMethods
    end


    module ClassMethods
      def add_search_by_end_of_day(field, format)

        method_name = "#{field}_lteq_end_of_day"

        define_method method_name do |date|
          where("#{field} <= ?", DateTime.strptime(date, format).end_of_day) 
        end

        search_methods method_name
      end
    end
  end
end


class Model
   include MetaSearch::ExtendedSearchMethods
   add_search_by_end_of_day :created_at, '%d/%m/%Y'
end

After add the module, this errors starts to rise:

undefined method `created_at_lteq_end_of_day' for #<ActiveRecord::Relation:0x007fcd3cdb0e28>

Other solution:

change define_method to define_singleton_method


Solution

  • ActiveSupport provides a pretty idiomatic and cool way of doing that, ActiveSupport::Concern :

    module Whatever
        extend ActiveSupport::Concern
    
        module ClassMethods
            def say_hello_to(to)
                puts "Hello #{to}"
            end
        end
    end
    
    class YourModel
        include Whatever
    
        say_hello_to "someone"
    end
    

    See the API doc. Although it is not directly related to your question, the included method is incredibly useful for models or controllers (scopes, helper methods, filters, etc), plus ActiveSupport::Concern handles dependencies between modules for free (both as in freedom and as in beer).