3
votes

Following are the code snippets:

Module: ActiveSupport::Concern

    module ActiveSupport
      module Concern
        def self.extended(base)
          base.instance_variable_set("@_dependencies", [])
        end

        def append_features(base)
          if base.instance_variable_defined?("@_dependencies")
            base.instance_variable_get("@_dependencies") << self
            return false
          else
            return false if base < self
            @_dependencies.each { |dep| base.send(:include, dep) }
            super
            base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
            base.send :include, const_get("InstanceMethods") if const_defined?("InstanceMethods")
            base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block")
          end
        end

        def included(base = nil, &block)
          if base.nil?
            @_included_block = block
          else
            super
          end
        end
      end
    end

Custom Module: GeneralScopes

    module GeneralScopes
        extend ActiveSupport::Concern

        included do
          scope :reverse_order_by_date, :order => "date DESC"
          scope :recent, lambda { |count| reverse_order_by_date.limit(count) }
        end
    end

Custom Class: User

    class User < ActiveRecord::Base
      include GeneralScopes
    end

Usage:

    User.recent(5) => Prints the recent five User instances available in database

I have following questions in context of above shown code snippets:

1) include(MODULE_NAME(s)). include is a private instance method of Module and this method is invoked by self implicitly. So what does self represent in the class body? In the class's instance method self represents the current object. In the class's class method self represents the class's Class object.

How does this include <MODULE_NAME> syntax from class body works?

2) extend(MODULE_NAME(s))

Quoted from "The Ruby Programming Language" by David Flanagan and Yukihiro Matsumoto (Oreilly), Chapter 7 Classes and Modules

Object.extend.This method makes the instance methods of the specified module or modules into singleton methods of the receiver object.(And if the receiver object is a Class instance, then the methods of the receiver become the class methods of that class)

I am unable to understand this in context of my GeneralScopes module shown above.

3)

    module GeneralScopes
        extend ActiveSupport::Concern

        included do
          scope :reverse_order_by_date, :order => "date DESC"
          scope :recent, lambda { |count| reverse_order_by_date.limit(count) }
        end
    end

module GeneralScopes I assume is invoking the ActiveSupport::Concern module's instance method "included(base = nil, &block)".How does this invocation of an instance method from module body work? Which object acts as the receiver when

     included do
          scope :reverse_order_by_date, :order => "date DESC"
          scope :recent, lambda { |count| reverse_order_by_date.limit(count) }
     end

is executed?

4) ActiveSupport::Concern module's instance method "included(base = nil, &block)"

        def included(base = nil, &block)
          if base.nil?
            @_included_block = block
          else
            super
          end
        end 

Here super is used.This super works in context of User class or ActiveSupport::Concern module? To which class the control goes when super is executed?

It would be a great help if anybody can make me understand the control flow executed or point me to any relevant resources explaining the concepts related to questions I have.

1

1 Answers

5
votes

1) include makes the module's methods available as instance methods of the class whereas extend makes the module's methods available as class methods. So self refers to either the instance or the class:

module M
  def foo
    puts self
  end
end

class A
  include M
end

A.new.foo # => #<A:0x007fcefa18e270> ('self' refers to the instance)

class B
  extend M
end

B.foo # => B ('self' refers to the class)

2) extend ActiveSupport::Concern makes ActiveSupport::Concern's methods available to GeneralScopes so you can call the included method.

3) The block is assigned to @_included_block and evaluated from within append_features (base.class_eval(&@_included_block)) which is called when GeneralScopes is included by User. From the docs:

When this module is included in another, Ruby calls append_features in this module, passing it the receiving module in mod.

4) super refers to ActiveSupport::Concern's parent, so Module's included is called.