Search code examples
rubypluginsincludeextend

Ruby Base.send calling it like instance method in model when using include and extend


Ruby library code

module Yaffle
  module ActsAsYaffle
    def self.included(base)
        base.extend(ClassMethods)
    end

    module ClassMethods
      def acts_as_yaffle(options = {})
        cattr_accessor :yaffle_text_field
        self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s

        include Yaffle::ActsAsYaffle::LocalInstanceMethods
      end
    end

    ...
    more code
    ...
  end
end

ActiveRecord::Base.send(:include, ActiveRecord::Acts::Taggable)

Why does when i call the acts_as_yaffle in the model, its being used like

class Hickwall < ActiveRecord::Base
   acts_as_yaffle
end

Does the ActiveRecord::Base.send(:include,...) included the ClassMethods as instance eventhough the base is extended (base.extend(ClassMethods) ? The acts_as_yaffle was declared as a ClassMethods.


Solution

  • It is a class method. Consider this example:

    class C
      def my_instance_method
        puts 'hello from instance'
      end
    
      def self.my_class_method
        puts 'hello from class'
      end
    
      my_instance_method
      # NoMethodError
    
      my_class_method
      # hello from class
    end
    

    inside class C ... end you can only call class methods, because there is no instance to send the method call to. The library code is written using a common pattern to put class methods inside a module. For better understanding, the example class I wrote above is equivalent to this:

    module M
      def my_instance_method
        puts 'hello from instance'
      end
    
      def self.included(base)
        base.extend(ClassMethods)
      end
    
      module ClassMethods
        def my_class_method
          puts 'hello from class'
        end
      end
    end