Avoid double time zone converter decoration when user-defined timestamp attribute with implicit type

If type is given as a symbol, it will lookup a fresh type object via
`Type.lookup`, but if type is omitted, `type_for_attribute` will lookup
the database type which is already decorated by `define_attribute`.

To avoid the double decoration, skip extra decoration if its type is
already decorated.
This commit is contained in:
Ryuta Kamizono 2020-07-20 17:55:10 +09:00
parent d784043d80
commit e0366022f6
4 changed files with 20 additions and 0 deletions

@ -6,6 +6,10 @@ module ActiveRecord
module AttributeMethods
module TimeZoneConversion
class TimeZoneConverter < DelegateClass(Type::Value) # :nodoc:
def self.new(subtype)
self === subtype ? subtype : super
end
def deserialize(value)
convert_time_to_time_zone(super)
end

@ -178,6 +178,10 @@ def define_attribute(name, cast_type, **) # :nodoc:
# `nil` values to `lock_version`, and not result in `ActiveRecord::StaleObjectError`
# during update record.
class LockingType < DelegateClass(Type::Value) # :nodoc:
def self.new(subtype)
self === subtype ? subtype : super
end
def deserialize(value)
super.to_i
end

@ -74,10 +74,12 @@ class CustomPropertiesTest < ActiveRecord::TestCase
end
starts_at_type = klass.type_for_attribute(:starts_at)
assert_equal 3, starts_at_type.precision
assert_equal 2, starts_at_type.limit
assert_equal 1, starts_at_type.scale
assert_kind_of Type::DateTime, starts_at_type
assert_instance_of Time, klass.new.starts_at
end
@ -85,9 +87,18 @@ class CustomPropertiesTest < ActiveRecord::TestCase
with_timezone_config aware_attributes: true, zone: "Pacific Time (US & Canada)" do
klass = Class.new(OverloadedType) do
attribute :starts_at, :datetime, precision: 3, default: -> { Time.now.utc }
attribute :ends_at, default: -> { Time.now.utc }
end
starts_at_type = klass.type_for_attribute(:starts_at)
ends_at_type = klass.type_for_attribute(:ends_at)
assert_instance_of AttributeMethods::TimeZoneConversion::TimeZoneConverter, starts_at_type
assert_instance_of AttributeMethods::TimeZoneConversion::TimeZoneConverter, ends_at_type
assert_kind_of Type::DateTime, starts_at_type.__getobj__
assert_kind_of Type::DateTime, ends_at_type.__getobj__
assert_instance_of ActiveSupport::TimeWithZone, klass.new.starts_at
assert_instance_of ActiveSupport::TimeWithZone, klass.new.ends_at
end
end

@ -1127,6 +1127,7 @@
t.string :overloaded_string_with_limit, limit: 255
t.string :string_with_default, default: "the original default"
t.string :inferred_string, limit: 255
t.datetime :starts_at, :ends_at
end
create_table :users, force: true do |t|