我有一个类,它有许多模块,它们根据一些运行时标准与它混合在一起。
我希望能够获得已混入此类的模块列表。你怎么能这样做?
更新
所以当我说类时我指的是对象,因为它是在运行时使用以下方式扩展的对象:
obj.extend(MyModule)
obj.included_modules和obj.ancestors不存在,因此您无法从那里获取混合的模块。
答案 0 :(得分:39)
尝试:
MyClass.ancestors.select {|o| o.class == Module }
例如:
>> Array.ancestors.select {|o| o.class == Module}
=> [Enumerable, Kernel]
更新
要在运行时将模块混合到对象实例中,您需要检索实例的本征类。在Ruby中没有干净的方法可以做到这一点,但是一个相当常见的习语如下:
(class << obj; self; end).included_modules
如果你发现自己经常使用它,你可以通常使用它:
module Kernel
def eigenclass
class << self
self
end
end
end
然后解决方案是:
obj.eigenclass.included_modules
答案 1 :(得分:39)
这可能是一个更好的主意:
MyClass.included_modules
irb(main):001:0> Array.included_modules
=> [Enumerable, Kernel]
答案 2 :(得分:22)
如果您正在寻找整个列表,Swanand's answer是您最好的选择。
另一方面,如果您想检查某个类是否包含特定模块,<
运算符是您的朋友:
module Foo
end
class Bar
include Foo
end
Bar < Foo
# => true
答案 3 :(得分:19)
obj.singleton_class.included_modules
如果你想检查是否包含模块:
obj.singleton_class.include? MyModule
答案 4 :(得分:6)
这是查看某个模块是否已被包含或扩展的另一种有效方法。
正如其他人所提到的,您可以通过检查ruby中所有类中存在的included_modules
类方法来确定模块是否包含在类中。
因此,如果您有一个名为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)
为什么这样做?引用这本书Eloquent Ruby, by Russ Olsen:
当你将一个模块混合到一个类中时,Ruby会稍微重新编写一个类层次结构,将该模块作为该类的一个伪超类插入。
这也意味着另一种确定模块是否已包含进入您的类的方法是做这样的事情(尽管我仍然更喜欢included_modules
方法):
# will also return true if MyClass.include(Comparable)
MyClass.new.kind_of?(Comparable)
答案 5 :(得分:3)
只有类可以使用故事方法,当您向实例添加方法时,您实际上是将它添加到对象元类中。您正在寻找的模块将在此元类祖先列表中。
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]
答案 6 :(得分:2)
我喜欢@AlexParamonov的解决方案,但我玩了一遍,发现它的效果也很好:
obj.class.include? MyModule
MyClass.include? MyModule
http://www.ruby-doc.org/core-2.1.2/Module.html#method-i-include-3F