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