如何在Rails中自动加载的代码中进行“猴子补丁”?

20

我正在使用类猴子补丁(monkey-patching)的方式对Rails引擎进行修改,大致如下:

SomeClass.class_eval do
  # ...
end

第一次访问网站时(至少在开发模式下),它可以正常运作,但第二次访问时,就像我的补丁从未存在过一样。我猜想这是Rails自动重新加载引擎(安装在vendor/中),而不重新加载我的代码。这是Rails 2.3。

有什么想法可以使我的代码也被重新加载吗?


我曾经遇到过类似的问题,唯一解决方法是在我的开发机器上以生产模式运行rails :(. 我也对此很感兴趣。 - kikito
@egarcia:哎呀,希望这次我们能找到更好的解决方案。 - pupeno
你是如何运行你的代码的?是使用Webbrick、Mongrel还是Passenger?请发布Rails和服务器版本。 - mpapis
6个回答

29

编辑:这个解决方案仅适用于Rails 3+,因为它依赖于Rails::Railtie中的一些功能。将此代码放入初始化程序中。

这个问题很旧了,但我找到了一个解决方案:

Rails.configuration.to_prepare do
  SomeClass.class_eval do
    # ...
  end
end

这会强制Rails在开发模式下每个请求都重新加载类,但在生产环境下只加载一次。


2
在Rails 5中对我有效。 - ttotherat
在Rails 7中对我有效。;-) - micred

9

我刚写了我的第一个猴子补丁,因此需要制定一套约定。这是我想出的:

  1. 将您的扩展放置在lib/ext/下。(由#rubyonrails IRC房间中的资深工作mad3建议) 在我的情况下,我正在向Mail::Message类(来自mail gem,由ActionMailer使用)添加一个方法,所以我创建了:

    /lib/ext/mail/message.rb

  2. 打开类或模块并添加您的代码:

    module Mail class Message def to_is_phone? !!(self.to.first =~ /^\+1\d{10}$/) end end end

  3. 创建一个初始化程序以加载所有猴子补丁。当引用常量时,Rails将自动加载文件,但由于您正在向现有的类/模块添加方法而不是定义新的类/模块,因此这种方法不起作用,因此您必须手动要求所有猴子补丁。 所以我创建了:

    /config/initializers/monkey_patches.rb

    其中包含:

    require 'ext/mail/message'


请注意,我目前正在使用Rails 4.1,但我认为这也适用于旧版本。 - odigity
6
monkey_patches.rb可以替换为Dir[Rails.root.join('lib/ext/*.rb')].each { |file| require file },这样所有的猴子补丁都会被加载。 - Dan Kohn
@DanKohn 双星号赢了! :) Dir[Rails.root.join('lib/ext/**/*.rb')].each { |file| require file } - viktor_vangel

5
如果您将补丁放在/config/initializers中的任何.rb文件中,它应该可以工作。

使用class_eval和to_prepare有什么区别? - montrealmike

0

0

虽然不太美观,但我发现如果将这种代码放在environments.rb的底部,它总是能够保证正确的启动加载顺序。


0

很遗憾,无法钩入Rails 2.x的重新加载机制。 您可以将补丁放置在应用程序或lib目录中的某个位置。(lib/core_ext可能是首选位置)。然后将该目录添加到配置文件中的autoload_paths中。

您可能还需要打开类,而不是使用class_eval。


猴子补丁已经在lib/上了,它已经在Rails 2的autoload_paths上了。我不确定为什么,但是用class关键字打开类会导致错误,在稍后抛出异常。 - pupeno

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接