24

我有一个 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"]

现在,我想将这部分提取到一个单独的模块中,该模块可以包含在我的其他 mixin 中。所以,我想出了以下内容:

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

我错过了什么?

4

4 回答 4

34
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]
于 2016-03-07T08:02:39.567 回答
2

实际上,您的模块扩展模块有效。问题出在您的测试中:当您使用 随机创建一个未命名的类时Class.new,您忘记包含MyModule. 作为旁注,您可以将只读访问器用于包含该模块的类并使用有用的Module#attr_reader方法。

于 2011-03-16T00:05:26.267 回答
1

您可能应该使用 extend 而不是 include,因为前者添加了类级别的方法,而后者 - 实例级别的方法(为什么您可以访问@classes)。

试试这个:

module MyModule
  extend ListIncludedClasses::ClassMethods
end
于 2010-10-12T18:59:44.570 回答
1

对 Cary 的回答的一个警告(这很棒!)是它只会选择已经被 VM 评估过的类。

因此,如果您在开发 Rails 控制台之类的设置中运行该代码,则需要require在检查模块是否已包含之前明确显示您感兴趣的文件。

你可以这样做:

Dir[Rails.root.join("app/models/**/*.rb")].each { |f| require f }
于 2021-08-17T19:08:20.743 回答