问题描述
这可能很愚蠢,但我包含了一个 gem,它代表我的项目所需的所有模型.我想向其中一个模型 Person
添加一个方法 to_custom_string
.
This might be silly, but I'm including a gem which represents all the models I need for my project. I want to add a method, to_custom_string
to one of those models, Person
.
我试图通过(遵循这个示例):config/initializers/extensions/person.rb
I was attempting to do this via (following this example):config/initializers/extensions/person.rb
其中包含以下内容:
class Person < ActiveRecord::Base
def to_custom_string
address.street.to_s
end
end
gem 中的 Person
类有一个 has_one :address
关联.
The Person
class in the gem has a has_one :address
association.
我遇到的问题是这个补丁似乎覆盖了 gem 中的 Person
类,而不是修补它.疯狂的是,这种覆盖行为只能通过 rake 体验(所有在 gem 的 Person
类中声明的关联都丢失了).
The problem I was experiencing was that this patch seems to override the Person
class from the gem, instead of patching it. What's crazy is that this override behavior was only experienced via rake (all of the associations declared in the Person
class from the gem are lost).
我的佣金任务是这样的:
My rake task was something like:
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 控制台中工作正常.
But copying and pasting the code in the rake task into rails console worked fine.
我目前的解决方案是将 Person
的代码从 gem 复制到我的 app/models
目录中,并在那里使用我的 to_custom_string
方法,我知道这是错误的.
My current solution is to copy the code for Person
from the gem into my app/models
directory, and have my to_custom_string
method there, which I know is wrong.
谁能解释一下为什么 a) irb 保留了我的 Person
关联,但 rake 没有,以及 b) 我如何让 rake 合作?
Can someone please explain why a) irb preserved my Person
associations, but rake did not, and b) how I can get rake to cooperate?
谢谢!
推荐答案
首先,我会创建一个 Module 并将其包含到 Person 中,而不是重新打开类.所以它看起来像那样
First of all instead of reopening the class I would create a Module and include it into the Person. So it would look like that
module CustomString
def to_custom_string
address.street.to_s
end
end
Person.send(:include, CustomString)
此外,在运行初始化程序时,Person 模型似乎尚不可用.如果仍然不起作用,您可能希望将其放在 application.rb 中.
Also it seems like the Person model is not yet available at the point of running the initializer. You may want to put this in your application.rb if still doesn't work.
config.railties_order = [ModelEngine::Engine, :main_app, :all]
我猜它在 irb 而不是在 rake 中工作的原因是因为它们查找类的方式不同.Irb(我相信你通过运行 rails 控制台来运行它)一次加载所有类,因此它从引擎加载类,然后它运行初始化程序,你已经定义了来自引擎的类.我猜(虽然我不确定)开发模式下的 Rake 使用常量的延迟加载.所以它不会在一开始就加载所有的类,只有当它找到一个未定义的常量时.然后它开始寻找可能定义该常量的文件.由于您将一些 Person 放在初始化程序中,它根本不会查找引擎的模型,因为它看到 Person 它已经具有 Person 定义.这就是为什么包含模块而不是重新打开类可能会有所帮助 -> 它强制它会从引擎中查找 Person 常量.
I guess the reason why it works in irb and not in rake is because they look up classes differently. Irb (which I believe you run by running rails console) loads all the classes at once therefore it loads the classes from engine, then it runs the initializer where you have the classes from engine already defined. I guess (though I'm not sure) Rake in development mode uses lazy loading of constants. So it doesn't load all the classes at the very beginning and only when it finds a constants that is undefined. Then it starts looking for a file that may define that constant. Since you put some Person in initializer it doesn't look up the engine's model at all cause at the point it sees Person it has the Person definition already. That's why the inclusion of module instead of reopening the class may help -> it enforces that it will lookup the Person constant from engine.
这篇关于Ruby on Rails Monkey 修补 Gem 的模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!