aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb32
-rw-r--r--actionpack/lib/action_dispatch/routing/redirection.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb5
-rw-r--r--activerecord/CHANGELOG.md10
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb1
-rw-r--r--activerecord/lib/active_record/relation.rb16
-rw-r--r--activerecord/test/cases/dirty_test.rb14
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