所以我有一个Rails 3.0引擎(gem)。
它在app / controllers / advanced_controller.rb提供了一个控制器,在app / helpers / advanced_helper.rb提供了一个相应的帮助器。 (当然还有一些观点)。
到目前为止,控制器/帮助器/视图只是在使用gem的应用程序中自动可用,很棒。
但是我想让本地应用程序选择性地覆盖引擎中AdvancedHelper的辅助方法(理想情况下可以调用'super')。想要允许,这是一个非常合理的事情,这是一个完全合理的(我认为是常见的)设计吗?
问题是,我似乎无法找到任何方法使其发挥作用。如果应用程序定义了它自己的app / helpers / advanced_helper.rb(AdvancedHelper),那么引擎中的那个永远不会被加载 - 所以如果你想替换那里的所有辅助方法(不调用super),那就行了,但不是如果你只是想超越一个。
所以这实际上是有道理的,所以我选择了一个不同的名字。我们打电话给我当地人./app/helpers/local_advanced_helper.rb(OrlyAdvancedHelper)。这个帮助器会被加载,如果我在那里放置一个不在原始引擎的AdvancedHelper中的方法,它可用于视图。
但是如果我在那里放一个与引擎的AdvancedHelper中的一个方法相同的方法......我的本地方法永远不会被调用。就像AdvancedHelper(来自引擎)在调用链中早于LocalAdvancedHelper(来自app)。实际上,如果我打开调试器,并查看helpers.ancestors,那就是正在发生的事情,它们与我在祖先链中想要的顺序相反。所以AdvancedHelper(来自引擎)理论上可以称为'super'来调用LocalAdvancedHelper(来自app) - 但这当然没有多大意义,你永远不想这样做。
但是 想要做什么......我做不到。
任何人都有任何想法,有没有办法提供这种设计,这对我来说似乎是完全合理的,应用程序可以选择性地从引擎中覆盖辅助方法?
任何人都有解释为什么它的工作原理是什么?我试着查看实际的Rails源代码,但很快就迷失了,围绕这些东西的代码非常抽象地分散在一堆地方。
这是一个非常深奥的问题,我很悲观,任何人都会有任何想法,我希望你让我感到惊讶!
==更新
好的,为了理解Rails代码的哪个部分被调用,我在每个帮助器上放了一个“def self.included; debugger; end”,然后在调试器中我可以引发一个异常来查看堆栈跟踪。
那仍然不是'真的帮助我找到它的底部,Rails代码跳到了整个地方并且非常令人困惑。
但很明显,具有'标准'名称的助手(即WidgetHelper for WidgetController)由不同的rails代码调用,包含在给定控制器的'master'视图助手模块中,而不是其他助手。我想知道我是否给了帮助者一个不同的名字,然后手动将它包含在我的控制器中(“helper OtherNamedAdvancedHelper”),如果这将改变加载顺序。
答案 0 :(得分:1)
我们可以使用 Module#class_eval 覆盖。
在主应用中,
MountedEngineHelper.class_eval do
def advanced_helper
...
end
end
通过这种方式,引擎助手中定义的其他方法仍然可用。
答案 1 :(得分:0)
感谢您的详细说明。我认为这确实是一个问题。它仍然存在于Rails 3.2.3中,因此我提交了an issue。
我想出的最不起泡的解决方法是做一个“半别名方法链”:
module MountedEngineHelper
def advanced_helper
...
end
end
module MyHelper
def advanced_helper_with_extra_behavior
advanced_helper
extra_behavior
end
end
明显的缺点是您必须更改模板以便调用助手。至少,你在那里明确存在额外的行为。
答案 2 :(得分:0)
来自Rails4的这些发行说明似乎与这个问题有关,并且可能会注意到它已被修复:
http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html#helpers-loading-order