1

我有一个与此类似的问题。我正在编写一个具有插件系统的应用程序。有一个 Addon mixin 模块,它检测何时包含并自动注册新的插件:

module Addon
  def self.included(receiver)
     addon = receiver.new   # Create an instance of the addon
     (snip)                 # Other stuff to register the addon 
     addon.on_register      # Tell the instance it was registered
  end
end

以下是如何使用 mixin 的示例:

class MyAddon
  def on_register
    puts "My addon was registered"
  end
  include Addon  # ** note that this is at the end of the class **
end

如上所述,此实现要求包含位于类的底部。否则 on_register 在 self.included 被调用时没有定义。

我担心插件开发人员可能会不小心将包含放在顶部,导致插件无法工作。或者可能有一个派生类或在 MyAddon 类被包含后会扩展它的东西。

有没有更好的方法来解决这个问题?

4

3 回答 3

2

在提炼出其他答案和我发现的其他一些信息之后,我想记录最终对我有用的答案。

正如这个问题所解释的,当定义一个类“完成”时,您无法在 include() 时间检测到。因此,依靠“包含”回调来创建对象并不是一个非常强大的解决方案。

相反,解决方案是发现所有插件并在加载所有内容实例化它们。对插件开发人员的唯一限制是他们的代码必须共享一个通用的顶级模块命名空间。

我仍然不知道这是否是最好的方法,但它肯定比我开始时更好。

这是在模块中搜索插件的代码。它从传入的命名空间开始并递归搜索包含插件类的类:

def find_and_instantiate(mod, addons)
   consts = mod.constants
   consts.each do |c|
     sym = mod.const_get(c)
     if (sym.class == Module)
       find_and_instantiate(sym, addons)
     else
       if (sym.class == Class && sym.include?(Addon))
         addons << sym.new(@container)            
       end
     end        
   end
 end
于 2012-11-11T14:17:50.320 回答
1

我能想到的最好方法是在声明方法后通知模块的用户需要包含它#on_register

module Addon
  def self.included(receiver)
        raise "include #{self.name} after the #on_register method is defined" unless receiver.method_defined? :on_register
        receiver.new.send(:on_register)
  end
end

这不太理想,但它可以防止加重错误,直到您找到更好的方法。

于 2012-11-11T02:28:14.137 回答
-1

对不起,但我必须把这个交给你。所以你想听听你的代码很聪明,伙计。是的,是的,它太聪明了。但是,如果您想关心其他开发人员,您将不得不停止聪明并开始变得正常。

Ruby 是一种面向对象的语言。OOP 的基础是我首先定义我的对象结构和它们的方法,然后,当一切都定义好后,我理想地用一个调用 MyApp.new 来实例化我的世界。这样,我首先定义的内容并不重要。'include' 调用用于建立模块继承层次结构,仅此而已。

但是不,你不想做 OOP。您想滥用included钩子并在其中实例化接收器(!),并调用其实例方法。这就是他们所说的混淆代码。换句话说,您从 OOP 回到了旧的良好goto风格的指令编程。Ruby 允许你混淆,不是限制你的能力,而是拥有巨大的权力,责任重大,你自己必须保持适度。

您将不得不重构您的代码以使其正常。我们无法为您做到这一点,因为我们不知道您想要实现什么。谁知道呢,也许你的想法不愧是天才。太糟糕了,我们不知道你的问题。你有一些插件系统。我问你:你需要吗?好的旧 mixin 系统对你来说还不够好吗?你确定你不是用 Ruby 编程 Java 吗?那么,你的应用程序会比 Rails 更强大吗?如果是,请继续使用插件系统。如果没有,那就别再胡闹了,把时间花在编写真正执行您的应用程序应该执行的操作的算法上,而不是提前创建不可分割的插件系统。如果你在一家公司工作——我对此表示怀疑——一定要让你的老板读到这篇文章。

于 2012-11-11T02:28:10.133 回答