这可能很愚蠢,但我要包含一个代表我项目所需模型的宝石.我想为to_custom_string其中一个模型添加一个方法Person.
我试图这样做(按照这个例子):
config/initializers/extensions/person.rb
其中包含的内容如下:
class Person < ActiveRecord::Base
def to_custom_string
address.street.to_s
end
end
Run Code Online (Sandbox Code Playgroud)
Persongem中的类具有has_one :address关联.
我遇到的问题是这个补丁似乎覆盖Person了gem中的类,而不是修补它.令人抓狂的是,这种覆盖行为只能通过rake来体验(Person来自gem 的类中声明的所有关联都会丢失).
我的佣金任务是这样的:
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
Run Code Online (Sandbox Code Playgroud)
打电话bundle exec rake convert:all_persons给了我:
Association named 'address' was not found; perhaps you misspelled it?
Run Code Online (Sandbox Code Playgroud)
但是将rake任务中的代码复制并粘贴到rails控制台中工作正常.
我目前的解决方案是Person将gem中的代码复制到我的app/models目录中,并在to_custom_string那里使用我的方法,我知道这是错误的.
有人可以解释为什么a)irb保留了我的Person协会,但是rake没有,和b)我怎么可以得到佣金合作?
谢谢!
首先,我将创建一个Module并将其包含在Person中,而不是重新打开该类.所以它看起来像那样
module CustomString
def to_custom_string
address.street.to_s
end
end
Person.send(:include, CustomString)
Run Code Online (Sandbox Code Playgroud)
此外,似乎Person模型在运行初始化程序时尚不可用.如果仍然不起作用,您可能希望将它放在application.rb中.
config.railties_order = [ModelEngine::Engine, :main_app, :all]
Run Code Online (Sandbox Code Playgroud)
我猜它在irb而不是rake中工作的原因是因为它们以不同的方式查找类.Irb(我相信你通过运行rails控制台运行)一次加载所有类,因此它从引擎加载类,然后它运行初始化程序,你已经定义了引擎中的类.我猜(虽然我不确定)Rake在开发模式下使用延迟加载常量.所以它不会在最开始时加载所有类,只有在找到未定义的常量时才会加载.然后它开始寻找可以定义该常量的文件.由于你在初始化程序中放置了一些Person,因此它不会查找引擎的模型,因为它看到Person已经具有Person定义.这就是为什么包含模块而不是重新打开类可能会有所帮助 - >它强制它将从引擎中查找Person常量.
我认为只要您重新打开该类,而不需要再次从 ActiveRecord::Base 继承,它就可以工作。所以,像这样:
class Person
def custom_string
address.to_street.to_s
end
end
Run Code Online (Sandbox Code Playgroud)
编辑:
在重新打开课程之前,您可能还需要添加如下行:
require_dependency ModelEngine::Engine.root.join('app', 'models', 'person').to_s
Run Code Online (Sandbox Code Playgroud)
其中 ModelEngine::Engine 只是包含所有模型的引擎的类。