Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 :).

like image 642
Andrey Deineko Avatar asked Feb 10 '23 09:02

Andrey Deineko


1 Answers

Regardless of whether you are using extend or include you are always copying over instance methods. The difference is where those instance methods live.

When you call Class#include you are "copying" all of the instance methods in the module to be instance methods in the class. It's similar to how inheritance work, and if you call Class#ancestors you'll see the module there.

When you call Object#extend you are copying all of the instance methods of the module to the object's singleton class. This is a class reserved just for this object instance that is created at runtime. This is how you get "class methods" (e.g. MyClass.hello_world); by adding them to the class's singleton. You can also do things like extend a particular object instance (e.g. s = String.new; s.extend(SomeModule); s.hello_world)

There are some other differences too. The context binding is different depending on whether you use extend or include. extend doesn't cause the module to show up in the ancestor chain while include does.

When trying to add both "class" and instance methods, one common pattern you'll see is doing things like this which uses the included callback to extend the base class with a ClassMethods module:

module MyModule
  def self.included(base)
    base.extend ClassMethods
  end

  module ClassMethods
    def hello_world
    end
  end
end

ActiveSupport::Concerns also abstracts this pattern allowing you to add both instance and "class" methods in one call.

I personally prefer having modules only work with instance methods and using singleton methods (e.g. def self.my_method) to have scoped methods (sort of like how you would use private methods). This allows consumers to use either extend or include however they want and have it work as expected.

I'm not sure if that answers your question or not, but there's some info for you

like image 68
Josh Bodah Avatar answered Feb 23 '23 11:02

Josh Bodah