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) включване (MODULE_NAME(s)). include е частен метод на екземпляр на Module и този метод се извиква от self имплицитно. И така, какво представлява self в тялото на класа? В екземпляра на класа методът self представлява текущия обект. В класовия метод self представлява обекта Class на класа.

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

2) разширяване (MODULE_NAME(s))

Цитирано от „Езикът за програмиране Ruby“ от Дейвид Фланаган и Юкихиро Мацумото (Oreilly), Глава 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

modul 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 "включен (база = нула, &блок)"

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

Тук се използва super. Това super работи в контекста на потребителски клас или модул ActiveSupport::Concern? Към кой клас отива управлението, когато се изпълни super?

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


person Jignesh Gohel    schedule 28.06.2012    source източник
comment
opensoul.org/blog/archives/2011/02/07/   -  person Amol Pujari    schedule 09.04.2013


Отговори (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, така че Module на included се извиква.

person Stefan    schedule 28.06.2012