Ruby-on-rails 从 Rails 中的 gem 覆盖模块方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/580314/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 20:59:38  来源:igfitidea点击:

Overriding a module method from a gem in Rails

ruby-on-railsrubygemwill-paginate

提问by Dave Smylie

The will_paginate gem is broken on my version of Oracle. The default paginate_by_sqlmethod in the WillPaginate module is inserting an extra 'AS' into a query and causing it to fail.

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

The code itself is easily fixed, but I'm not sure of the best way to get Rails to pick up my change.

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

I don't want to change the code in the gem itself, as that will leave my code broken on other machines.

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

I tried creating an lib/test.rb file containing:

我尝试创建一个包含以下内容的 lib/test.rb 文件:

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

and requiring it from environment.rb, but it's not picking up my changes. I also tried requiring it from controllers/application.rb, but again, not picking up my changes.

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

Temporarily, I got it to work by overriding the method within the specific model itself, but this is a bit of a hack, and means I can't use it on any of the other models in this project.

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

I'm sure there's an easy way to do this, but I'm not having any luck tracking it down using Google.

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

采纳答案by Sarah Mei

What you are doing will work, but your code needs to look like this:

您正在做的事情会起作用,但您的代码需要如下所示:

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

In other words, go into finder.rb, delete everything except the module headers and the method you want to override, then save to a file in lib and include in environment.rb. Voila, instant monkey patch!

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

回答by Steve Graham

A more concise solution:

更简洁的解决方案:

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

Put the the code into an initializer file in config/initializers. This is the correct place to put code that needs to be run when the environment is loaded. It also better organises your code, making each file's intent clearer, thus bugs will be easier to track down. Do not clutter up environment.rb!

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

回答by Abram

Ok, I'm just going to make this easier for people like myself who come along and still struggle a bit after reading the other answers.

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

Firstfind the code that you want to change on the github repo by searching for the line of code (you could easily find this using pry) you want to change in the gem, and then selecting Codeon the left instead of Issues

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

enter image description here

在此处输入图片说明

enter image description here

在此处输入图片说明

NextCopy the content of the module you want to change and place it into an aptly named .rbfile inside of your config/initializers folder. Here is an example:

接下来复制您要更改的模块的内容,并将其放入.rbconfig/initializers 文件夹内的适当命名的文件中。下面是一个例子:

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

Now, change it to:

现在,将其更改为:

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

Now, make any additional changes to the code and restart your server.

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

Away you go!

走开!