【问题标题】:Overriding a module method from a gem in Rails从 Rails 中的 gem 覆盖模块方法
【发布时间】:2010-10-09 11:33:29
【问题描述】:

will_paginate gem 在我的 Oracle 版本上已损坏。 WillPaginate 模块中的默认paginate_by_sql 方法是在查询中插入额外的“AS”并导致查询失败。

代码本身很容易修复,但我不确定让 Rails 接受我的更改的最佳方法。

我不想更改 gem 本身的代码,因为这会使我的代码在其他机器上损坏。

我尝试创建一个 lib/test.rb 文件,其中包含:

module WillPaginate
  def paginate_by_sql
    (my code goes here)
  end
end

并从 environment.rb 中要求它,但它没有接受我的更改。 我也尝试从 controllers/application.rb 中要求它,但同样,没有接受我的更改。

暂时,我通过覆盖特定模型本身的方法来让它工作,但这有点小技巧,意味着我不能在这个项目的任何其他模型上使用它。

我确信有一个简单的方法可以做到这一点,但我没有任何运气使用 Google 跟踪它。

【问题讨论】:

  • 如果 gem 代码坏了,肯定是到处坏了吗?您是否记录了项目的错误? wiki.github.com/mislav/will_paginate/report-bugs
  • 还没有 - 错误出现在处理 oracle 特定情况的块中。原因是因为我使用的是 oracleenhanced(而不是 oracle 或 oci)适配器。我认为大多数人都在使用 oracle 适配器,不会遇到这种情况)无论如何都会很快记录错误。

标签: ruby-on-rails ruby gem will-paginate


【解决方案1】:

你正在做的事情会起作用,但你的代码需要看起来像这样:

module WillPaginate
  module Finder
    module ClassMethods
      def paginate_by_sql(sql, options)
        # your code here
      end
    end
  end
end

换句话说,进入 finder.rb,删除除模块头和要覆盖的方法之外的所有内容,然后保存到 lib 中的文件并包含在 environment.rb 中。瞧,即时猴子补丁!

【讨论】:

  • 这应该在初始化器config/initializers - 不要把它放在environment.rb 文件中!
【解决方案2】:

更简洁的解决方案:

WillPaginate::Finder::ClassMethods.module_eval do
 def paginate_by_sql sql, options
   # Your code here
 end
end

将代码放入 config/initializers 中的初始化文件中。这是放置加载环境时需要运行的代码的正确位置。它还可以更好地组织您的代码,使每个文件的意图更清晰,从而更容易追踪错误。不要弄乱环境。rb!

【讨论】:

  • 如果底层类是通过autoload加载的有趣的极端情况:stackoverflow.com/questions/8736451/…
  • 很酷。在新的paginate_by_sql 中,我可以访问它的旧版本吗?我可以打电话给super(sql, options)吗?
  • 重写 gem 方法代码必须在 initializers 文件夹中的原因是什么?我尝试将它放在其他地方并要求该文件,但它不起作用。如果我没记错的话,我所做的将适用于常规 Ruby 应用程序,但由于某种原因不适用于 Rails 应用程序......
【解决方案3】:

好的,我只是想让像我这样的人在阅读其他答案后仍然有点挣扎。

首先通过在 gem 中搜索要更改的代码行(您可以使用 pry 轻松找到)在 github 存储库中找到要更改的代码,然后然后选择左侧的Code 而不是Issues

下一步 复制您要更改的模块的内容并将其放入 config/initializers 文件夹内一个恰当命名的 .rb 文件中。这是一个例子:

module Forem
  module TopicsHelper
    def link_to_latest_post(post)
      text = "#{time_ago_in_words(post.created_at)} #{t("ago_by")} #{post.user}"
      link_to text, forum_topic_path(post.topic.forum, post.topic, :anchor => "post-#{post.id}")
    end
  end
end

现在,将其更改为:

Forem::TopicsHelper.module_eval do
  def link_to_latest_post(post)
    text = "#{time_ago_in_words(post.created_at)} #{t("ago_by")} #{post.user}"
    link_to text, forum_topic_path(post.topic.forum, post.topic, :anchor => "post-#{post.id}")
  end
end

现在,对代码进行任何其他更改并重新启动您的服务器。

你走吧!

【讨论】:

  • 你能像这样调用原始方法吗?或者你有 alias_method_chain (无论如何在 Rails 中)?
  • 谢谢@Abram!这很有帮助!
猜你喜欢
  • 2013-07-10
  • 1970-01-01
  • 2011-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多