#ruby #activesupport-concern
#ruby #activesupport-concern
Вопрос:
Рассмотрим следующий код:
require 'active_support/concern'
module Inner
end
module Outer
extend ActiveSupport::Concern
included do
include Inner
end
end
class FirstClass
include Outer
end
module SecondInner
end
module SecondOuter
include SecondInner
end
class SecondClass
include SecondOuter
end
Почему порядок предков отличается для модулей, включенных через AS::Concern, по сравнению с обычным Ruby?
FirstClass.ancestors
# => [FirstClass, Inner, Outer, Object, PP::ObjectMixin, Kernel, BasicObject]
SecondClass.ancestors
# => [SecondClass, SecondOuter, SecondInner, Object, PP::ObjectMixin, Kernel, BasicObject]
Ответ №1:
ActiveSupport::Concern
не изменяет порядок поиска предков.
Если вы измените module Outer
, чтобы использовать чистый Ruby для выполнения того же, что и AS, но без AS, вы увидите, что он имеет ту же цепочку предков:
module Outer
def self.included(base)
base.send(:include, Inner)
end
end
SecondClass.ancestors
#=> [SecondClass, SecondOuter, SecondInner, Object, PP::ObjectMixin, Kernel, BasicObject]
Комментарии:
1. Я хочу сказать, что синтаксический сахар, предоставляемый AS, не влияет на поиск предков Ruby.
2. Хорошо, так почему порядок отличается в этом случае? Имеет ли это какое-то отношение к тому, когда
append_feature
вызывается в каждом случае?3.@jcm это на самом деле вполне может иметь какое-то отношение к этому, поскольку
include
вызываетModule.append_features
каждый параметр в обратном порядке.
Ответ №2:
Ответ Андрея Дейнеко послужил важной основой для понимания того, что происходит.
Что происходит, когда модули включаются в классы или другие модули? Есть две вещи, которые кажутся актуальными:
append_features
вызывается.
Когда этот модуль включается в другой, Ruby вызывает append_features в этом модуле, передавая ему принимающий модуль в mod. Реализация Ruby по умолчанию заключается в добавлении констант, методов и переменных модуля этого модуля в mod, если этот модуль еще не был добавлен в mod или один из его предков. Смотрите также Module#include .
included
вызывается
Обратный вызов вызывается всякий раз, когда получатель включен в другой модуль или класс. Это следует использовать предпочтительнее Module.append_features, если ваш код хочет выполнить какое-либо действие, когда модуль включен в другой.
Мы не можем подключиться append_features
, но мы можем определить included
в наших модулях.
module Inner
def self.included(base)
puts "including #{self} in #{base}"
end
end
module Outer
def self.included(base)
puts "including #{self} in #{base}"
base.send(:include, Inner)
end
end
module SecondOuter
include Inner
def self.included(base)
puts "including #{self} in #{base}"
end
end
class FirstClass
include Outer
end
class SecondClass
include SecondOuter
end
Разница между Outer и SecondOuter заключается в том, как Inner
используется. In Outer
, Inner
не включен, а просто включен в любой другой модуль, который включает Outer
. Тем SecondOuter
не менее, Inner
включено.
Когда приведенный выше код вставляется в консоль, на экране выводятся следующие инструкции:
including Inner in SecondOuter
including Outer in FirstClass
including Inner in FirstClass
including SecondOuter in SecondClass
Первый и четвертый операторы объясняют порядок SecondClass
предков. Inner
является предком SecondOuter
, который, в свою очередь, является предком SecondClass
. Таким образом, мы имеем
SecondOuter.ancestors
=> [SecondOuter, Inner]
SecondClass.ancestors
=> [SecondClass, SecondOuter, Inner, Object, PP::ObjectMixin, Kernel, BasicObject]
Третий и четвертый операторы показывают, почему внешний и внутренний порядок модулей меняются местами для FirstClass
предков:
Во-первых, Inner
не является предком Outer
.
Во-вторых, Outer
включается в FirstClass
before Inner
is , но Inner.included
разрешается до Outer.included
does . Это приводит к
Outer.ancestors
=> [Outer]
FirstClass.ancestors
=> [FirstClass, Inner, Outer, Object, PP::ObjectMixin, Kernel, BasicObject]
Когда мы расширяем AS::Concern и помещаем include SomeModule
оператор в included do
блок, мы фактически включаем SomeModule
аналогично тому, как Outer
это делается выше.