Search code examples
ruby

Can I add class methods and instance methods from the same module?


Newbie question:

I know how include and extend work, what I am wondering is if there is a way to get both class and instance methods from a single module?

This is how I do it with two modules:

module InstanceMethods
    def mod1
        "mod1"
    end
end

module ClassMethods
    def mod2
        "mod2"
    end
end

class Testing
    include InstanceMethods
    extend ClassMethods 
end

t = Testing.new
puts t.mod1
puts Testing::mod2

Thanks for taking your time ...


Solution

  • There is a common idiom for that. It makes use of included object model hook. This hook gets invoked every time when a module is included to a module/class

    module MyExtensions
      def self.included(base)
        # base is our target class. Invoke `extend` on it and pass nested module with class methods.
        base.extend ClassMethods
      end
    
      def mod1
        "mod1"
      end
    
      module ClassMethods
        def mod2
          "mod2"
        end
      end
    end
    
    class Testing
      include MyExtensions
    end
    
    t = Testing.new
    puts t.mod1
    puts Testing::mod2
    # >> mod1
    # >> mod2
    

    I personally like to group instance method to a nested module as well. But this is less accepted practice, as far as I know.

    module MyExtensions
      def self.included(base)
        base.extend ClassMethods
        base.include(InstanceMethods)
    
        # or this, if you have an old ruby and the line above doesn't work
        # base.send :include, InstanceMethods
      end
    
      module InstanceMethods
        def mod1
          "mod1"
        end
      end
    
      module ClassMethods
        def mod2
          "mod2"
        end
      end
    end