Rails3 引擎助手覆盖

发布于 11-16 00:04 字数 1493 浏览 2 评论 0原文

所以我有一个 Rails 3.0 引擎(gem)。

它在 app/controllers/advanced_controller.rb 处提供了一个控制器,在 app/helpers/advanced_helper.rb 处提供了一个相应的帮助器。 (当然还有一些观点)。

到目前为止一切顺利,控制器/帮助器/视图在使用 gem 的应用程序中自动可用,太棒了。

但我想让本地应用程序选择性地覆盖引擎中 AdvancedHelper 的辅助方法(并且最好能够调用“超级”)。这是一个非常合理的事情,想要允许,对吧,一个完全合理的(我认为常见的)设计?

问题是,我似乎找不到任何方法让它发挥作用。如果应用程序定义了它自己的 app/helpers/advanced_helper.rb (AdvancedHelper),那么来自引擎的那个根本不会被加载——所以如果你想替换其中的所有帮助器方法(不调用 super),那么这将是可行的,但如果你只是想覆盖一个则不然。

所以这实际上是有道理的,所以我选择了一个不同的名字。我们将本地的命名为 ./app/helpers/local_advanced_helper.rb (LocalAdvancedHelper)。这个助手确实被加载了,如果我在其中放置了原始引擎的 AdvancedHelper 中没有的方法,它就可供视图使用。

但是,如果我在那里放置一个与引擎的 AdvancedHelper 中的方法同名的方法......我本地的方法永远不会被调用。就像 AdvancedHelper(来自引擎)在调用链中比 LocalAdvancedHelper(来自应用程序)更早。事实上,如果我打开调试器,并查看 helpers.ancestors,这正是发生的情况,它们在祖先链中的顺序与我想要的相反。因此,AdvancedHelper(来自引擎)理论上可以调用“super”来调用 LocalAdvancedHelper(来自应用程序)——但这当然没有多大意义,你永远不想这样做。

但我想做想做的事...我做不到。

任何人都有任何想法,有什么方法可以提供这种设计,这对我来说似乎完全合理,应用程序可以有选择地覆盖引擎中的辅助方法?

任何人都可以解释为什么它会这样工作吗?我尝试查看实际的 Rails 源代码,但很快就迷失了方向,围绕这些东西的代码非常抽象,分散在很多地方。

这是一个非常深奥的问题,我悲观任何人都会有任何想法,我希望你给我惊喜!

== 更新

好的,为了了解 Rails 代码的哪一部分被调用,我在每个助手上放置了一个“def self.included ; debugger ; end”,然后在调试器中我可以引发异常以查看堆栈跟踪。

这仍然不能真正帮助我弄清问题的根源,Rails 代码到处乱跳,而且非常令人困惑。

但很明显,具有“标准”名称的助手(即 WidgetController 的 WidgetHelper)由不同的 Rails 代码调用,以包含在给定控制器的“主”视图助手模块中,而不是其他助手。我想知道是否给助手一个不同的名称,然后使用(“helper OtherNamedAdvancedHelper”)手动将其包含在我的控制器中,如果这会改变加载顺序。

So I have a Rails 3.0 Engine (gem).

It provides a controller at app/controllers/advanced_controller.rb, and a corresonding helper at app/helpers/advanced_helper.rb. (And some views of course).

So far so good, the controller/helper/views are just automatically available in the application using the gem, great.

But I want to let the local application selective over-ride helper methods from AdvancedHelper in the engine (and ideally be able to call 'super'). That's a pretty reasonable thing to want to allow, right, a perfectly reasonable (and I'd think common) design?

Problem is, I can't seem to find any way to make it work. If the application defines it's own app/helpers/advanced_helper.rb (AdvancedHelper), then the one from the engine never gets loaded at all -- so that would work if you wanted to replace ALL the helper methods in there (without calling super), but not if you just want to over-ride one.

So that kind of makes sense actually, so I pick a different name. Let's call my local one ./app/helpers/local_advanced_helper.rb (LocalAdvancedHelper). This helper DOES get loaded, if I put a method in there that wasn't in the original engine's AdvancedHelper, it is available to views.

But if I put a method in there with the same name as one in the engine's AdvancedHelper... my local one NEVER gets called. It's like the AdvancedHelper (from engine) is earlier in the call chain than the LocalAdvancedHelper (from app). Indeed, if I turn on the debugger, and look at helpers.ancestors, that's exactly what's going on, they're in the reverse order I'd want in the ancestor chain. So AdvancedHelper (from engine) could theoretically call 'super' to call up to LocalAdvancedHelper (from app) -- but that of course wouldn't make a lot of sense to do, you'd never want to do that.

But what I would want to do... I can't do.

Anyone have any ideas, is there any way to provide this design, which seems perfectly reasonable to me, where an app can selectively over-ride a helper method from an Engine?

Anyone have any explanation of why it's working the way it is? I tried looking at actual Rails source code, but got lost pretty quick, the code around this stuff is awfully abstract split amongst a bunch of places.

This is pretty esoteric question, I'm pessimistic anyone will have any ideas, I hope you surprise me!

== Update

Okay, in order to understand what part of Rails code is being called where, I put a "def self.included ; debugger ; end" on each of my helpers, then in the debugger I can raise an exception to see a stack trace.

That still isnt' really helping me get to the bottom of it, the Rails code jumps all over the place and is pretty confusing.

But it's clear that a helper with the 'standard' name (ie WidgetHelper for WidgetController) is called by different rails code, to include in the 'master' view helper module for a given controller, than other helpers are. I'm wondering if I give the helper a different name, then manually include it in my controller with ("helper OtherNamedAdvancedHelper"), if that will change the load order.

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

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

发布评论

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

评论(3

坏尐絯℡2024-11-23 00:04:45

我们可以使用 Module#class_eval 覆盖。

在主应用程序中,

MountedEngineHelper.class_eval do
  def advanced_helper
    ...
  end
end

这样引擎助手中定义的其他方法仍然可用。

We can use Module#class_eval to override.

In main app,

MountedEngineHelper.class_eval do
  def advanced_helper
    ...
  end
end

In this way other methods defined in engine helper are still available.

や三分注定2024-11-23 00:04:45

感谢您的详细阐述。我认为这确实是一个问题。而且它仍然存在于 Rails 3.2.3 中,因此我提交了一个问题

我想出的最难闻的解决方法是做一个“半别名方法链”:

module MountedEngineHelper
  def advanced_helper
    ...
  end
end

module MyHelper
  def advanced_helper_with_extra_behavior
    advanced_helper
    extra_behavior
  end
end

明显的缺点是你必须更改你的模板以便调用你的助手。至少,你在那里明确了额外行为的存在。

Thanks for your elaboration. I think this really is a problem. And it is still present in Rails 3.2.3, so I filed an issue.

The least-smelling workaround I came up with is to do a "half alias method chain":

module MountedEngineHelper
  def advanced_helper
    ...
  end
end

module MyHelper
  def advanced_helper_with_extra_behavior
    advanced_helper
    extra_behavior
  end
end

The obvious drawback is that you have to change your templates so that your helper is called. At least, you make the existence of extra behavior explicit there.

风启觞2024-11-23 00:04:45

Rails4 的这些发行说明似乎与这个问题密切相关,并且可能注意到它已被修复:

http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html#helpers-loading-order

These release notes from Rails4 seem enticingly related to this problem, and potentially note it's been fixed:

http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html#helpers-loading-order

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