49

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?

Ed de Almeida
  • 3,675
  • 4
  • 25
  • 57
Andrey Deineko
  • 51,333
  • 10
  • 112
  • 145

2 Answers2

101

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.

Simone Carletti
  • 173,507
  • 49
  • 363
  • 364
  • 2
    everything sounds logic, but still.. Lets look at example. What if `second_class_method` implemented as `raise "When am I raised?"`, when will it be raised? – Andrey Deineko Jan 18 '15 at 14:05
  • 6
    It will be raised almost immediately, as soon as the includer will include your module. That will actually cause the ruby code parsing process and will cause the interpreter to immediately crash. You can try it yourself. – Simone Carletti Jan 18 '15 at 14:32
29

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
  extend ActiveSupport::Concern
 ...
  included do
    validates :attr, presence: true
    has_many :groups
  end
 ...
end
patrickbadley
  • 2,510
  • 2
  • 29
  • 30
Nermin
  • 6,118
  • 13
  • 23
  • so having validations creates restrictions on where the module can be included? – stackjlei Jul 14 '17 at 17:29
  • why wouldn't it work if you set scopes outside of the include do block? it still will be set in the context of the class that included the module, right? – stackjlei Jul 14 '17 at 17:41
  • 4
    it won't be "be set in the context of the class that included the module". `validates` is a method. any method you run inside a module's body is run in the context of that module at the "load time". what you gonna get is "NoMethodError" because for a module, `validates` doesn't mean much – meandre Aug 13 '19 at 09:20