5

这可能很愚蠢,但我包含了一个 gem,它代表了我的项目所需的所有模型。我想向to_custom_string其中一个模型添加一个方法,Person.

我试图通过(按照这个例子)做到这一点: config/initializers/extensions/person.rb

其中包含以下内容:

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

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

我遇到的问题是这个补丁似乎覆盖Person了 gem 中的类,而不是修补它。疯狂的是,这种覆盖行为只能通过 rake 经历(Person在 gem 的类中声明的所有关联都丢失了)。

我的 rake 任务是这样的:

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)我如何让rake合作?

谢谢!

4

2 回答 2

8

首先,我不会重新打开课程,而是创建一个模块并将其包含到 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 定义。这就是为什么包含模块而不是重新打开课程可能会有所帮助->

于 2012-08-17T01:48:21.670 回答
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 只是包含所有模型的引擎的类。

于 2012-08-16T20:42:32.210 回答