diff --git a/CHANGELOG.md b/CHANGELOG.md index e8054e9329..c31ca8388a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ For instructions on upgrading to newer versions, visit ### Resolved Issues +* \#1936 Allow setting n levels deep embedded documents atomically without + conflicting mods when not using nested attributes or documents themselves + in an update call from the parent. + * \#1957/\#1954 Ensure database name is set with inheritance. (Hans Hasselberg) diff --git a/lib/mongoid/atomic.rb b/lib/mongoid/atomic.rb index fca2c82935..eda60a10ed 100644 --- a/lib/mongoid/atomic.rb +++ b/lib/mongoid/atomic.rb @@ -252,6 +252,9 @@ def delayed_atomic_pulls # @example Get the atomic paths. # document.atomic_paths # + # @todo: Durran: Should probably raise error for embedded docs w/o + # metadata. + # # @return [ Object ] The associated path. # # @since 2.1.0 diff --git a/lib/mongoid/relations/embedded/many.rb b/lib/mongoid/relations/embedded/many.rb index 63fccc6ba1..e203199eeb 100644 --- a/lib/mongoid/relations/embedded/many.rb +++ b/lib/mongoid/relations/embedded/many.rb @@ -307,7 +307,11 @@ def substitute(replacement) atomically(:$set) do base.delayed_atomic_sets.clear if replacement.first.is_a?(Hash) - replacement = Many.builder(base, metadata, replacement).build + replacement = replacement.map do |doc| + attributes = { metadata: metadata, _parent: base } + attributes.merge!(doc) + Factory.build(klass, attributes) + end end docs = replacement.compact proxy.target = docs @@ -319,6 +323,10 @@ def substitute(replacement) end if _assigning? name = _unscoped.first.atomic_path + base._children.each do |child| + child.delayed_atomic_sets.clear + end + base.instance_variable_set(:@_children, nil) base.delayed_atomic_sets[name] = proxy.as_document end end diff --git a/spec/functional/mongoid/relations/embedded/many_spec.rb b/spec/functional/mongoid/relations/embedded/many_spec.rb index 2fe7d16902..2f1e7545e7 100644 --- a/spec/functional/mongoid/relations/embedded/many_spec.rb +++ b/spec/functional/mongoid/relations/embedded/many_spec.rb @@ -3025,4 +3025,53 @@ end end end + + context "when adding a document" do + + let(:person) do + Person.new + end + + let(:address_one) do + Address.new(street: "hobrecht") + end + + let(:first_add) do + person.addresses.push(address_one) + end + end + + context "when updating multiple levels in one update" do + + let!(:person) do + Person.create( + addresses: [ + { locations: [{ name: "home" }]} + ] + ) + end + + context "when updating with hashes" do + + let(:from_db) do + Person.find(person.id) + end + + before do + from_db.update_attributes( + addresses: [ + { locations: [{ name: "work" }]} + ] + ) + end + + let(:updated) do + person.reload.addresses.first.locations.first + end + + it "updates the nested document" do + updated.name.should eq("work") + end + end + end end