Skip to content

Commit 1fc8052

Browse files
neilshwekyp-mongo
andauthored
MONGOID-5227 Add test and adjust docs for dotted attribute assignment in default scope (#5474)
* MONGOID-5227 Add test and adjust docs for dotted attribute assignment in default scope * MONGOID-5227 adjust docs * MONGOID-5227 clarify the docs and add tests * Update docs/reference/queries.txt Co-authored-by: Oleg Pudeyev <[email protected]> Co-authored-by: Oleg Pudeyev <[email protected]>
1 parent 784bd9a commit 1fc8052

File tree

3 files changed

+108
-6
lines changed

3 files changed

+108
-6
lines changed

docs/reference/queries.txt

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1887,8 +1887,8 @@ in the default scope, the value in the default scope takes precedence:
18871887
Band.new # => #<Band _id: 5c3f74ddce4ef3791abbb088, name: nil, active: false>
18881888

18891889
Because a default scope initializes fields in new models as just described,
1890-
defining a default scope with a dotted key and a simple literal value is
1891-
not possible:
1890+
defining a default scope with a dotted key and a simple literal value, while
1891+
possible, is not recommended:
18921892

18931893
.. code-block:: ruby
18941894

@@ -1900,7 +1900,36 @@ not possible:
19001900
default_scope ->{ where('tags.foo' => 'bar') }
19011901
end
19021902

1903-
Band.create! # exception: BSON::String::IllegalKey ('tags.foo' is an illegal key in MongoDB. Keys may not start with '$' or contain a '.'.)
1903+
Band.create!
1904+
# => Created document: {"_id"=>BSON::ObjectId('632de48f3282a404bee1877b'), "tags.foo"=>"bar"}
1905+
Band.create!(tags: { 'foo' => 'bar' })
1906+
# => Created document: {"_id"=>BSON::ObjectId('632de4ad3282a404bee1877c'), "tags.foo"=>"bar", "tags"=>{"foo"=>"bar"}}
1907+
Band.all.to_a
1908+
# => [ #<Band _id: 632de4ad3282a404bee1877c, tags: {"foo"=>"bar"}> ]
1909+
1910+
Mongoid 8 allows dotted keys to be used in Mongoid, and when creating a document,
1911+
the scope is added as a dotted key in the attributes:
1912+
1913+
.. code-block:: ruby
1914+
1915+
Band.new.attribute
1916+
# => {"_id"=>BSON::ObjectId('632de97d3282a404bee1877d'), "tags.foo"=>"bar"}
1917+
1918+
Whereas when querying, Mongoid looks for an embedded document:
1919+
1920+
.. code-block:: ruby
1921+
1922+
Band.create!
1923+
# => Created document: {"_id"=>BSON::ObjectId('632de48f3282a404bee1877b'), "tags.foo"=>"bar"}
1924+
Band.where
1925+
# => #<Mongoid::Criteria
1926+
selector: {"tags.foo"=>"bar"}
1927+
options: {}
1928+
class: Band
1929+
embedded: false>
1930+
# This looks for something like: { tags: { "foo" => "bar" } }
1931+
Band.count
1932+
# => 0
19041933

19051934
A workaround is to define the default scope as a complex query:
19061935

@@ -1914,9 +1943,11 @@ A workaround is to define the default scope as a complex query:
19141943
default_scope ->{ where('tags.foo' => {'$eq' => 'bar'}) }
19151944
end
19161945

1917-
Band.create!(tags: {hello: 'world'})
1918-
Band.create!(tags: {foo: 'bar'})
1919-
Band.count # => 1
1946+
Band.create!(tags: { hello: 'world' })
1947+
Band.create!(tags: { foo: 'bar' })
1948+
# does not add a "tags.foo" dotted attribute
1949+
Band.count
1950+
# => 1
19201951

19211952
You can tell Mongoid not to apply the default scope by using
19221953
``unscoped``, which can be inline or take a block.

spec/mongoid/scopable_spec.rb

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,76 @@
121121
expect(selector).to eq({'active' => true})
122122
end
123123
end
124+
125+
context "when the default scope is dotted" do
126+
127+
let(:criteria) do
128+
Band.where('tags.foo' => 'bar')
129+
end
130+
131+
before do
132+
Band.default_scope ->{ criteria }
133+
end
134+
135+
after do
136+
Band.default_scoping = nil
137+
end
138+
139+
let!(:band) do
140+
Band.create!
141+
end
142+
143+
it "adds the scope as a dotted key attribute" do
144+
expect(band.attributes['tags.foo']).to eq('bar')
145+
end
146+
147+
it "adds the default scope to the class" do
148+
expect(Band.default_scoping.call).to eq(criteria)
149+
end
150+
151+
it "flags as being default scoped" do
152+
expect(Band).to be_default_scoping
153+
end
154+
155+
it "does not find the correct document" do
156+
expect(Band.count).to eq(0)
157+
end
158+
end
159+
160+
context "when the default scope is dotted with a query" do
161+
162+
let(:criteria) do
163+
Band.where('tags.foo' => {'$eq' => 'bar'})
164+
end
165+
166+
before do
167+
Band.default_scope ->{ criteria }
168+
end
169+
170+
after do
171+
Band.default_scoping = nil
172+
end
173+
174+
let!(:band) do
175+
Band.create!('tags' => { 'foo' => 'bar' })
176+
end
177+
178+
it "does not add the scope as a dotted key attribute" do
179+
expect(band.attributes).to_not have_key('tags.foo')
180+
end
181+
182+
it "adds the default scope to the class" do
183+
expect(Band.default_scoping.call).to eq(criteria)
184+
end
185+
186+
it "flags as being default scoped" do
187+
expect(Band).to be_default_scoping
188+
end
189+
190+
it "finds the correct document" do
191+
expect(Band.where.first).to eq(band)
192+
end
193+
end
124194
end
125195

126196
describe ".default_scopable?" do

spec/support/models/band.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class Band
2121
field :founded, type: Date
2222
field :deleted, type: Boolean
2323
field :mojo, type: Object
24+
field :tags, type: Hash
2425
field :fans
2526

2627
embeds_many :records, cascade_callbacks: true

0 commit comments

Comments
 (0)