Как мне сказать, какие модули были смешаны в классе? - PullRequest
33 голосов
/ 25 августа 2009

У меня есть класс с несколькими модулями, которые смешаны с ним на основе некоторых критериев времени выполнения.

Я хочу получить список модулей, которые были добавлены в этот класс. Как ты можешь это сделать?

UPDATE

Поэтому, когда я сказал класс, я имел в виду объект, поскольку он является объектом, который расширяется во время выполнения с помощью:

obj.extend(MyModule)

obj.included_modules и obj.ancestors не существует, поэтому вы не можете получить модули, которые были смешаны оттуда.

Ответы [ 7 ]

39 голосов
/ 25 августа 2009

Это может быть лучшей идеей:

MyClass.included_modules
irb(main):001:0> Array.included_modules
=> [Enumerable, Kernel]
39 голосов
/ 25 августа 2009

Попробуйте:

MyClass.ancestors.select {|o| o.class == Module }

например:

>> Array.ancestors.select {|o| o.class == Module}
=> [Enumerable, Kernel]

UPDATE

Чтобы объединить модули в экземпляр объекта во время выполнения, вам нужно получить собственный класс экземпляра. Нет ясного способа сделать это в Ruby, но довольно распространенная идиома заключается в следующем:

(class << obj; self; end).included_modules

Если вы часто этим пользуетесь, вы можете сделать его общедоступным:

module Kernel
  def eigenclass
    class << self
      self
    end
  end
end

и тогда решение будет таким:

obj.eigenclass.included_modules
22 голосов
/ 25 августа 2009

Если вы ищете весь список, ответ Суонанда - ваш лучший выбор.

Если, с другой стороны, вы хотите проверить, включает ли класс определенный модуль, оператор < является вашим другом:

module Foo
end

class Bar
  include Foo
end

Bar < Foo
# => true
19 голосов
/ 19 июля 2013
obj.singleton_class.included_modules

А если вы хотите проверить, включен ли модуль:

obj.singleton_class.include? MyModule
6 голосов
/ 16 октября 2012

Вот еще один эффективный способ узнать, был ли модуль включен или расширен классом.

Как уже упоминалось, вы можете определить, включен ли модуль в класс, проверив метод класса included_modules, который существует во всех классах в ruby.

Итак, если у вас есть класс с именем MyClass, и вы хотите посмотреть, включает ли он модуль Comparable, вы можете сделать что-то вроде этого:

# will return true if MyClass.include(Comparable)
MyClass.included_modules.include?(Comparable)

Если вы хотите определить, имеет ли ваш класс расширенный модуль ActiveRecord::Querying, как это делают все классы моделей rails, вы можете использовать это:

# will return true if MyClass.extend(ActiveRecord::Querying)
MyClass.kind_of?(ActiveRecord::Querying)

Почему это работает? Цитирую книгу Красноречивый Рубин, Расс Олсен :

Когда вы смешиваете модуль в классе, Ruby немного переписывает иерархию классов, вставляя модуль как своего рода псевдо суперкласс класса.

Это также означает, что другой способ определить, был ли модуль включен в ваш класс, - сделать что-то подобное (хотя я все еще предпочитаю метод included_modules):

# will also return true if MyClass.include(Comparable)
MyClass.new.kind_of?(Comparable)
3 голосов
/ 25 августа 2009

Только классы могут рассказывать методы, и когда вы добавляете метод к экземпляру, вы фактически добавляете его в метакласс объектов. Модуль, который вы ищете, будет в этом списке предков метакласса.

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]
2 голосов
/ 21 марта 2014

Мне нравится решение @ AlexParamonov, но я поиграл и обнаружил, что это работает так же хорошо:

obj.class.include? MyModule

MyClass.include? MyModule

http://www.ruby -doc.org / ядро-2.1.2 / Module.html # метод-я-включать-3F * +1011 *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...