mongoid 7.0.3 → 7.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +2 -0
- data.tar.gz.sig +1 -0
- data/LICENSE +1 -0
- data/README.md +3 -2
- data/Rakefile +12 -0
- data/lib/mongoid.rb +2 -1
- data/lib/mongoid/association/embedded/embeds_many.rb +2 -1
- data/lib/mongoid/association/embedded/embeds_one.rb +2 -1
- data/lib/mongoid/association/proxy.rb +1 -1
- data/lib/mongoid/association/relatable.rb +23 -21
- data/lib/mongoid/atomic.rb +13 -3
- data/lib/mongoid/atomic/paths/embedded.rb +1 -1
- data/lib/mongoid/attributes.rb +28 -20
- data/lib/mongoid/attributes/dynamic.rb +15 -14
- data/lib/mongoid/config/environment.rb +21 -8
- data/lib/mongoid/copyable.rb +5 -1
- data/lib/mongoid/criteria.rb +7 -1
- data/lib/mongoid/criteria/modifiable.rb +13 -2
- data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
- data/lib/mongoid/criteria/queryable/extensions/regexp.rb +3 -3
- data/lib/mongoid/criteria/queryable/key.rb +67 -8
- data/lib/mongoid/criteria/queryable/mergeable.rb +5 -4
- data/lib/mongoid/criteria/queryable/selectable.rb +3 -4
- data/lib/mongoid/criteria/queryable/selector.rb +9 -31
- data/lib/mongoid/extensions/hash.rb +4 -2
- data/lib/mongoid/extensions/regexp.rb +1 -1
- data/lib/mongoid/extensions/string.rb +5 -3
- data/lib/mongoid/fields.rb +2 -1
- data/lib/mongoid/matchable.rb +14 -15
- data/lib/mongoid/matchable/all.rb +4 -3
- data/lib/mongoid/matchable/default.rb +71 -24
- data/lib/mongoid/matchable/regexp.rb +2 -2
- data/lib/mongoid/persistable/pushable.rb +11 -2
- data/lib/mongoid/persistence_context.rb +6 -6
- data/lib/mongoid/positional.rb +1 -1
- data/lib/mongoid/query_cache.rb +3 -2
- data/lib/mongoid/validatable/macros.rb +1 -1
- data/lib/mongoid/validatable/uniqueness.rb +1 -1
- data/lib/mongoid/version.rb +2 -1
- data/lib/rails/generators/mongoid/model/templates/model.rb.tt +1 -1
- data/spec/README.md +18 -0
- data/spec/app/models/delegating_patient.rb +16 -0
- data/spec/app/models/other_owner_object.rb +2 -0
- data/spec/integration/app_spec.rb +192 -0
- data/spec/integration/associations/embedded_spec.rb +62 -0
- data/spec/integration/criteria/time_with_zone_spec.rb +32 -0
- data/spec/integration/document_spec.rb +22 -0
- data/spec/integration/matchable_spec.rb +680 -0
- data/spec/lite_spec_helper.rb +15 -5
- data/spec/mongoid/association/embedded/embedded_in_spec.rb +58 -0
- data/spec/mongoid/association/embedded/embeds_many_models.rb +53 -0
- data/spec/mongoid/association/embedded/embeds_many_spec.rb +10 -0
- data/spec/mongoid/association/embedded/embeds_one_dnl_models.rb +6 -0
- data/spec/mongoid/association/embedded/embeds_one_models.rb +51 -0
- data/spec/mongoid/association/embedded/embeds_one_spec.rb +46 -0
- data/spec/mongoid/association/referenced/belongs_to_spec.rb +23 -6
- data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +2 -1
- data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +2 -1
- data/spec/mongoid/association/referenced/has_one_spec.rb +12 -2
- data/spec/mongoid/attributes/dynamic_spec.rb +153 -0
- data/spec/mongoid/attributes_spec.rb +19 -7
- data/spec/mongoid/clients/factory_spec.rb +2 -2
- data/spec/mongoid/clients/options_spec.rb +4 -4
- data/spec/mongoid/clients/sessions_spec.rb +20 -7
- data/spec/mongoid/clients/transactions_spec.rb +36 -15
- data/spec/mongoid/clients_spec.rb +2 -2
- data/spec/mongoid/contextual/atomic_spec.rb +20 -10
- data/spec/mongoid/contextual/geo_near_spec.rb +1 -0
- data/spec/mongoid/contextual/map_reduce_spec.rb +20 -5
- data/spec/mongoid/contextual/mongo_spec.rb +76 -53
- data/spec/mongoid/criteria/modifiable_spec.rb +59 -10
- data/spec/mongoid/criteria/queryable/extensions/numeric_spec.rb +54 -0
- data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +7 -7
- data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +1 -1
- data/spec/mongoid/criteria/queryable/key_spec.rb +48 -6
- data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +762 -0
- data/spec/mongoid/criteria/queryable/selectable_spec.rb +5 -224
- data/spec/mongoid/criteria/queryable/selector_spec.rb +37 -0
- data/spec/mongoid/criteria_spec.rb +7 -2
- data/spec/mongoid/document_fields_spec.rb +88 -0
- data/spec/mongoid/document_persistence_context_spec.rb +33 -0
- data/spec/mongoid/extensions/string_spec.rb +35 -7
- data/spec/mongoid/indexable_spec.rb +6 -4
- data/spec/mongoid/matchable/default_spec.rb +10 -3
- data/spec/mongoid/matchable/regexp_spec.rb +2 -2
- data/spec/mongoid/matchable_spec.rb +2 -2
- data/spec/mongoid/persistable/pushable_spec.rb +55 -1
- data/spec/mongoid/query_cache_spec.rb +2 -1
- data/spec/mongoid/relations/proxy_spec.rb +1 -1
- data/spec/mongoid/scopable_spec.rb +2 -1
- data/spec/mongoid/tasks/database_rake_spec.rb +13 -13
- data/spec/mongoid/tasks/database_spec.rb +1 -1
- data/spec/mongoid/validatable/uniqueness_spec.rb +33 -6
- data/spec/spec_helper.rb +4 -37
- data/spec/support/child_process_helper.rb +76 -0
- data/spec/support/cluster_config.rb +158 -0
- data/spec/support/constraints.rb +29 -19
- data/spec/support/expectations.rb +17 -3
- data/spec/support/spec_config.rb +12 -4
- metadata +525 -464
- metadata.gz.sig +2 -0
data/spec/lite_spec_helper.rb
CHANGED
@@ -4,7 +4,6 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
4
4
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
5
5
|
|
6
6
|
require "mongoid"
|
7
|
-
require "rspec"
|
8
7
|
|
9
8
|
# MRI 2.5 and JRuby 9.2 change visibility of Object#pp when 'pp' is required,
|
10
9
|
# which happens when RSpec reports anything. This creates an issue for tests
|
@@ -36,16 +35,27 @@ else
|
|
36
35
|
end
|
37
36
|
|
38
37
|
RSpec.configure do |config|
|
38
|
+
config.expect_with(:rspec) do |c|
|
39
|
+
c.syntax = [:should, :expect]
|
40
|
+
end
|
41
|
+
|
39
42
|
if SpecConfig.instance.ci?
|
40
43
|
config.add_formatter(RSpec::Core::Formatters::JsonFormatter, File.join(File.dirname(__FILE__), '../tmp/rspec.json'))
|
41
44
|
end
|
42
45
|
|
43
46
|
if SpecConfig.instance.ci?
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
+
timeout = if SpecConfig.instance.app_tests?
|
48
|
+
# Allow 5 minutes per test for the app tests, since they install
|
49
|
+
# gems for Rails applications which can take a long time.
|
50
|
+
300
|
51
|
+
else
|
52
|
+
# Allow a max of 30 seconds per test.
|
53
|
+
# Tests should take under 10 seconds ideally but it seems
|
54
|
+
# we have some that run for more than 10 seconds in CI.
|
55
|
+
30
|
56
|
+
end
|
47
57
|
config.around(:each) do |example|
|
48
|
-
TimeoutInterrupt.timeout(
|
58
|
+
TimeoutInterrupt.timeout(timeout) do
|
49
59
|
example.run
|
50
60
|
end
|
51
61
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "spec_helper"
|
2
|
+
require_relative './embeds_one_models'
|
2
3
|
|
3
4
|
describe Mongoid::Association::Embedded::EmbeddedIn do
|
4
5
|
|
@@ -313,6 +314,42 @@ describe Mongoid::Association::Embedded::EmbeddedIn do
|
|
313
314
|
it 'returns nil' do
|
314
315
|
expect(association.inverses).to eq(nil)
|
315
316
|
end
|
317
|
+
|
318
|
+
context 'when class_name is given and is a plain string' do
|
319
|
+
let(:association) do
|
320
|
+
EomParent.relations['child']
|
321
|
+
end
|
322
|
+
|
323
|
+
it 'returns the inverse in an array' do
|
324
|
+
inverses = association.inverses
|
325
|
+
expect(inverses).to eq([:parent])
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
context 'when class_name is given and is a :: prefixed string' do
|
330
|
+
let(:association) do
|
331
|
+
EomCcChild.relations['parent']
|
332
|
+
end
|
333
|
+
|
334
|
+
it 'returns the inverse in an array' do
|
335
|
+
pending 'MONGOID-4751'
|
336
|
+
|
337
|
+
inverses = association.inverses
|
338
|
+
expect(inverses).to eq([:child])
|
339
|
+
end
|
340
|
+
|
341
|
+
context 'when other associations referencing unloaded classes exist' do
|
342
|
+
let(:association) do
|
343
|
+
EomDnlChild.relations['parent']
|
344
|
+
end
|
345
|
+
|
346
|
+
it 'does not load other classes' do
|
347
|
+
inverses = association.inverses
|
348
|
+
expect(inverses).to eq([:child])
|
349
|
+
expect(Object.const_defined?(:EoDnlMarker)).to be false
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
316
353
|
end
|
317
354
|
end
|
318
355
|
end
|
@@ -549,6 +586,16 @@ describe Mongoid::Association::Embedded::EmbeddedIn do
|
|
549
586
|
it 'returns the class name option' do
|
550
587
|
expect(association.relation_class_name).to eq('OtherContainer')
|
551
588
|
end
|
589
|
+
|
590
|
+
context ':class_name is a :: prefixed string' do
|
591
|
+
let(:association) do
|
592
|
+
EomCcChild.relations['parent']
|
593
|
+
end
|
594
|
+
|
595
|
+
it 'returns the :: prefixed string' do
|
596
|
+
expect(association.relation_class_name).to eq('::EomCcParent')
|
597
|
+
end
|
598
|
+
end
|
552
599
|
end
|
553
600
|
|
554
601
|
context 'when the class_name option is not specified' do
|
@@ -557,6 +604,17 @@ describe Mongoid::Association::Embedded::EmbeddedIn do
|
|
557
604
|
expect(association.relation_class_name).to eq('Container')
|
558
605
|
end
|
559
606
|
end
|
607
|
+
|
608
|
+
context 'when another association in the model is referencing a third model class' do
|
609
|
+
let(:association) do
|
610
|
+
EomDnlChild.relations['parent']
|
611
|
+
end
|
612
|
+
|
613
|
+
it 'does not attempt to load the third class' do
|
614
|
+
expect(association.relation_class_name).to eq('EomDnlParent')
|
615
|
+
expect(Object.const_defined?(:EoDnlMarker)).to be false
|
616
|
+
end
|
617
|
+
end
|
560
618
|
end
|
561
619
|
|
562
620
|
describe '#klass' do
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
class EmmCongress
|
5
|
+
include Mongoid::Document
|
6
|
+
|
7
|
+
embeds_many :legislators, class_name: 'EmmLegislator'
|
8
|
+
|
9
|
+
field :name, type: String
|
10
|
+
end
|
11
|
+
|
12
|
+
class EmmLegislator
|
13
|
+
include Mongoid::Document
|
14
|
+
|
15
|
+
embedded_in :congress, class_name: 'EmmCongress'
|
16
|
+
|
17
|
+
field :a, type: Integer, default: 0
|
18
|
+
field :b, type: Integer, default: 0
|
19
|
+
end
|
20
|
+
|
21
|
+
# Models with associations with :class_name as a :: prefixed string
|
22
|
+
|
23
|
+
class EmmCcCongress
|
24
|
+
include Mongoid::Document
|
25
|
+
|
26
|
+
embeds_many :legislators, class_name: '::EmmCcLegislator'
|
27
|
+
|
28
|
+
field :name, type: String
|
29
|
+
end
|
30
|
+
|
31
|
+
class EmmCcLegislator
|
32
|
+
include Mongoid::Document
|
33
|
+
|
34
|
+
embedded_in :congress, class_name: '::EmmCcCongress'
|
35
|
+
|
36
|
+
field :a, type: Integer, default: 0
|
37
|
+
field :b, type: Integer, default: 0
|
38
|
+
end
|
39
|
+
|
40
|
+
class EmmManufactory
|
41
|
+
include Mongoid::Document
|
42
|
+
|
43
|
+
embeds_many :products, order: :id.desc, class_name: 'EmmProduct'
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
class EmmProduct
|
48
|
+
include Mongoid::Document
|
49
|
+
|
50
|
+
embedded_in :manufactory, class_name: 'EmmManufactory'
|
51
|
+
|
52
|
+
field :name, type: String
|
53
|
+
end
|
@@ -646,6 +646,16 @@ describe Mongoid::Association::Embedded::EmbedsMany do
|
|
646
646
|
it 'returns the class name option' do
|
647
647
|
expect(association.klass).to eq(_class)
|
648
648
|
end
|
649
|
+
|
650
|
+
context 'when the class name is prefixed with ::' do
|
651
|
+
let(:options) do
|
652
|
+
{ class_name: '::OtherEmbeddedObject' }
|
653
|
+
end
|
654
|
+
|
655
|
+
it 'returns the class name option' do
|
656
|
+
expect(association.klass).to eq(_class)
|
657
|
+
end
|
658
|
+
end
|
649
659
|
end
|
650
660
|
|
651
661
|
context 'when the class_name option is not specified' do
|
@@ -0,0 +1,51 @@
|
|
1
|
+
class EomParent
|
2
|
+
include Mongoid::Document
|
3
|
+
|
4
|
+
embeds_one :child, class_name: 'EomChild'
|
5
|
+
|
6
|
+
field :name, type: String
|
7
|
+
end
|
8
|
+
|
9
|
+
class EomChild
|
10
|
+
include Mongoid::Document
|
11
|
+
|
12
|
+
embedded_in :parent, class_name: 'EomParent'
|
13
|
+
|
14
|
+
field :a, type: Integer, default: 0
|
15
|
+
field :b, type: Integer, default: 0
|
16
|
+
end
|
17
|
+
|
18
|
+
# Models with associations with :class_name as a :: prefixed string
|
19
|
+
|
20
|
+
class EomCcParent
|
21
|
+
include Mongoid::Document
|
22
|
+
|
23
|
+
embeds_one :child, class_name: '::EomCcChild'
|
24
|
+
end
|
25
|
+
|
26
|
+
class EomCcChild
|
27
|
+
include Mongoid::Document
|
28
|
+
|
29
|
+
embedded_in :parent, class_name: '::EomCcParent'
|
30
|
+
end
|
31
|
+
|
32
|
+
# Models referencing other models which should not be loaded unless the
|
33
|
+
# respective association is referenced
|
34
|
+
|
35
|
+
autoload :EomDnlMissingChild, 'mongoid/association/embedded/embeds_one_dnl_models'
|
36
|
+
|
37
|
+
class EomDnlParent
|
38
|
+
include Mongoid::Document
|
39
|
+
|
40
|
+
embeds_one :child, class_name: 'EomDnlChild'
|
41
|
+
embeds_one :missing_child, class_name: 'EomDnlMissingChild'
|
42
|
+
end
|
43
|
+
|
44
|
+
autoload :EomDnlMissingParent, 'mongoid/association/embedded/embeds_one_dnl_models'
|
45
|
+
|
46
|
+
class EomDnlChild
|
47
|
+
include Mongoid::Document
|
48
|
+
|
49
|
+
embedded_in :parent, class_name: 'EomDnlParent'
|
50
|
+
embedded_in :missing_parent, class_name: 'EomDnlMissingParent'
|
51
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "spec_helper"
|
2
|
+
require_relative './embeds_one_models'
|
2
3
|
|
3
4
|
describe Mongoid::Association::Embedded::EmbedsOne do
|
4
5
|
|
@@ -444,6 +445,30 @@ describe Mongoid::Association::Embedded::EmbedsOne do
|
|
444
445
|
it 'returns the :as attribute' do
|
445
446
|
expect(association.inverses).to eq([ :containable ])
|
446
447
|
end
|
448
|
+
|
449
|
+
context 'when class_name is given and is a :: prefixed string' do
|
450
|
+
|
451
|
+
let(:association) do
|
452
|
+
EomCcParent.relations['child']
|
453
|
+
end
|
454
|
+
|
455
|
+
it 'returns the inverse in an array' do
|
456
|
+
inverses = association.inverses
|
457
|
+
expect(inverses).to eq([:parent])
|
458
|
+
end
|
459
|
+
|
460
|
+
context 'when other associations referencing unloaded classes exist' do
|
461
|
+
let(:association) do
|
462
|
+
EomDnlParent.relations['child']
|
463
|
+
end
|
464
|
+
|
465
|
+
it 'does not load other classes' do
|
466
|
+
inverses = association.inverses
|
467
|
+
expect(inverses).to eq([:parent])
|
468
|
+
expect(Object.const_defined?(:EoDnlMarker)).to be false
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
447
472
|
end
|
448
473
|
end
|
449
474
|
end
|
@@ -604,6 +629,16 @@ describe Mongoid::Association::Embedded::EmbedsOne do
|
|
604
629
|
it 'returns the class name option' do
|
605
630
|
expect(association.relation_class_name).to eq('OtherEmbeddedObject')
|
606
631
|
end
|
632
|
+
|
633
|
+
context ':class_name is a :: prefixed string' do
|
634
|
+
let(:association) do
|
635
|
+
EomCcParent.relations['child']
|
636
|
+
end
|
637
|
+
|
638
|
+
it 'returns the :: prefixed string' do
|
639
|
+
expect(association.relation_class_name).to eq('::EomCcChild')
|
640
|
+
end
|
641
|
+
end
|
607
642
|
end
|
608
643
|
|
609
644
|
context 'when the class_name option is not specified' do
|
@@ -612,6 +647,17 @@ describe Mongoid::Association::Embedded::EmbedsOne do
|
|
612
647
|
expect(association.relation_class_name).to eq('EmbeddedObject')
|
613
648
|
end
|
614
649
|
end
|
650
|
+
|
651
|
+
context 'when another association in the model is referencing a third model class' do
|
652
|
+
let(:association) do
|
653
|
+
EomDnlParent.relations['child']
|
654
|
+
end
|
655
|
+
|
656
|
+
it 'does not attempt to load the third class' do
|
657
|
+
expect(association.relation_class_name).to eq('EomDnlChild')
|
658
|
+
expect(Object.const_defined?(:EoDnlMarker)).to be false
|
659
|
+
end
|
660
|
+
end
|
615
661
|
end
|
616
662
|
|
617
663
|
describe '#klass' do
|
@@ -1622,6 +1622,17 @@ describe Mongoid::Association::Referenced::BelongsTo do
|
|
1622
1622
|
end
|
1623
1623
|
end
|
1624
1624
|
|
1625
|
+
context 'when the :class_name option is scoped with ::' do
|
1626
|
+
|
1627
|
+
let(:options) do
|
1628
|
+
{ class_name: '::OtherOwnerObject' }
|
1629
|
+
end
|
1630
|
+
|
1631
|
+
it 'returns the class name option' do
|
1632
|
+
expect(association.relation_class_name).to eq('::OtherOwnerObject')
|
1633
|
+
end
|
1634
|
+
end
|
1635
|
+
|
1625
1636
|
context 'when the class_name option is not specified' do
|
1626
1637
|
|
1627
1638
|
it 'uses the name of the relation to deduce the class name' do
|
@@ -1644,17 +1655,23 @@ describe Mongoid::Association::Referenced::BelongsTo do
|
|
1644
1655
|
|
1645
1656
|
context 'when the :class_name option is specified' do
|
1646
1657
|
|
1647
|
-
let
|
1648
|
-
|
1649
|
-
|
1658
|
+
let(:options) do
|
1659
|
+
{ class_name: 'OtherOwnerObject' }
|
1660
|
+
end
|
1661
|
+
|
1662
|
+
it 'returns the named class' do
|
1663
|
+
expect(association.relation_class).to eq(OtherOwnerObject)
|
1650
1664
|
end
|
1665
|
+
end
|
1666
|
+
|
1667
|
+
context 'when the :class_name option is scoped with ::' do
|
1651
1668
|
|
1652
1669
|
let(:options) do
|
1653
|
-
{ class_name: 'OtherOwnerObject' }
|
1670
|
+
{ class_name: '::OtherOwnerObject' }
|
1654
1671
|
end
|
1655
1672
|
|
1656
|
-
it 'returns the class
|
1657
|
-
expect(association.relation_class).to eq(
|
1673
|
+
it 'returns the named class' do
|
1674
|
+
expect(association.relation_class).to eq(OtherOwnerObject)
|
1658
1675
|
end
|
1659
1676
|
end
|
1660
1677
|
|
@@ -2686,7 +2686,8 @@ describe Mongoid::Association::Referenced::HasAndBelongsToMany::Proxy do
|
|
2686
2686
|
expect(preferences).to eq([ preference_one ])
|
2687
2687
|
end
|
2688
2688
|
|
2689
|
-
context 'when providing a collation'
|
2689
|
+
context 'when providing a collation' do
|
2690
|
+
min_server_version '3.4'
|
2690
2691
|
|
2691
2692
|
let(:preferences) do
|
2692
2693
|
person.preferences.where(name: "FIRST").collation(locale: 'en_US', strength: 2).to_a
|
@@ -2904,7 +2904,8 @@ describe Mongoid::Association::Referenced::HasMany::Proxy do
|
|
2904
2904
|
expect(posts).to eq([ post_one ])
|
2905
2905
|
end
|
2906
2906
|
|
2907
|
-
context 'when providing a collation'
|
2907
|
+
context 'when providing a collation' do
|
2908
|
+
min_server_version '3.4'
|
2908
2909
|
|
2909
2910
|
let(:posts) do
|
2910
2911
|
person.posts.where(title: "FIRST").collation(locale: 'en_US', strength: 2)
|
@@ -909,12 +909,12 @@ describe Mongoid::Association::Referenced::HasOne do
|
|
909
909
|
end
|
910
910
|
end
|
911
911
|
|
912
|
-
it 'returns the
|
912
|
+
it 'returns the inferred unqualified class name' do
|
913
913
|
define_classes
|
914
914
|
|
915
915
|
expect(
|
916
916
|
HasOneAssociationClassName::OwnedClass.relations['owner_class'].relation_class_name
|
917
|
-
).to eq('
|
917
|
+
).to eq('OwnerClass')
|
918
918
|
end
|
919
919
|
end
|
920
920
|
|
@@ -945,6 +945,16 @@ describe Mongoid::Association::Referenced::HasOne do
|
|
945
945
|
expect(association.relation_class_name).to eq('BelongingObject')
|
946
946
|
end
|
947
947
|
end
|
948
|
+
|
949
|
+
context "when the class is not defined" do
|
950
|
+
let(:name) do
|
951
|
+
:undefined_class
|
952
|
+
end
|
953
|
+
|
954
|
+
it 'does not trigger autoloading' do
|
955
|
+
expect(association.relation_class_name).to eq('UndefinedClass')
|
956
|
+
end
|
957
|
+
end
|
948
958
|
end
|
949
959
|
|
950
960
|
describe '#relation_class' do
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "spec_helper"
|
5
|
+
|
6
|
+
describe Mongoid::Attributes::Dynamic do
|
7
|
+
shared_examples_for 'dynamic field' do
|
8
|
+
let(:raw_attributes) do
|
9
|
+
{attr_name => 'foo bar'}
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'when reading attributes' do
|
13
|
+
context 'an unsaved model' do
|
14
|
+
it 'can be read' do
|
15
|
+
bar = Bar.new(raw_attributes)
|
16
|
+
expect(bar.send(attr_name)).to eq('foo bar')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'saved model' do
|
21
|
+
it 'can be read' do
|
22
|
+
bar = Bar.new(raw_attributes)
|
23
|
+
bar.save!
|
24
|
+
|
25
|
+
bar = Bar.find(bar.id)
|
26
|
+
expect(bar.send(attr_name)).to eq('foo bar')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when attribute is not set' do
|
31
|
+
it 'cannot be read' do
|
32
|
+
bar = Bar.new
|
33
|
+
expect do
|
34
|
+
bar.send(attr_name)
|
35
|
+
end.to raise_error(NoMethodError)
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'reading via read_attribute' do
|
39
|
+
it 'returns nil' do
|
40
|
+
bar = Bar.new
|
41
|
+
expect(bar.read_attribute(:foo)).to be nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'reading via []' do
|
46
|
+
it 'returns nil' do
|
47
|
+
bar = Bar.new
|
48
|
+
expect(bar[:foo]).to be nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'when writing attributes via constructor' do
|
55
|
+
it 'can be written' do
|
56
|
+
bar = Bar.new(raw_attributes)
|
57
|
+
bar.save!
|
58
|
+
|
59
|
+
bar = Bar.find(bar.id)
|
60
|
+
expect(bar.send(attr_name)).to eq('foo bar')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'when writing attributes via attributes=' do
|
65
|
+
it 'can be written' do
|
66
|
+
bar = Bar.new
|
67
|
+
bar.attributes = raw_attributes
|
68
|
+
bar.save!
|
69
|
+
|
70
|
+
bar = Bar.find(bar.id)
|
71
|
+
expect(bar.send(attr_name)).to eq('foo bar')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'when writing attributes via write_attribute' do
|
76
|
+
it 'can be written' do
|
77
|
+
bar = Bar.new
|
78
|
+
bar.write_attribute(attr_name, 'foo bar')
|
79
|
+
bar.save!
|
80
|
+
|
81
|
+
bar = Bar.find(bar.id)
|
82
|
+
expect(bar.send(attr_name)).to eq('foo bar')
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'when writing attributes via []=' do
|
87
|
+
context 'string key' do
|
88
|
+
it 'can be written' do
|
89
|
+
bar = Bar.new
|
90
|
+
bar[attr_name.to_s] = 'foo bar'
|
91
|
+
bar.save!
|
92
|
+
|
93
|
+
bar = Bar.find(bar.id)
|
94
|
+
expect(bar.send(attr_name)).to eq('foo bar')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'symbol key' do
|
99
|
+
it 'can be written' do
|
100
|
+
bar = Bar.new
|
101
|
+
bar[attr_name.to_sym] = 'foo bar'
|
102
|
+
bar.save!
|
103
|
+
|
104
|
+
bar = Bar.find(bar.id)
|
105
|
+
expect(bar.send(attr_name)).to eq('foo bar')
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'when writing attributes via #{attribute}=' do
|
111
|
+
context 'when attribute is not already set' do
|
112
|
+
let(:bar) { Bar.new }
|
113
|
+
|
114
|
+
it 'cannot be written' do
|
115
|
+
expect do
|
116
|
+
bar.send("#{attr_name}=", 'foo bar')
|
117
|
+
bar.save!
|
118
|
+
end.to raise_error(NoMethodError)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'when attribute is already set' do
|
123
|
+
let(:bar) { Bar.new(attr_name => 'foo bar') }
|
124
|
+
|
125
|
+
it 'can be written' do
|
126
|
+
bar.send("#{attr_name}=", 'new foo bar')
|
127
|
+
bar.save!
|
128
|
+
|
129
|
+
_bar = Bar.find(bar.id)
|
130
|
+
expect(_bar.send(attr_name)).to eq('new foo bar')
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'when attribute name is alphanumeric' do
|
137
|
+
let(:attr_name) { 'foo' }
|
138
|
+
|
139
|
+
it_behaves_like 'dynamic field'
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'when attribute name contains spaces' do
|
143
|
+
let(:attr_name) { 'hello world' }
|
144
|
+
|
145
|
+
it_behaves_like 'dynamic field'
|
146
|
+
end
|
147
|
+
|
148
|
+
context 'when attribute name contains special characters' do
|
149
|
+
let(:attr_name) { 'hello%world' }
|
150
|
+
|
151
|
+
it_behaves_like 'dynamic field'
|
152
|
+
end
|
153
|
+
end
|