Как узнать, какие модули были добавлены в класс?
У меня есть класс, который имеет несколько модулей, которые смешиваются с ним на основе некоторых критериев времени выполнения.
Я хочу получить список того, какие модули были смешаны с этим классом. Как вы можете это сделать?
ОБНОВЛЕНИЕ
Итак, когда я сказал класс, я имел в виду объект, поскольку он является объектом, который расширяется во время выполнения, используя:
obj.extend(MyModule)
obj.included_modules и obj.ancestors не существуют, поэтому вы не можете получить модули, которые были смешаны оттуда.
Ответы
Ответ 1
Try:
MyClass.ancestors.select {|o| o.class == Module }
например:
>> Array.ancestors.select {|o| o.class == Module}
=> [Enumerable, Kernel]
ОБНОВЛЕНИЕ
Чтобы получить модули, смешанные в экземпляр объекта во время выполнения, вам нужно будет получить eigenclass экземпляра. В Ruby нет чистого способа сделать это, но достаточно распространенная идиома такова:
(class << obj; self; end).included_modules
Если вы обнаружите, что используете это много, вы можете сделать его общедоступным:
module Kernel
def eigenclass
class << self
self
end
end
end
и решение тогда:
obj.eigenclass.included_modules
Ответ 2
Это может быть лучше:
MyClass.included_modules
irb(main):001:0> Array.included_modules
=> [Enumerable, Kernel]
Ответ 3
Если вы ищете весь список, Swanand answer - ваш лучший выбор.
Если, с другой стороны, вы хотите проверить, включает ли класс определенный модуль, оператор <
является вашим другом:
module Foo
end
class Bar
include Foo
end
Bar < Foo
# => true
Ответ 4
obj.singleton_class.included_modules
И если вы хотите проверить, включен ли модуль:
obj.singleton_class.include? MyModule
Ответ 5
Вот еще один эффективный способ увидеть, был ли модуль включен или расширен каким-либо классом.
Как уже упоминалось, вы можете определить, включен ли модуль в класс, проверяя метод класса included_modules
, который существует для всех классов в ruby.
Итак, если у вас есть класс с именем MyClass
, и вы хотите посмотреть, включил ли он модуль Comparable
, вы могли бы сделать что-то вроде этого:
# will return true if MyClass.include(Comparable)
MyClass.included_modules.include?(Comparable)
Если вы хотите определить, расширил ли ваш класс модуль ActiveRecord::Querying
, как это делают все классы модели рельсов, вы можете фактически использовать это:
# will return true if MyClass.extend(ActiveRecord::Querying)
MyClass.kind_of?(ActiveRecord::Querying)
Почему это работает? Чтобы процитировать книгу Eloquent Ruby, Russ Olsen:
Когда вы смешиваете модуль в класс, Ruby немного переписывает иерархию классов, вставляя модуль как своего рода псевдо-суперкласс класса.
Это также означает, что другой способ определить, был ли модуль включен в ваш класс, - это сделать что-то вроде этого (хотя я по-прежнему предпочитаю метод included_modules
):
# will also return true if MyClass.include(Comparable)
MyClass.new.kind_of?(Comparable)
Ответ 6
Только классы могут использовать методы истории и при добавлении метода к экземпляру, который вы фактически добавляете к метаклассу объектов. Модуль, который вы ищете, будет находиться в списке предков метакласса.
module TestModule; end
obj = "test"
obj.extend(TestModule)
class Object
def metaclass
class << self; self; end
end
end
obj.metaclass.ancestors
# => [TestModule, String, Comparable, Object, Kernel, BasicObject]
Ответ 7
Мне нравится решение @AlexParamonov, но я играл и узнал, что это работает так же хорошо:
obj.class.include? MyModule
MyClass.include? MyModule
http://www.ruby-doc.org/core-2.1.2/Module.html#method-i-include-3F