mongoid 5.0.2 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/LICENSE +1 -1
- data/README.md +1 -1
- data/lib/mongoid/changeable.rb +1 -1
- data/lib/mongoid/clients.rb +1 -0
- data/lib/mongoid/clients/options.rb +119 -7
- data/lib/mongoid/config.rb +7 -0
- data/lib/mongoid/config/options.rb +15 -0
- data/lib/mongoid/contextual/geo_near.rb +12 -0
- data/lib/mongoid/contextual/mongo.rb +6 -0
- data/lib/mongoid/criteria.rb +2 -56
- data/lib/mongoid/criteria/findable.rb +4 -1
- data/lib/mongoid/criteria/includable.rb +142 -0
- data/lib/mongoid/criteria/modifiable.rb +13 -1
- data/lib/mongoid/document.rb +21 -0
- data/lib/mongoid/fields/foreign_key.rb +5 -1
- data/lib/mongoid/fields/localized.rb +16 -1
- data/lib/mongoid/fields/validators/macro.rb +1 -0
- data/lib/mongoid/findable.rb +1 -0
- data/lib/mongoid/loggable.rb +1 -1
- data/lib/mongoid/matchable.rb +6 -1
- data/lib/mongoid/persistable.rb +2 -2
- data/lib/mongoid/relations/eager.rb +12 -5
- data/lib/mongoid/relations/eager/base.rb +3 -1
- data/lib/mongoid/relations/referenced/many.rb +39 -6
- data/lib/mongoid/relations/targets/enumerable.rb +3 -3
- data/lib/mongoid/scopable.rb +5 -2
- data/lib/mongoid/version.rb +1 -1
- data/spec/app/models/address.rb +2 -0
- data/spec/app/models/agent.rb +2 -0
- data/spec/app/models/alert.rb +2 -0
- data/spec/app/models/post.rb +1 -0
- data/spec/config/mongoid.yml +1 -0
- data/spec/mongoid/changeable_spec.rb +1 -1
- data/spec/mongoid/clients/options_spec.rb +57 -0
- data/spec/mongoid/config_spec.rb +38 -0
- data/spec/mongoid/contextual/geo_near_spec.rb +19 -0
- data/spec/mongoid/criteria/findable_spec.rb +11 -0
- data/spec/mongoid/criteria/modifiable_spec.rb +126 -0
- data/spec/mongoid/criteria_spec.rb +81 -5
- data/spec/mongoid/document_spec.rb +56 -0
- data/spec/mongoid/fields/foreign_key_spec.rb +23 -0
- data/spec/mongoid/fields/localized_spec.rb +32 -14
- data/spec/mongoid/matchable_spec.rb +127 -1
- data/spec/mongoid/persistable_spec.rb +24 -0
- data/spec/mongoid/relations/embedded/many_spec.rb +16 -0
- data/spec/mongoid/relations/referenced/many_spec.rb +60 -0
- data/spec/mongoid/relations/referenced/many_to_many_spec.rb +40 -0
- data/spec/mongoid/scopable_spec.rb +67 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/support/authorization.rb +2 -1
- metadata +6 -5
- metadata.gz.sig +0 -0
@@ -437,4 +437,23 @@ describe Mongoid::Contextual::GeoNear do
|
|
437
437
|
expect(geo_near.time).to_not be_nil
|
438
438
|
end
|
439
439
|
end
|
440
|
+
|
441
|
+
describe "#empty_and_chainable" do
|
442
|
+
|
443
|
+
let!(:collection) do
|
444
|
+
Bar.collection
|
445
|
+
end
|
446
|
+
|
447
|
+
let(:criteria) do
|
448
|
+
Bar.all
|
449
|
+
end
|
450
|
+
|
451
|
+
let(:geo_near) do
|
452
|
+
described_class.new(collection, criteria, [ 52, 13 ])
|
453
|
+
end
|
454
|
+
|
455
|
+
it "returns true" do
|
456
|
+
expect(geo_near.empty_and_chainable?).to be(true)
|
457
|
+
end
|
458
|
+
end
|
440
459
|
end
|
@@ -54,6 +54,17 @@ describe Mongoid::Criteria::Findable do
|
|
54
54
|
it "returns the matching document" do
|
55
55
|
expect(found).to eq(band)
|
56
56
|
end
|
57
|
+
|
58
|
+
context "when finding by a JSON-dumped id" do
|
59
|
+
|
60
|
+
let(:found) do
|
61
|
+
Band.find(JSON.load(JSON.dump(band.id)))
|
62
|
+
end
|
63
|
+
|
64
|
+
it "properly parses the id format" do
|
65
|
+
expect(found).to eq(band)
|
66
|
+
end
|
67
|
+
end
|
57
68
|
end
|
58
69
|
|
59
70
|
context "when the id does not match" do
|
@@ -1227,4 +1227,130 @@ describe Mongoid::Criteria::Modifiable do
|
|
1227
1227
|
end
|
1228
1228
|
end
|
1229
1229
|
end
|
1230
|
+
|
1231
|
+
describe '#create_with' do
|
1232
|
+
|
1233
|
+
context 'when called on the class' do
|
1234
|
+
|
1235
|
+
let(:attrs) do
|
1236
|
+
{ 'username' => 'Turnip' }
|
1237
|
+
end
|
1238
|
+
|
1239
|
+
it 'returns a criteria with the defined attributes' do
|
1240
|
+
expect(Person.create_with(attrs).selector).to eq(attrs)
|
1241
|
+
end
|
1242
|
+
|
1243
|
+
context 'when a method is chained' do
|
1244
|
+
|
1245
|
+
context 'when a write method is chained' do
|
1246
|
+
|
1247
|
+
it 'executes the method' do
|
1248
|
+
expect(Person.create_with(attrs).new.username).to eq('Turnip')
|
1249
|
+
end
|
1250
|
+
end
|
1251
|
+
|
1252
|
+
context 'when a write method is chained' do
|
1253
|
+
|
1254
|
+
let(:query) do
|
1255
|
+
{ 'age' => 50 }
|
1256
|
+
end
|
1257
|
+
|
1258
|
+
let(:new_person) do
|
1259
|
+
Person.create_with(attrs).find_or_create_by(query)
|
1260
|
+
end
|
1261
|
+
|
1262
|
+
it 'executes the write' do
|
1263
|
+
expect(new_person.username).to eq('Turnip')
|
1264
|
+
expect(new_person.age).to eq(50)
|
1265
|
+
end
|
1266
|
+
|
1267
|
+
context 'when the attributes are shared with the write method args' do
|
1268
|
+
|
1269
|
+
let(:query) do
|
1270
|
+
{ 'username' => 'Beet', 'age' => 50 }
|
1271
|
+
end
|
1272
|
+
|
1273
|
+
let(:new_person) do
|
1274
|
+
Person.create_with(attrs).find_or_create_by(query)
|
1275
|
+
end
|
1276
|
+
|
1277
|
+
it 'gives the write method args precedence' do
|
1278
|
+
# @todo: uncomment when MONGOID-4193 is closed
|
1279
|
+
#expect(new_person.username).to eq('Beet')
|
1280
|
+
expect(new_person.age).to eq(50)
|
1281
|
+
end
|
1282
|
+
end
|
1283
|
+
end
|
1284
|
+
end
|
1285
|
+
end
|
1286
|
+
|
1287
|
+
context 'when called on a criteria' do
|
1288
|
+
|
1289
|
+
let(:criteria_selector) do
|
1290
|
+
{ 'username' => 'Artichoke', 'age' => 25 }
|
1291
|
+
end
|
1292
|
+
|
1293
|
+
let(:criteria) do
|
1294
|
+
Person.where(criteria_selector)
|
1295
|
+
end
|
1296
|
+
|
1297
|
+
context 'when the original criteria shares attributes with the attribute args' do
|
1298
|
+
|
1299
|
+
context 'when all the original attributes are shared with the new attributes' do
|
1300
|
+
|
1301
|
+
let(:attrs) do
|
1302
|
+
{ 'username' => 'Beet', 'age' => 50 }
|
1303
|
+
end
|
1304
|
+
|
1305
|
+
it 'overwrites all the original attributes' do
|
1306
|
+
expect(criteria.create_with(attrs).selector).to eq(attrs)
|
1307
|
+
end
|
1308
|
+
end
|
1309
|
+
end
|
1310
|
+
|
1311
|
+
context 'when only some of the original attributes are shared with the attribute args' do
|
1312
|
+
|
1313
|
+
let(:attrs) do
|
1314
|
+
{ 'username' => 'Beet' }
|
1315
|
+
end
|
1316
|
+
|
1317
|
+
it 'only overwrites the shared attributes' do
|
1318
|
+
expect(criteria.create_with(attrs).selector).to eq(criteria_selector.merge!(attrs))
|
1319
|
+
end
|
1320
|
+
end
|
1321
|
+
|
1322
|
+
context 'when a method is chained' do
|
1323
|
+
|
1324
|
+
let(:attrs) do
|
1325
|
+
{ 'username' => 'Turnip' }
|
1326
|
+
end
|
1327
|
+
|
1328
|
+
let(:query) do
|
1329
|
+
{ 'username' => 'Beet', 'age' => 50 }
|
1330
|
+
end
|
1331
|
+
|
1332
|
+
context 'when a write method is chained' do
|
1333
|
+
|
1334
|
+
it 'executes the method' do
|
1335
|
+
# @todo: uncomment when MONGOID-4193 is closed
|
1336
|
+
#expect(criteria.create_with(attrs).new.username).to eq('Beet')
|
1337
|
+
expect(criteria.create_with(attrs).new.age).to eq(25)
|
1338
|
+
end
|
1339
|
+
end
|
1340
|
+
|
1341
|
+
context 'when a write method is chained' do
|
1342
|
+
|
1343
|
+
let(:new_person) do
|
1344
|
+
criteria.create_with(attrs).find_or_create_by(query)
|
1345
|
+
end
|
1346
|
+
|
1347
|
+
it 'executes the query' do
|
1348
|
+
# @todo: uncomment when MONGOID-4193 is closed
|
1349
|
+
#expect(new_person.username).to eq('Beet')
|
1350
|
+
#expect(new_person.age).to eq(50)
|
1351
|
+
end
|
1352
|
+
end
|
1353
|
+
end
|
1354
|
+
end
|
1355
|
+
end
|
1230
1356
|
end
|
@@ -1147,12 +1147,72 @@ describe Mongoid::Criteria do
|
|
1147
1147
|
end
|
1148
1148
|
end
|
1149
1149
|
|
1150
|
-
context "when providing a
|
1150
|
+
context "when providing a list of associations" do
|
1151
1151
|
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1152
|
+
let!(:user) do
|
1153
|
+
User.create(posts: [ post1 ], descriptions: [ description1 ])
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
let!(:post1) do
|
1157
|
+
Post.create
|
1158
|
+
end
|
1159
|
+
|
1160
|
+
let!(:description1) do
|
1161
|
+
Description.create(details: 1)
|
1162
|
+
end
|
1163
|
+
|
1164
|
+
let(:result) do
|
1165
|
+
User.includes(:posts, :descriptions).first
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
it "executes the query" do
|
1169
|
+
expect(result).to eq(user)
|
1170
|
+
end
|
1171
|
+
|
1172
|
+
it "includes the related objects" do
|
1173
|
+
expect(result.posts).to eq([ post1 ])
|
1174
|
+
expect(result.descriptions).to eq([ description1 ])
|
1175
|
+
end
|
1176
|
+
end
|
1177
|
+
|
1178
|
+
context "when providing a nested association" do
|
1179
|
+
|
1180
|
+
let!(:user) do
|
1181
|
+
User.create
|
1182
|
+
end
|
1183
|
+
|
1184
|
+
before do
|
1185
|
+
p = Post.create(alerts: [ Alert.create ])
|
1186
|
+
user.posts = [ p ]
|
1187
|
+
user.save
|
1188
|
+
end
|
1189
|
+
|
1190
|
+
let(:result) do
|
1191
|
+
User.includes(:posts => [:alerts]).first
|
1192
|
+
end
|
1193
|
+
|
1194
|
+
it "executes the query" do
|
1195
|
+
expect(result).to eq(user)
|
1196
|
+
end
|
1197
|
+
|
1198
|
+
it "includes the related objects" do
|
1199
|
+
expect(result.posts.size).to eq(1)
|
1200
|
+
expect(result.posts.first.alerts.size).to eq(1)
|
1201
|
+
end
|
1202
|
+
end
|
1203
|
+
|
1204
|
+
context "when providing a deeply nested association" do
|
1205
|
+
|
1206
|
+
let!(:user) do
|
1207
|
+
User.create
|
1208
|
+
end
|
1209
|
+
|
1210
|
+
let(:results) do
|
1211
|
+
User.includes(:posts => [{ :alerts => :items }]).to_a
|
1212
|
+
end
|
1213
|
+
|
1214
|
+
it "executes the query" do
|
1215
|
+
expect(results.first).to eq(user)
|
1156
1216
|
end
|
1157
1217
|
end
|
1158
1218
|
|
@@ -1759,6 +1819,22 @@ describe Mongoid::Criteria do
|
|
1759
1819
|
person.preferences.create(name: "two")
|
1760
1820
|
end
|
1761
1821
|
|
1822
|
+
context "when one of the related items is deleted" do
|
1823
|
+
|
1824
|
+
before do
|
1825
|
+
person.preferences = [ preference_one, preference_two ]
|
1826
|
+
preference_two.delete
|
1827
|
+
end
|
1828
|
+
|
1829
|
+
let(:criteria) do
|
1830
|
+
Person.where(id: person.id).includes(:preferences)
|
1831
|
+
end
|
1832
|
+
|
1833
|
+
it "only loads the existing related items" do
|
1834
|
+
expect(criteria.entries.first.preferences).to eq([ preference_one ])
|
1835
|
+
end
|
1836
|
+
end
|
1837
|
+
|
1762
1838
|
context "when the criteria has no options" do
|
1763
1839
|
|
1764
1840
|
let!(:criteria) do
|
@@ -441,6 +441,62 @@ describe Mongoid::Document do
|
|
441
441
|
end
|
442
442
|
end
|
443
443
|
|
444
|
+
describe "#as_json" do
|
445
|
+
|
446
|
+
let!(:person) do
|
447
|
+
Person.new(title: "Sir")
|
448
|
+
end
|
449
|
+
|
450
|
+
context "when no options are provided" do
|
451
|
+
|
452
|
+
it "does not apply any options" do
|
453
|
+
expect(person.as_json["title"]).to eq("Sir")
|
454
|
+
expect(person.as_json["age"]).to eq(100)
|
455
|
+
end
|
456
|
+
|
457
|
+
context "when options for the super method are provided" do
|
458
|
+
|
459
|
+
let(:options) do
|
460
|
+
{ only: :title }
|
461
|
+
end
|
462
|
+
|
463
|
+
it "passes the options through to the super method" do
|
464
|
+
expect(person.as_json(options)["title"]).to eq("Sir")
|
465
|
+
expect(person.as_json(options).keys).not_to include("age")
|
466
|
+
end
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
context "when the Mongoid-specific options are provided" do
|
471
|
+
|
472
|
+
let(:options) do
|
473
|
+
{ compact: true }
|
474
|
+
end
|
475
|
+
|
476
|
+
it "applies the Mongoid-specific options" do
|
477
|
+
expect(person.as_json(options)["title"]).to eq("Sir")
|
478
|
+
expect(person.as_json(options)["age"]).to eq(100)
|
479
|
+
expect(person.as_json(options).keys).not_to include("lunch_time")
|
480
|
+
end
|
481
|
+
|
482
|
+
context "when options for the super method are provided" do
|
483
|
+
|
484
|
+
let(:options) do
|
485
|
+
{ compact: true, only: [:title, :pets, :ssn] }
|
486
|
+
end
|
487
|
+
|
488
|
+
it "passes the options through to the super method" do
|
489
|
+
expect(person.as_json(options)["title"]).to eq("Sir")
|
490
|
+
expect(person.as_json(options)["pets"]).to eq(false)
|
491
|
+
end
|
492
|
+
|
493
|
+
it "applies the Mongoid-specific options" do
|
494
|
+
expect(person.as_json(options).keys).not_to include("ssn")
|
495
|
+
end
|
496
|
+
end
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
444
500
|
describe "#as_document" do
|
445
501
|
|
446
502
|
let!(:person) do
|
@@ -444,6 +444,29 @@ describe Mongoid::Fields::ForeignKey do
|
|
444
444
|
end
|
445
445
|
end
|
446
446
|
end
|
447
|
+
|
448
|
+
context "when the metadata is polymoprhic" do
|
449
|
+
|
450
|
+
let(:metadata) do
|
451
|
+
Agent.reflect_on_association(:names)
|
452
|
+
end
|
453
|
+
|
454
|
+
let(:field) do
|
455
|
+
described_class.new(:nameable_id, type: Object, metadata: metadata)
|
456
|
+
end
|
457
|
+
|
458
|
+
let(:value) do
|
459
|
+
BSON::ObjectId.new().to_s
|
460
|
+
end
|
461
|
+
|
462
|
+
let(:evolved) do
|
463
|
+
field.evolve(value)
|
464
|
+
end
|
465
|
+
|
466
|
+
it "does not change the foreign key" do
|
467
|
+
expect(evolved).to be(value)
|
468
|
+
end
|
469
|
+
end
|
447
470
|
end
|
448
471
|
|
449
472
|
describe "#lazy?" do
|
@@ -269,32 +269,50 @@ describe Mongoid::Fields::Localized do
|
|
269
269
|
::I18n.fallbacks[:de] = [ :de, :en, :es ]
|
270
270
|
end
|
271
271
|
|
272
|
-
context
|
272
|
+
context 'when fallbacks are enabled' do
|
273
273
|
|
274
|
-
|
275
|
-
field.demongoize({ "en" => 1 })
|
276
|
-
end
|
274
|
+
context "when the first fallback translation exists" do
|
277
275
|
|
278
|
-
|
279
|
-
|
276
|
+
let(:value) do
|
277
|
+
field.demongoize({ "en" => 1 })
|
278
|
+
end
|
279
|
+
|
280
|
+
it "returns the fallback translation" do
|
281
|
+
expect(value).to eq(1)
|
282
|
+
end
|
280
283
|
end
|
281
|
-
end
|
282
284
|
|
283
|
-
|
285
|
+
context "when another fallback translation exists" do
|
284
286
|
|
285
|
-
|
286
|
-
|
287
|
+
let(:value) do
|
288
|
+
field.demongoize({ "es" => 100 })
|
289
|
+
end
|
290
|
+
|
291
|
+
it "returns the fallback translation" do
|
292
|
+
expect(value).to eq(100)
|
293
|
+
end
|
287
294
|
end
|
288
295
|
|
289
|
-
|
290
|
-
|
296
|
+
context "when the fallback translation does not exist" do
|
297
|
+
|
298
|
+
let(:value) do
|
299
|
+
field.demongoize({ "fr" => 50 })
|
300
|
+
end
|
301
|
+
|
302
|
+
it "returns nil" do
|
303
|
+
expect(value).to be_nil
|
304
|
+
end
|
291
305
|
end
|
292
306
|
end
|
293
307
|
|
294
|
-
context
|
308
|
+
context 'when fallbacks are disabled' do
|
309
|
+
|
310
|
+
let(:field) do
|
311
|
+
described_class.new(:description, localize: true, type: Integer, fallbacks: false)
|
312
|
+
end
|
295
313
|
|
296
314
|
let(:value) do
|
297
|
-
field.demongoize({ "
|
315
|
+
field.demongoize({ "es" => 100 })
|
298
316
|
end
|
299
317
|
|
300
318
|
it "returns nil" do
|
@@ -149,7 +149,8 @@ describe Mongoid::Matchable do
|
|
149
149
|
Address.new(
|
150
150
|
services: ["first", "second", "third"],
|
151
151
|
number: 100,
|
152
|
-
map: { key: "value" }
|
152
|
+
map: { key: "value" },
|
153
|
+
street: "Clarkenwell Road"
|
153
154
|
)
|
154
155
|
end
|
155
156
|
|
@@ -303,6 +304,131 @@ describe Mongoid::Matchable do
|
|
303
304
|
end
|
304
305
|
end
|
305
306
|
|
307
|
+
context "with a $not selector" do
|
308
|
+
|
309
|
+
context "regexes" do
|
310
|
+
|
311
|
+
context "when the predicate matches" do
|
312
|
+
|
313
|
+
let(:selector) do
|
314
|
+
{
|
315
|
+
street: {"$not" => /Avenue/}
|
316
|
+
}
|
317
|
+
end
|
318
|
+
|
319
|
+
it "returns true" do
|
320
|
+
expect(document.matches?(selector)).to be true
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
context "when the predicate does not match" do
|
325
|
+
|
326
|
+
let(:selector) do
|
327
|
+
{
|
328
|
+
street: {"$not" => /Road/}
|
329
|
+
}
|
330
|
+
end
|
331
|
+
|
332
|
+
it "returns false" do
|
333
|
+
expect(document.matches?(selector)).to be false
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
context "other operators" do
|
339
|
+
|
340
|
+
context "numerical comparisons" do
|
341
|
+
|
342
|
+
context "$lt and $gt" do
|
343
|
+
|
344
|
+
context "when the predicate matches" do
|
345
|
+
|
346
|
+
let(:selector) do
|
347
|
+
{
|
348
|
+
number: {"$not" => {"$lt" => 0}}
|
349
|
+
}
|
350
|
+
end
|
351
|
+
|
352
|
+
it "returns true" do
|
353
|
+
expect(document.matches?(selector)).to be true
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
context "when the predicate does not match" do
|
358
|
+
|
359
|
+
let(:selector) do
|
360
|
+
{
|
361
|
+
number: {"$not" => {"$gt" => 50}}
|
362
|
+
}
|
363
|
+
end
|
364
|
+
|
365
|
+
it "returns false" do
|
366
|
+
expect(document.matches?(selector)).to be false
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
context "$in" do
|
372
|
+
|
373
|
+
context "when the predicate matches" do
|
374
|
+
|
375
|
+
let(:selector) do
|
376
|
+
{
|
377
|
+
number: {"$not" => {"$in" => [10]}}
|
378
|
+
}
|
379
|
+
end
|
380
|
+
|
381
|
+
it "returns true" do
|
382
|
+
expect(document.matches?(selector)).to be true
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
context "when the predicate does not match" do
|
387
|
+
|
388
|
+
let(:selector) do
|
389
|
+
{
|
390
|
+
number: {"$not" => {"$in" => [100]}}
|
391
|
+
}
|
392
|
+
end
|
393
|
+
|
394
|
+
it "returns false" do
|
395
|
+
expect(document.matches?(selector)).to be false
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
context "symbol keys" do
|
403
|
+
|
404
|
+
context "when the predicate matches" do
|
405
|
+
|
406
|
+
let(:selector) do
|
407
|
+
{
|
408
|
+
street: {:$not => /Avenue/}
|
409
|
+
}
|
410
|
+
end
|
411
|
+
|
412
|
+
it "returns true" do
|
413
|
+
expect(document.matches?(selector)).to be true
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
context "when the predicate does not match" do
|
418
|
+
|
419
|
+
let(:selector) do
|
420
|
+
{
|
421
|
+
street: {:$not => /Road/}
|
422
|
+
}
|
423
|
+
end
|
424
|
+
|
425
|
+
it "returns false" do
|
426
|
+
expect(document.matches?(selector)).to be false
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
306
432
|
context "with an $in selector" do
|
307
433
|
|
308
434
|
context "when the attributes match" do
|