Skip to content

Commit 4d7dcfc

Browse files
authored
MONGOID-5331 Document hash assignment to associations (#5494)
* MONGOID-5331 change buildable docs * MONGOID-5331 add proxy tests and fix embedded_in proxy * MONGOID-5331 update models * Update spec/mongoid/association/embedded/embedded_in/proxy_spec.rb * MONGOID-5331 update doc strings in batchable * MONGOID-5331 add docs * MONGOID-5331 add docs/release notes * Apply suggestions from code review * Update spec/mongoid/association/embedded/embedded_in/proxy_spec.rb
1 parent 0ffd05d commit 4d7dcfc

File tree

14 files changed

+133
-12
lines changed

14 files changed

+133
-12
lines changed

docs/reference/associations.txt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,38 @@ documents that exist in the application:
875875
band.tours.destroy_all
876876

877877

878+
.. _hash-assignment:
879+
880+
Hash Assignment
881+
---------------
882+
883+
Embedded associations allow the user to assign a ``Hash`` instead of a document
884+
to an association. On assignment, this hash is coerced into a document of the
885+
class of the association that it's being assigned to. Take the following
886+
example:
887+
888+
.. code:: ruby
889+
890+
class Band
891+
include Mongoid::Document
892+
embeds_many :albums
893+
end
894+
895+
class Album
896+
include Mongoid::Document
897+
field :name, type: String
898+
embedded_in :band
899+
end
900+
901+
band = Band.create!
902+
band.albums = [ { name: "Narrow Stairs" }, { name: "Transatlanticism" } ]
903+
p band.albums
904+
# => [ #<Album _id: 633c71e93282a4357bb608e5, name: "Narrow Stairs">, #<Album _id: 633c71e93282a4357bb608e6, name: "Transatlanticism"> ]
905+
906+
This works for ``embeds_one``, ``embeds_many``, and ``embedded_in`` associations.
907+
Note that you cannot assign hashes to referenced associations.
908+
909+
878910
Common Behavior
879911
===============
880912

docs/release-notes/mongoid-8.1.txt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,3 +341,34 @@ This time, the value for the ``:age`` field is maintained.
341341
The default for the ``:replace`` option will be changed to ``false`` in
342342
Mongoid 9.0, therefore it is recommended to explicitly specify this option
343343
while using ``#upsert`` in 8.1 for easier upgradability.
344+
345+
346+
Allow Hash Assignment to ``embedded_in``
347+
----------------------------------------
348+
349+
Mongoid 8.1 allows the assignment of a hash to an ``embedded_in`` association.
350+
On assignment, the hash will be coerced into a document of the class of the
351+
association that it is being assigned to. This functionality already exists
352+
for ``embeds_one`` and ``embeds_many`` associations. Consider the following
353+
example:
354+
355+
.. code:: ruby
356+
357+
class Band
358+
include Mongoid::Document
359+
field :name, type: String
360+
embeds_many :albums
361+
end
362+
363+
class Album
364+
include Mongoid::Document
365+
embedded_in :band
366+
end
367+
368+
album = Album.new
369+
album.band = { name: "Death Cab For Cutie" }
370+
p album.band
371+
# => <Band _id: 633c74113282a438a15d2b56, name: "Death Cab For Cutie">
372+
373+
See the section on :ref:`Hash Assignment on Embedded Associations <hash-assignment>`
374+
for more details

lib/mongoid/association/embedded/batchable.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def batch_remove(docs, method = :delete)
9797
# @example Batch replace the documents.
9898
# batchable.batch_replace([ doc_one, doc_two ])
9999
#
100-
# @param [ Array<Document> ] docs The docs to replace with.
100+
# @param [ Array<Document> | Array<Hash> ] docs The docs to replace with.
101101
#
102102
# @return [ Array<Hash> ] The inserts.
103103
def batch_replace(docs)
@@ -235,7 +235,7 @@ def inserts_valid=(value)
235235
# @example Normalize the docs.
236236
# batchable.normalize_docs(docs)
237237
#
238-
# @param [ Array<Hash | Document> ] docs The docs to normalize.
238+
# @param [ Array<Document> | Array<Hash> ] docs The docs to normalize.
239239
#
240240
# @return [ Array<Document> ] The docs.
241241
def normalize_docs(docs)

lib/mongoid/association/embedded/embedded_in/buildable.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ module Buildable
1515
# @example Build the document.
1616
# Builder.new(meta, attrs).build
1717
#
18-
# @param [ Object ] base The object.
19-
# @param [ Object ] object The parent hash or document.
18+
# @param [ Document ] base The object.
19+
# @param [ Document | Hash ] object The parent hash or document.
2020
# @param [ String ] type Not used in this context.
2121
# @param [ Hash ] selected_fields Fields which were retrieved via
2222
# #only. If selected_fields are specified, fields not listed in it

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def initialize(base, target, association)
3030
# @example Substitute the new document.
3131
# person.name.substitute(new_name)
3232
#
33-
# @param [ Document ] replacement A document to replace the target.
33+
# @param [ Document | Hash ] replacement A document to replace the target.
3434
#
3535
# @return [ Document | nil ] The association or nil.
3636
def substitute(replacement)
@@ -40,6 +40,7 @@ def substitute(replacement)
4040
return nil
4141
end
4242
_base.new_record = true
43+
replacement = Factory.build(klass, replacement) if replacement.is_a?(::Hash)
4344
self._target = replacement
4445
bind_one
4546
self

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ module Buildable
1717
# @example Build the documents.
1818
# Builder.new(meta, attrs).build
1919
#
20-
# @param [ Object ] base The base object.
21-
# @param [ Object ] object The object to use to build the association.
20+
# @param [ Document ] base The base object.
21+
# @param [ Array<Document> | Array<Hash> ] object The object to use
22+
# to build the association.
2223
# @param [ String ] type Not used in this context.
2324
# @param [ Hash ] selected_fields Fields which were retrieved via
2425
# #only. If selected_fields are specified, fields not listed in it

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ def shift(count = nil)
332332
# @example Substitute the association's target.
333333
# person.addresses.substitute([ address ])
334334
#
335-
# @param [ Array<Document> ] docs The replacement docs.
335+
# @param [ Array<Document> | Array<Hash> ] docs The replacement docs.
336336
#
337337
# @return [ Many ] The proxied association.
338338
def substitute(docs)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ module Buildable
1717
# Builder.new(meta, attrs).build
1818
#
1919
# @param [ Document ] base The document this association hangs off of.
20-
# @param [ Document ] object The related document.
20+
# @param [ Document | Hash ] object The related document.
2121
# @param [ String ] _type Not used in this context.
2222
# @param [ Hash ] selected_fields Fields which were retrieved via
2323
# #only. If selected_fields are specified, fields not listed in it

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def initialize(base, target, association)
4343
# @example Substitute the new document.
4444
# person.name.substitute(new_name)
4545
#
46-
# @param [ Document ] replacement A document to replace the target.
46+
# @param [ Document | Hash ] replacement A document to replace the target.
4747
#
4848
# @return [ Document | nil ] The association or nil.
4949
def substitute(replacement)

spec/mongoid/association/embedded/embedded_in/proxy_spec.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,4 +616,31 @@
616616
end
617617
end
618618
end
619+
620+
context "when assigning a hash" do
621+
let(:building_address) { BuildingAddress.new }
622+
623+
before do
624+
building_address.building = { name: "Chrysler" }
625+
end
626+
627+
it "creates the objects correctly" do
628+
expect(building_address.building).to be_a(Building)
629+
expect(building_address.building.name).to eq("Chrysler")
630+
end
631+
end
632+
633+
context "when replacing an association with a hash" do
634+
let(:building_address) { BuildingAddress.new }
635+
636+
before do
637+
building_address.building = { name: "Chrysler" }
638+
building_address.building = { name: "Empire State" }
639+
end
640+
641+
it "creates the objects correctly" do
642+
expect(building_address.building).to be_a(Building)
643+
expect(building_address.building.name).to eq("Empire State")
644+
end
645+
end
619646
end

0 commit comments

Comments
 (0)