Модули Ruby, смешанные с использованием включения и расширения - как это работает?

Ниже приведены фрагменты кода:

Модуль: 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

Пользовательский модуль: 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

Пользовательский класс: Пользователь

    class User < ActiveRecord::Base
      include GeneralScopes
    end

Использование:

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

У меня есть следующие вопросы в контексте приведенных выше фрагментов кода:

1) включить(ИМЯ_МОДУЛЯ(ов)). include — это закрытый метод экземпляра Module, и этот метод вызывается self неявно. Так что же представляет self в теле класса? В экземпляре класса метод self представляет текущий объект. В классе метод класса self представляет объект Class класса.

Как работает этот синтаксис include <MODULE_NAME> из тела класса?

2) расширить (ИМЯ_МОДУЛЯ (ов))

Цитата из «Язык программирования Ruby» Дэвида Фланагана и Юкихиро Мацумото (Орейли), Глава 7. Классы и модули

Object.extend. Этот метод превращает методы экземпляра указанного модуля или модулей в одноэлементные методы объекта-приемника. (И если объект-приемник является экземпляром класса, то методы получателя становятся методами класса этого класс)

Я не могу понять это в контексте моего модуля GeneralScopes, показанного выше.

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

Я предполагаю, что модуль GeneralScopes вызывает метод экземпляра модуля ActiveSupport::Concern "included(base = nil, &block)". Как работает этот вызов метода экземпляра из тела модуля? Какой объект выступает в качестве приемника, когда

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

выполняется?

4) Метод экземпляра модуля ActiveSupport::Concern "включен (база = nil, &block)"

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

Здесь используется super. Этот super работает в контексте класса User или модуля ActiveSupport::Concern? К какому классу переходит элемент управления при выполнении super?

Было бы очень полезно, если бы кто-нибудь помог мне понять выполняемый поток управления или указал мне на любые соответствующие ресурсы, объясняющие концепции, связанные с вопросами, которые у меня есть.


person Jignesh Gohel    schedule 28.06.2012    source источник


Ответы (1)


1) include делает методы модуля доступными как методы экземпляра класса, тогда как extend делает методы модуля доступными как методы класса. Таким образом, self относится либо к экземпляру, либо к классу:

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 делает методы ActiveSupport::Concern доступными для GeneralScopes, чтобы вы могли вызвать метод included.

3) Блок назначается @_included_block и оценивается из append_features (base.class_eval(&@_included_block)), который вызывается, когда GeneralScopes включается пользователем. Из документов:

Когда этот модуль включается в другой, Ruby вызывает append_features в этом модуле, передавая ему принимающий модуль в mod.

4) super относится к родителю ActiveSupport::Concern, поэтому included Module а> называется.

person Stefan    schedule 28.06.2012