модуль ActiveSupport::Callbacks::ClassMethods
Публичные методы экземпляров
# File activesupport/lib/active_support/callbacks.rb, line 900
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 Определяет наборы событий в жизненном цикле объекта, которые поддерживают обратные вызовы.
define_callbacks :validate define_callbacks :initialize, :save, :destroy
Параметры
-
:terminator- Определяет, когда фильтр перед (before) прервёт цепочку обратных вызовов, предотвращая вызов последующих фильтров перед (before) и вокруг (around), а также срабатывание события. Это должна быть лямбда-функция для выполнения. Текущий объект и результат лямбда-функции обратного вызова будут переданы в лямбда-функцию-терминатор.define_callbacks :validate, terminator: ->(target, result_lambda) { result_lambda.call == false }В этом примере, если любой обратный вызов перед валидацией возвращает
false, любые последующие обратные вызовы перед (before) и вокруг (around) не будут выполнены.По умолчанию, терминатор прерывает цепочку, когда обратный вызов генерирует
: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относится к типу обратного вызова (перед/после/вокруг), а:nameотносится к методу, для которого определяются обратные вызовы.Декларация вида
define_callbacks :save, scope: [:name]
вызовет метод
Audit#save.
Примечания
names передаваемый в define_callbacks не должен оканчиваться на !, ? или =.
Вызов define_callbacks несколько раз с тем же names перепишет ранее зарегистрированные обратные вызовы с помощью set_callback.
# File activesupport/lib/active_support/callbacks.rb, line 810
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 Удаляет все установленные обратные вызовы для данного события.
# File activesupport/lib/active_support/callbacks.rb, line 736
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 Устанавливает обратный вызов для данного события.
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 или блок; или как объект, который отвечает на определённый метод, определённый аргументом :scope в define_callbacks.
Если задан proc, lambda или блок, его тело оценивается в контексте текущего объекта. Он также может опционально принимать текущий объект в качестве аргумента.
Обратные вызовы перед (before) и вокруг (around) вызываются в порядке, в котором они заданы; обратные вызовы после (after) вызываются в обратном порядке.
Обратные вызовы вокруг (around) могут получить значение возврата из события, если оно не было прервано, из вызова yield.
Параметры
-
:if- Символ или массив символов, каждый из которых является именем метода экземпляра или proc; обратный вызов будет вызван только тогда, когда они все вернут true.Если задан proc, его тело оценивается в контексте текущего объекта. Он также может опционально принимать текущий объект в качестве аргумента.
-
:unless- Символ или массив символов, каждый из которых является именем метода экземпляра или proc; обратный вызов будет вызван только тогда, когда они все вернут false.Если задан proc, его тело оценивается в контексте текущего объекта. Он также может опционально принимать текущий объект в качестве аргумента.
-
:prepend- Еслиtrue, обратный вызов будет добавлен в начало существующей цепочки, а не в конец.
# File activesupport/lib/active_support/callbacks.rb, line 785
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 Пропускает ранее установленный обратный вызов. Как и с 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).
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.