跳至內文 跳至搜尋

Active Support 回呼

回呼是在物件生命週期間,於關鍵點執行的程式碼鉤子。典型用途是讓基本類別定義與其提供之其他功能相關的一組回呼,讓子類別得以安裝回呼,以增強或修改基本功能,而不必覆寫或重新定義基本類別的方法。

混用這個模組,可以定義物件生命週期中會支援回呼的事件(透過 ClassMethods#define_callbacks),設定要呼叫的實例方法、程式碼或回呼物件(透過 ClassMethods#set_callback),並在適當時間執行已安裝的回呼(透過 run_callbacks)。

預設會以拋出 :abort 中止回呼。詳細資訊,請見 ClassMethods#define_callbacks

支援三種類型的回呼:之前回呼,在特定事件之前執行;之後回呼,在事件之後執行;以及周圍回呼,將事件包圍起來的區塊,在執行 yield 時引發該事件。回呼程式碼可以包含在實例方法、程式碼或 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)

執行給定事件的回呼。

依據設定順序呼叫之前和周圍回呼,執行區塊(如果提供了一個區塊),然後按反序執行之後回呼。

如果回呼鏈已終止,則傳回 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