Search code examples
ruby-on-railsrubymetaprogrammingmixins

extending class with modules containing class methods of the same name


I extended my ActiveRecord class by the notable pattern described here and here. I can't find a way to safely make my new included class method (extend_with_mod_a and extend_with_mod_b) call their own class method (bar)

require 'active_record'

module ModA
  extend ActiveSupport::Concern

  module ClassMethods

    def extend_with_mod_a
      puts "extending #{self} with ModA"
      bar
    end

    def bar
      puts "this is method bar of ModA"
    end

  end

end


module ModB
  extend ActiveSupport::Concern

  module ClassMethods

    def extend_with_mod_b
      puts "extending #{self} with ModB"
      bar
    end

    def bar
      puts "this is method bar of ModB"
    end

  end

end

ActiveRecord::Base.send :include, ModA
ActiveRecord::Base.send :include, ModB

class TestModel < ActiveRecord::Base

  extend_with_mod_a
  extend_with_mod_b

end

Output is

extending with ModA
this is method bar of ModB
extending with ModB
this is method bar of ModB

Of course which bar method gets called depends on ActiveRecord include calls order. I would like to use something like ModA::ClassMethods.bar in extend_with_mod_a method definition


Solution

  • module ModA
      extend ActiveSupport::Concern
    
      module ClassMethods
        def bar
          puts "this is method bar of ModA"
        end
    
        # grab UnboundMethod of ModA::ClassMethods::bar
        a_bar = instance_method(:bar)
    
        # using define_method to capture a_bar
        define_method :extend_with_mod_a do
          puts "extending #{self} with ModA"
          # invoke ModA::ClassMethods::bar properly bound to the class being extended/included with ModA
          a_bar.bind(self).call
        end
      end
    end