diff options
author | Sean Griffin <sean@seantheprogrammer.com> | 2016-02-02 09:15:47 -0700 |
---|---|---|
committer | Sean Griffin <sean@seantheprogrammer.com> | 2016-02-02 09:17:45 -0700 |
commit | 5bb26008ce7c979a87f0d8aef0dd10514838787f (patch) | |
tree | 20f719bc4ce22cd30e252b37280780b500157303 | |
parent | f7775c74d0a9462e89325b145f1aafd029646ba6 (diff) | |
download | rails-5bb26008ce7c979a87f0d8aef0dd10514838787f.tar.gz rails-5bb26008ce7c979a87f0d8aef0dd10514838787f.tar.bz2 rails-5bb26008ce7c979a87f0d8aef0dd10514838787f.zip |
Avoid infinite recursion when bad values are passed to tz aware fields
We had previously updated this to attempt to map over whatever was
passed in, so that additional types like range and array could benefit
from this behavior without the time zone converter having to deal with
every known type.
However, the default behavior of a type is to just yield the given value
to `map`, which means that if we don't actually know how to handle a
value, we'll just recurse infinitely. Since both uses of `map` in this
case occur in cases where we know receiving the same object will
recurse, we can just break on reference equality.
Fixes #23241.
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb | 14 | ||||
-rw-r--r-- | activerecord/test/cases/attribute_methods_test.rb | 7 |
2 files changed, 19 insertions, 2 deletions
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 061628725d..6d345689fa 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -20,7 +20,7 @@ module ActiveRecord nil end else - map(super) { |t| cast(t) } + map_avoiding_infinite_recursion(super) { |v| cast(v) } end end @@ -34,13 +34,23 @@ module ActiveRecord elsif value.is_a?(::Float) value else - map(value) { |v| convert_time_to_time_zone(v) } + map_avoiding_infinite_recursion(value) { |v| convert_time_to_time_zone(v) } end end def set_time_zone_without_conversion(value) ::Time.zone.local_to_utc(value).in_time_zone end + + def map_avoiding_infinite_recursion(value) + map(value) do |v| + if value.equal?(v) + nil + else + yield(value) + end + end + end end extend ActiveSupport::Concern diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 94dfbc3346..ef84624a8d 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -714,6 +714,13 @@ class AttributeMethodsTest < ActiveRecord::TestCase end end + def test_time_zone_aware_attributes_dont_recurse_infinitely_on_invalid_values + in_time_zone "Pacific Time (US & Canada)" do + record = @target.new(bonus_time: []) + assert_equal nil, record.bonus_time + end + end + def test_setting_time_zone_conversion_for_attributes_should_write_value_on_class_variable Topic.skip_time_zone_conversion_for_attributes = [:field_a] Minimalistic.skip_time_zone_conversion_for_attributes = [:field_b] |