Skip to content

Commit 4ab8851

Browse files
authored
MONGOID-5452 add legacy_attributes to 7.5 (#5424)
* MONGOID-5365 Change #attributes return value to Hash with a flag (#5301) * MONGOID-5365 Change #attributes return value to Hash with a flag * MONGOID-5365 add config_override to tests * MONGOID-5365 rename flag and flip default * MONGOID-5365 fix old flag ref * Update lib/mongoid/config.rb * Update docs/reference/configuration.txt * Update docs/reference/configuration.txt * MONGOID-5365 fix config test
1 parent 284548c commit 4ab8851

File tree

8 files changed

+161
-2
lines changed

8 files changed

+161
-2
lines changed

docs/reference/configuration.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,12 @@ for details on driver options.
369369
# to parent contexts by default. (default: false)
370370
join_contexts: false
371371

372+
# When this flag is true, the attributes method on a document will return
373+
# a BSON::Document when that document is retrieved from the database, and
374+
# a Hash otherwise. When this flag is false, the attributes method will
375+
# always return a Hash. (default: true)
376+
legacy_attributes: true
377+
372378
# Maintain legacy behavior of pluck and distinct, which does not demongoize
373379
# values on returning them. Setting this option to false will cause
374380
# pluck and distinct to return demongoized values. Setting this option to

docs/release-notes/mongoid-7.5.txt

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ example:
206206
.. code-block:: ruby
207207

208208
Band.all.map(:name)
209-
209+
210210
# Equivalent to:
211211
Band.pluck(:name)
212212

@@ -244,3 +244,42 @@ The ``Document#to_a`` method is deprecated in Mongoid 7.5.
244244
Mongoid 7.5 fixes incorrect usage of the driver's ``update_one`` method from
245245
Mongoid's ``upsert`` method. Mongoid's ``upsert`` actually performs a
246246
replacing upsert, and Mongoid 7.5 correctly calls ``replace_one``.
247+
248+
249+
Force the ``attributes`` Method to Always Return a ``Hash``
250+
-----------------------------------------------------------
251+
252+
Mongoid 7.5 with the ``Mongoid.legacy_attributes`` option set to ``false``
253+
will always return a ``Hash`` when calling the ``attributes`` method.
254+
For example:
255+
256+
.. code-block:: ruby
257+
258+
class Band
259+
include Mongoid::Document
260+
261+
field :name
262+
end
263+
264+
band = Band.create!(name: "The Rolling Stones")
265+
p band.attributes.class
266+
# => Hash
267+
268+
band = Band.first
269+
p band.attributes.class
270+
# => Hash
271+
272+
In Mongoid 7.4 and earlier, and in 7.5 with the ``Mongoid.legacy_attributes``
273+
option set to ``true``, the ``attributes`` method on a document will return a
274+
``BSON::Document`` when retrieving that document from the database, but will
275+
return a ``Hash`` when instantiating a new document:
276+
277+
.. code-block:: ruby
278+
279+
band = Band.create!(name: "The Rolling Stones")
280+
p band.attributes.class
281+
# => Hash
282+
283+
band = Band.first
284+
p band.attributes.class
285+
# => BSON::Document

lib/mongoid/config.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@ module Config
116116
# using and's instead of overwriting them.
117117
option :overwrite_chained_operators, default: true
118118

119+
# When this flag is true, the attributes method on a document will return
120+
# a BSON::Document when that document is retrieved from the database, and
121+
# a Hash otherwise. When this flag is false, the attributes method will
122+
# always return a Hash.
123+
option :legacy_attributes, default: true
124+
119125
# Has Mongoid been configured? This is checking that at least a valid
120126
# client config exists.
121127
#

lib/mongoid/document.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,15 @@ module ClassMethods
277277
# criteria.
278278
#
279279
# @return [ Document ] A new document.
280+
#
281+
# @api private
280282
def instantiate(attrs = nil, selected_fields = nil)
281-
attributes = attrs || {}
283+
attributes = if Mongoid.legacy_attributes
284+
attrs
285+
else
286+
attrs&.to_h
287+
end || {}
288+
282289
doc = allocate
283290
doc.__selected_fields = selected_fields
284291
doc.instance_variable_set(:@attributes, attributes)

spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22

33
require "spec_helper"
4+
require_relative "../has_and_belongs_to_many_models.rb"
45

56
describe Mongoid::Association::Referenced::HasAndBelongsToMany::Proxy do
67

@@ -3770,4 +3771,33 @@ class Distributor
37703771
expect(p2.d_ids).to match_array([d2.id])
37713772
end
37723773
end
3774+
3775+
# This test is for MONGOID-5344 which tests that the initial call to
3776+
# signature_ids refers to the same array as subsequent calls to signature_ids.
3777+
# Prior to the change in that ticket, this test broke because the array
3778+
# returned from write_attribute (which is triggered the first time the
3779+
# foreign key array is referenced, to set the default), refers to a different
3780+
# array to the one stored in the attributes hash. This happened because,
3781+
# when retrieving a document from the database, the attributes hash is actually
3782+
# a BSON::Document, which applies a transformation to the array before
3783+
# storing it.
3784+
context "when executing concat on foreign key array from the db" do
3785+
config_override :legacy_attributes, false
3786+
3787+
before do
3788+
HabtmmContract.create!
3789+
HabtmmSignature.create!
3790+
end
3791+
3792+
let!(:contract) { HabtmmContract.first }
3793+
let!(:signature) { HabtmmSignature.first }
3794+
3795+
before do
3796+
contract.signature_ids.concat([signature.id])
3797+
end
3798+
3799+
it "works on the first attempt" do
3800+
expect(contract.signature_ids).to eq([signature.id])
3801+
end
3802+
end
37733803
end

spec/mongoid/association/referenced/has_many/proxy_spec.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4089,4 +4089,24 @@
40894089
expect(band.same_name).to eq([agent])
40904090
end
40914091
end
4092+
4093+
context "when executing concat on foreign key array from the db" do
4094+
config_override :legacy_attributes, false
4095+
4096+
before do
4097+
Agent.create!
4098+
Basic.create!
4099+
end
4100+
4101+
let!(:agent) { Agent.first }
4102+
let!(:basic) { Basic.first }
4103+
4104+
before do
4105+
agent.basic_ids.concat([basic.id])
4106+
end
4107+
4108+
it "works on the first attempt" do
4109+
expect(agent.basic_ids).to eq([basic.id])
4110+
end
4111+
end
40924112
end

spec/mongoid/attributes_spec.rb

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1675,6 +1675,50 @@
16751675
end
16761676
end
16771677
end
1678+
1679+
context "when comparing the object_ids of the written value" do
1680+
config_override :legacy_attributes, false
1681+
1682+
before do
1683+
Person.create!
1684+
end
1685+
1686+
let(:person) do
1687+
Person.first
1688+
end
1689+
1690+
context "when the field is not resizable" do
1691+
let(:test) do
1692+
person.write_attribute(:test, "aliased field to test")
1693+
end
1694+
1695+
it "has the same object_id as the attributes hash value" do
1696+
expect(test.object_id).to eq(person.test.object_id)
1697+
end
1698+
end
1699+
1700+
context "when the field is resizable" do
1701+
1702+
let(:arrays) do
1703+
person.write_attribute(:arrays, [])
1704+
end
1705+
1706+
it "has the same object_id as the attributes hash value" do
1707+
expect(arrays.object_id).to eq(person.arrays.object_id)
1708+
end
1709+
end
1710+
1711+
context "when the field is a HABTM foreign key array" do
1712+
1713+
let(:preference_ids) do
1714+
person.write_attribute(:preference_ids, [])
1715+
end
1716+
1717+
it "has the same object_id as the attributes hash value" do
1718+
expect(preference_ids.object_id).to eq(person.preference_ids.object_id)
1719+
end
1720+
end
1721+
end
16781722
end
16791723

16801724
describe "#typed_value_for" do

spec/mongoid/config_spec.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,13 @@
335335
it_behaves_like "a config option"
336336
end
337337

338+
context 'when setting the legacy_attributes option in the config' do
339+
let(:option) { :legacy_attributes }
340+
let(:default) { true }
341+
342+
it_behaves_like "a config option"
343+
end
344+
338345
describe "#load!" do
339346

340347
before(:all) do

0 commit comments

Comments
 (0)