我试图在Rails 4.2中使用初始化程序向ActiveRecord模型添加方法。但是,只需在初始化程序中重新打开该类就会破坏现有的属性访问器。
模型app / models / thing.rb:
class Thing < ActiveRecord::Base
end
迁移db / migrate / 20160914144414_create_things.rb:
class CreateThings < ActiveRecord::Migration
def change
create_table :things do |t|
t.integer :test_field
t.timestamps null: false
end
end
end
initializer config / initializers / thing.rb:
class Thing
def self.new_method
"hello"
end
end
测试文件test / models / thing_test.rb:
require File.expand_path("../../test_helper", __FILE__)
class ThingTest < ActiveSupport::TestCase
test "the truth" do
thing = Thing.new
thing.test_field = 1
puts thing.new_method
end
end
当我在这里进行测试时,得到的是:
❯❯❯ rtest test/models/thing_test.rb
1) Error:
ThingTest#test_the_truth:
NoMethodError: undefined method `test_field=' for #<Thing:0x00000101464658>
test/models/thing_test.rb:6:in `block in <class:ThingTest>'
如果我使用这种替代语法将方法添加到类中,它可以工作:
Thing.class_eval do
def self.new_method
"hello"
end
end
我想我很高兴以这种方式这样做,但我想知道为什么重新开课不行。
答案 0 :(得分:2)
我在文件顶部放置了一个调试器语句。在调试器中,我可以查询Thing.superclass,我得到ActiveRecord :: Base。我可以创建一个Thing实例并在其上调用test_field。
仅观察实验的行为会改变其结果。当您在那里的调试器语句处停止时,类不存在。但是,当您在调试器控制台中评估Thing
时,它会自动加载。
您可以在真正的初始化程序中执行的操作是以某种方式触发模型的加载。
# initializers/thing.rb
Thing # trigger autoloading
class Thing
...
end
# or
require 'app/models/thing.rb' # load explicitly
class Thing
...
end
答案 1 :(得分:1)
关键是你没有重新开课:你正在定义一个新课程。在开发和测试中,您的类在首次使用时会被加载。通过在初始化程序中定义Thing
,可以防止你的thing.rb文件被加载。
当您使用Thing.class_eval
时,您没有定义新类,因此rails会从thing.rb加载Thing
:此时您正在添加Thing
而不是替换它。< / p>
答案 2 :(得分:0)
我认为它发生了,因为你声明了没有继承的方法。您的默认访问者来自<header>
<div class="menu_links">
<a href="#">Link A</a>
<a href="#">Link B</a>
</div>
</header>
。因此,当您声明新方法时,您将覆盖继承者的所有内容。