获取包含模块的类的列表

发布于 2024-09-27 19:31:51 字数 1526 浏览 7 评论 0原文

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

我缺少什么?

I have a mixin for which I would like to get a list of all the classes that have included it. In the mixin module, I did the following:

module MyModule
  def self.included(base)
    @classes ||= []
    @classes << base.name
  end

  def self.classes
    @classes
  end
end

class MyClass
  include MyModule
end

This works pretty well:

> MyModule.classes #=> nil
> MyClass.new #=> #<MyClass ...>
> MyModule.classes #=> ["MyClass"]

Now, I would like to extract this part out into a separate module that can be included in my other mixins. So, I came up with the following:

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

This doesn't work though, because the #included(module_base) method being added to MyModule from ListIncludedClasses is never getting run. Interestingly enough, it does successfully add #classes to 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

What am I missing?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

み零 2024-10-04 19:31:51
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]

请参阅 ObjectSpace::
每个_对象

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]

See ObjectSpace::
each_object
.

紫竹語嫣☆ 2024-10-04 19:31:51

对 Cary 的答案(这太棒了!)的一个警告是,它只会选择已经由虚拟机评估过的类。

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

你可以这样做:

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

One caveat to Cary's answer (which is great!) is that it will only pick up on classes that have already been evaluated by the VM.

So, if you're running that code in a setting like a development Rails console you'll need to explicitly require the files you're interested in before checking if the module has been included.

You can do that like this:

Dir[Rails.root.join("app/models/**/*.rb")].each { |f| require f }
七颜 2024-10-04 19:31:51

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

Actually, your module extension module works. The problem is in your test: when you created a random unnamed class with Class.new, you forgot to include MyModule. As a side note, you can take your read-only accessor for classes that include the module and use the helpful Module#attr_reader method.

骄兵必败 2024-10-04 19:31:51

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

试试这个:

module MyModule
  extend ListIncludedClasses::ClassMethods
end

You probably should use extend instead of include since former adds class level methods, while latter - instance level methods (why you have access to @classes).

Try this:

module MyModule
  extend ListIncludedClasses::ClassMethods
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文