lib/active_support/callbacks.rb in activesupport-4.0.0.beta1 vs lib/active_support/callbacks.rb in activesupport-4.0.0.rc1

- old
+ new

@@ -59,10 +59,12 @@ included do extend ActiveSupport::DescendantsTracker end + CALLBACK_FILTER_TYPES = [:before, :after, :around] + # 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. @@ -129,11 +131,17 @@ def next_id @@_callback_sequence += 1 end def matches?(_kind, _filter) - @kind == _kind && @filter == _filter + if @_is_object_filter + _filter_matches = @filter.to_s.start_with?(_method_name_for_object_filter(_kind, _filter, false)) + else + _filter_matches = (@filter == _filter) + end + + @kind == _kind && _filter_matches end def duplicates?(other) matches?(other.kind, other.filter) end @@ -232,10 +240,20 @@ end @compiled_options = conditions.flatten.join(" && ") end + def _method_name_for_object_filter(kind, filter, append_next_id = true) + class_name = filter.kind_of?(Class) ? filter.to_s : filter.class.to_s + class_name.gsub!(/<|>|#/, '') + class_name.gsub!(/\/|:/, "_") + + method_name = "_callback_#{kind}_#{class_name}" + method_name << "_#{next_id}" if append_next_id + method_name + end + # Filters support: # # Arrays:: Used in conditions. This is used to specify # multiple conditions. Used internally to # merge conditions from skip_* filters. @@ -253,24 +271,28 @@ # Procs:: define_method'ed into methods. # Objects:: # a method is created that calls the before_foo method # on the object. def _compile_filter(filter) - method_name = "_callback_#{@kind}_#{next_id}" + @_is_object_filter = false + case filter when Array filter.map {|f| _compile_filter(f)} when Symbol filter when String "(#{filter})" when Proc + method_name = "_callback_#{@kind}_#{next_id}" @klass.send(:define_method, method_name, &filter) return method_name if filter.arity <= 0 method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ") else + method_name = _method_name_for_object_filter(kind, filter) + @_is_object_filter = true @klass.send(:define_method, "#{method_name}_object") { filter } _normalize_legacy_filter(kind, filter) scopes = Array(chain.config[:scope]) method_to_call = scopes.map{ |s| s.is_a?(Symbol) ? send(s) : s }.join("_") @@ -312,18 +334,15 @@ def initialize(name, config) @name = name @config = { :terminator => "false", :scope => [ :kind ] - }.merge(config) + }.merge!(config) end def compile - method = [] - method << "value = nil" - method << "halted = false" - + method = ["value = nil", "halted = false"] callbacks = "value = !halted && (!block_given? || yield)" reverse_each do |callback| callbacks = callback.apply(callbacks) end method << callbacks @@ -393,10 +412,10 @@ end # This is used internally to append, prepend and skip callbacks to the # CallbackChain. def __update_callbacks(name, filters = [], block = nil) #:nodoc: - type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before + type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before options = filters.last.is_a?(Hash) ? filters.pop : {} filters.unshift(block) if block ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target| chain = target.send("_#{name}_callbacks")