为什么ActiveRecord after_initialize和after_find回调文档提到块级变量?

时间:2017-07-22 12:07:19

标签: ruby-on-rails ruby

我已在after_initialize模型中定义了after_findUser回调:

请假设我的User也有一个名为email的属性。

class User < ApplicationRecord
  after_initialize do |user|
    puts "You have initialized an object! Email: #{email}"
  end

  after_find do |user|
    puts "You have found an object! Email: #{email}"
  end
end

遵循我在Rails Guides

中找到的文档

这可以按预期工作。即当我拨打User.newUser.first时,我看到输出按预期打印:

2.3.3 :001 > User.new(email: 'foo@gmail.com')
You have initialized an object! Email: foo@gmail.com
=> #<User id: nil, email: "foo@gmail.com", password_digest: nil, 
created_at: nil, updated_at: nil, email_confirmation_token: nil, 
terms_of_service: false, country_id: nil> 

2.3.3 :002 > User.first
User Load (0.6ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" 
ASC LIMIT $1  [["LIMIT", 1]]
You have found an object! Email: mary@gmail.com
You have initialized an object! Email: mary@gmail.com
 => #<User id: 10, email: "mary@gmail.com", password_digest: 
"$...", created_at: "2017-06-10 06:46:05", updated_at: "2017-07-02 
09:52:46", email_confirmation_token: "yStnErkVCFC9mYNHKFNYdA", 
terms_of_service: true, country_id: nil> 

我的问题是为什么回调文档中提到了块局部变量user的存在?正如您从上面所经历的那样可以看出,user局部变量未被使用,但我仍然可以访问实例化的User实例和/找到的实例。

3 个答案:

答案 0 :(得分:0)

这些没有record item的回调(在你的情况下是一个user实例)会有更少的意义(如果有的话) - 除了测试目的,就像你刚刚确认自己的记录有通过调用puts找到,通常情况下,在实际使用案例中,您可能需要在您的应用中使用此对象之前进行阅读/修改。

因此,当您定义回调时,ActiveRecord会在内部存储它们。 ActiveRecord假定您需要此实例,并且在调用块do ... end时,它会使用找到的记录(user)执行此操作。 理论上(我没有检查ActiveRecord代码库中的实际实现)可能看起来像:

 record = find_record
 if after_find_callback
   after_find_callback.call(record)
 end

答案 1 :(得分:0)

这不是必需的,它是可选的。您可以执行以下任一操作并获得相同的结果。假设User具有:name属性:

after_initialize do |user|
  puts user.name
end

after_initialize do
  puts name
end

我的猜测是文档使用|user|变量,因为它更清晰,特别是对于新的Ruby / Rails开发人员,当有明确的接收者时。

换句话说,第一个选项不会改变执行的上下文(或“词法范围”)和self的定义,而第二个选项会改变,这可能会让人感到困惑。< / p>

答案 2 :(得分:-1)

我认为它只是用于文档,因此人们可以知道块内部发生的事件是user的目标,是的,实际上并没有使用它。

您可以定义不含user变量:

after_initialize do
  puts "You have initialized an object! Email: #{email}"
end

来自after_initialize source code

def after_initialize(&block)
  ActiveSupport.on_load(:after_initialize, yield: true, &block)
end

source code of on_load method

# Declares a block that will be executed when a Rails component is fully
# loaded.
def on_load(name, options = {}, &block)
  @loaded[name].each do |base|
    execute_hook(base, options, block)
  end

  @load_hooks[name] << [block, options]
end

def execute_hook(base, options, block)
  if options[:yield]
    block.call(base)
  else
    base.instance_eval(&block)
  end
end

因为选项yield: true,将在execute_hook方法中调用该块:

if options[:yield]
  block.call(base)
...

所以,你的变量是什么并不重要。

顺便说一下,您也可以使用after_initialize方法代替阻止,就像其他回调一样(after_save, after_create):

after_initialize :after_initialize_method
def after_initialize_method
  puts "You have initialized an object! Email: #{email}"
end