diff --git a/activerecord/lib/active_record/attribute/user_provided_default.rb b/activerecord/lib/active_record/attribute/user_provided_default.rb index 29d37ac6893f6e2a3b20953921a3320ca7b384a1..e0bee8c17ebebedb012ae55f908e8edc323f5b2d 100644 --- a/activerecord/lib/active_record/attribute/user_provided_default.rb +++ b/activerecord/lib/active_record/attribute/user_provided_default.rb @@ -3,8 +3,9 @@ module ActiveRecord class Attribute # :nodoc: class UserProvidedDefault < FromUser - def initialize(name, value, type) + def initialize(name, value, type, database_default) super(name, value, type) + @database_default = database_default end def type_cast(value) @@ -14,6 +15,18 @@ def type_cast(value) super end end + + def changed_in_place_from?(old_value) + super || changed_from?(database_default.value) + end + + def with_type(type) + self.class.new(name, value_before_type_cast, type, database_default) + end + + protected + + attr_reader :database_default end end end diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb index e574aebd6ceaedfe21044dcef60f25105434f77a..c89099589ea2c6b5f5b778cf1f75ce53d8d8bdee 100644 --- a/activerecord/lib/active_record/attributes.rb +++ b/activerecord/lib/active_record/attributes.rb @@ -242,6 +242,7 @@ def define_default_attribute(name, value, type, from_user:) name, value, type, + _default_attributes[name], ) else default_attribute = Attribute.from_database(name, value, type) diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb index 4619293ac6d26efe3f32a8b0891eaacdb30b4169..eeda9335ad4db2cc89df1aedc5ba988bfad6fcc6 100644 --- a/activerecord/test/cases/attributes_test.rb +++ b/activerecord/test/cases/attributes_test.rb @@ -135,6 +135,12 @@ def deserialize(*) assert_equal 2, klass.new.counter end + test "user provided defaults are persisted even if unchanged" do + model = OverloadedType.create! + + assert_equal "the overloaded default", model.reload.string_with_default + end + if current_adapter?(:PostgreSQLAdapter) test "arrays types can be specified" do klass = Class.new(OverloadedType) do diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 3a7cc572e65c033c09e05f2934cb10096d9fcf3c..216f228142e0724f5c7e2601ffd316e93685032e 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -623,32 +623,6 @@ def test_datetime_attribute_doesnt_change_if_zone_is_modified_in_string end end - test "defaults with type that implements `serialize`" do - type = Class.new(ActiveRecord::Type::Value) do - def cast(value) - value.to_i - end - - def serialize(value) - value.to_s - end - end - - model_class = Class.new(ActiveRecord::Base) do - self.table_name = 'numeric_data' - attribute :foo, type.new, default: 1 - end - - model = model_class.new - assert_not model.foo_changed? - - model = model_class.new(foo: 1) - assert_not model.foo_changed? - - model = model_class.new(foo: '1') - assert_not model.foo_changed? - end - test "in place mutation detection" do pirate = Pirate.create!(catchphrase: "arrrr") pirate.catchphrase << " matey!"