2016-09-14 38 views
0

我想在Rails 4.2中使用初始化程序來將方法添加到ActiveRecord模型中。但是,只需在初始化程序中重新打開該類即可打破現有的屬性訪問器。在初始化程序中斷重新打開類屬性訪問器

模型應用程序/模型/ thing.rb:

class Thing < ActiveRecord::Base 
end 

遷移DB /遷移/ 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 

初始化配置/初始化/ thing.rb:

class Thing 
    def self.new_method 
    "hello" 
    end 
end 

test file 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

我沒有在初始化器中創建類,我試圖重新打開它並添加一個方法。我的理解是,Ruby允許這個 –

+0

初始化程序不會先執行。當初始化器運行時,該類已經存在。 –

+0

我把一個調試器語句放在文件的頂部。在調試器中,我可以查詢「Thing.superclass」,並獲得ActiveRecord :: Base。我可以創建一個Thing的實例並調用test_field。 –

回答

1

關鍵是你不重新開課:你正在定義一個新的。在開發和測試中,您的類在首次使用時加載。通過在您的初始化程序中定義Thing,可以防止您的thing.rb文件被加載。

當您使用Thing.class_eval你不定義一個新的類,所以軌道載荷Thing從thing.rb:在這一點上你要添加到Thing,而不是取代它。

0

我認爲這是因爲你聲明的方法沒有繼承而發生的。您的默認訪問者來自ActiveRecord::Base。所以當你聲明新方法時,你會覆蓋繼承者的所有內容。

+0

如果我使用'class Thing

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 
+0

感謝您的解釋 - 這不是我自己達成的結論! –