Skip to content

Commit 995fc64

Browse files
committed
Fix MONGOID-5136 .touch with custom field is broken for embedded documents
1 parent 28e2d40 commit 995fc64

File tree

4 files changed

+56
-2
lines changed

4 files changed

+56
-2
lines changed

lib/mongoid/atomic.rb

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,11 +385,35 @@ def touch_atomic_updates(field = nil)
385385
updates = atomic_updates
386386
return {} unless atomic_updates.key?("$set")
387387
touches = {}
388+
wanted_keys = %w(updated_at u_at)
389+
# TODO this permits field to be passed as an empty string in which case
390+
# it is ignored, get rid of this behavior.
391+
if field.present?
392+
wanted_keys << field.to_s
393+
end
388394
updates["$set"].each_pair do |key, value|
389-
key_regex = /updated_at|u_at#{"|" + field if field.present?}/
390-
touches.merge!({ key => value }) if key =~ key_regex
395+
if wanted_keys.include?(key.split('.').last)
396+
touches.update(key => value)
397+
end
391398
end
392399
{ "$set" => touches }
393400
end
401+
402+
# Returns the $set atomic updates affecting the specified field.
403+
#
404+
# @param [ String ] field The field name.
405+
#
406+
# @api private
407+
def set_field_atomic_updates(field)
408+
updates = atomic_updates
409+
return {} unless atomic_updates.key?("$set")
410+
sets = {}
411+
updates["$set"].each_pair do |key, value|
412+
if key.split('.').last == field
413+
sets.update(key => value)
414+
end
415+
end
416+
{ "$set" => sets }
417+
end
394418
end
395419
end

lib/mongoid/touchable.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ def touch(field = nil)
4141
# _association.inverse_association.options but inverse_association
4242
# seems to not always/ever be set here. See MONGOID-5014.
4343
_parent.touch
44+
45+
if field
46+
# If we are told to also touch a field, perform a separate write
47+
# for that field. See MONGOID-5136.
48+
# In theory we should combine the writes, which would require
49+
# passing the fields to be updated to the parents - MONGOID-5142.
50+
sets = set_field_atomic_updates(field)
51+
selector = atomic_selector
52+
_root.collection.find(selector).update_one(positionally(selector, sets), session: _session)
53+
end
4454
else
4555
# If the current document is not embedded, it is composition root
4656
# and we need to persist the write here.

spec/mongoid/touchable_spec.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,24 @@
132132
include_examples 'updates the child'
133133
include_examples 'updates the parent when :touch is true'
134134
include_examples 'updates the parent when :touch is not set'
135+
136+
context 'when also updating an additional field' do
137+
it 'persists the update to the additional field' do
138+
entrance
139+
update_time
140+
entrance.touch(:last_used_at)
141+
142+
entrance.reload
143+
building.reload
144+
145+
# This is the assertion we want.
146+
entrance.last_used_at.should == update_time
147+
148+
# Check other timestamps for good measure.
149+
entrance.updated_at.should == update_time
150+
building.updated_at.should == update_time
151+
end
152+
end
135153
end
136154

137155
context "when the document is referenced" do

spec/mongoid/touchable_spec_models.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class Entrance
1616
include Mongoid::Timestamps
1717

1818
embedded_in :building
19+
20+
field :last_used_at, type: Time
1921
end
2022

2123
class Floor

0 commit comments

Comments
 (0)