Ruby on Rails Monkey修补Gem的模型

时间:2012-08-16 20:05:57

标签: ruby-on-rails

这可能很愚蠢,但我要包含一个代表我项目所需模型的宝石。我想在其中一个模型to_custom_string中添加一个方法Person

我试图通过(this示例后)来做到这一点: config/initializers/extensions/person.rb

包含类似的内容:

class Person < ActiveRecord::Base
  def to_custom_string
    address.street.to_s
  end
end

gem中的Person类具有has_one :address关联。

我遇到的问题是这个补丁似乎覆盖了gem中的Person类,而不是修补它。令人抓狂的是,这种覆盖行为只能通过rake体验(来自gem的Person类中声明的所有关联都会丢失)。

我的佣金任务类似于:

namespace :convert
  task :all_persons => :environment do
    Person.where(:param => value).includes(:address).find_in_batches(:batch_size => 2000) do |persons|
      persons.each do |person|
        puts person.to_custom_string
      end
    end
  end
end

致电bundle exec rake convert:all_persons给了我:

Association named 'address' was not found; perhaps you misspelled it?

但是将rake任务中的代码复制并粘贴到rails控制台中工作正常。

我目前的解决方案是将Person的代码从gem复制到我的app/models目录中,并在那里使用to_custom_string方法,我知道这是错误的。

有人可以解释为什么a)irb保留了我的Person协会,但是rake没有,b)我怎么可以耙合作?

谢谢!

2 个答案:

答案 0 :(得分:7)

首先,我将创建一个Module并将其包含在Person中,而不是重新打开该类。所以它看起来像那样

  module CustomString
    def to_custom_string
      address.street.to_s
    end
  end

  Person.send(:include, CustomString)

在运行初始化程序时,似乎尚未提供Person模型。您可能希望将它放在application.rb中,如果仍然无效。

  config.railties_order = [ModelEngine::Engine, :main_app, :all]

我猜它在irb而不是rake中工作的原因是因为它们以不同的方式查找类。 Irb(我相信你通过运行rails控制台运行)一次加载所有类,因此它从引擎加载类,然后它运行初始化程序,你已经定义了引擎中的类。我猜(虽然我不确定)Rake在开发模式下使用延迟加载常量。所以它不会在最开始时加载所有类,只有在找到未定义的常量时才会加载。然后它开始寻找可以定义该常量的文件。由于你在初始化程序中放置了一些Person,因此它不会查找引擎的模型,因为它看到Person已经具有Person定义。这就是为什么包含模块而不是重新打开课程可能会有所帮助 - &gt;它强制它将从引擎中查找Person常量。

答案 1 :(得分:4)

我认为只要您重新打开该类,它就会起作用,而不会再次从ActiveRecord :: Base继承。所以,像这样:

class Person
  def custom_string
    address.to_street.to_s
  end
end

编辑:

在重新打开课程之前,您可能还需要添加这样的一行:

require_dependency ModelEngine::Engine.root.join('app', 'models', 'person').to_s

其中ModelEngine :: Engine只是包含所有模型的引擎的类。