Changeset 8664
- Timestamp:
- 01/19/08 02:44:45 (4 months ago)
- Files:
-
- trunk/actionpack/lib/action_controller/dispatcher.rb (modified) (6 diffs)
- trunk/actionpack/test/controller/dispatcher_test.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/callbacks.rb (modified) (2 diffs)
- trunk/activerecord/lib/active_record/validations.rb (modified) (8 diffs)
- trunk/activerecord/test/cases/validations_test.rb (modified) (1 diff)
- trunk/activesupport/CHANGELOG (modified) (1 diff)
- trunk/activesupport/lib/active_support.rb (modified) (1 diff)
- trunk/activesupport/lib/active_support/callbacks.rb (added)
- trunk/activesupport/lib/active_support/testing/setup_and_teardown.rb (modified) (5 diffs)
- trunk/activesupport/test/callbacks_test.rb (added)
- trunk/activesupport/test/test_test.rb (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/actionpack/lib/action_controller/dispatcher.rb
r8488 r8664 12 12 end 13 13 14 # Declare a block to be called before each dispatch.15 # Run in the order declared.16 def before_dispatch(*method_names, &block)17 callbacks[:before].concat method_names18 callbacks[:before] << block if block_given?19 end20 21 # Declare a block to be called after each dispatch.22 # Run in reverse of the order declared.23 def after_dispatch(*method_names, &block)24 callbacks[:after].concat method_names25 callbacks[:after] << block if block_given?26 end27 28 14 # Add a preparation callback. Preparation callbacks are run before every 29 15 # request in development mode, and before the first request in production … … 35 21 # code adding a preparation block may be reloaded. 36 22 def to_prepare(identifier = nil, &block) 23 @prepare_dispatch_callbacks ||= [] 24 callback = ActiveSupport::Callbacks::Callback.new(:prepare_dispatch, block, :identifier => identifier) 25 37 26 # Already registered: update the existing callback 38 if identifier 39 if callback = callbacks[:prepare].assoc(identifier) 40 callback[1] = block 41 else 42 callbacks[:prepare] << [identifier, block] 43 end 27 # TODO: Ruby one liner for Array#find returning index 28 if identifier && callback_for_identifier = @prepare_dispatch_callbacks.find { |c| c.identifier == identifier } 29 index = @prepare_dispatch_callbacks.index(callback_for_identifier) 30 @prepare_dispatch_callbacks[index] = callback 44 31 else 45 callbacks[:prepare] << block32 @prepare_dispatch_callbacks.concat([callback]) 46 33 end 47 34 end … … 91 78 self.error_file_path = "#{::RAILS_ROOT}/public" if defined? ::RAILS_ROOT 92 79 93 cattr_accessor :callbacks94 self.callbacks = Hash.new { |h, k| h[k] = [] }95 96 80 cattr_accessor :unprepared 97 81 self.unprepared = true 98 82 83 include ActiveSupport::Callbacks 84 define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch 99 85 100 86 before_dispatch :reload_application … … 116 102 @@guard.synchronize do 117 103 begin 118 run_callbacks :before 104 run_callbacks :before_dispatch 119 105 handle_request 120 106 rescue Exception => exception 121 107 failsafe_rescue exception 122 108 ensure 123 run_callbacks :after ,:reverse_each109 run_callbacks :after_dispatch, :enumerator => :reverse_each 124 110 end 125 111 end … … 153 139 154 140 if unprepared || force 155 run_callbacks :prepare 141 run_callbacks :prepare_dispatch 156 142 self.unprepared = false 157 143 end … … 178 164 end 179 165 180 def run_callbacks(kind, enumerator = :each)181 callbacks[kind].send!(enumerator) do |callback|182 case callback183 when Proc; callback.call(self)184 when String, Symbol; send!(callback)185 when Array; callback[1].call(self)186 else raise ArgumentError, "Unrecognized callback #{callback.inspect}"187 end188 end189 end190 191 166 def failsafe_rescue(exception) 192 167 self.class.failsafe_response(@output, '500 Internal Server Error', exception) do trunk/actionpack/test/controller/dispatcher_test.rb
r8564 r8664 12 12 ENV['REQUEST_METHOD'] = 'GET' 13 13 14 Dispatcher. callbacks[:prepare].clear14 Dispatcher.instance_variable_set("@prepare_dispatch_callbacks", []) 15 15 @dispatcher = Dispatcher.new(@output) 16 16 end trunk/activerecord/lib/active_record/callbacks.rb
r8301 r8664 184 184 end 185 185 186 CALLBACKS.each do |method| 187 base.class_eval <<-"end_eval" 188 def self.#{method}(*callbacks, &block) 189 callbacks << block if block_given? 190 write_inheritable_array(#{method.to_sym.inspect}, callbacks) 191 end 192 end_eval 193 end 186 base.send :include, ActiveSupport::Callbacks 187 base.define_callbacks *CALLBACKS 194 188 end 195 189 … … 302 296 notify(method) 303 297 304 callbacks_for(method).each do |callback| 305 result = case callback 306 when Symbol 307 self.send(callback) 308 when String 309 eval(callback, binding) 310 when Proc, Method 311 callback.call(self) 312 else 313 if callback.respond_to?(method) 314 callback.send(method, self) 315 else 316 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." 317 end 318 end 319 return false if result == false 298 result = run_callbacks(method) { |result, object| result == false } 299 300 if result != false && respond_to_without_attributes?(method) 301 result = send(method) 320 302 end 321 303 322 result = send(method) if respond_to_without_attributes?(method)323 324 304 return result 325 end326 327 def callbacks_for(method)328 self.class.read_inheritable_attribute(method.to_sym) or []329 end330 331 def invoke_and_notify(method)332 notify(method)333 send(method) if respond_to_without_attributes?(method)334 305 end 335 306 trunk/activerecord/lib/active_record/validations.rb
r8575 r8664 280 280 alias_method_chain :update_attribute, :validation_skipping 281 281 end 282 283 base.send :include, ActiveSupport::Callbacks 284 285 # TODO: Use helper ActiveSupport::Callbacks#define_callbacks instead 286 %w( validate validate_on_create validate_on_update ).each do |validation_method| 287 base.class_eval <<-"end_eval" 288 def self.#{validation_method}(*methods, &block) 289 options = methods.extract_options! 290 methods << block if block_given? 291 methods.map! { |method| Callback.new(:#{validation_method}, method, options) } 292 existing_methods = read_inheritable_attribute(:#{validation_method}) || [] 293 write_inheritable_attribute(:#{validation_method}, existing_methods | methods) 294 end 295 296 def self.#{validation_method}_callback_chain 297 read_inheritable_attribute(:#{validation_method}) || [] 298 end 299 end_eval 300 end 282 301 end 283 302 … … 325 344 # 326 345 # This usage applies to #validate_on_create and #validate_on_update as well. 327 def validate(*methods, &block)328 methods << block if block_given?329 write_inheritable_set(:validate, methods)330 end331 332 def validate_on_create(*methods, &block)333 methods << block if block_given?334 write_inheritable_set(:validate_on_create, methods)335 end336 337 def validate_on_update(*methods, &block)338 methods << block if block_given?339 write_inheritable_set(:validate_on_update, methods)340 end341 342 def condition_block?(condition)343 condition.respond_to?("call") && (condition.arity == 1 || condition.arity == -1)344 end345 346 # Determine from the given condition (whether a block, procedure, method or string)347 # whether or not to validate the record. See #validates_each.348 def evaluate_condition(condition, record)349 case condition350 when Symbol; record.send(condition)351 when String; eval(condition, record.instance_eval { binding })352 else353 if condition_block?(condition)354 condition.call(record)355 else356 raise(357 ActiveRecordError,358 "Validations need to be either a symbol, string (to be eval'ed), proc/method, or " +359 "class implementing a static validation method"360 )361 end362 end363 end364 346 365 347 # Validates each attribute against a block. … … 380 362 # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should 381 363 # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The 382 # method, proc or string should return or evaluate to a true or false value. 364 # method, proc or string should return or evaluate to a true or false value. 383 365 def validates_each(*attrs) 384 366 options = attrs.extract_options!.symbolize_keys … … 386 368 387 369 # Declare the validation. 388 send(validation_method(options[:on] || :save)) do |record| 389 # 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 390 unless (options[:if] && !evaluate_condition(options[:if], record)) || (options[:unless] && evaluate_condition(options[:unless], record)) 391 attrs.each do |attr| 392 value = record.send(attr) 393 next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank]) 394 yield record, attr, value 395 end 370 send(validation_method(options[:on] || :save), options) do |record| 371 attrs.each do |attr| 372 value = record.send(attr) 373 next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank]) 374 yield record, attr, value 396 375 end 397 376 end … … 516 495 # can't use validates_each here, because it cannot cope with nonexistent attributes, 517 496 # while errors.add_on_empty can 518 send(validation_method(configuration[:on])) do |record| 519 unless (configuration[:if] && !evaluate_condition(configuration[:if], record)) || (configuration[:unless] && evaluate_condition(configuration[:unless], record)) 520 record.errors.add_on_blank(attr_names, configuration[:message]) 521 end 522 end 497 send(validation_method(configuration[:on]), configuration) do |record| 498 record.errors.add_on_blank(attr_names, configuration[:message]) 499 end 523 500 end 524 501 … … 912 889 end 913 890 914 915 891 private 916 def write_inheritable_set(key, methods)917 existing_methods = read_inheritable_attribute(key) || []918 write_inheritable_attribute(key, existing_methods | methods)919 end920 921 892 def validation_method(on) 922 893 case on … … 960 931 errors.clear 961 932 962 run_ validations(:validate)933 run_callbacks(:validate) 963 934 validate 964 935 965 936 if new_record? 966 run_ validations(:validate_on_create)937 run_callbacks(:validate_on_create) 967 938 validate_on_create 968 939 else 969 run_ validations(:validate_on_update)940 run_callbacks(:validate_on_update) 970 941 validate_on_update 971 942 end … … 991 962 def validate_on_update # :doc: 992 963 end 993 994 private995 def run_validations(validation_method)996 validations = self.class.read_inheritable_attribute(validation_method.to_sym)997 if validations.nil? then return end998 validations.each do |validation|999 if validation.is_a?(Symbol)1000 self.send(validation)1001 elsif validation.is_a?(String)1002 eval(validation, binding)1003 elsif validation_block?(validation)1004 validation.call(self)1005 elsif validation_class?(validation, validation_method)1006 validation.send(validation_method, self)1007 else1008 raise(1009 ActiveRecordError,1010 "Validations need to be either a symbol, string (to be eval'ed), proc/method, or " +1011 "class implementing a static validation method"1012 )1013 end1014 end1015 end1016 1017 def validation_block?(validation)1018 validation.respond_to?("call") && (validation.arity == 1 || validation.arity == -1)1019 end1020 1021 def validation_class?(validation, validation_method)1022 validation.respond_to?(validation_method)1023 end1024 964 end 1025 965 end trunk/activerecord/test/cases/validations_test.rb
r8661 r8664 1030 1030 def test_invalid_validator 1031 1031 Topic.validate 3 1032 assert_raise(A ctiveRecord::ActiveRecordError) { t = Topic.create }1032 assert_raise(ArgumentError) { t = Topic.create } 1033 1033 end 1034 1034 trunk/activesupport/CHANGELOG
r8649 r8664 1 1 *SVN* 2 3 * Extract ActiveSupport::Callbacks from Active Record, test case setup and teardown, and ActionController::Dispatcher. #10727 [Josh Peek] 2 4 3 5 * Introducing DateTime #utc, #utc? and #utc_offset, for duck-typing compatibility with Time. Closes #10002 [Geoff Buesing] trunk/activesupport/lib/active_support.rb
r8570 r8664 27 27 require 'active_support/basic_object' 28 28 require 'active_support/inflector' 29 require 'active_support/callbacks' 29 30 30 31 require 'active_support/core_ext' trunk/activesupport/lib/active_support/testing/setup_and_teardown.rb
r8570 r8664 3 3 module SetupAndTeardown 4 4 def self.included(base) 5 base.extend ClassMethods 5 base.send :include, ActiveSupport::Callbacks 6 base.define_callbacks :setup, :teardown 6 7 7 8 begin … … 10 11 rescue LoadError 11 12 base.alias_method_chain :run, :callbacks 12 end13 end14 15 module ClassMethods16 def setup(*method_names, &block)17 method_names << block if block_given?18 (@setup_callbacks ||= []).concat method_names19 end20 21 def teardown(*method_names, &block)22 method_names << block if block_given?23 (@teardown_callbacks ||= []).concat method_names24 end25 26 def setup_callback_chain27 @setup_callbacks ||= []28 29 if superclass.respond_to?(:setup_callback_chain)30 superclass.setup_callback_chain + @setup_callbacks31 else32 @setup_callbacks33 end34 end35 36 def teardown_callback_chain37 @teardown_callbacks ||= []38 39 if superclass.respond_to?(:teardown_callback_chain)40 superclass.teardown_callback_chain + @teardown_callbacks41 else42 @teardown_callbacks43 end44 13 end 45 14 end … … 64 33 begin 65 34 teardown 66 run_callbacks :teardown, : reverse_each35 run_callbacks :teardown, :enumerator => :reverse_each 67 36 rescue Test::Unit::AssertionFailedError => e 68 37 add_failure(e.message, e.backtrace) … … 99 68 begin 100 69 teardown 101 run_callbacks :teardown, : reverse_each70 run_callbacks :teardown, :enumerator => :reverse_each 102 71 rescue Test::Unit::AssertionFailedError => e 103 72 add_failure(e.message, e.backtrace) … … 112 81 yield(Test::Unit::TestCase::FINISHED, name) 113 82 end 114 115 protected116 def run_callbacks(kind, enumerator = :each)117 self.class.send("#{kind}_callback_chain").send(enumerator) do |callback|118 case callback119 when Proc; callback.call(self)120 when String, Symbol; send!(callback)121 else raise ArgumentError, "Unrecognized callback #{callback.inspect}"122 end123 end124 end125 83 end 126 84 end trunk/activesupport/test/test_test.rb
r8570 r8664 80 80 81 81 def test_inherited_setup_callbacks 82 assert_equal [:reset_callback_record, :foo], self.class.setup_callback_chain 82 assert_equal [:reset_callback_record, :foo], self.class.setup_callback_chain.map(&:method) 83 83 assert_equal [:foo], @called_back 84 assert_equal [:foo, :sentinel, :foo], self.class.teardown_callback_chain 84 assert_equal [:foo, :sentinel, :foo], self.class.teardown_callback_chain.map(&:method) 85 85 end 86 86 … … 105 105 106 106 def test_inherited_setup_callbacks 107 assert_equal [:reset_callback_record, :foo, :bar], self.class.setup_callback_chain 107 assert_equal [:reset_callback_record, :foo, :bar], self.class.setup_callback_chain.map(&:method) 108 108 assert_equal [:foo, :bar], @called_back 109 assert_equal [:foo, :sentinel, :foo, :bar], self.class.teardown_callback_chain 109 assert_equal [:foo, :sentinel, :foo, :bar], self.class.teardown_callback_chain.map(&:method) 110 110 end 111 111