Search code examples
rubymoduleincludeextendself

Using extend self in module


Before voting for closing due to question duplication I want to say that my question is really simple one (not asked in above mentioned questions).

There are two modules, one defines module method using extend self, another defines mixin method.

module A
  extend self
  def module_a_meth
    "Called module_a_meth"
  end
end

module B
  def module_b_meth
    "Called module_b_meth"
 end
end

There is a class, where I both include and extend these modules:

class Test
  include A
  extend A
  include B
  extend B
end

When we includeing module, its methods become class' instance methods, when extending - class methods.

Question: it doesn't matter for class, if methods in module defined as module methods or mixin methods, right? I mean, when included - EVERY method (either module methods or mixin methods) become instance methods, and when extended - either become class methods.

If I'm wrong - where is the difference?

obj = Test.new

puts obj.module_a_meth
puts obj.module_b_meth
puts Test.module_a_meth
puts Test.module_b_meth

#=> Called module_a_meth
#=> Called module_b_meth
#=> Called module_a_meth
#=> Called module_b_meth

EDIT

Please start your answer with Yes or No, since my question implies this type of answer :).


Solution

  • First of all, regarding the actual question: No :).

    Class (or any other object) cares how methods are defined in a module you're including. Basically, method's in a module you've described are defined as mixin methods. extend self doesn't redefine methods to be a module methods, but, basically, duplicates them to both contexts.

    It's pretty much a question about how does extend work, it's just a tricky case.

    First of all, think of extend as an include in object's singleton class context. Those two definitions are equal:

    module SomeModule
      def hi
        'hi'
      end
    end
    
    class SomeClass
      extend SomeModule
    end
    
    class SomeClass
      class << self
        include SomeModule
      end
    end
    

    Given that, by using extend self in a module you're saying: Take all of the mixin methods I've defined and extend module's singleton class with them. This magic is a result of ruby's nature: an ability to re-open any definition. Here's how a verbose version of extend self would look like:

       module Module1
          def hi
            'hi'
          end
        end
    
        module Module1
          extend Module1 # which is self
    
          #### now "hi" is both here:
          # def hi; end
          #### and here:
          # class << self; def hi; end
        end
    
        Module1.hi #  => 'hi'
        class SomeClass; include Module1; end;
        SomeClass.new.hi # => 'hi'
    

    __ EDIT __

    Just a quick proof that object cares about how methods in a module are defined:

    module SomeModule
      def self.hi
        'hi'
      end
    end
    
    object = 'some string'
    class << object
      include SomeModule
    end
    object.hi # => NoMethodError: undefined method