使用块调用create时,Rails 3 after_initialize未运行

HMC*_*tch 11 activerecord ruby-on-rails-3

我正在尝试使用设置对象的一些默认值after_initialize.我遇到的问题是无论对象是如何创建的,我都希望调用它.

我的课:

class Foo < ActiveRecord::Base

  serialize :data

  after_initialize :init

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

end
Run Code Online (Sandbox Code Playgroud)

一切工作正常,如果我打电话Foo.new,Foo.new(:bar => "things")Foo.create(:baz => 'stuff').然而,当我使用块用createafter_initialize回调没有得到运行.

obj = Foo.create do |f|
  f.bar = "words"
  f.data = { :attr_1 => 1, :attr_2 => 2 }
end
Run Code Online (Sandbox Code Playgroud)

这只是产生obj.baz=> nil而不是"baz"正确设置其他属性.

我是否因为执行回调的方式而遗漏了某些内容,与使用块调用create并且没有或者是默认值被块阻塞的区别?

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.不是最干净的,但它解决了这个问题.

谢谢Brandon指出我正确的方向.

Mic*_*ley 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
Run Code Online (Sandbox Code Playgroud)

这是一个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
 => {}
Run Code Online (Sandbox Code Playgroud)

如您所见,dataafter_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
Run Code Online (Sandbox Code Playgroud)

所以在它之前create调用new并赋值.以下是相关部分:objectyieldnew

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

正如您所看到的,new无条件地在initialize回调之前调用回调,因此create甚至在您通过的块之前产生回调.当块获取对象时,该after_initialize方法已经执行.

仔细检查(1)您的Rails版本是最新的(截至目前我相信3.0.5)并且(2)没有任何设置baz而没有您意识到它.