跳至内容 跳至搜索

Active Support 回调

回调是在对象生命周期中的关键点运行的代码钩子。典型的用例是让一个基类定义一组与它提供的其他功能相关的回调,以便子类可以安装增强或修改基类功能的回调,而无需覆盖或重新定义基类的方法。

混合此模块允许您定义对象生命周期中将支持回调的事件(通过 ClassMethods#define_callbacks),设置要调用的实例方法、proc 或回调对象(通过 ClassMethods#set_callback),并在适当的时间运行已安装的回调(通过 run_callbacks)。

默认情况下,回调通过抛出 :abort 来中止。有关详细信息,请参见 ClassMethods#define_callbacks

支持三种类型的回调:before 回调,在某个事件之前运行;after 回调,在事件之后运行;around 回调,包围事件的块,在它们产生时触发事件。回调代码可以包含在实例方法、proc 或 lambda 中,也可以包含在响应特定预定方法的回调对象中。有关详细信息,请参见 ClassMethods#set_callback

class Record
  include ActiveSupport::Callbacks
  define_callbacks :save

  def save
    run_callbacks :save do
      puts "- save"
    end
  end
end

class PersonRecord < Record
  set_callback :save, :before, :saving_message
  def saving_message
    puts "saving..."
  end

  set_callback :save, :after do |object|
    puts "saved"
  end
end

person = PersonRecord.new
person.save

输出

saving...
- save
saved
命名空间
方法
R

常量

CALLBACK_FILTER_TYPES = [:before, :after, :around].freeze
 

实例公共方法

run_callbacks(kind, type = nil)

运行给定事件的回调。

按设置顺序调用 before 和 around 回调,生成块(如果有),然后以相反顺序运行 after 回调。

如果回调链被中止,则返回 false。否则返回块的结果,如果未设置回调,则返回 nil,如果设置了回调但未给出块,则返回 true

run_callbacks :save do
  save
end
# File activesupport/lib/active_support/callbacks.rb, line 96
def run_callbacks(kind, type = nil)
  callbacks = __callbacks[kind.to_sym]

  if callbacks.empty?
    yield if block_given?
  else
    env = Filters::Environment.new(self, false, nil)

    next_sequence = callbacks.compile(type)

    # Common case: no 'around' callbacks defined
    if next_sequence.final?
      next_sequence.invoke_before(env)
      env.value = !env.halted && (!block_given? || yield)
      next_sequence.invoke_after(env)
      env.value
    else
      invoke_sequence = Proc.new do
        skipped = nil

        while true
          current = next_sequence
          current.invoke_before(env)
          if current.final?
            env.value = !env.halted && (!block_given? || yield)
          elsif current.skip?(env)
            (skipped ||= []) << current
            next_sequence = next_sequence.nested
            next
          else
            next_sequence = next_sequence.nested
            begin
              target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
              target.send(method, *arguments, &block)
            ensure
              next_sequence = current
            end
          end
          current.invoke_after(env)
          skipped.pop.invoke_after(env) while skipped&.first
          break env.value
        end
      end

      invoke_sequence.call
    end
  end
end