Skip to content

Commit e08fff0

Browse files
authored
MONGOID-4397 update_attributes does not reflect changes to embedded documents in the attributes hash (#5232)
* MONGOID-4397 potential fix * MONGOID-4397 old implementation * Revert "MONGOID-4397 old implementation" This reverts commit 252b2f1. * MONGOID-4397 second implementation moved to binding * MONGOID-4397 finish embeds_one/many tests * MONGOID-4397 fix tests * MONGOID-4397 fix tests * MONGOID-4397 take hash assignments into account
1 parent ffcf2b5 commit e08fff0

File tree

6 files changed

+481
-4
lines changed

6 files changed

+481
-4
lines changed

lib/mongoid/association/embedded/embeds_many/proxy.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ def build(attributes = {}, type = nil)
9595
# @return [ self ] The empty association.
9696
def clear
9797
batch_clear(_target.dup)
98+
update_attributes_hash
9899
self
99100
end
100101

@@ -146,6 +147,7 @@ def delete(document)
146147
doc.delete(suppress: true)
147148
unbind_one(doc)
148149
end
150+
update_attributes_hash
149151
end
150152
reindex
151153
doc
@@ -257,6 +259,7 @@ def initialize(base, target, association)
257259
integrate(doc)
258260
doc._index = index
259261
end
262+
update_attributes_hash
260263
@_unscoped = _target.dup
261264
@_target = scope(_target)
262265
end
@@ -292,6 +295,8 @@ def pop(count = nil)
292295
end
293296
else
294297
delete(_target[-1])
298+
end.tap do
299+
update_attributes_hash
295300
end
296301
end
297302

@@ -315,6 +320,8 @@ def shift(count = nil)
315320
end
316321
else
317322
delete(_target[0])
323+
end.tap do
324+
update_attributes_hash
318325
end
319326
end
320327

@@ -329,6 +336,7 @@ def shift(count = nil)
329336
# @return [ Many ] The proxied association.
330337
def substitute(docs)
331338
batch_replace(docs)
339+
update_attributes_hash
332340
self
333341
end
334342

@@ -366,6 +374,7 @@ def append(document)
366374
end
367375
_unscoped.push(document)
368376
integrate(document)
377+
update_attributes_hash
369378
document._index = _unscoped.size - 1
370379
execute_callback :after_add, document
371380
end
@@ -399,6 +408,7 @@ def criteria
399408
def delete_one(document)
400409
_target.delete_one(document)
401410
_unscoped.delete_one(document)
411+
update_attributes_hash
402412
reindex
403413
end
404414

@@ -487,6 +497,7 @@ def remove_all(conditions = {}, method = :delete)
487497
criteria = where(conditions || {})
488498
removed = criteria.size
489499
batch_remove(criteria, method)
500+
update_attributes_hash
490501
removed
491502
end
492503

@@ -519,6 +530,17 @@ def as_attributes
519530
_unscoped.map { |doc| doc.send(:as_attributes) }
520531
end
521532

533+
# Update the _base's attributes hash with the _target's attributes
534+
#
535+
# @api private
536+
def update_attributes_hash
537+
if !_target.empty?
538+
_base.attributes.merge!(_association.store_as => _target.map(&:attributes))
539+
else
540+
_base.attributes.delete(_association.store_as)
541+
end
542+
end
543+
522544
class << self
523545

524546
# Returns true if the association is an embedded one. In this case

lib/mongoid/association/embedded/embeds_one/proxy.rb

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def initialize(base, target, association)
3131
characterize_one(_target)
3232
bind_one
3333
characterize_one(_target)
34+
update_attributes_hash(_target)
3435
_base._reset_memoized_descendants!
3536
_target.save if persistable?
3637
end
@@ -80,11 +81,15 @@ def substitute(replacement)
8081
end
8182
end
8283
unbind_one
83-
return nil unless replacement
84+
unless replacement
85+
update_attributes_hash(replacement)
86+
return nil
87+
end
8488
replacement = Factory.build(klass, replacement) if replacement.is_a?(::Hash)
8589
self._target = replacement
8690
characterize_one(_target)
8791
bind_one
92+
update_attributes_hash(_target)
8893
_target.save if persistable?
8994
end
9095
self
@@ -112,6 +117,20 @@ def persistable?
112117
_base.persisted? && !_binding? && !_building? && !_assigning?
113118
end
114119

120+
# Update the _base's attributes hash with the _target's attributes
121+
#
122+
# @param replacement [ Document | nil ] The doc to use to update the
123+
# attributes hash.
124+
#
125+
# @api private
126+
def update_attributes_hash(replacement)
127+
if replacement
128+
_base.attributes.merge!(_association.store_as => replacement.attributes)
129+
else
130+
_base.attributes.delete(_association.store_as)
131+
end
132+
end
133+
115134
class << self
116135

117136
# Returns true if the association is an embedded one. In this case

lib/mongoid/traversable.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ def parentize(document)
233233
def remove_child(child)
234234
name = child.association_name
235235
if child.embedded_one?
236+
self.attributes.delete(child._association.store_as)
236237
remove_ivar(name)
237238
else
238239
relation = send(name)

spec/mongoid/attributes/nested_spec_models.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,26 @@ class NestedPost
2323
has_many :comments, class_name: "NestedComment"
2424
accepts_nested_attributes_for :comments
2525
end
26+
27+
class NestedBook
28+
include Mongoid::Document
29+
30+
embeds_one :cover, class_name: "NestedCover"
31+
embeds_many :pages, class_name: "NestedPage"
32+
33+
accepts_nested_attributes_for :cover, :pages
34+
end
35+
36+
class NestedCover
37+
include Mongoid::Document
38+
39+
field :title, type: String
40+
embedded_in :book, class_name: "NestedBook"
41+
end
42+
43+
class NestedPage
44+
include Mongoid::Document
45+
46+
field :number, type: Integer
47+
embedded_in :book, class_name: "NestedBook"
48+
end

0 commit comments

Comments
 (0)