我有一个mixin,我希望得到一个包含它的所有类的列表。在mixin模块中,我做了以下事情:
module MyModule
def self.included(base)
@classes ||= []
@classes << base.name
end
def self.classes
@classes
end
end
class MyClass
include MyModule
end
这非常有效:
> MyModule.classes #=> nil
> MyClass.new #=> #<MyClass ...>
> MyModule.classes #=> ["MyClass"]
现在,我想将这部分提取到一个单独的模块中,该模块可以包含在我的其他mixins中。所以,我想出了以下内容:
module ListIncludedClasses
def self.included(base)
p "...adding #{base.name} to #{self.name}.classes"
@classes ||= []
@classes << base.name
base.extend(ClassMethods)
end
def self.classes
@classes
end
module ClassMethods
def included(module_base)
p "...adding #{module_base.name} to #{self.name}.classes"
@module_classes ||= []
@module_classes << module_base.name
super(module_base)
end
def classes
@module_classes
end
end
end
module MyModule
include ListIncludedClasses
end
这不起作用,因为从ListIncludedClasses添加到MyModule的#included(module_base)方法永远不会运行。有趣的是,它确实成功地将#classes添加到MyModule。
> MyModule.classes #=>
"...adding Rateable to ListIncludedClasses.classes"
=> nil
> ListIncludedClasses #=> ["MyModule"]
> MyClass.new #=> #<MyClass ...>
# ^^ THIS SHOULD BE ADDING MyClass TO MyModule.classes ^^
> MyModule.classes #=> nil
我错过了什么?
答案 0 :(得分:17)
module MyMod; end
class A; include MyMod; end
class B < A; end
class C; end
ObjectSpace.each_object(Class).select { |c| c.included_modules.include? MyMod }
#=> [B, A]
答案 1 :(得分:2)
实际上,您的模块扩展模块可以正常工作。问题在于您的测试:当您使用Class.new
创建随机未命名的类时,您忘记包含MyModule
。作为旁注,您可以为包含该模块的类使用只读访问器,并使用有用的Module#attr_reader
方法。
答案 2 :(得分:1)
你可能应该使用extend而不是include,因为前者添加了类级方法,而后者 - 实例级方法(为什么你有权访问@classes
)。
试试这个:
module MyModule
extend ListIncludedClasses::ClassMethods
end