跳至内容 跳至搜索
方法
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 - 确定是否应由 :terminator 选项终止 after 回调。默认情况下,无论回调链是否终止,都将执行 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”是定义回调的方法。在此上下文中,: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

回调可以指定为命名实例方法的符号;作为 proc、lambda 或块;或作为响应由 define_callbacks:scope 参数确定的特定方法的对象。

如果给出了 proc、lambda 或块,则其主体在当前对象的上下文中进行评估。它还可以选择接受当前对象作为参数。

在设置之前和周围的回调按设置的顺序调用;在相反的顺序中调用之后回调。

如果未停止,则周围回调可以从yield 调用中访问事件的返回值。

选项
  • :if - 一个符号或一组符号,每个符号都命名一个实例方法或一个 proc;只有当它们全部返回 true 值时,才会调用回调。

    如果给出了 proc,则其主体在当前对象的上下文中进行评估。它还可以选择接受当前对象作为参数。

  • :unless - 一个符号或一组符号,每个符号都命名一个实例方法或一个 proc;只有当它们全部返回 false 值时,才会调用回调。

    如果给出了 proc,则其主体在当前对象的上下文中进行评估。它还可以选择接受当前对象作为参数。

  • :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

如果尚未设置回调,将引发 ArgumentError(除非 :raise 选项设置为 false)。

# 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