diff --git a/actionpack/lib/action_controller/abstract/callbacks.rb b/actionpack/lib/action_controller/abstract/callbacks.rb index affe053baceb5..dd4213e847f8a 100644 --- a/actionpack/lib/action_controller/abstract/callbacks.rb +++ b/actionpack/lib/action_controller/abstract/callbacks.rb @@ -32,32 +32,32 @@ def skip_filter(*names, &blk) skip_around_filter(*names, &blk) end + def _insert_callbacks(names, block) + options = names.last.is_a?(Hash) ? names.pop : {} + _normalize_callback_options(options) + names.push(block) if block + names.each do |name| + yield name, options + end + end + [:before, :after, :around].each do |filter| class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def #{filter}_filter(*names, &blk) - options = names.last.is_a?(Hash) ? names.pop : {} - _normalize_callback_options(options) - names.push(blk) if block_given? - names.each do |name| - process_action_callback(:#{filter}, name, options) + _insert_callbacks(names, blk) do |name, options| + _set_callback(:process_action, :#{filter}, name, options) end end def prepend_#{filter}_filter(*names, &blk) - options = names.last.is_a?(Hash) ? names.pop : {} - _normalize_callback_options(options) - names.push(blk) if block_given? - names.each do |name| - process_action_callback(:#{filter}, name, options.merge(:prepend => true)) + _insert_callbacks(names, blk) do |name, options| + _set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true)) end end def skip_#{filter}_filter(*names, &blk) - options = names.last.is_a?(Hash) ? names.pop : {} - _normalize_callback_options(options) - names.push(blk) if block_given? - names.each do |name| - skip_process_action_callback(:#{filter}, name, options) + _insert_callbacks(names, blk) do |name, options| + _skip_callback(:process_action, :#{filter}, name, options) end end diff --git a/actionpack/test/abstract_controller/callbacks_test.rb b/actionpack/test/abstract_controller/callbacks_test.rb index 1de60868c3e9e..826e76892ed02 100644 --- a/actionpack/test/abstract_controller/callbacks_test.rb +++ b/actionpack/test/abstract_controller/callbacks_test.rb @@ -8,7 +8,7 @@ class ControllerWithCallbacks < AbstractController::Base end class Callback1 < ControllerWithCallbacks - process_action_callback :before, :first + _set_callback :process_action, :before, :first def first @text = "Hello world" diff --git a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb index cca93a0b9f7f3..8bac2dff192dd 100644 --- a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb +++ b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb @@ -202,6 +202,8 @@ def self.#{ivar}=(obj) def #{ivar}=(obj) self.class.#{ivar} = obj end RUBY end + + self.send("#{ivar}=", yield) if block_given? end end @@ -214,8 +216,8 @@ def #{ivar}=(obj) self.class.#{ivar} = obj end # @return An Array of attributes turned into inheritable accessors. # # @api public - def extlib_inheritable_accessor(*syms) + def extlib_inheritable_accessor(*syms, &block) extlib_inheritable_reader(*syms) - extlib_inheritable_writer(*syms) + extlib_inheritable_writer(*syms, &block) end end diff --git a/activesupport/lib/active_support/new_callbacks.rb b/activesupport/lib/active_support/new_callbacks.rb index 58d4c47ccb132..86cf5c01a8013 100644 --- a/activesupport/lib/active_support/new_callbacks.rb +++ b/activesupport/lib/active_support/new_callbacks.rb @@ -385,8 +385,10 @@ module ClassMethods # The _run_save_callbacks method can optionally take a key, which # will be used to compile an optimized callback method for each # key. See #define_callbacks for more information. - def _define_runner(symbol, str, options) - str = <<-RUBY_EVAL + def _define_runner(symbol, callbacks, options) + body = callbacks.compile(nil, :terminator => send("_#{symbol}_terminator")) + + body = <<-RUBY_EVAL def _run_#{symbol}_callbacks(key = nil) if key key = key.hash.to_s.gsub(/-/, '_') @@ -400,13 +402,13 @@ def _run_#{symbol}_callbacks(key = nil) :#{symbol}, key, self) { yield if block_given? } end else - #{str} + #{body} end end RUBY_EVAL undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks") - class_eval str, __FILE__, __LINE__ + class_eval body, __FILE__, __LINE__ before_name, around_name, after_name = options.values_at(:before, :after, :around) @@ -463,60 +465,62 @@ def _run__#{klass.split("::").last}__#{kind}__#{key}__callbacks # In that case, each action_name would get its own compiled callback # method that took into consideration the per_key conditions. This # is a speed improvement for ActionPack. + def _update_callbacks(name, filters = CallbackChain.new(name), block = nil) + type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before + options = filters.last.is_a?(Hash) ? filters.pop : {} + filters.unshift(block) if block + + callbacks = send("_#{name}_callbacks") + yield callbacks, type, filters, options if block_given? + + _define_runner(name, callbacks, options) + end + + def _set_callback(name, *filters, &block) + _update_callbacks(name, filters, block) do |callbacks, type, filters, options| + filters.map! do |filter| + # overrides parent class + callbacks.delete_if do |c| + c.matches?(type, name, filter) + end + Callback.new(filter, type, options.dup, self, name) + end + + options[:prepend] ? callbacks.unshift(*filters) : callbacks.push(*filters) + end + end + + def _skip_callback(name, *filters, &block) + _update_callbacks(name, filters, block) do |callbacks, type, filters, options| + filters.each do |filter| + callbacks = send("_#{name}_callbacks=", callbacks.clone(self)) + + filter = callbacks.find {|c| c.matches?(type, name, filter) } + per_key = options[:per_key] || {} + if filter && options.any? + filter.recompile!(options, per_key) + else + callbacks.delete(filter) + end + end + end + end + def define_callbacks(*symbols) terminator = symbols.pop if symbols.last.is_a?(String) symbols.each do |symbol| - self.extlib_inheritable_accessor("_#{symbol}_terminator") - self.send("_#{symbol}_terminator=", terminator) - self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - extlib_inheritable_accessor :_#{symbol}_callbacks - self._#{symbol}_callbacks = CallbackChain.new(:#{symbol}) + extlib_inheritable_accessor("_#{symbol}_terminator") { terminator } - def self.#{symbol}_callback(*filters, &blk) - type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before - options = filters.last.is_a?(Hash) ? filters.pop : {} - filters.unshift(blk) if block_given? - - filters.map! do |filter| - # overrides parent class - self._#{symbol}_callbacks.delete_if {|c| c.matches?(type, :#{symbol}, filter)} - Callback.new(filter, type, options.dup, self, :#{symbol}) - end - options[:prepend] ? - self._#{symbol}_callbacks.unshift(*filters) : - self._#{symbol}_callbacks.push(*filters) - _define_runner(:#{symbol}, - self._#{symbol}_callbacks.compile(nil, :terminator => _#{symbol}_terminator), - options) - end - - def self.skip_#{symbol}_callback(*filters, &blk) - type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before - options = filters.last.is_a?(Hash) ? filters.pop : {} - filters.unshift(blk) if block_given? - filters.each do |filter| - self._#{symbol}_callbacks = self._#{symbol}_callbacks.clone(self) - - filter = self._#{symbol}_callbacks.find {|c| c.matches?(type, :#{symbol}, filter) } - per_key = options[:per_key] || {} - if filter && options.any? - filter.recompile!(options, per_key) - else - self._#{symbol}_callbacks.delete(filter) - end - _define_runner(:#{symbol}, - self._#{symbol}_callbacks.compile(nil, :terminator => _#{symbol}_terminator), - options) - end - - end - + extlib_inheritable_accessor("_#{symbol}_callbacks") do + CallbackChain.new(symbol) + end + + self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def self.reset_#{symbol}_callbacks - self._#{symbol}_callbacks = CallbackChain.new(:#{symbol}) - _define_runner(:#{symbol}, self._#{symbol}_callbacks.compile, {}) + _update_callbacks(:#{symbol}) end - self.#{symbol}_callback(:before) + self._set_callback(:#{symbol}, :before) RUBY_EVAL end end diff --git a/activesupport/test/new_callback_inheritance_test.rb b/activesupport/test/new_callback_inheritance_test.rb index 95020389b0b82..9a1882b9d3d2d 100644 --- a/activesupport/test/new_callback_inheritance_test.rb +++ b/activesupport/test/new_callback_inheritance_test.rb @@ -11,8 +11,8 @@ def initialize(action_name) end define_callbacks :dispatch - dispatch_callback :before, :before1, :before2, :per_key => {:if => proc {|c| c.action_name == "index" || c.action_name == "update" }} - dispatch_callback :after, :after1, :after2, :per_key => {:if => proc {|c| c.action_name == "update" || c.action_name == "delete" }} + _set_callback :dispatch, :before, :before1, :before2, :per_key => {:if => proc {|c| c.action_name == "index" || c.action_name == "update" }} + _set_callback :dispatch, :after, :after1, :after2, :per_key => {:if => proc {|c| c.action_name == "update" || c.action_name == "delete" }} def before1 @log << "before1" @@ -39,12 +39,12 @@ def dispatch end class Parent < GrandParent - skip_dispatch_callback :before, :before2, :per_key => {:unless => proc {|c| c.action_name == "update" }} - skip_dispatch_callback :after, :after2, :per_key => {:unless => proc {|c| c.action_name == "delete" }} + _skip_callback :dispatch, :before, :before2, :per_key => {:unless => proc {|c| c.action_name == "update" }} + _skip_callback :dispatch, :after, :after2, :per_key => {:unless => proc {|c| c.action_name == "delete" }} end class Child < GrandParent - skip_dispatch_callback :before, :before2, :per_key => {:unless => proc {|c| c.action_name == "update" }}, :if => :state_open? + _skip_callback :dispatch, :before, :before2, :per_key => {:unless => proc {|c| c.action_name == "update" }}, :if => :state_open? def state_open? @state == :open diff --git a/activesupport/test/new_callbacks_test.rb b/activesupport/test/new_callbacks_test.rb index 7bec47224db23..de501e9f7b876 100644 --- a/activesupport/test/new_callbacks_test.rb +++ b/activesupport/test/new_callbacks_test.rb @@ -10,11 +10,11 @@ class Record define_callbacks :save def self.before_save(*filters, &blk) - save_callback(:before, *filters, &blk) + _set_callback(:save, :before, *filters, &blk) end def self.after_save(*filters, &blk) - save_callback(:after, *filters, &blk) + _set_callback(:save, :after, *filters, &blk) end class << self @@ -64,10 +64,10 @@ def save end class PersonSkipper < Person - skip_save_callback :before, :before_save_method, :if => :yes - skip_save_callback :after, :before_save_method, :unless => :yes - skip_save_callback :after, :before_save_method, :if => :no - skip_save_callback :before, :before_save_method, :unless => :no + _skip_callback :save, :before, :before_save_method, :if => :yes + _skip_callback :save, :after, :before_save_method, :unless => :yes + _skip_callback :save, :after, :before_save_method, :if => :no + _skip_callback :save, :before, :before_save_method, :unless => :no def yes; true; end def no; false; end end @@ -77,8 +77,8 @@ class ParentController define_callbacks :dispatch - dispatch_callback :before, :log, :per_key => {:unless => proc {|c| c.action_name == :index || c.action_name == :show }} - dispatch_callback :after, :log2 + _set_callback :dispatch, :before, :log, :per_key => {:unless => proc {|c| c.action_name == :index || c.action_name == :show }} + _set_callback :dispatch, :after, :log2 attr_reader :action_name, :logger def initialize(action_name) @@ -102,8 +102,8 @@ def dispatch end class Child < ParentController - skip_dispatch_callback :before, :log, :per_key => {:if => proc {|c| c.action_name == :update} } - skip_dispatch_callback :after, :log2 + _skip_callback :dispatch, :before, :log, :per_key => {:if => proc {|c| c.action_name == :update} } + _skip_callback :dispatch, :after, :log2 end class OneTimeCompile < Record @@ -188,19 +188,19 @@ class MySuper class AroundPerson < MySuper attr_reader :history - save_callback :before, :nope, :if => :no - save_callback :before, :nope, :unless => :yes - save_callback :after, :tweedle - save_callback :before, "tweedle_dee" - save_callback :before, proc {|m| m.history << "yup" } - save_callback :before, :nope, :if => proc { false } - save_callback :before, :nope, :unless => proc { true } - save_callback :before, :yup, :if => proc { true } - save_callback :before, :yup, :unless => proc { false } - save_callback :around, :tweedle_dum - save_callback :around, :w0tyes, :if => :yes - save_callback :around, :w0tno, :if => :no - save_callback :around, :tweedle_deedle + _set_callback :save, :before, :nope, :if => :no + _set_callback :save, :before, :nope, :unless => :yes + _set_callback :save, :after, :tweedle + _set_callback :save, :before, "tweedle_dee" + _set_callback :save, :before, proc {|m| m.history << "yup" } + _set_callback :save, :before, :nope, :if => proc { false } + _set_callback :save, :before, :nope, :unless => proc { true } + _set_callback :save, :before, :yup, :if => proc { true } + _set_callback :save, :before, :yup, :unless => proc { false } + _set_callback :save, :around, :tweedle_dum + _set_callback :save, :around, :w0tyes, :if => :yes + _set_callback :save, :around, :w0tno, :if => :no + _set_callback :save, :around, :tweedle_deedle def no; false; end def yes; true; end @@ -260,7 +260,7 @@ class HyphenatedCallbacks define_callbacks :save attr_reader :stuff - save_callback :before, :omg, :per_key => {:if => :yes} + _set_callback :save, :before, :omg, :per_key => {:if => :yes} def yes() true end @@ -354,15 +354,15 @@ class CallbackTerminator define_callbacks :save, "result == :halt" - save_callback :before, :first - save_callback :before, :second - save_callback :around, :around_it - save_callback :before, :third - save_callback :after, :first - save_callback :around, :around_it - save_callback :after, :second - save_callback :around, :around_it - save_callback :after, :third + _set_callback :save, :before, :first + _set_callback :save, :before, :second + _set_callback :save, :around, :around_it + _set_callback :save, :before, :third + _set_callback :save, :after, :first + _set_callback :save, :around, :around_it + _set_callback :save, :after, :second + _set_callback :save, :around, :around_it + _set_callback :save, :after, :third attr_reader :history, :saved @@ -412,7 +412,7 @@ class UsingObjectBefore include ActiveSupport::NewCallbacks define_callbacks :save - save_callback :before, CallbackObject.new + _set_callback :save, :before, CallbackObject.new attr_accessor :record def initialize @@ -430,7 +430,7 @@ class UsingObjectAround include ActiveSupport::NewCallbacks define_callbacks :save - save_callback :around, CallbackObject.new + _set_callback :save, :around, CallbackObject.new attr_accessor :record def initialize