如何通过元编程覆盖Ruby on Rails模块方法?

时间:2010-02-11 22:22:36

标签: ruby-on-rails ruby metaprogramming

我希望我仍然不知道如何实现元编程。

对于从我的Admin :: Base控制器呈现的视图,我想覆盖位于ActionView :: Helpers :: AssetTagHelper下的Rails compute_public_path,以便我所有管理员的布局文件都在public / admin下。

可能有更好的方法,但此时我想学习如何这样做。

我把它放在Admin :: Base控制器中:

module ActionView::Helpers::AssetTagHelper
  def compute_public_path(source, dir, ext = nil, include_host = true)
    super(source, "admin/#{dir}", ext = nil, include_host = true)
  end
end

但它给了我:

super: no superclass method `compute_public_path' for #<ActionView::Base:0x1032240a8>

这并不让我感到惊讶。


如果我在我的管理员助手中尝试此操作:

def compute_public_path_with_admin(source, dir, ext = nil, include_host = true)
  compute_public_path_without_admin(source, "admin/#{dir}", ext, include_host)
end

alias_method_chain :compute_public_path, :admin

我得到模块“AdminHelper”的“未定义方法'compute_public_path'”,我猜这是因为compute_public_path是私有方法。

我发现这有效:

ActionView::Helpers::AssetTagHelper.class_eval do
  def compute_public_path_with_admin(source, dir, ext = nil, include_host = true)
    compute_public_path_without_admin(source, "admin/#{dir}", ext, include_host)
  end

  alias_method_chain :compute_public_path, :admin
end

只要我将config.cache_classes设置为false,否则会出现堆栈级错误。

感谢Squeegy让我指向了正确的方向。

如何通过禁用类缓存来实现此功能?

2 个答案:

答案 0 :(得分:1)

def compute_public_path_with_admin(source, dir, ext = nil, include_host = true)
  compute_public_path_without_admin(source, "admin/#{dir}", ext, include_host)
end

alias_method_chain :compute_public_path, :admin

compute_public_path现在将调用您的实现,然后调用原始实现。

请参阅:Is alias_method_chain synonymous with alias_method?

答案 1 :(得分:0)

请不要这样做。

接受的答案将得到您想要的,但您想要的可能会产生危险的安全隐患。

为什么您希望管理员布局位于public下? Rails默认情况下将其置于app/views/layouts之下的原因是:没有人能够在那里阅读其来源。

如果你把它们放在public中,它们基本上对任何访问者都是可读的。

如果您将它们放在所属的位置,则不会公开您的布局代码。最重要的是,您可以按照任何方式构建app/views/layouts,而无需任何元编程,即无需破解框架内部。