获取包含模块的类列表

Posted

技术标签:

【中文标题】获取包含模块的类列表【英文标题】:Getting a list of classes that include a module 【发布时间】:2011-04-24 11:44:39 【问题描述】:

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

我错过了什么?

【问题讨论】:

别忘了表明我回答了你的问题! 你试过这个吗:***.com/questions/3527445/… 【参考方案1】:
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]

【讨论】:

【参考方案2】:

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

【讨论】:

感谢您的回复。是的,当我现在尝试时它有效,我不确定我当时在做什么。我想我在 Rails 项目中拥有它,而不是孤立它,所以谁知道呢。并不是我忘记了 MyClassinclude 的定义,只是我没有在上面的示例中重新键入它。否则,MyClass.new 会抛出 uninitialized constant MyClassModule#attr_reader 也创建了实例方法,所以是的,我可以在 ClassMethods 中使用它,然后在 ListIncludedClassesclass &lt;&lt; self 块中使用它。它更高效,因此在生产中这是可行的方法。【参考方案3】:

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

试试这个:

module MyModule
  extend ListIncludedClasses::ClassMethods
end

【讨论】:

不知道你有没有漏掉,但是extend(ClassMethods)在self.included(base)方法中,所以是对类方法的扩展。还有其他的实例方法需要添加;这是包括实例方法和扩展类方法的普遍接受的技术。此外,按照您的建议,直接扩展类方法没有区别。【参考方案4】:

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

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

你可以这样做:

Dir[Rails.root.join("app/models/**/*.rb")].each  |f| require f 

【讨论】:

以上是关于获取包含模块的类列表的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Facebook iOS SDK 4.0 获取包含用户名和 ID 的用户好友列表

学习--反射

saltstack主机管理项目:编写插件基类-获取主机列表-提取yaml配置文件

获取当前包中所有模块的列表

从嵌套列表中获取项目

获取 List 中不同值的列表