跳到內容 跳到搜尋
方法
D
R
S

實例公開方法

define_callbacks(*names)

定義物件生命週期中支援回呼的事件組。

define_callbacks :validate
define_callbacks :initialize, :save, :destroy
選項
  • :terminator - 決定何時 before 篩選器將停止回呼鏈,防止後續的 before 和 around 回呼被呼叫,以及事件被觸發。這應該是一個要執行的 lambda。目前的物件和回呼的結果 lambda 將提供給 terminator lambda。

    define_callbacks :validate, terminator: ->(target, result_lambda) { result_lambda.call == false }
    

    在此範例中,如果任何 before 驗證回呼傳回 false,任何後續的 before 和 around 回呼都不會被執行。

    預設的 terminator 在回呼擲回 :abort 時停止鏈。

  • :skip_after_callbacks_if_terminated - 決定 after 回呼是否應由 :terminator 選項終止。預設上,無論回呼鏈是否終止,都會執行 after 回呼。如果 :terminator 選項設為 nil,此選項無效。

  • :scope - 指出在物件用作回呼時應執行哪些方法。

    class Audit
      def before(caller)
        puts 'Audit: before is called'
      end
    
      def before_save(caller)
        puts 'Audit: before_save is called'
      end
    end
    
    class Account
      include ActiveSupport::Callbacks
    
      define_callbacks :save
      set_callback :save, :before, Audit.new
    
      def save
        run_callbacks :save do
          puts 'save in main'
        end
      end
    end
    

    在上述情況中,每當您儲存帳戶時,方法 Audit#before 將會被呼叫。另一方面

    define_callbacks :save, scope: [:kind, :name]
    

    反之,會觸發 Audit#before_save。這是透過在給定的實例上呼叫 #{kind}_#{name} 來建構的。在此情況下,「kind」為「before」,而「name」為「save」。在此脈絡中,:kind:name 具有特殊意義::kind 指的是回呼的類型(before/after/around),而 :name 指的是定義回呼的方法。

    類似這樣的宣告

    define_callbacks :save, scope: [:name]
    

    會呼叫 Audit#save

注意事項

傳遞給 define_callbacksnames 不能以 !?= 結尾。

使用相同的 names 多次呼叫 define_callbacks 會覆寫先前使用 set_callback 註冊的回呼。

# File activesupport/lib/active_support/callbacks.rb, line 940
        def define_callbacks(*names)
          options = names.extract_options!

          names.each do |name|
            name = name.to_sym

            ([self] + self.descendants).each do |target|
              target.set_callbacks name, CallbackChain.new(name, options)
            end

            module_eval <<-RUBY, __FILE__, __LINE__ + 1
              def _run_#{name}_callbacks(&block)
                run_callbacks #{name.inspect}, &block
              end

              def self._#{name}_callbacks
                get_callbacks(#{name.inspect})
              end

              def self._#{name}_callbacks=(value)
                set_callbacks(#{name.inspect}, value)
              end

              def _#{name}_callbacks
                __callbacks[#{name.inspect}]
              end
            RUBY
          end
        end

reset_callbacks(name)

移除給定事件的所有已設定回呼。

# File activesupport/lib/active_support/callbacks.rb, line 850
def reset_callbacks(name)
  callbacks = get_callbacks name

  self.descendants.each do |target|
    chain = target.get_callbacks(name).dup
    callbacks.each { |c| chain.delete(c) }
    target.set_callbacks name, chain
  end

  set_callbacks(name, callbacks.dup.clear)
end

set_callback(name, *filter_list, &block)

安裝給定事件的回呼。

set_callback :save, :before, :before_method
set_callback :save, :after,  :after_method, if: :condition
set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }

第二個參數表示回呼是在事件的 :before:after:around 中執行。如果省略,則假設為 :before。這表示上面的第一個範例也可以寫成

set_callback :save, :before_method

回呼可以指定為命名實例方法的符號;作為程序、lambda 或區塊;或作為回應由 define_callbacks:scope 參數決定的特定方法的物件。

如果給定程序、lambda 或區塊,其主體會在目前物件的脈絡中評估。它也可以選擇接受目前物件作為參數。

Before 和 around 回呼會按照設定順序呼叫;after 回呼會按照相反順序呼叫。

如果事件沒有中斷,around 回呼可以從 yield 呼叫存取其回傳值。

選項
  • :if - 符號或符號陣列,每個符號都命名實例方法或程序;只有當它們全部回傳 true 值時,才會呼叫回呼。

    如果給定程序,其主體會在目前物件的脈絡中評估。它也可以選擇接受目前物件作為參數。

  • :unless - 一個符號或符號陣列,每個符號命名一個實例方法或一個程序;只有當它們全部傳回 false 值時,才會呼叫回呼。

    如果給定程序,其主體會在目前物件的脈絡中評估。它也可以選擇接受目前物件作為參數。

  • :prepend - 如果為 true,會將回呼加到現有鏈之前,而不是加到後面。

# File activesupport/lib/active_support/callbacks.rb, line 776
def set_callback(name, *filter_list, &block)
  type, filters, options = normalize_callback_params(filter_list, block)

  self_chain = get_callbacks name
  mapped = filters.map do |filter|
    Callback.build(self_chain, filter, type, options)
  end

  __update_callbacks(name) do |target, chain|
    options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
    target.set_callbacks name, chain
  end
end

skip_callback(name, *filter_list, &block)

略過先前設定的回呼。如同 set_callback,可以傳入 :if:unless 選項,以控制何時略過回呼。

注意:此範例使用 PersonRecord#saving_message,您可以在 這裡 看到它們的定義

class Writer < PersonRecord
  attr_accessor :age
  skip_callback :save, :before, :saving_message, if: -> { age > 18 }
end

當 if 選項傳回 true 時,會略過回呼。

writer = Writer.new
writer.age = 20
writer.save

輸出

- save
saved

當 if 選項傳回 false 時,會略過回呼。

young_writer = Writer.new
young_writer.age = 17
young_writer.save

輸出

saving...
- save
saved

如果尚未設定回呼(除非 :raise 選項設定為 false),將會引發 ArgumentError

# File activesupport/lib/active_support/callbacks.rb, line 825
def skip_callback(name, *filter_list, &block)
  type, filters, options = normalize_callback_params(filter_list, block)

  options[:raise] = true unless options.key?(:raise)

  __update_callbacks(name) do |target, chain|
    filters.each do |filter|
      callback = chain.find { |c| c.matches?(type, filter) }

      if !callback && options[:raise]
        raise ArgumentError, "#{type.to_s.capitalize} #{name} callback #{filter.inspect} has not been defined"
      end

      if callback && (options.key?(:if) || options.key?(:unless))
        new_callback = callback.merge_conditional_options(chain, if_option: options[:if], unless_option: options[:unless])
        chain.insert(chain.index(callback), new_callback)
      end

      chain.delete(callback)
    end
    target.set_callbacks name, chain
  end
end