Skip to content

Commit 89dc6aa

Browse files
Persist default values for Rails 6.1
This extends the fix from 1f150f6 to Rails 6.1, and adds test coverage. Specifically, it adds a record reload to the "stores the default on creation" test to ensure that the postcondition holds. Without the reload, the "stores the default on creation" test would pass even before 1f150f6. This commit also adds a test for when the record is partially loaded, and changes some Rails private API calls to equivalent public API calls.
1 parent 62cc449 commit 89dc6aa

File tree

3 files changed

+33
-14
lines changed

3 files changed

+33
-14
lines changed

lib/active_record/typed_store/behavior.rb

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -74,20 +74,29 @@ def attribute?(attr_name)
7474

7575
private
7676

77-
def attribute_names_for_partial_inserts
78-
# Contrary to all vanilla Rails types, typedstore attribute have an inherent default
79-
# value that doesn't match the database column default.
80-
# As such we need to insert them on partial inserts even if they weren't changed.
81-
super | self.class.typed_stores.keys.map(&:to_s)
82-
end
77+
if ActiveRecord.version.segments.first >= 7
78+
def attribute_names_for_partial_inserts
79+
# Contrary to all vanilla Rails types, typedstore attribute have an inherent default
80+
# value that doesn't match the database column default.
81+
# As such we need to insert them on partial inserts even if they weren't changed.
82+
super | self.class.typed_stores.keys.map(&:to_s)
83+
end
8384

84-
def attribute_names_for_partial_updates
85-
# On partial updates we shouldn't need to force stores to be persisted. However since
86-
# we weren't persisting them for a while on insertion, we now need to gracefully deal
87-
# with existing records that may have been persisted with a `NULL` store
88-
# We use `blank?` as an heuristic to detect these.
89-
super | self.class.typed_stores.keys.map(&:to_s).select do |store|
90-
@attributes.key?(store) && @attributes[store].value_before_type_cast.blank?
85+
def attribute_names_for_partial_updates
86+
# On partial updates we shouldn't need to force stores to be persisted. However since
87+
# we weren't persisting them for a while on insertion, we now need to gracefully deal
88+
# with existing records that may have been persisted with a `NULL` store
89+
# We use `blank?` as an heuristic to detect these.
90+
super | self.class.typed_stores.keys.map(&:to_s).select do |store|
91+
has_attribute?(store) && read_attribute_before_type_cast(store).blank?
92+
end
93+
end
94+
else
95+
# Rails 6.1 capability
96+
def attribute_names_for_partial_writes
97+
super | self.class.typed_stores.keys.map(&:to_s).select do |store|
98+
has_attribute?(store) && read_attribute_before_type_cast(store).blank?
99+
end
91100
end
92101
end
93102
end

spec/active_record/typed_store_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,7 @@
958958
describe DirtyTrackingModel do
959959
it 'stores the default on creation' do
960960
model = DirtyTrackingModel.create!
961+
model = DirtyTrackingModel.find(model.id)
961962
expect(model.settings_before_type_cast).to_not be_blank
962963
end
963964

@@ -973,4 +974,13 @@
973974
expect(model.settings_changed?).to be false
974975
expect(model.changes).to be_empty
975976
end
977+
978+
it 'does not update missing attributes in partially loaded records' do
979+
model = DirtyTrackingModel.create!(active: true)
980+
model = DirtyTrackingModel.select(:id, :title).find(model.id)
981+
model.update!(title: "Hello")
982+
983+
model = DirtyTrackingModel.find(model.id)
984+
expect(model.active).to be true
985+
end
976986
end

spec/support/models.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ class MarshalTypedStoreModel < ActiveRecord::Base
190190
]
191191

192192
class DirtyTrackingModel < ActiveRecord::Base
193-
after_update :read_active
193+
after_update :read_active, if: -> { has_attribute?(:settings) }
194194

195195
typed_store(:settings) do |f|
196196
f.boolean :active, default: false, null: false

0 commit comments

Comments
 (0)