【问题标题】:How can I override a Ruby on Rails module method via metaprogramming?如何通过元编程覆盖 Ruby on Rails 模块方法?
【发布时间】:2010-02-11 22:22:35
【问题描述】:

尽管我很想,但我仍然不知道如何实现元编程。

对于从我的 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 为我指明了正确的方向。

如何通过禁用类缓存来完成这项工作?

【问题讨论】:

    标签: ruby-on-rails ruby metaprogramming


    【解决方案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?

    【讨论】:

    • 但是我应该把这段代码放在哪里呢?它是进入控制器还是进入初始化器?
    • 将它放置在具有您要覆盖的方法的类或模块中。就像您所有管理员视图共享的助手一样?实际上并不是从哪里调用这个方法,所以你可能需要玩一点。
    • 如果方法是私有的怎么办?我似乎无法覆盖它。
    • 感谢您的输入 Squeegy,我仍然需要弄清楚如何使其与类缓存一起工作,但感谢您,我现在已经很接近了。
    【解决方案2】:

    请不要这样做。

    接受的答案会得到你想要的,但你想要的可能会带来危险的安全隐患。

    您为什么希望您的管理布局在public 下? Rails 默认将它们放在app/views/layouts 下是有原因的:没有人可以在那里阅读它们的源代码。

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

    如果你把它们放在它们所属的地方,你就不会暴露你的布局代码。最重要的是,您可以以任何您想要的方式构建 app/views/layouts,而无需任何元编程,即无需破解框架内部结构。

    【讨论】:

      猜你喜欢
      • 2012-07-08
      • 1970-01-01
      • 1970-01-01
      • 2016-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-05
      相关资源
      最近更新 更多