2011-03-05 52 views
11

我想爲使用after_initialize的對象設置一些默認值。我遇到的問題是無論對象是如何創建的,我都希望調用它。Rails 3使用塊調用create之後,after_initialize不會運行

我的類:

class Foo < ActiveRecord::Base 

    serialize :data 

    after_initialize :init 

    def init 
    self.data ||= {} 
    self.bar ||= "bar" 
    self.baz ||= "baz" 
    end 

end 

一切正常,如果我叫Foo.newFoo.new(:bar => "things")Foo.create(:baz => 'stuff')。但是,當我使用create的塊時,after_initialize回調無法運行。

obj = Foo.create do |f| 
    f.bar = "words" 
    f.data = { :attr_1 => 1, :attr_2 => 2 } 
end 

這只是產生obj.baz =>nil代替"baz"與正確設置其他屬性。

我是否錯過了回調執行的方式,不同之處在於調用create和block之間的區別,或者是缺省值被塊所破壞?

UPDATE

發現的問題。

事實證明,調用create與塊和無細微差別。當你在沒有塊的情況下調用create並傳入參數散列時,對於所有意圖和目的,您要調用Foo.new({<hash of argument>}).save,並且after_initialize回調將在保存之前執行,就像您期望的那樣。

當你用塊調用create時會發生一些不同的事情。事件的順序是Foo.new被傳入的任何參數調用,然後調用after_initialize,然後塊運行。因此,如果您正在使用塊(與我一樣)與散列參數交替使用,以增加可讀性,那麼您可以稍微調整一下,因爲在您打算設置的所有參數都已設置好之前,您的after_initialize就會運行。

我得到了一點,因爲我在after_initialize做了一些額外的工作,根據傳遞的值設置了一些額外的必需屬性。由於after_initialize被調用時沒有任何設置,所以沒有設置正確,我的驗證失敗。我最後不得不打電話給init。一旦在after_initialize和一次在before_validation。不是最乾淨的,但它解決了這個問題。

感謝布蘭登指引我朝着正確的方向前進。

+0

如果你用'Foo.new'(即用聲明的塊方法)做同樣的事情,同樣的事情會發生嗎? – 2011-03-05 03:55:07

+0

Foo.new與塊工作得很好。 – HMCFletch 2011-03-06 04:01:05

+0

@HMCFletch - 發生了什麼的最佳解釋 - 感謝分享 – 2014-05-06 09:45:55

回答

7

我無法重現這一點。我正好有得心應手具有以下(簡化)類的應用程序:

class Service < ActiveRecord::Base 
    serialize  :data, Hash 
    after_initialize :create_default_data 
    attr_accessible :data, :token 

    protected 

    def create_default_data 
     self.data ||= Hash.new 
    end 
end 

下面是一個IRB會議:

ruby-1.9.2-p136 :001 > obj = Service.create do |s| 
ruby-1.9.2-p136 :002 >  s.token = "abc" 
ruby-1.9.2-p136 :003?> end 
=> #<Service id: 22, user_id: nil, type: nil, data: {}, created_at: "2011-03-05 04:18:00", updated_at: "2011-03-05 04:18:00", token: "abc"> 
ruby-1.9.2-p136 :004 > obj.data 
=> {} 

正如你所看到的,data作爲初始化的after_initialize方法將一個空的哈希。Rails代碼表明這也是有道理的;在create

def create(attributes = nil, &block) 
    if attributes.is_a?(Array) 
    attributes.collect { |attr| create(attr, &block) } 
    else 
    object = new(attributes) 
    yield(object) if block_given? 
    object.save 
    object 
    end 
end 

所以create電話new和分配前yield S中的價值object。下面是相關的部分new

def initialize(attributes = nil) 
    # truncated for space 
    result = yield self if block_given? 
    run_callbacks :initialize 
    result 
end 

正如你所看到的,new無條件調用initialize回調返回之前,因此前create甚至屈服於你傳遞塊。 當您的塊獲取對象時,after_initialize方法已執行

仔細檢查(1)您的Rails版本是否是最新版本(現在我相信3.0.5版本),並且(2)沒有您意識到,沒有設置baz

+1

感謝您的粗體。這正是導致我的問題。我在我的問題的底部寫了一些解釋,詳細說明發生了什麼。謝謝您的幫助。 – HMCFletch 2011-03-06 04:38:59

+0

非常有趣,而且很好,你確定發生了什麼事情。我無法想象你是唯一一個被這個咬傷的人...... – 2011-03-06 04:51:15

相關問題