Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

self.included – including class methods from a module in Ruby

I read this post: Ruby modules - included do end block – but still don't get when you would use the self.included do ... end block in a module.

The post says that code in the block will be ran when you include the module, but what's the point of that if a module's sole purpose is to be included? Wouldn't that code need to be run anyway? That block doesn't need to be there in order for that code to be run, right?

What would be the difference between the two below:

module M
  def self.included(base)
    base.extend ClassMethods
    base.class_eval do
      scope :disabled, -> { where(disabled: true) }
    end
  end

  module ClassMethods
    ...
  end
end

vs.

module M
  def self.some_class_method
     ...
  end

  scope :disabled, -> { where(disabled: true) }
end
like image 968
stackjlei Avatar asked Jul 14 '17 17:07

stackjlei


Video Answer


2 Answers

module M
  # self.included is the hook which automatically runs when this module is included
  def self.included(base)
    puts 'this will be printed when this module will be included inside a class'
    base.extend ClassMethods
    base.class_eval do
      scope :disabled, -> { where(disabled: true) }
    end
  end

  def print_object_class
    self.class.name # here self will be the object of class which includes this module
  end

  module ClassMethods
    def print_class_name
      self.name # Here self would be the class which is including this module
    end
  end
end

I tried to modify the above module to help you understand how a module(concern) can be helpful in code reusability

self.included is a hook which runs automatically when a module is included inside a class.

any method declare in the module ClassMethods will become class methods for the class including this module

any method declare outside the module ClassMethods will become instance methods for the class including this module

For Ex suppose there is a class Product and you have included the module in it

class Product < ActiveRecord::Base
  include M
  puts 'after including M'
end

If you try this example in you rails console you will notice that as soon as the module M gets included in class Product included hook of module run and

this will be printed when this module will be included inside a class this is printed on console

after that after including M this will be printed on the console.

Also you can try following commands

Product.disabled # a scope with name 'disabled' is avaialble because of including the module M
Product.print_class_name # Outputs => 'Product' This method is available to class with the help of module M
Product.new.print_object_class #Outputs => 'Product'

It also offers reusability, include this module M in any class and that class gets access to all those methods described in the module.

Whereas your second example is a mere example of basic module

module N
  def self.abc
    puts 'basic module'
  end
end

Now abc method define in module can be accessible using only this module

N.abc # outputs 'basic module'

class Product < ActiveRecord::Base
  include  N
end

Product.abc #raises exception, No method found on class Product Product.new.abc #raises exception, No method found on object of class Product

I hope this may help you understand the concept of module better. Please let me know if you still have any doubts.

like image 20
Nimish Gupta Avatar answered Sep 28 '22 02:09

Nimish Gupta


What's the difference between the two examples?

The first code block adds the class methods in ClassMethods to the including class and calls the scope method on it as well. The second one does neither of these things and will result in a NoMethodError because the module has no scope class method. self.some_class_method will not be available on the including class once the module is included.

For the full story on how module inclusion works in Ruby, read my answer here:
Inheriting class methods from modules / mixins in Ruby

What's the point of self.included if a module's sole purpose is to be included?

Inclusion is not the only purpose of modules. They are also used for other things such as namespacing or simply storing various class methods that are then callable on the module itself.

Why doesn't Ruby include class methods automatically?

Theoretically Ruby could automatically add all class methods defined in a module to the including class, but in practice that would be a bad idea, because you would not get to choose anymore whether you want to include class methods — all class methods would be included every time, whether or not they are intended to be included. Consider this example:

module M
  def self.class_method
    "foo"
  end

  def self.configure_module
    # add configuration for this module
  end
end

class C
  include M
end

Here, the configure_module method is obviously not supposed to be added to C, as its purpose is to set the configuration for the module object. Yet, if we had auto-inclusion for class methods, you would not be able to prevent it from being included.

But all instance methods are already included! How is that okay then?

Instance methods in a module are only really useful if they are included into a class, since modules cannot have instances, only classes can. So in a module every instance method is expected to be included somewhere to work.

A "class" method on a module is different, because it can be called on the module itself, so it can be used just fine regardless of whether it's also added to the including class. That is why it is better that you have a choice there.

like image 137
Mate Solymosi Avatar answered Sep 28 '22 02:09

Mate Solymosi