<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>activesupport/lib/active_support/callbacks.rb</filename>
    </added>
    <added>
      <filename>activesupport/test/callbacks_test.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -11,20 +11,6 @@ module ActionController
         new(output).dispatch_cgi(cgi, session_options)
       end
 
-      # Declare a block to be called before each dispatch.
-      # Run in the order declared.
-      def before_dispatch(*method_names, &amp;block)
-        callbacks[:before].concat method_names
-        callbacks[:before] &lt;&lt; block if block_given?
-      end
-
-      # Declare a block to be called after each dispatch.
-      # Run in reverse of the order declared.
-      def after_dispatch(*method_names, &amp;block)
-        callbacks[:after].concat method_names
-        callbacks[:after] &lt;&lt; block if block_given?
-      end
-
       # Add a preparation callback. Preparation callbacks are run before every
       # request in development mode, and before the first request in production
       # mode.
@@ -34,15 +20,16 @@ module ActionController
       # existing callback. Passing an identifier is a suggested practice if the
       # code adding a preparation block may be reloaded.
       def to_prepare(identifier = nil, &amp;block)
+        @prepare_dispatch_callbacks ||= []
+        callback = ActiveSupport::Callbacks::Callback.new(:prepare_dispatch, block, :identifier =&gt; identifier)
+
         # Already registered: update the existing callback
-        if identifier
-          if callback = callbacks[:prepare].assoc(identifier)
-            callback[1] = block
-          else
-            callbacks[:prepare] &lt;&lt; [identifier, block]
-          end
+        # TODO: Ruby one liner for Array#find returning index
+        if identifier &amp;&amp; callback_for_identifier = @prepare_dispatch_callbacks.find { |c| c.identifier == identifier }
+          index = @prepare_dispatch_callbacks.index(callback_for_identifier)
+          @prepare_dispatch_callbacks[index] = callback
         else
-          callbacks[:prepare] &lt;&lt; block
+          @prepare_dispatch_callbacks.concat([callback])
         end
       end
 
@@ -90,12 +77,11 @@ module ActionController
     cattr_accessor :error_file_path
     self.error_file_path = &quot;#{::RAILS_ROOT}/public&quot; if defined? ::RAILS_ROOT
 
-    cattr_accessor :callbacks
-    self.callbacks = Hash.new { |h, k| h[k] = [] }
-
     cattr_accessor :unprepared
     self.unprepared = true
 
+    include ActiveSupport::Callbacks
+    define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch
 
     before_dispatch :reload_application
     before_dispatch :prepare_application
@@ -115,12 +101,12 @@ module ActionController
     def dispatch
       @@guard.synchronize do
         begin
-          run_callbacks :before
+          run_callbacks :before_dispatch
           handle_request
         rescue Exception =&gt; exception
           failsafe_rescue exception
         ensure
-          run_callbacks :after, :reverse_each
+          run_callbacks :after_dispatch, :enumerator =&gt; :reverse_each
         end
       end
     end
@@ -152,7 +138,7 @@ module ActionController
       ActiveRecord::Base.verify_active_connections! if defined?(ActiveRecord)
 
       if unprepared || force
-        run_callbacks :prepare
+        run_callbacks :prepare_dispatch
         self.unprepared = false
       end
     end
@@ -177,17 +163,6 @@ module ActionController
         @controller.process(@request, @response).out(@output)
       end
 
-      def run_callbacks(kind, enumerator = :each)
-        callbacks[kind].send!(enumerator) do |callback|
-          case callback
-          when Proc; callback.call(self)
-          when String, Symbol; send!(callback)
-          when Array; callback[1].call(self)
-          else raise ArgumentError, &quot;Unrecognized callback #{callback.inspect}&quot;
-          end
-        end
-      end
-
       def failsafe_rescue(exception)
         self.class.failsafe_response(@output, '500 Internal Server Error', exception) do
           if @controller ||= defined?(::ApplicationController) ? ::ApplicationController : Base</diff>
      <filename>actionpack/lib/action_controller/dispatcher.rb</filename>
    </modified>
    <modified>
      <diff>@@ -11,7 +11,7 @@ class DispatcherTest &lt; Test::Unit::TestCase
     @output = StringIO.new
     ENV['REQUEST_METHOD'] = 'GET'
 
-    Dispatcher.callbacks[:prepare].clear
+    Dispatcher.instance_variable_set(&quot;@prepare_dispatch_callbacks&quot;, [])
     @dispatcher = Dispatcher.new(@output)
   end
 </diff>
      <filename>actionpack/test/controller/dispatcher_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -183,14 +183,8 @@ module ActiveRecord
         base.send :alias_method_chain, method, :callbacks
       end
 
-      CALLBACKS.each do |method|
-        base.class_eval &lt;&lt;-&quot;end_eval&quot;
-          def self.#{method}(*callbacks, &amp;block)
-            callbacks &lt;&lt; block if block_given?
-            write_inheritable_array(#{method.to_sym.inspect}, callbacks)
-          end
-        end_eval
-      end
+      base.send :include, ActiveSupport::Callbacks
+      base.define_callbacks *CALLBACKS
     end
 
     # Is called when the object was instantiated by one of the finders, like &lt;tt&gt;Base.find&lt;/tt&gt;.
@@ -301,38 +295,15 @@ module ActiveRecord
       def callback(method)
         notify(method)
 
-        callbacks_for(method).each do |callback|
-          result = case callback
-            when Symbol
-              self.send(callback)
-            when String
-              eval(callback, binding)
-            when Proc, Method
-              callback.call(self)
-            else
-              if callback.respond_to?(method)
-                callback.send(method, self)
-              else
-                raise ActiveRecordError, &quot;Callbacks must be a symbol denoting the method to call, a string to be evaluated, a block to be invoked, or an object responding to the callback method.&quot;
-              end
-          end
-          return false if result == false
-        end
+        result = run_callbacks(method) { |result, object| result == false }
 
-        result = send(method) if respond_to_without_attributes?(method)
+        if result != false &amp;&amp; respond_to_without_attributes?(method)
+          result = send(method)
+        end
 
         return result
       end
 
-      def callbacks_for(method)
-        self.class.read_inheritable_attribute(method.to_sym) or []
-      end
-
-      def invoke_and_notify(method)
-        notify(method)
-        send(method) if respond_to_without_attributes?(method)
-      end
-
       def notify(method) #:nodoc:
         self.class.changed
         self.class.notify_observers(method, self)</diff>
      <filename>activerecord/lib/active_record/callbacks.rb</filename>
    </modified>
    <modified>
      <diff>@@ -279,6 +279,25 @@ module ActiveRecord
         alias_method_chain :save!, :validation
         alias_method_chain :update_attribute, :validation_skipping
       end
+
+      base.send :include, ActiveSupport::Callbacks
+
+      # TODO: Use helper ActiveSupport::Callbacks#define_callbacks instead
+      %w( validate validate_on_create validate_on_update ).each do |validation_method|
+        base.class_eval &lt;&lt;-&quot;end_eval&quot;
+          def self.#{validation_method}(*methods, &amp;block)
+            options = methods.extract_options!
+            methods &lt;&lt; block if block_given?
+            methods.map! { |method| Callback.new(:#{validation_method}, method, options) }
+            existing_methods = read_inheritable_attribute(:#{validation_method}) || []
+            write_inheritable_attribute(:#{validation_method}, existing_methods | methods)
+          end
+
+          def self.#{validation_method}_callback_chain
+            read_inheritable_attribute(:#{validation_method}) || []
+          end
+        end_eval
+      end
     end
 
     # All of the following validations are defined in the class scope of the model that you're interested in validating.
@@ -324,43 +343,6 @@ module ActiveRecord
       #   end
       #
       # This usage applies to #validate_on_create and #validate_on_update as well.
-      def validate(*methods, &amp;block)
-        methods &lt;&lt; block if block_given?
-        write_inheritable_set(:validate, methods)
-      end
-
-      def validate_on_create(*methods, &amp;block)
-        methods &lt;&lt; block if block_given?
-        write_inheritable_set(:validate_on_create, methods)
-      end
-
-      def validate_on_update(*methods, &amp;block)
-        methods &lt;&lt; block if block_given?
-        write_inheritable_set(:validate_on_update, methods)
-      end
-
-      def condition_block?(condition)
-        condition.respond_to?(&quot;call&quot;) &amp;&amp; (condition.arity == 1 || condition.arity == -1)
-      end
-
-      # Determine from the given condition (whether a block, procedure, method or string)
-      # whether or not to validate the record.  See #validates_each.
-      def evaluate_condition(condition, record)
-        case condition
-          when Symbol; record.send(condition)
-          when String; eval(condition, record.instance_eval { binding })
-          else
-            if condition_block?(condition)
-              condition.call(record)
-            else
-              raise(
-                ActiveRecordError,
-                &quot;Validations need to be either a symbol, string (to be eval'ed), proc/method, or &quot; +
-                &quot;class implementing a static validation method&quot;
-              )
-            end
-          end
-      end
 
       # Validates each attribute against a block.
       #
@@ -379,20 +361,17 @@ module ActiveRecord
       #   method, proc or string should return or evaluate to a true or false value.
       # * &lt;tt&gt;unless&lt;/tt&gt; - Specifies a method, proc or string to call to determine if the validation should
       #   not occur (e.g. :unless =&gt; :skip_validation, or :unless =&gt; Proc.new { |user| user.signup_step &lt;= 2 }).  The
-      #   method, proc or string should return or evaluate to a true or false value.      
+      #   method, proc or string should return or evaluate to a true or false value.
       def validates_each(*attrs)
         options = attrs.extract_options!.symbolize_keys
         attrs   = attrs.flatten
 
         # Declare the validation.
-        send(validation_method(options[:on] || :save)) do |record|
-          # Don't validate when there is an :if condition and that condition is false or there is an :unless condition and that condition is true
-          unless (options[:if] &amp;&amp; !evaluate_condition(options[:if], record)) || (options[:unless] &amp;&amp; evaluate_condition(options[:unless], record))
-            attrs.each do |attr|
-              value = record.send(attr)
-              next if (value.nil? &amp;&amp; options[:allow_nil]) || (value.blank? &amp;&amp; options[:allow_blank])
-              yield record, attr, value
-            end
+        send(validation_method(options[:on] || :save), options) do |record|
+          attrs.each do |attr|
+            value = record.send(attr)
+            next if (value.nil? &amp;&amp; options[:allow_nil]) || (value.blank? &amp;&amp; options[:allow_blank])
+            yield record, attr, value
           end
         end
       end
@@ -515,11 +494,9 @@ module ActiveRecord
 
         # can't use validates_each here, because it cannot cope with nonexistent attributes,
         # while errors.add_on_empty can
-	send(validation_method(configuration[:on])) do |record|
-	  unless (configuration[:if] &amp;&amp; !evaluate_condition(configuration[:if], record)) || (configuration[:unless] &amp;&amp; evaluate_condition(configuration[:unless], record))
-	    record.errors.add_on_blank(attr_names, configuration[:message])
-	  end
-	end
+        send(validation_method(configuration[:on]), configuration) do |record|
+          record.errors.add_on_blank(attr_names, configuration[:message])
+        end
       end
 
       # Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time:
@@ -911,13 +888,7 @@ module ActiveRecord
         end
       end
 
-
       private
-        def write_inheritable_set(key, methods)
-          existing_methods = read_inheritable_attribute(key) || []
-          write_inheritable_attribute(key, existing_methods | methods)
-        end
-
         def validation_method(on)
           case on
             when :save   then :validate
@@ -959,14 +930,14 @@ module ActiveRecord
     def valid?
       errors.clear
 
-      run_validations(:validate)
+      run_callbacks(:validate)
       validate
 
       if new_record?
-        run_validations(:validate_on_create)
+        run_callbacks(:validate_on_create)
         validate_on_create
       else
-        run_validations(:validate_on_update)
+        run_callbacks(:validate_on_update)
         validate_on_update
       end
 
@@ -990,36 +961,5 @@ module ActiveRecord
       # Overwrite this method for validation checks used only on updates.
       def validate_on_update # :doc:
       end
-
-    private
-      def run_validations(validation_method)
-        validations = self.class.read_inheritable_attribute(validation_method.to_sym)
-        if validations.nil? then return end
-        validations.each do |validation|
-          if validation.is_a?(Symbol)
-            self.send(validation)
-          elsif validation.is_a?(String)
-            eval(validation, binding)
-          elsif validation_block?(validation)
-            validation.call(self)
-          elsif validation_class?(validation, validation_method)
-            validation.send(validation_method, self)
-          else
-            raise(
-              ActiveRecordError,
-              &quot;Validations need to be either a symbol, string (to be eval'ed), proc/method, or &quot; +
-              &quot;class implementing a static validation method&quot;
-            )
-          end
-        end
-      end
-
-      def validation_block?(validation)
-        validation.respond_to?(&quot;call&quot;) &amp;&amp; (validation.arity == 1 || validation.arity == -1)
-      end
-
-      def validation_class?(validation, validation_method)
-        validation.respond_to?(validation_method)
-      end
   end
 end</diff>
      <filename>activerecord/lib/active_record/validations.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1017,7 +1017,7 @@ class ValidationsTest &lt; ActiveSupport::TestCase
 
   def test_invalid_validator
     Topic.validate 3
-    assert_raise(ActiveRecord::ActiveRecordError) { t = Topic.create }
+    assert_raise(ArgumentError) { t = Topic.create }
   end
 
   def test_throw_away_typing</diff>
      <filename>activerecord/test/cases/validations_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,7 @@
 *SVN*
 
+* Extract ActiveSupport::Callbacks from Active Record, test case setup and teardown, and ActionController::Dispatcher.  #10727 [Josh Peek]
+
 * Introducing DateTime #utc, #utc? and #utc_offset, for duck-typing compatibility with Time. Closes #10002 [Geoff Buesing]
 
 * Time#to_json uses Numeric#to_utc_offset_s to output a cross-platform-consistent representation without having to convert to DateTime. References #9750 [Geoff Buesing]</diff>
      <filename>activesupport/CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -26,6 +26,7 @@ $:.unshift(File.dirname(__FILE__))
 require 'active_support/vendor'
 require 'active_support/basic_object'
 require 'active_support/inflector'
+require 'active_support/callbacks'
 
 require 'active_support/core_ext'
 </diff>
      <filename>activesupport/lib/active_support.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,8 @@ module ActiveSupport
   module Testing
     module SetupAndTeardown
       def self.included(base)
-        base.extend ClassMethods
+        base.send :include, ActiveSupport::Callbacks
+        base.define_callbacks :setup, :teardown
 
         begin
           require 'mocha'
@@ -12,38 +13,6 @@ module ActiveSupport
         end
       end
 
-      module ClassMethods
-        def setup(*method_names, &amp;block)
-          method_names &lt;&lt; block if block_given?
-          (@setup_callbacks ||= []).concat method_names
-        end
-
-        def teardown(*method_names, &amp;block)
-          method_names &lt;&lt; block if block_given?
-          (@teardown_callbacks ||= []).concat method_names
-        end
-
-        def setup_callback_chain
-          @setup_callbacks ||= []
-
-          if superclass.respond_to?(:setup_callback_chain)
-            superclass.setup_callback_chain + @setup_callbacks
-          else
-            @setup_callbacks
-          end
-        end
-
-        def teardown_callback_chain
-          @teardown_callbacks ||= []
-
-          if superclass.respond_to?(:teardown_callback_chain)
-            superclass.teardown_callback_chain + @teardown_callbacks
-          else
-            @teardown_callbacks
-          end
-        end
-      end
-
       # This redefinition is unfortunate but test/unit shows us no alternative.
       def run_with_callbacks(result) #:nodoc:
         return if @method_name.to_s == &quot;default_test&quot;
@@ -63,7 +32,7 @@ module ActiveSupport
         ensure
           begin
             teardown
-            run_callbacks :teardown, :reverse_each
+            run_callbacks :teardown, :enumerator =&gt; :reverse_each
           rescue Test::Unit::AssertionFailedError =&gt; e
             add_failure(e.message, e.backtrace)
           rescue *Test::Unit::TestCase::PASSTHROUGH_EXCEPTIONS
@@ -98,7 +67,7 @@ module ActiveSupport
           ensure
             begin
               teardown
-              run_callbacks :teardown, :reverse_each
+              run_callbacks :teardown, :enumerator =&gt; :reverse_each
             rescue Test::Unit::AssertionFailedError =&gt; e
               add_failure(e.message, e.backtrace)
             rescue StandardError, ScriptError
@@ -111,17 +80,6 @@ module ActiveSupport
         result.add_run
         yield(Test::Unit::TestCase::FINISHED, name)
       end
-
-      protected
-        def run_callbacks(kind, enumerator = :each)
-          self.class.send(&quot;#{kind}_callback_chain&quot;).send(enumerator) do |callback|
-            case callback
-            when Proc; callback.call(self)
-            when String, Symbol; send!(callback)
-            else raise ArgumentError, &quot;Unrecognized callback #{callback.inspect}&quot;
-            end
-          end
-        end
     end
   end
 end</diff>
      <filename>activesupport/lib/active_support/testing/setup_and_teardown.rb</filename>
    </modified>
    <modified>
      <diff>@@ -79,9 +79,9 @@ class SetupAndTeardownTest &lt; Test::Unit::TestCase
   teardown :foo, :sentinel, :foo
 
   def test_inherited_setup_callbacks
-    assert_equal [:reset_callback_record, :foo], self.class.setup_callback_chain
+    assert_equal [:reset_callback_record, :foo], self.class.setup_callback_chain.map(&amp;:method)
     assert_equal [:foo], @called_back
-    assert_equal [:foo, :sentinel, :foo], self.class.teardown_callback_chain
+    assert_equal [:foo, :sentinel, :foo], self.class.teardown_callback_chain.map(&amp;:method)
   end
 
   protected
@@ -104,9 +104,9 @@ class SubclassSetupAndTeardownTest &lt; SetupAndTeardownTest
   teardown :bar
 
   def test_inherited_setup_callbacks
-    assert_equal [:reset_callback_record, :foo, :bar], self.class.setup_callback_chain
+    assert_equal [:reset_callback_record, :foo, :bar], self.class.setup_callback_chain.map(&amp;:method)
     assert_equal [:foo, :bar], @called_back
-    assert_equal [:foo, :sentinel, :foo, :bar], self.class.teardown_callback_chain
+    assert_equal [:foo, :sentinel, :foo, :bar], self.class.teardown_callback_chain.map(&amp;:method)
   end
 
   protected</diff>
      <filename>activesupport/test/test_test.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>3ffdfa84fc1f9bebc578fe957646af5f194ca625</id>
    </parent>
  </parents>
  <author>
    <name>Jeremy Kemper</name>
    <email>jeremy@bitsweat.net</email>
  </author>
  <url>http://github.com/rails/rails/commit/aae37bb4f7edd6a1820e420a60560369c6064f33</url>
  <id>aae37bb4f7edd6a1820e420a60560369c6064f33</id>
  <committed-date>2008-01-18T18:44:45-08:00</committed-date>
  <authored-date>2008-01-18T18:44:45-08:00</authored-date>
  <message>Extract ActiveSupport::Callbacks from Active Record, test case setup and teardown, and ActionController::Dispatcher. Closes #10727.


git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@8664 5ecf4fe2-1ee6-0310-87b1-e25e094e27de</message>
  <tree>5642ff690036cac09bc697a0798dd984128d6e73</tree>
  <committer>
    <name>Jeremy Kemper</name>
    <email>jeremy@bitsweat.net</email>
  </committer>
</commit>
