lib/active_support/callbacks.rb in activesupport-4.1.16 vs lib/active_support/callbacks.rb in activesupport-4.2.0.beta1
- old
+ new
@@ -69,11 +69,12 @@
# 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, or +true+ if no block is given.
+ # 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
def run_callbacks(kind, &block)
@@ -115,28 +116,26 @@
end
end
ENDING = End.new
class Before
- def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter)
+ def self.build(next_callback, user_callback, user_conditions, chain_config, filter)
halted_lambda = chain_config[:terminator]
if chain_config.key?(:terminator) && user_conditions.any?
- halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
+ halting_and_conditional(next_callback, user_callback, user_conditions, halted_lambda, filter)
elsif chain_config.key? :terminator
- halting(callback_sequence, user_callback, halted_lambda, filter)
+ halting(next_callback, user_callback, halted_lambda, filter)
elsif user_conditions.any?
- conditional(callback_sequence, user_callback, user_conditions)
+ conditional(next_callback, user_callback, user_conditions)
else
- simple callback_sequence, user_callback
+ simple next_callback, user_callback
end
end
- private
-
- def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
- callback_sequence.before do |env|
+ def self.halting_and_conditional(next_callback, user_callback, user_conditions, halted_lambda, filter)
+ lambda { |env|
target = env.target
value = env.value
halted = env.halted
if !halted && user_conditions.all? { |c| c.call(target, value) }
@@ -144,17 +143,17 @@
env.halted = halted_lambda.call(target, result)
if env.halted
target.send :halted_callback_hook, filter
end
end
-
- env
- end
+ next_callback.call env
+ }
end
+ private_class_method :halting_and_conditional
- def self.halting(callback_sequence, user_callback, halted_lambda, filter)
- callback_sequence.before do |env|
+ def self.halting(next_callback, user_callback, halted_lambda, filter)
+ lambda { |env|
target = env.target
value = env.value
halted = env.halted
unless halted
@@ -162,183 +161,186 @@
env.halted = halted_lambda.call(target, result)
if env.halted
target.send :halted_callback_hook, filter
end
end
-
- env
- end
+ next_callback.call env
+ }
end
+ private_class_method :halting
- def self.conditional(callback_sequence, user_callback, user_conditions)
- callback_sequence.before do |env|
+ def self.conditional(next_callback, user_callback, user_conditions)
+ lambda { |env|
target = env.target
value = env.value
if user_conditions.all? { |c| c.call(target, value) }
user_callback.call target, value
end
-
- env
- end
+ next_callback.call env
+ }
end
+ private_class_method :conditional
- def self.simple(callback_sequence, user_callback)
- callback_sequence.before do |env|
+ def self.simple(next_callback, user_callback)
+ lambda { |env|
user_callback.call env.target, env.value
-
- env
- end
+ next_callback.call env
+ }
end
+ private_class_method :simple
end
class After
- def self.build(callback_sequence, user_callback, user_conditions, chain_config)
+ def self.build(next_callback, user_callback, user_conditions, chain_config)
if chain_config[:skip_after_callbacks_if_terminated]
if chain_config.key?(:terminator) && user_conditions.any?
- halting_and_conditional(callback_sequence, user_callback, user_conditions)
+ halting_and_conditional(next_callback, user_callback, user_conditions)
elsif chain_config.key?(:terminator)
- halting(callback_sequence, user_callback)
+ halting(next_callback, user_callback)
elsif user_conditions.any?
- conditional callback_sequence, user_callback, user_conditions
+ conditional next_callback, user_callback, user_conditions
else
- simple callback_sequence, user_callback
+ simple next_callback, user_callback
end
else
if user_conditions.any?
- conditional callback_sequence, user_callback, user_conditions
+ conditional next_callback, user_callback, user_conditions
else
- simple callback_sequence, user_callback
+ simple next_callback, user_callback
end
end
end
- private
-
- def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
- callback_sequence.after do |env|
+ def self.halting_and_conditional(next_callback, user_callback, user_conditions)
+ lambda { |env|
+ env = next_callback.call env
target = env.target
value = env.value
halted = env.halted
if !halted && user_conditions.all? { |c| c.call(target, value) }
user_callback.call target, value
end
-
env
- end
+ }
end
+ private_class_method :halting_and_conditional
- def self.halting(callback_sequence, user_callback)
- callback_sequence.after do |env|
+ def self.halting(next_callback, user_callback)
+ lambda { |env|
+ env = next_callback.call env
unless env.halted
user_callback.call env.target, env.value
end
-
env
- end
+ }
end
+ private_class_method :halting
- def self.conditional(callback_sequence, user_callback, user_conditions)
- callback_sequence.after do |env|
+ def self.conditional(next_callback, user_callback, user_conditions)
+ lambda { |env|
+ env = next_callback.call env
target = env.target
value = env.value
if user_conditions.all? { |c| c.call(target, value) }
user_callback.call target, value
end
-
env
- end
+ }
end
+ private_class_method :conditional
- def self.simple(callback_sequence, user_callback)
- callback_sequence.after do |env|
+ def self.simple(next_callback, user_callback)
+ lambda { |env|
+ env = next_callback.call env
user_callback.call env.target, env.value
-
env
- end
+ }
end
+ private_class_method :simple
end
class Around
- def self.build(callback_sequence, user_callback, user_conditions, chain_config)
+ def self.build(next_callback, user_callback, user_conditions, chain_config)
if chain_config.key?(:terminator) && user_conditions.any?
- halting_and_conditional(callback_sequence, user_callback, user_conditions)
+ halting_and_conditional(next_callback, user_callback, user_conditions)
elsif chain_config.key? :terminator
- halting(callback_sequence, user_callback)
+ halting(next_callback, user_callback)
elsif user_conditions.any?
- conditional(callback_sequence, user_callback, user_conditions)
+ conditional(next_callback, user_callback, user_conditions)
else
- simple(callback_sequence, user_callback)
+ simple(next_callback, user_callback)
end
end
- private
-
- def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
- callback_sequence.around do |env, &run|
+ def self.halting_and_conditional(next_callback, user_callback, user_conditions)
+ lambda { |env|
target = env.target
value = env.value
halted = env.halted
if !halted && user_conditions.all? { |c| c.call(target, value) }
user_callback.call(target, value) {
- env = run.call env
+ env = next_callback.call env
env.value
}
-
env
else
- run.call env
+ next_callback.call env
end
- end
+ }
end
+ private_class_method :halting_and_conditional
- def self.halting(callback_sequence, user_callback)
- callback_sequence.around do |env, &run|
+ def self.halting(next_callback, user_callback)
+ lambda { |env|
target = env.target
value = env.value
if env.halted
- run.call env
+ next_callback.call env
else
user_callback.call(target, value) {
- env = run.call env
+ env = next_callback.call env
env.value
}
env
end
- end
+ }
end
+ private_class_method :halting
- def self.conditional(callback_sequence, user_callback, user_conditions)
- callback_sequence.around do |env, &run|
+ def self.conditional(next_callback, user_callback, user_conditions)
+ lambda { |env|
target = env.target
value = env.value
if user_conditions.all? { |c| c.call(target, value) }
user_callback.call(target, value) {
- env = run.call env
+ env = next_callback.call env
env.value
}
env
else
- run.call env
+ next_callback.call env
end
- end
+ }
end
+ private_class_method :conditional
- def self.simple(callback_sequence, user_callback)
- callback_sequence.around do |env, &run|
+ def self.simple(next_callback, user_callback)
+ lambda { |env|
user_callback.call(env.target, env.value) {
- env = run.call env
+ env = next_callback.call env
env.value
}
env
- end
+ }
end
+ private_class_method :simple
end
end
class Callback #:nodoc:#
def self.build(chain, filter, kind, options)
@@ -385,21 +387,21 @@
false
end
end
# Wraps code with filter
- def apply(callback_sequence)
+ def apply(next_callback)
user_conditions = conditions_lambdas
user_callback = make_lambda @filter
case kind
when :before
- Filters::Before.build(callback_sequence, user_callback, user_conditions, chain_config, @filter)
+ Filters::Before.build(next_callback, user_callback, user_conditions, chain_config, @filter)
when :after
- Filters::After.build(callback_sequence, user_callback, user_conditions, chain_config)
+ Filters::After.build(next_callback, user_callback, user_conditions, chain_config)
when :around
- Filters::Around.build(callback_sequence, user_callback, user_conditions, chain_config)
+ Filters::Around.build(next_callback, user_callback, user_conditions, chain_config)
end
end
private
@@ -412,19 +414,12 @@
# Symbols:: A method to call.
# Strings:: Some content to evaluate.
# Procs:: A proc to call with the object.
# Objects:: An object with a <tt>before_foo</tt> method on it to call.
#
- # All of these objects are compiled into methods and handled
- # the same after this point:
- #
- # Symbols:: Already methods.
- # Strings:: class_eval'd into methods.
- # Procs:: using define_method compiled into methods.
- # Objects::
- # a method is created that calls the before_foo method
- # on the object.
+ # All of these objects are converted into a lambda and handled
+ # the same after this point.
def make_lambda(filter)
case filter
when Symbol
lambda { |target, _, &blk| target.send filter, &blk }
when String
@@ -467,46 +462,10 @@
@if.map { |c| make_lambda c } +
@unless.map { |c| invert_lambda make_lambda c }
end
end
- # Execute before and after filters in a sequence instead of
- # chaining them with nested lambda calls, see:
- # https://github.com/rails/rails/issues/18011
- class CallbackSequence
- def initialize(&call)
- @call = call
- @before = []
- @after = []
- end
-
- def before(&before)
- @before.unshift(before)
- self
- end
-
- def after(&after)
- @after.push(after)
- self
- end
-
- def around(&around)
- CallbackSequence.new do |*args|
- around.call(*args) {
- self.call(*args)
- }
- end
- end
-
- def call(*args)
- @before.each { |b| b.call(*args) }
- value = @call.call(*args)
- @after.each { |a| a.call(*args) }
- value
- end
- end
-
# An Array with a compile method.
class CallbackChain #:nodoc:#
include Enumerable
attr_reader :name, :config
@@ -547,13 +506,12 @@
@mutex = Mutex.new
end
def compile
@callbacks || @mutex.synchronize do
- final_sequence = CallbackSequence.new { |env| Filters::ENDING.call(env) }
- @callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
- callback.apply callback_sequence
+ @callbacks ||= @chain.reverse.inject(Filters::ENDING) do |chain, callback|
+ callback.apply chain
end
end
end
def append(*callbacks)
@@ -606,11 +564,11 @@
# Install a callback for the given event.
#
# set_callback :save, :before, :before_meth
# set_callback :save, :after, :after_meth, if: :condition
- # set_callback :save, :around, ->(r, &block) { stuff; result = block.call; stuff }
+ # set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }
#
# The second arguments indicates whether the callback is to be run +:before+,
# +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
# means the first example above can also be written as:
#
@@ -758,18 +716,12 @@
# define_callbacks :save, scope: [:name]
#
# would call <tt>Audit#save</tt>.
def define_callbacks(*names)
options = names.extract_options!
- if options.key?(:terminator) && String === options[:terminator]
- ActiveSupport::Deprecation.warn "String based terminators are deprecated, please use a lambda"
- value = options[:terminator]
- line = class_eval "lambda { |result| #{value} }", __FILE__, __LINE__
- options[:terminator] = lambda { |target, result| target.instance_exec(result, &line) }
- end
names.each do |name|
- class_attribute "_#{name}_callbacks", instance_writer: false
+ class_attribute "_#{name}_callbacks"
set_callbacks name, CallbackChain.new(name, options)
end
end
protected