Method: ActiveSupport::Callbacks#run_callbacks
- Defined in:
- activesupport/lib/active_support/callbacks.rb
#run_callbacks(kind, type = nil) ⇒ Object
Runs the callbacks for the given event.
Calls the before and around callbacks in the order they were set, yields the block (if given one), and then runs the after callbacks in reverse order.
If the callback chain was halted, returns false. Otherwise returns the result of the block, nil if no callbacks have been set, or true if callbacks have been set but no block is given.
run_callbacks :save do
save
end
–
As this method is used in many places, and often wraps large portions of user code, it has an additional design goal of minimizing its impact on the visible call stack. An exception from inside a :before or :after callback can be as noisy as it likes – but when control has passed smoothly through and into the supplied block, we want as little evidence as possible that we were here.
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# 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.(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 |