我正在尝试使用after_initialize
为对象设置一些默认值。我遇到的问题是,无论对象是如何创建的,我都希望调用它。
我的课程:
class Foo < ActiveRecord::Base
serialize :data
after_initialize :init
def init
self.data ||= {}
self.bar ||= "bar"
self.baz ||= "baz"
end
end
如果我致电Foo.new
,Foo.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
=&gt; <{1}}代替nil
,其他属性设置正确。
我是否因为执行回调的方式而遗漏了某些内容,与使用块调用create并且没有或者是否被块阻止的默认值存在差异?
更新
发现了这个问题。
事实证明,使用块而不使用块调用"baz"
会略有不同。当您在没有块的情况下调用create
并且仅传入参数哈希时,出于所有意图和目的,您正在调用create
,而Foo.new({<hash of argument>}).save
回调在您之前的保存之前执行期待。
当你用一个块来调用after_initialize
时会发生一些不同的事情。使用您传入的任何参数调用事件的顺序create
,然后调用Foo.new
,然后运行该块。因此,如果您正在使用块(就像我一样)与哈希参数互换,只是为了让事情更具可读性,您可以得到一点,因为在您打算设置的所有参数之前运行after_initialize
集。
我有点因为我在after_initialize
设置了一些额外的工作,根据传递的值来设置一些额外的必需属性。由于调用after_initialize
时实际上没有设置任何内容,因此未正确设置任何内容并且我的验证失败。
我最终不得不打电话给after_initialize
。进入init
后进入after_initialize
。不是最干净的,但它解决了这个问题。
感谢布兰登指出我正确的方向。
答案 0 :(得分: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
并将值object
分配给它yield
。以下是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
而没有您意识到这一点。