我有一个类似于this one的问题。我正在编写一个具有插件系统的应用程序。有一个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
如上所述,此实现要求include位于类的底部。否则,在调用self.included时未定义on_register。
我担心的是,插件开发人员可能会意外地将包含在顶部,导致插件无法正常工作。或者可能存在派生类或者在已经包含MyAddon类之后扩展它的东西。
有没有更好的方法来解决这个问题?
答案 0 :(得分:2)
在提炼出我发现的其他答案和其他一些信息之后,我想记录最终为我工作的答案。
正如this question所解释的那样,你无法在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
答案 1 :(得分: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
这不太理想,但在发现更好的方法之前,它会防止加重错误。
答案 2 :(得分:-1)
很抱歉,但我必须把它交给你。因此,您希望听到您的代码很聪明,朋友。是的,它太聪明了。但如果你想关注其他开发者,你将不得不停止聪明并开始正常。
Ruby是一种OO语言。 OOP的基础是我首先定义我的对象结构及其方法,然后,当定义了所有内容时,我通过一次调用MyApp.new来理想地实例化我的世界。这样,我首先定义的并不重要。 'include'调用用于建立模块继承层次结构,没有别的。
但不,你不想做OOP。你想滥用included
钩子并在其中实例化接收器(!),并调用它的实例方法。这就是他们所谓的混淆代码。换句话说,你从OOP回到旧的goto
样式指令编程。 Ruby允许你混淆,而不是限制你的能力,但是你有很大的责任,你自己必须适度运用。
你将需要重构你的代码以使其正常。我们不能为你做那件事,因为我们不知道你想要达到什么目的。谁知道,也许你的想法值得一个天才。太糟糕了,我们不知道你的问题。你有一些插件系统。我问你:你需要它吗?不是很好的老混合系统对你来说足够好吗?你确定你没有在Ruby中编写Java吗?那么,你的应用程序会比Rails更强大吗?如果是,请继续使用插件系统。如果不是 - 停止玩游戏并投入时间编写实际执行应用程序假设的算法,而不是提前创建不可分割的插件系统。如果你在一家公司工作 - 我怀疑 - 确保你让老板读这篇文章。