Ruby modules - included do end block

There is a module MyModule:

module MyModule

  extend ActiveSupport::Concern

  def first_method
  end

  def second_method
  end

  included do
    second_class_method
  end

  module ClassMethods
    def first_class_method
    end

    def second_class_method
    end
  end
end

When some class includes this module, it will have 2 methods exposed as instance methods (first_method and second_method) and 2 class methods (first_class_method and second_class_method) - it is clear.

It is said, that

included block will be executed within the context of the class that is including the module.

What does it mean exactly? Meaning, when exactly would this method (second_class_method) be executed?


Solution 1:

Here's a practical example.

class MyClass
  include MyModule
end

When you will include the module in a class, the included hook will be called. Therefore, second_class_method will be called within the scope of Class.

What happens here is

  1. first_method and second_method are included as instance-methods of MyClass.

    instance = MyClass.new
    instance.first_method
    # => whatever returned value of first_method is
    
  2. The methods of ClassMethods are automatically mixed as class methods of MyClass. This is a common Ruby pattern, that ActiveSupport::Concern encapsulates. The non-Rails Ruby code is

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

    Which results in

    MyClass.this_is_a_class_method
    

    or in your case

    MyClass.first_class_method
    
  3. included is a hook that is effectively to the following code

    # non-Rails version
    module MyModule
      def self.included(base)
        base.class_eval do
          # somecode
        end
      end
    end
    
    # Rails version with ActiveSupport::Concerns
    module MyModule
      included do
        # somecode
      end
    end
    

    It's mostly "syntactic sugar" for common patterns. What happens in practice, is that when you mix the module, that code is executed in the context of the mixer class.

Solution 2:

included is called when you include module into a class, it is used for defining relations, scopes, validations, ... It gets called before you even have created object from that class.

example

module M
...
  included do
    validates :attr, presence: true
    has_many :groups
  end
 ...
end