public
Description: Ruby on Rails
Homepage: http://rubyonrails.org
Clone URL: git://github.com/rails/rails.git
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
jeremy (author)
Fri Jan 18 18:44:45 -0800 2008
commit  aae37bb4f7edd6a1820e420a60560369c6064f33
tree    5642ff690036cac09bc697a0798dd984128d6e73
parent  3ffdfa84fc1f9bebc578fe957646af5f194ca625
...
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
...
34
35
36
 
 
 
37
38
39
40
41
42
43
 
 
 
 
44
45
 
46
47
48
...
90
91
92
93
94
95
96
97
98
 
 
99
100
101
...
115
116
117
118
 
119
120
121
122
123
 
124
125
126
...
152
153
154
155
 
156
157
158
...
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
...
11
12
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
15
16
...
20
21
22
23
24
25
26
 
 
 
 
 
 
27
28
29
30
31
 
32
33
34
35
...
77
78
79
 
 
 
80
81
82
83
84
85
86
87
...
101
102
103
 
104
105
106
107
108
 
109
110
111
112
...
138
139
140
 
141
142
143
144
...
163
164
165
 
 
 
 
 
 
 
 
 
 
 
166
167
168
0
@@ -11,20 +11,6 @@ module ActionController
0
         new(output).dispatch_cgi(cgi, session_options)
0
       end
0
 
0
-      # Declare a block to be called before each dispatch.
0
-      # Run in the order declared.
0
-      def before_dispatch(*method_names, &block)
0
-        callbacks[:before].concat method_names
0
-        callbacks[:before] << block if block_given?
0
-      end
0
-
0
-      # Declare a block to be called after each dispatch.
0
-      # Run in reverse of the order declared.
0
-      def after_dispatch(*method_names, &block)
0
-        callbacks[:after].concat method_names
0
-        callbacks[:after] << block if block_given?
0
-      end
0
-
0
       # Add a preparation callback. Preparation callbacks are run before every
0
       # request in development mode, and before the first request in production
0
       # mode.
0
@@ -34,15 +20,16 @@ module ActionController
0
       # existing callback. Passing an identifier is a suggested practice if the
0
       # code adding a preparation block may be reloaded.
0
       def to_prepare(identifier = nil, &block)
0
+        @prepare_dispatch_callbacks ||= []
0
+        callback = ActiveSupport::Callbacks::Callback.new(:prepare_dispatch, block, :identifier => identifier)
0
+
0
         # Already registered: update the existing callback
0
-        if identifier
0
-          if callback = callbacks[:prepare].assoc(identifier)
0
-            callback[1] = block
0
-          else
0
-            callbacks[:prepare] << [identifier, block]
0
-          end
0
+        # TODO: Ruby one liner for Array#find returning index
0
+        if identifier && callback_for_identifier = @prepare_dispatch_callbacks.find { |c| c.identifier == identifier }
0
+          index = @prepare_dispatch_callbacks.index(callback_for_identifier)
0
+          @prepare_dispatch_callbacks[index] = callback
0
         else
0
-          callbacks[:prepare] << block
0
+          @prepare_dispatch_callbacks.concat([callback])
0
         end
0
       end
0
 
0
@@ -90,12 +77,11 @@ module ActionController
0
     cattr_accessor :error_file_path
0
     self.error_file_path = "#{::RAILS_ROOT}/public" if defined? ::RAILS_ROOT
0
 
0
-    cattr_accessor :callbacks
0
-    self.callbacks = Hash.new { |h, k| h[k] = [] }
0
-
0
     cattr_accessor :unprepared
0
     self.unprepared = true
0
 
0
+    include ActiveSupport::Callbacks
0
+    define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch
0
 
0
     before_dispatch :reload_application
0
     before_dispatch :prepare_application
0
@@ -115,12 +101,12 @@ module ActionController
0
     def dispatch
0
       @@guard.synchronize do
0
         begin
0
-          run_callbacks :before
0
+          run_callbacks :before_dispatch
0
           handle_request
0
         rescue Exception => exception
0
           failsafe_rescue exception
0
         ensure
0
-          run_callbacks :after, :reverse_each
0
+          run_callbacks :after_dispatch, :enumerator => :reverse_each
0
         end
0
       end
0
     end
0
@@ -152,7 +138,7 @@ module ActionController
0
       ActiveRecord::Base.verify_active_connections! if defined?(ActiveRecord)
0
 
0
       if unprepared || force
0
-        run_callbacks :prepare
0
+        run_callbacks :prepare_dispatch
0
         self.unprepared = false
0
       end
0
     end
0
@@ -177,17 +163,6 @@ module ActionController
0
         @controller.process(@request, @response).out(@output)
0
       end
0
 
0
-      def run_callbacks(kind, enumerator = :each)
0
-        callbacks[kind].send!(enumerator) do |callback|
0
-          case callback
0
-          when Proc; callback.call(self)
0
-          when String, Symbol; send!(callback)
0
-          when Array; callback[1].call(self)
0
-          else raise ArgumentError, "Unrecognized callback #{callback.inspect}"
0
-          end
0
-        end
0
-      end
0
-
0
       def failsafe_rescue(exception)
0
         self.class.failsafe_response(@output, '500 Internal Server Error', exception) do
0
           if @controller ||= defined?(::ApplicationController) ? ::ApplicationController : Base
...
11
12
13
14
 
15
16
17
...
11
12
13
 
14
15
16
17
0
@@ -11,7 +11,7 @@ class DispatcherTest < Test::Unit::TestCase
0
     @output = StringIO.new
0
     ENV['REQUEST_METHOD'] = 'GET'
0
 
0
-    Dispatcher.callbacks[:prepare].clear
0
+    Dispatcher.instance_variable_set("@prepare_dispatch_callbacks", [])
0
     @dispatcher = Dispatcher.new(@output)
0
   end
0
 
...
183
184
185
186
187
188
189
190
191
192
193
 
 
194
195
196
...
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
 
321
322
 
 
 
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
...
183
184
185
 
 
 
 
 
 
 
 
186
187
188
189
190
...
295
296
297
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
299
 
300
301
302
303
304
305
306
 
 
 
 
 
 
 
 
 
307
308
309
0
@@ -183,14 +183,8 @@ module ActiveRecord
0
         base.send :alias_method_chain, method, :callbacks
0
       end
0
 
0
-      CALLBACKS.each do |method|
0
-        base.class_eval <<-"end_eval"
0
-          def self.#{method}(*callbacks, &block)
0
-            callbacks << block if block_given?
0
-            write_inheritable_array(#{method.to_sym.inspect}, callbacks)
0
-          end
0
-        end_eval
0
-      end
0
+      base.send :include, ActiveSupport::Callbacks
0
+      base.define_callbacks *CALLBACKS
0
     end
0
 
0
     # Is called when the object was instantiated by one of the finders, like <tt>Base.find</tt>.
0
@@ -301,38 +295,15 @@ module ActiveRecord
0
       def callback(method)
0
         notify(method)
0
 
0
-        callbacks_for(method).each do |callback|
0
-          result = case callback
0
-            when Symbol
0
-              self.send(callback)
0
-            when String
0
-              eval(callback, binding)
0
-            when Proc, Method
0
-              callback.call(self)
0
-            else
0
-              if callback.respond_to?(method)
0
-                callback.send(method, self)
0
-              else
0
-                raise ActiveRecordError, "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."
0
-              end
0
-          end
0
-          return false if result == false
0
-        end
0
+        result = run_callbacks(method) { |result, object| result == false }
0
 
0
-        result = send(method) if respond_to_without_attributes?(method)
0
+        if result != false && respond_to_without_attributes?(method)
0
+          result = send(method)
0
+        end
0
 
0
         return result
0
       end
0
 
0
-      def callbacks_for(method)
0
-        self.class.read_inheritable_attribute(method.to_sym) or []
0
-      end
0
-
0
-      def invoke_and_notify(method)
0
-        notify(method)
0
-        send(method) if respond_to_without_attributes?(method)
0
-      end
0
-
0
       def notify(method) #:nodoc:
0
         self.class.changed
0
         self.class.notify_observers(method, self)
...
279
280
281
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
283
284
...
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
...
379
380
381
382
 
383
384
385
386
387
388
389
390
391
392
393
394
395
 
 
 
 
 
396
397
398
...
515
516
517
518
519
520
521
522
 
 
 
523
524
525
...
911
912
913
914
915
916
917
918
919
920
921
922
923
...
959
960
961
962
 
963
964
965
966
 
967
968
969
 
970
971
972
...
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
...
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
...
343
344
345
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
346
347
348
...
361
362
363
 
364
365
366
367
368
369
 
 
 
 
 
 
 
 
370
371
372
373
374
375
376
377
...
494
495
496
 
 
 
 
 
497
498
499
500
501
502
...
888
889
890
 
891
 
 
 
 
 
892
893
894
...
930
931
932
 
933
934
935
936
 
937
938
939
 
940
941
942
943
...
961
962
963
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
964
965
0
@@ -279,6 +279,25 @@ module ActiveRecord
0
         alias_method_chain :save!, :validation
0
         alias_method_chain :update_attribute, :validation_skipping
0
       end
0
+
0
+      base.send :include, ActiveSupport::Callbacks
0
+
0
+      # TODO: Use helper ActiveSupport::Callbacks#define_callbacks instead
0
+      %w( validate validate_on_create validate_on_update ).each do |validation_method|
0
+        base.class_eval <<-"end_eval"
0
+          def self.#{validation_method}(*methods, &block)
0
+            options = methods.extract_options!
0
+            methods << block if block_given?
0
+            methods.map! { |method| Callback.new(:#{validation_method}, method, options) }
0
+            existing_methods = read_inheritable_attribute(:#{validation_method}) || []
0
+            write_inheritable_attribute(:#{validation_method}, existing_methods | methods)
0
+          end
0
+
0
+          def self.#{validation_method}_callback_chain
0
+            read_inheritable_attribute(:#{validation_method}) || []
0
+          end
0
+        end_eval
0
+      end
0
     end
0
 
0
     # All of the following validations are defined in the class scope of the model that you're interested in validating.
0
@@ -324,43 +343,6 @@ module ActiveRecord
0
       #   end
0
       #
0
       # This usage applies to #validate_on_create and #validate_on_update as well.
0
-      def validate(*methods, &block)
0
-        methods << block if block_given?
0
-        write_inheritable_set(:validate, methods)
0
-      end
0
-
0
-      def validate_on_create(*methods, &block)
0
-        methods << block if block_given?
0
-        write_inheritable_set(:validate_on_create, methods)
0
-      end
0
-
0
-      def validate_on_update(*methods, &block)
0
-        methods << block if block_given?
0
-        write_inheritable_set(:validate_on_update, methods)
0
-      end
0
-
0
-      def condition_block?(condition)
0
-        condition.respond_to?("call") && (condition.arity == 1 || condition.arity == -1)
0
-      end
0
-
0
-      # Determine from the given condition (whether a block, procedure, method or string)
0
-      # whether or not to validate the record.  See #validates_each.
0
-      def evaluate_condition(condition, record)
0
-        case condition
0
-          when Symbol; record.send(condition)
0
-          when String; eval(condition, record.instance_eval { binding })
0
-          else
0
-            if condition_block?(condition)
0
-              condition.call(record)
0
-            else
0
-              raise(
0
-                ActiveRecordError,
0
-                "Validations need to be either a symbol, string (to be eval'ed), proc/method, or " +
0
-                "class implementing a static validation method"
0
-              )
0
-            end
0
-          end
0
-      end
0
 
0
       # Validates each attribute against a block.
0
       #
0
@@ -379,20 +361,17 @@ module ActiveRecord
0
       #   method, proc or string should return or evaluate to a true or false value.
0
       # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
0
       #   not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }).  The
0
-      #   method, proc or string should return or evaluate to a true or false value.      
0
+      #   method, proc or string should return or evaluate to a true or false value.
0
       def validates_each(*attrs)
0
         options = attrs.extract_options!.symbolize_keys
0
         attrs   = attrs.flatten
0
 
0
         # Declare the validation.
0
-        send(validation_method(options[:on] || :save)) do |record|
0
-          # 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
0
-          unless (options[:if] && !evaluate_condition(options[:if], record)) || (options[:unless] && evaluate_condition(options[:unless], record))
0
-            attrs.each do |attr|
0
-              value = record.send(attr)
0
-              next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
0
-              yield record, attr, value
0
-            end
0
+        send(validation_method(options[:on] || :save), options) do |record|
0
+          attrs.each do |attr|
0
+            value = record.send(attr)
0
+            next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
0
+            yield record, attr, value
0
           end
0
         end
0
       end
0
@@ -515,11 +494,9 @@ module ActiveRecord
0
 
0
         # can't use validates_each here, because it cannot cope with nonexistent attributes,
0
         # while errors.add_on_empty can
0
-  send(validation_method(configuration[:on])) do |record|
0
-    unless (configuration[:if] && !evaluate_condition(configuration[:if], record)) || (configuration[:unless] && evaluate_condition(configuration[:unless], record))
0
-      record.errors.add_on_blank(attr_names, configuration[:message])
0
-    end
0
-  end
0
+        send(validation_method(configuration[:on]), configuration) do |record|
0
+          record.errors.add_on_blank(attr_names, configuration[:message])
0
+        end
0
       end
0
 
0
       # Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time:
0
@@ -911,13 +888,7 @@ module ActiveRecord
0
         end
0
       end
0
 
0
-
0
       private
0
-        def write_inheritable_set(key, methods)
0
-          existing_methods = read_inheritable_attribute(key) || []
0
-          write_inheritable_attribute(key, existing_methods | methods)
0
-        end
0
-
0
         def validation_method(on)
0
           case on
0
             when :save   then :validate
0
@@ -959,14 +930,14 @@ module ActiveRecord
0
     def valid?
0
       errors.clear
0
 
0
-      run_validations(:validate)
0
+      run_callbacks(:validate)
0
       validate
0
 
0
       if new_record?
0
-        run_validations(:validate_on_create)
0
+        run_callbacks(:validate_on_create)
0
         validate_on_create
0
       else
0
-        run_validations(:validate_on_update)
0
+        run_callbacks(:validate_on_update)
0
         validate_on_update
0
       end
0
 
0
@@ -990,36 +961,5 @@ module ActiveRecord
0
       # Overwrite this method for validation checks used only on updates.
0
       def validate_on_update # :doc:
0
       end
0
-
0
-    private
0
-      def run_validations(validation_method)
0
-        validations = self.class.read_inheritable_attribute(validation_method.to_sym)
0
-        if validations.nil? then return end
0
-        validations.each do |validation|
0
-          if validation.is_a?(Symbol)
0
-            self.send(validation)
0
-          elsif validation.is_a?(String)
0
-            eval(validation, binding)
0
-          elsif validation_block?(validation)
0
-            validation.call(self)
0
-          elsif validation_class?(validation, validation_method)
0
-            validation.send(validation_method, self)
0
-          else
0
-            raise(
0
-              ActiveRecordError,
0
-              "Validations need to be either a symbol, string (to be eval'ed), proc/method, or " +
0
-              "class implementing a static validation method"
0
-            )
0
-          end
0
-        end
0
-      end
0
-
0
-      def validation_block?(validation)
0
-        validation.respond_to?("call") && (validation.arity == 1 || validation.arity == -1)
0
-      end
0
-
0
-      def validation_class?(validation, validation_method)
0
-        validation.respond_to?(validation_method)
0
-      end
0
   end
0
 end
...
1017
1018
1019
1020
 
1021
1022
1023
...
1017
1018
1019
 
1020
1021
1022
1023
0
@@ -1017,7 +1017,7 @@ class ValidationsTest < ActiveSupport::TestCase
0
 
0
   def test_invalid_validator
0
     Topic.validate 3
0
-    assert_raise(ActiveRecord::ActiveRecordError) { t = Topic.create }
0
+    assert_raise(ArgumentError) { t = Topic.create }
0
   end
0
 
0
   def test_throw_away_typing
...
1
2
 
 
3
4
5
...
1
2
3
4
5
6
7
0
@@ -1,5 +1,7 @@
0
 *SVN*
0
 
0
+* Extract ActiveSupport::Callbacks from Active Record, test case setup and teardown, and ActionController::Dispatcher.  #10727 [Josh Peek]
0
+
0
 * Introducing DateTime #utc, #utc? and #utc_offset, for duck-typing compatibility with Time. Closes #10002 [Geoff Buesing]
0
 
0
 * 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]
...
26
27
28
 
29
30
31
...
26
27
28
29
30
31
32
0
@@ -26,6 +26,7 @@ $:.unshift(File.dirname(__FILE__))
0
 require 'active_support/vendor'
0
 require 'active_support/basic_object'
0
 require 'active_support/inflector'
0
+require 'active_support/callbacks'
0
 
0
 require 'active_support/core_ext'
0
 
...
2
3
4
5
 
 
6
7
8
...
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
...
63
64
65
66
 
67
68
69
...
98
99
100
101
 
102
103
104
...
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
...
2
3
4
 
5
6
7
8
9
...
13
14
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
17
18
...
32
33
34
 
35
36
37
38
...
67
68
69
 
70
71
72
73
...
80
81
82
 
 
 
 
 
 
 
 
 
 
 
83
84
85
0
@@ -2,7 +2,8 @@ module ActiveSupport
0
   module Testing
0
     module SetupAndTeardown
0
       def self.included(base)
0
-        base.extend ClassMethods
0
+        base.send :include, ActiveSupport::Callbacks
0
+        base.define_callbacks :setup, :teardown
0
 
0
         begin
0
           require 'mocha'
0
@@ -12,38 +13,6 @@ module ActiveSupport
0
         end
0
       end
0
 
0
-      module ClassMethods
0
-        def setup(*method_names, &block)
0
-          method_names << block if block_given?
0
-          (@setup_callbacks ||= []).concat method_names
0
-        end
0
-
0
-        def teardown(*method_names, &block)
0
-          method_names << block if block_given?
0
-          (@teardown_callbacks ||= []).concat method_names
0
-        end
0
-
0
-        def setup_callback_chain
0
-          @setup_callbacks ||= []
0
-
0
-          if superclass.respond_to?(:setup_callback_chain)
0
-            superclass.setup_callback_chain + @setup_callbacks
0
-          else
0
-            @setup_callbacks
0
-          end
0
-        end
0
-
0
-        def teardown_callback_chain
0
-          @teardown_callbacks ||= []
0
-
0
-          if superclass.respond_to?(:teardown_callback_chain)
0
-            superclass.teardown_callback_chain + @teardown_callbacks
0
-          else
0
-            @teardown_callbacks
0
-          end
0
-        end
0
-      end
0
-
0
       # This redefinition is unfortunate but test/unit shows us no alternative.
0
       def run_with_callbacks(result) #:nodoc:
0
         return if @method_name.to_s == "default_test"
0
@@ -63,7 +32,7 @@ module ActiveSupport
0
         ensure
0
           begin
0
             teardown
0
-            run_callbacks :teardown, :reverse_each
0
+            run_callbacks :teardown, :enumerator => :reverse_each
0
           rescue Test::Unit::AssertionFailedError => e
0
             add_failure(e.message, e.backtrace)
0
           rescue *Test::Unit::TestCase::PASSTHROUGH_EXCEPTIONS
0
@@ -98,7 +67,7 @@ module ActiveSupport
0
           ensure
0
             begin
0
               teardown
0
-              run_callbacks :teardown, :reverse_each
0
+              run_callbacks :teardown, :enumerator => :reverse_each
0
             rescue Test::Unit::AssertionFailedError => e
0
               add_failure(e.message, e.backtrace)
0
             rescue StandardError, ScriptError
0
@@ -111,17 +80,6 @@ module ActiveSupport
0
         result.add_run
0
         yield(Test::Unit::TestCase::FINISHED, name)
0
       end
0
-
0
-      protected
0
-        def run_callbacks(kind, enumerator = :each)
0
-          self.class.send("#{kind}_callback_chain").send(enumerator) do |callback|
0
-            case callback
0
-            when Proc; callback.call(self)
0
-            when String, Symbol; send!(callback)
0
-            else raise ArgumentError, "Unrecognized callback #{callback.inspect}"
0
-            end
0
-          end
0
-        end
0
     end
0
   end
0
 end
...
79
80
81
82
 
83
84
 
85
86
87
...
104
105
106
107
 
108
109
 
110
111
112
...
79
80
81
 
82
83
 
84
85
86
87
...
104
105
106
 
107
108
 
109
110
111
112
0
@@ -79,9 +79,9 @@ class SetupAndTeardownTest < Test::Unit::TestCase
0
   teardown :foo, :sentinel, :foo
0
 
0
   def test_inherited_setup_callbacks
0
-    assert_equal [:reset_callback_record, :foo], self.class.setup_callback_chain
0
+    assert_equal [:reset_callback_record, :foo], self.class.setup_callback_chain.map(&:method)
0
     assert_equal [:foo], @called_back
0
-    assert_equal [:foo, :sentinel, :foo], self.class.teardown_callback_chain
0
+    assert_equal [:foo, :sentinel, :foo], self.class.teardown_callback_chain.map(&:method)
0
   end
0
 
0
   protected
0
@@ -104,9 +104,9 @@ class SubclassSetupAndTeardownTest < SetupAndTeardownTest
0
   teardown :bar
0
 
0
   def test_inherited_setup_callbacks
0
-    assert_equal [:reset_callback_record, :foo, :bar], self.class.setup_callback_chain
0
+    assert_equal [:reset_callback_record, :foo, :bar], self.class.setup_callback_chain.map(&:method)
0
     assert_equal [:foo, :bar], @called_back
0
-    assert_equal [:foo, :sentinel, :foo, :bar], self.class.teardown_callback_chain
0
+    assert_equal [:foo, :sentinel, :foo, :bar], self.class.teardown_callback_chain.map(&:method)
0
   end
0
 
0
   protected

Comments