跳到內容 跳到搜尋

Active Support 回呼

回呼是物件生命週期中關鍵點執行的程式碼掛勾。典型的使用案例是讓基底類別定義一組與其提供的其他功能相關的回呼,以便子類別可以安裝回呼來增強或修改基底功能,而不需要覆寫或重新定義基底類別的方法。

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

預設情況下,回呼會透過拋出 :abort 來停止。有關詳細資訊,請參閱 ClassMethods#define_callbacks

支援三種類型的回呼:before 回呼,在特定事件之前執行;after 回呼,在事件之後執行;以及 around 回呼,圍繞事件的區塊,在 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)

執行給定事件的回呼。

按設定順序呼叫 before 和 around 回呼,讓出區塊(如果給定),然後按相反順序執行 after 回呼。

如果回呼鏈已暫停,則傳回 false。否則傳回區塊的結果、未設定任何回呼時傳回 nil,或已設定回呼但未給定區塊時傳回 true

run_callbacks :save do
  save
end
# File activesupport/lib/active_support/callbacks.rb, line 97
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