diff options
-rw-r--r-- | actionpack/lib/action_dispatch/routing/mapper.rb | 32 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/redirection.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/form_helper.rb | 5 | ||||
-rw-r--r-- | activerecord/CHANGELOG.md | 10 | ||||
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb | 1 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation.rb | 16 | ||||
-rw-r--r-- | activerecord/test/cases/dirty_test.rb | 14 |
7 files changed, 49 insertions, 31 deletions
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 05cbcf709e..0c19b493ab 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -152,7 +152,7 @@ module ActionDispatch end def conditions - { :path_info => @path }.merge(constraints).merge(request_method_condition) + { :path_info => @path }.merge!(constraints).merge!(request_method_condition) end def requirements @@ -252,7 +252,7 @@ module ActionDispatch def defaults_from_constraints(constraints) url_keys = [:protocol, :subdomain, :domain, :host, :port] - constraints.slice(*url_keys).select{ |k, v| v.is_a?(String) || v.is_a?(Fixnum) } + constraints.select { |k, v| url_keys.include?(k) && (v.is_a?(String) || v.is_a?(Fixnum)) } end end @@ -285,7 +285,7 @@ module ActionDispatch # of most Rails applications, this is beneficial. def root(options = {}) options = { :to => options } if options.is_a?(String) - match '/', { :as => :root, :via => :get }.merge(options) + match '/', { :as => :root, :via => :get }.merge!(options) end # Matches a url pattern to one or more routes. Any symbols in a pattern @@ -641,19 +641,16 @@ module ActionDispatch # resources :posts # end def scope(*args) - options = args.extract_options! - options = options.dup - - options[:path] = args.flatten.join('/') if args.any? + options = args.extract_options!.dup recover = {} + options[:path] = args.flatten.join('/') if args.any? options[:constraints] ||= {} - unless options[:constraints].is_a?(Hash) - block, options[:constraints] = options[:constraints], {} - end if options[:constraints].is_a?(Hash) (options[:defaults] ||= {}).reverse_merge!(defaults_from_constraints(options[:constraints])) + else + block, options[:constraints] = options[:constraints], {} end scope_options.each do |option| @@ -663,8 +660,8 @@ module ActionDispatch end end - recover[:block] = @scope[:blocks] - @scope[:blocks] = merge_blocks_scope(@scope[:blocks], block) + recover[:blocks] = @scope[:blocks] + @scope[:blocks] = merge_blocks_scope(@scope[:blocks], block) recover[:options] = @scope[:options] @scope[:options] = merge_options_scope(@scope[:options], options) @@ -672,12 +669,7 @@ module ActionDispatch yield self ensure - scope_options.each do |option| - @scope[option] = recover[option] if recover.has_key?(option) - end - - @scope[:options] = recover[:options] - @scope[:blocks] = recover[:block] + @scope.merge!(recover) end # Scopes routes to a specific controller @@ -853,7 +845,7 @@ module ActionDispatch end def merge_options_scope(parent, child) #:nodoc: - (parent || {}).except(*override_keys(child)).merge(child) + (parent || {}).except(*override_keys(child)).merge!(child) end def merge_shallow_scope(parent, child) #:nodoc: @@ -866,7 +858,7 @@ module ActionDispatch def defaults_from_constraints(constraints) url_keys = [:protocol, :subdomain, :domain, :host, :port] - constraints.slice(*url_keys).select{ |k, v| v.is_a?(String) || v.is_a?(Fixnum) } + constraints.select { |k, v| url_keys.include?(k) && (v.is_a?(String) || v.is_a?(Fixnum)) } end end diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb index d70063d0e9..d751e04e6a 100644 --- a/actionpack/lib/action_dispatch/routing/redirection.rb +++ b/actionpack/lib/action_dispatch/routing/redirection.rb @@ -75,7 +75,7 @@ module ActionDispatch :port => request.optional_port, :path => request.path, :params => request.query_parameters - }.merge options + }.merge! options if !params.empty? && url_options[:path].match(/%\{\w*\}/) url_options[:path] = (url_options[:path] % escape_path(params)) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 6abf1e1751..c79d30ea88 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -702,6 +702,11 @@ module ActionView # <% end %> # ... # <% end %> + # + # Note that fields_for will automatically generate a hidden field + # to store the ID of the record. There are circumstances where this + # hidden field is not needed and you can pass <tt>hidden_field_id: false</tt> + # to prevent fields_for from rendering it automatically. def fields_for(record_name, record_object = nil, options = {}, &block) builder = instantiate_builder(record_name, record_object, options) output = capture(builder, &block) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 1a34ed441f..66359fb80f 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,5 +1,11 @@ ## Rails 4.0.0 (unreleased) ## +* Fix dirty attribute checks for TimeZoneConversion with nil and blank + datetime attributes. Setting a nil datetime to a blank string should not + result in a change being flagged. Fix #8310 + + *Alisdair McDiarmid* + * Prevent mass assignment to the type column of polymorphic associations when using `build` Fix #8265 @@ -452,11 +458,11 @@ *kennyj* -* Use inversed parent for first and last child of has_many association. +* Use inversed parent for first and last child of `has_many` association. *Ravil Bayramgalin* -* Fix Column.microseconds and Column.fast_string_to_date to avoid converting +* Fix `Column.microseconds` and `Column.fast_string_to_time` to avoid converting timestamp seconds to a float, since it occasionally results in inaccuracies with microsecond-precision times. Fixes #7352. diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb index 427c61079a..47a8b576c0 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -34,6 +34,7 @@ module ActiveRecord if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name]) method_body, line = <<-EOV, __LINE__ + 1 def #{attr_name}=(original_time) + original_time = nil if original_time.blank? time = original_time unless time.acts_like?(:time) time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 3ee55c580e..f0f170b684 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -31,6 +31,14 @@ module ActiveRecord @default_scoped = false end + def initialize_copy(other) + # This method is a hot spot, so for now, use Hash[] to dup the hash. + # https://bugs.ruby-lang.org/issues/7166 + @values = Hash[@values] + @values[:bind] = @values[:bind].dup if @values.key? :bind + reset + end + def insert(values) primary_key_value = nil @@ -90,14 +98,6 @@ module ActiveRecord scoping { @klass.new(*args, &block) } end - def initialize_copy(other) - # This method is a hot spot, so for now, use Hash[] to dup the hash. - # https://bugs.ruby-lang.org/issues/7166 - @values = Hash[@values] - @values[:bind] = @values[:bind].dup if @values.key? :bind - reset - end - alias build new # Tries to create a new record with the same scoped attributes diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index d4fc5f204b..6fd9a9d11d 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -203,6 +203,20 @@ class DirtyTest < ActiveRecord::TestCase end end + def test_nullable_datetime_not_marked_as_changed_if_new_value_is_blank + in_time_zone 'Edinburgh' do + target = Class.new(ActiveRecord::Base) + target.table_name = 'topics' + + topic = target.create + assert_equal nil, topic.written_on + + topic.written_on = "" + assert_equal nil, topic.written_on + assert !topic.written_on_changed? + end + end + def test_integer_zero_to_string_zero_not_marked_as_changed pirate = Pirate.new pirate.parrot_id = 0 |