mongoid 8.1.3 → 8.1.11

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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +43 -45
  3. data/lib/mongoid/association/accessors.rb +5 -1
  4. data/lib/mongoid/association/eager_loadable.rb +3 -0
  5. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +18 -3
  6. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +1 -1
  7. data/lib/mongoid/association/referenced/has_many/enumerable.rb +21 -4
  8. data/lib/mongoid/association/referenced/has_many/proxy.rb +11 -2
  9. data/lib/mongoid/atomic.rb +9 -7
  10. data/lib/mongoid/attributes/readonly.rb +8 -3
  11. data/lib/mongoid/contextual/mongo.rb +1 -1
  12. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +15 -1
  13. data/lib/mongoid/criteria/queryable/selectable.rb +1 -1
  14. data/lib/mongoid/document.rb +8 -1
  15. data/lib/mongoid/equality.rb +1 -0
  16. data/lib/mongoid/extensions/hash.rb +4 -4
  17. data/lib/mongoid/fields.rb +13 -8
  18. data/lib/mongoid/interceptable.rb +6 -7
  19. data/lib/mongoid/matcher.rb +15 -1
  20. data/lib/mongoid/serializable.rb +7 -7
  21. data/lib/mongoid/timestamps/created.rb +8 -1
  22. data/lib/mongoid/touchable.rb +1 -1
  23. data/lib/mongoid/traversable.rb +36 -1
  24. data/lib/mongoid/validatable/associated.rb +98 -17
  25. data/lib/mongoid/validatable/macros.rb +15 -0
  26. data/lib/mongoid/validatable/numericality.rb +19 -0
  27. data/lib/mongoid/validatable.rb +9 -0
  28. data/lib/mongoid/version.rb +5 -1
  29. data/spec/integration/app_spec.rb +24 -5
  30. data/spec/integration/associations/embeds_one_spec.rb +25 -5
  31. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +40 -0
  32. data/spec/mongoid/association/eager_spec.rb +24 -2
  33. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +35 -0
  34. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +4 -0
  35. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +4 -0
  36. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +28 -37
  37. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +42 -0
  38. data/spec/mongoid/attributes/readonly_spec.rb +19 -0
  39. data/spec/mongoid/attributes_spec.rb +16 -0
  40. data/spec/mongoid/contextual/mongo_spec.rb +78 -3
  41. data/spec/mongoid/criteria/queryable/selectable_spec.rb +29 -0
  42. data/spec/mongoid/document_spec.rb +27 -0
  43. data/spec/mongoid/equality_spec.rb +6 -0
  44. data/spec/mongoid/fields_spec.rb +3 -3
  45. data/spec/mongoid/interceptable_spec.rb +80 -0
  46. data/spec/mongoid/interceptable_spec_models.rb +47 -111
  47. data/spec/mongoid/serializable_spec.rb +16 -9
  48. data/spec/mongoid/timestamps/created_spec.rb +23 -0
  49. data/spec/mongoid/validatable/associated_spec.rb +27 -34
  50. data/spec/mongoid/validatable/numericality_spec.rb +16 -0
  51. data/spec/shared/lib/mrss/docker_runner.rb +1 -2
  52. data/spec/shared/lib/mrss/release/candidate.rb +281 -0
  53. data/spec/shared/lib/mrss/release/product_data.rb +144 -0
  54. data/spec/shared/lib/mrss/server_version_registry.rb +1 -1
  55. data/spec/shared/lib/tasks/candidate.rake +64 -0
  56. data/spec/shared/share/Dockerfile.erb +15 -85
  57. data/spec/shared/shlib/distro.sh +10 -0
  58. data/spec/shared/shlib/server.sh +33 -26
  59. data/spec/shared/shlib/set_env.sh +9 -68
  60. data/spec/support/expectations.rb +20 -17
  61. data/spec/support/models/band.rb +1 -0
  62. data/spec/support/models/lat_lng.rb +6 -0
  63. data/spec/support/models/name.rb +10 -0
  64. metadata +15 -38
  65. checksums.yaml.gz.sig +0 -0
  66. data.tar.gz.sig +0 -0
  67. metadata.gz.sig +0 -0
@@ -15,32 +15,113 @@ module Mongoid
15
15
  #
16
16
  # validates_associated :name, :addresses
17
17
  # end
18
- class AssociatedValidator < ActiveModel::EachValidator
18
+ class AssociatedValidator < ActiveModel::Validator
19
+ # Required by `validates_with` so that the validator
20
+ # gets added to the correct attributes.
21
+ def attributes
22
+ options[:attributes]
23
+ end
19
24
 
20
- # Validates that the associations provided are either all nil or all
21
- # valid. If neither is true then the appropriate errors will be added to
22
- # the parent document.
25
+ # Checks that the named associations of the given record
26
+ # (`attributes`) are valid. This does NOT load the associations
27
+ # from the database, and will only validate records that are dirty
28
+ # or unpersisted.
23
29
  #
24
- # @example Validate the association.
25
- # validator.validate_each(document, :name, name)
30
+ # If anything is not valid, appropriate errors will be added to
31
+ # the `document` parameter.
32
+ #
33
+ # @param [ Mongoid::Document ] document the document with the
34
+ # associations to validate.
35
+ def validate(document)
36
+ options[:attributes].each do |attr_name|
37
+ validate_association(document, attr_name)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ # Validates that the given association provided is either nil,
44
+ # persisted and unchanged, or invalid. Otherwise, the appropriate errors
45
+ # will be added to the parent document.
26
46
  #
27
47
  # @param [ Document ] document The document to validate.
28
48
  # @param [ Symbol ] attribute The association to validate.
29
- # @param [ Object ] value The value of the association.
30
- def validate_each(document, attribute, value)
31
- begin
32
- document.begin_validate
33
- valid = Array.wrap(value).collect do |doc|
34
- if doc.nil? || doc.flagged_for_destroy?
35
- true
49
+ def validate_association(document, attribute)
50
+ # grab the proxy from the instance variable directly; we don't want
51
+ # any loading logic to run; we just want to see if it's already
52
+ # been loaded.
53
+ proxy = document.ivar(attribute)
54
+ return unless proxy
55
+
56
+ # if the variable exists, now we see if it is a proxy, or an actual
57
+ # document. It might be a literal document instead of a proxy if this
58
+ # document was created with a Document instance as a provided attribute,
59
+ # e.g. "Post.new(message: Message.new)".
60
+ target = proxy.respond_to?(:_target) ? proxy._target : proxy
61
+
62
+ # Now, fetch the list of documents from the target. Target may be a
63
+ # single value, or a list of values, and in the case of HasMany,
64
+ # might be a rather complex collection. We need to do this without
65
+ # triggering a load, so it's a bit of a delicate dance.
66
+ list = get_target_documents(target)
67
+
68
+ valid = document.validating do
69
+ # Now, treating the target as an array, look at each element
70
+ # and see if it is valid, but only if it has already been
71
+ # persisted, or changed, and hasn't been flagged for destroy.
72
+ #
73
+ # use map.all? instead of just all?, because all? will do short-circuit
74
+ # evaluation and terminate on the first failed validation.
75
+ list.map do |value|
76
+ if value && !value.flagged_for_destroy? && (!value.persisted? || value.changed?)
77
+ value.validated? ? true : value.valid?
36
78
  else
37
- doc.validated? ? true : doc.valid?
79
+ true
38
80
  end
39
81
  end.all?
40
- ensure
41
- document.exit_validate
42
82
  end
43
- document.errors.add(attribute, :invalid, **options) unless valid
83
+
84
+ document.errors.add(attribute, :invalid) unless valid
85
+ end
86
+
87
+ private
88
+
89
+ # Examine the given target object and return an array of
90
+ # documents (possibly empty) that the target represents.
91
+ #
92
+ # @param [ Array | Mongoid::Document | Mongoid::Association::Proxy | HasMany::Enumerable ] target
93
+ # the target object to examine.
94
+ #
95
+ # @return [ Array<Mongoid::Document> ] the list of documents
96
+ def get_target_documents(target)
97
+ if target.respond_to?(:_loaded?)
98
+ get_target_documents_for_has_many(target)
99
+ else
100
+ get_target_documents_for_other(target)
101
+ end
102
+ end
103
+
104
+ # Returns the list of all currently in-memory values held by
105
+ # the target. The target will not be loaded.
106
+ #
107
+ # @param [ HasMany::Enumerable ] target the target that will
108
+ # be examined for in-memory documents.
109
+ #
110
+ # @return [ Array<Mongoid::Document> ] the in-memory documents
111
+ # held by the target.
112
+ def get_target_documents_for_has_many(target)
113
+ [ *target._loaded.values, *target._added.values ]
114
+ end
115
+
116
+ # Returns the target as an array. If the target represents a single
117
+ # value, it is wrapped in an array.
118
+ #
119
+ # @param [ Array | Mongoid::Document | Mongoid::Association::Proxy ] target
120
+ # the target to return.
121
+ #
122
+ # @return [ Array<Mongoid::Document> ] the target, as an array.
123
+ def get_target_documents_for_other(target)
124
+ Array.wrap(target)
44
125
  end
45
126
  end
46
127
  end
@@ -84,6 +84,21 @@ module Mongoid
84
84
  def validates_presence_of(*args)
85
85
  validates_with(PresenceValidator, _merge_attributes(args))
86
86
  end
87
+
88
+ # Validates whether or not a field contains a numeric value.
89
+ #
90
+ # @example
91
+ # class Person
92
+ # include Mongoid::Document
93
+ # field :cost
94
+ #
95
+ # validates_numericality_of :cost
96
+ # end
97
+ #
98
+ # @param [ Object... ] *args The names of the field(s) to validate.
99
+ def validates_numericality_of(*args)
100
+ validates_with(NumericalityValidator, _merge_attributes(args))
101
+ end
87
102
  end
88
103
  end
89
104
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongoid
4
+ module Validatable
5
+ # A specialization of the ActiveModel numericality validator, which adds
6
+ # logic to recognize and accept BSON::Decimal128 as a number.
7
+ class NumericalityValidator < ActiveModel::Validations::NumericalityValidator
8
+ private
9
+
10
+ # Ensure that BSON::Decimal128 is treated as a BigDecimal during the
11
+ # validation step.
12
+ def prepare_value_for_validation(value, record, attr_name)
13
+ result = super
14
+
15
+ result.is_a?(BSON::Decimal128) ? result.to_big_decimal : result
16
+ end
17
+ end
18
+ end
19
+ end
@@ -5,6 +5,7 @@ require "mongoid/validatable/localizable"
5
5
  require "mongoid/validatable/associated"
6
6
  require "mongoid/validatable/format"
7
7
  require "mongoid/validatable/length"
8
+ require "mongoid/validatable/numericality"
8
9
  require "mongoid/validatable/queryable"
9
10
  require "mongoid/validatable/presence"
10
11
  require "mongoid/validatable/uniqueness"
@@ -37,6 +38,14 @@ module Mongoid
37
38
  Threaded.exit_validate(self)
38
39
  end
39
40
 
41
+ # Perform a validation within the associated block.
42
+ def validating
43
+ begin_validate
44
+ yield
45
+ ensure
46
+ exit_validate
47
+ end
48
+
40
49
  # Given the provided options, are we performing validations?
41
50
  #
42
51
  # @example Are we performing validations?
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mongoid
4
- VERSION = "8.1.3"
4
+ # The current version of Mongoid
5
+ #
6
+ # Note that this file is automatically updated via `rake candidate:create`.
7
+ # Manual changes to this file will be overwritten by that rake task.
8
+ VERSION = '8.1.11'
5
9
  end
@@ -16,6 +16,10 @@ describe 'Mongoid application tests' do
16
16
  skip 'Set APP_TESTS=1 in environment to run application tests'
17
17
  end
18
18
 
19
+ if SpecConfig.instance.rails_version < '7.1'
20
+ skip 'App tests require Rails > 7.0 (see https://stackoverflow.com/questions/79360526)'
21
+ end
22
+
19
23
  require 'fileutils'
20
24
  require 'mrss/child_process_helper'
21
25
  require 'open-uri'
@@ -26,6 +30,8 @@ describe 'Mongoid application tests' do
26
30
  context 'demo application' do
27
31
  context 'sinatra' do
28
32
  it 'runs' do
33
+ skip 'https://jira.mongodb.org/browse/MONGOID-5826'
34
+
29
35
  clone_application(
30
36
  'https://github.com/mongoid/mongoid-demo',
31
37
  subdir: 'sinatra-minimal',
@@ -45,6 +51,8 @@ describe 'Mongoid application tests' do
45
51
 
46
52
  context 'rails-api' do
47
53
  it 'runs' do
54
+ skip 'https://jira.mongodb.org/browse/MONGOID-5826'
55
+
48
56
  clone_application(
49
57
  'https://github.com/mongoid/mongoid-demo',
50
58
  subdir: 'rails-api',
@@ -87,6 +95,12 @@ describe 'Mongoid application tests' do
87
95
  end
88
96
 
89
97
  context 'new application - rails' do
98
+ before(:all) do
99
+ if SpecConfig.instance.rails_version < '7.1'
100
+ skip '`rails new` with rails < 7.1 fails because modern concurrent-ruby removed logger dependency'
101
+ end
102
+ end
103
+
90
104
  it 'creates' do
91
105
  install_rails
92
106
 
@@ -95,7 +109,7 @@ describe 'Mongoid application tests' do
95
109
  check_call(%w(rails new mongoid-test --skip-spring --skip-active-record), env: clean_env)
96
110
 
97
111
  Dir.chdir('mongoid-test') do
98
- adjust_app_gemfile
112
+ adjust_app_gemfile(add_sprockets: SpecConfig.instance.rails_version != '8.0')
99
113
  check_call(%w(bundle install), env: clean_env)
100
114
 
101
115
  check_call(%w(rails g model post), env: clean_env)
@@ -117,7 +131,7 @@ describe 'Mongoid application tests' do
117
131
  check_call(%w(rails new mongoid-test-config --skip-spring --skip-active-record), env: clean_env)
118
132
 
119
133
  Dir.chdir('mongoid-test-config') do
120
- adjust_app_gemfile
134
+ adjust_app_gemfile(add_sprockets: SpecConfig.instance.rails_version != '8.0')
121
135
  check_call(%w(bundle install), env: clean_env)
122
136
 
123
137
  mongoid_config_file = File.join(TMP_BASE,'mongoid-test-config/config/mongoid.yml')
@@ -139,7 +153,7 @@ describe 'Mongoid application tests' do
139
153
  if (rails_version = SpecConfig.instance.rails_version) == 'master'
140
154
  else
141
155
  check_call(%w(gem list))
142
- check_call(%w(gem install rails --no-document -v) + ["~> #{rails_version}.0"])
156
+ check_call(%w(gem install rails --no-document --force -v) + ["~> #{rails_version}.0"])
143
157
  end
144
158
  end
145
159
 
@@ -205,7 +219,7 @@ describe 'Mongoid application tests' do
205
219
  FileUtils.rm_rf(File.basename(repo_url))
206
220
  check_call(%w(git clone) + [repo_url])
207
221
  Dir.chdir(File.join(*[File.basename(repo_url), subdir].compact)) do
208
- adjust_app_gemfile
222
+ adjust_app_gemfile(add_sprockets: false)
209
223
  adjust_rails_defaults
210
224
  check_call(%w(bundle install), env: clean_env)
211
225
  puts `git diff`
@@ -262,7 +276,7 @@ describe 'Mongoid application tests' do
262
276
  end
263
277
  end
264
278
 
265
- def adjust_app_gemfile(rails_version: SpecConfig.instance.rails_version)
279
+ def adjust_app_gemfile(rails_version: SpecConfig.instance.rails_version, add_sprockets: true)
266
280
  remove_bundler_req
267
281
 
268
282
  gemfile_lines = IO.readlines('Gemfile')
@@ -280,12 +294,17 @@ describe 'Mongoid application tests' do
280
294
  gemfile_lines << "gem 'rails', '~> #{rails_version}.0'\n"
281
295
  end
282
296
  end
297
+ gemfile_lines << "gem 'sprockets-rails'\n" if add_sprockets
283
298
  File.open('Gemfile', 'w') do |f|
284
299
  f << gemfile_lines.join
285
300
  end
286
301
  end
287
302
 
288
303
  def adjust_rails_defaults(rails_version: SpecConfig.instance.rails_version)
304
+ if !rails_version.match?(/^\d+\.\d+$/)
305
+ # This must be pre-release version, we trim it
306
+ rails_version = rails_version.split('.')[0..1].join('.')
307
+ end
289
308
  if File.exist?('config/application.rb')
290
309
  lines = IO.readlines('config/application.rb')
291
310
  lines.each do |line|
@@ -3,8 +3,7 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe 'embeds_one associations' do
6
-
7
- context 're-associating the same object' do
6
+ context 'when re-associating the same object' do
8
7
  context 'with dependent: destroy' do
9
8
  let(:canvas) do
10
9
  Canvas.create!(palette: Palette.new)
@@ -16,7 +15,7 @@ describe 'embeds_one associations' do
16
15
  canvas.palette = canvas.palette
17
16
  canvas.save!
18
17
  canvas.reload
19
- canvas.palette.should == palette
18
+ expect(canvas.palette).to eq palette
20
19
  end
21
20
  end
22
21
  end
@@ -30,12 +29,33 @@ describe 'embeds_one associations' do
30
29
  end
31
30
 
32
31
  it 'loads the association correctly' do
33
- expect { klass }.to_not raise_error
34
- expect { klass.new.address }.to_not raise_error
32
+ expect { klass }.not_to raise_error
33
+ expect { klass.new.address }.not_to raise_error
35
34
  instance = klass.new
36
35
  address = Address.new
37
36
  instance.address = address
38
37
  expect(instance.address).to eq address
39
38
  end
40
39
  end
40
+
41
+ context 'when parent is persisted' do
42
+ let!(:person) do
43
+ Person.create!
44
+ end
45
+
46
+ context 'when assigning the new child' do
47
+ context 'when assigning an attribute to the child' do
48
+ before do
49
+ # person.reload
50
+ person.name = Name.new
51
+ person.name.first_name = 'Dmitry'
52
+ person.save!
53
+ end
54
+
55
+ it 'persists the child' do
56
+ expect(person.reload.name.first_name).to eq 'Dmitry'
57
+ end
58
+ end
59
+ end
60
+ end
41
61
  end
@@ -2,6 +2,28 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
+ module HabtmSpec
6
+ class Page
7
+ include Mongoid::Document
8
+ embeds_many :blocks, class_name: 'HabtmSpec::Block'
9
+ end
10
+
11
+ class Block
12
+ include Mongoid::Document
13
+ embedded_in :page, class_name: 'HabtmSpec::Page'
14
+ end
15
+
16
+ class ImageBlock < Block
17
+ has_and_belongs_to_many :attachments, inverse_of: nil, class_name: 'HabtmSpec::Attachment'
18
+ accepts_nested_attributes_for :attachments
19
+ end
20
+
21
+ class Attachment
22
+ include Mongoid::Document
23
+ field :file, type: String
24
+ end
25
+ end
26
+
5
27
  describe 'has_and_belongs_to_many associations' do
6
28
 
7
29
  context 'when an anonymous class defines a has_and_belongs_to_many association' do
@@ -18,4 +40,22 @@ describe 'has_and_belongs_to_many associations' do
18
40
  expect(klass.new.movies.build).to be_a Movie
19
41
  end
20
42
  end
43
+
44
+ context 'when an embedded has habtm relation' do
45
+ let(:attachment) { HabtmSpec::Attachment.create!(file: 'foo.jpg') }
46
+
47
+ let(:page) { HabtmSpec::Page.create! }
48
+
49
+ let(:image_block) do
50
+ image_block = page.blocks.build({
51
+ _type: 'HabtmSpec::ImageBlock',
52
+ attachment_ids: [ attachment.id.to_s ],
53
+ attachments_attributes: { '1234' => { file: 'bar.jpg', id: attachment.id.to_s } }
54
+ })
55
+ end
56
+
57
+ it 'does not raise on save' do
58
+ expect { image_block.save! }.not_to raise_error
59
+ end
60
+ end
21
61
  end
@@ -14,14 +14,36 @@ describe Mongoid::Association::EagerLoadable do
14
14
  Mongoid::Contextual::Mongo.new(criteria)
15
15
  end
16
16
 
17
+ let(:association_host) { Account }
18
+
17
19
  let(:inclusions) do
18
20
  includes.map do |key|
19
- Account.reflect_on_association(key)
21
+ association_host.reflect_on_association(key)
20
22
  end
21
23
  end
22
24
 
23
25
  let(:doc) { criteria.first }
24
26
 
27
+ context 'when root is an STI subclass' do
28
+ # Driver has_one Vehicle
29
+ # Vehicle belongs_to Driver
30
+ # Truck is a Vehicle
31
+
32
+ before do
33
+ Driver.create!(vehicle: Truck.new)
34
+ end
35
+
36
+ let(:criteria) { Truck.all }
37
+ let(:includes) { %i[ driver ] }
38
+ let(:association_host) { Truck }
39
+
40
+ it 'preloads the driver' do
41
+ expect(doc.ivar(:driver)).to be false
42
+ context.preload(inclusions, [ doc ])
43
+ expect(doc.ivar(:driver)).to be == Driver.first
44
+ end
45
+ end
46
+
25
47
  context "when belongs_to" do
26
48
 
27
49
  let!(:account) do
@@ -42,7 +64,7 @@ describe Mongoid::Association::EagerLoadable do
42
64
  it "preloads the parent" do
43
65
  expect(doc.ivar(:person)).to be false
44
66
  context.preload(inclusions, [doc])
45
- expect(doc.ivar(:person)).to eq(doc.person)
67
+ expect(doc.ivar(:person)).to be == person
46
68
  end
47
69
  end
48
70
 
@@ -2310,9 +2310,37 @@ describe Mongoid::Association::Embedded::EmbedsMany::Proxy do
2310
2310
  person.addresses.create!(street: "Bond St")
2311
2311
  end
2312
2312
 
2313
+ let(:address) { person.addresses.first }
2314
+
2313
2315
  it "returns true" do
2314
2316
  expect(person.addresses.exists?).to be true
2315
2317
  end
2318
+
2319
+ context 'when given specifying conditions' do
2320
+ context 'when the record exists in the association' do
2321
+ it 'returns true by condition' do
2322
+ expect(person.addresses.exists?(street: 'Bond St')).to be true
2323
+ end
2324
+
2325
+ it 'returns true by id' do
2326
+ expect(person.addresses.exists?(address._id)).to be true
2327
+ end
2328
+
2329
+ it 'returns false when given false' do
2330
+ expect(person.addresses.exists?(false)).to be false
2331
+ end
2332
+
2333
+ it 'returns false when given nil' do
2334
+ expect(person.addresses.exists?(nil)).to be false
2335
+ end
2336
+ end
2337
+
2338
+ context 'when the record does not exist in the association' do
2339
+ it 'returns false' do
2340
+ expect(person.addresses.exists?(street: 'Garfield Ave')).to be false
2341
+ end
2342
+ end
2343
+ end
2316
2344
  end
2317
2345
 
2318
2346
  context "when no documents exist in the database" do
@@ -2324,6 +2352,13 @@ describe Mongoid::Association::Embedded::EmbedsMany::Proxy do
2324
2352
  it "returns false" do
2325
2353
  expect(person.addresses.exists?).to be false
2326
2354
  end
2355
+
2356
+ context 'when given specifying conditions' do
2357
+ it 'returns false' do
2358
+ expect(person.addresses.exists?(street: 'Hyde Park Dr')).to be false
2359
+ expect(person.addresses.exists?(street: 'Garfield Ave')).to be false
2360
+ end
2361
+ end
2327
2362
  end
2328
2363
  end
2329
2364
 
@@ -27,6 +27,10 @@ describe Mongoid::Association::Embedded::EmbedsMany do
27
27
  expect(legislator.attributes.keys).to eq(['_id', 'a'])
28
28
  end
29
29
 
30
+ it 'allows accessing the parent' do
31
+ expect { legislator.congress }.not_to raise_error
32
+ end
33
+
30
34
  context 'when using only with $' do
31
35
  before do
32
36
  Patient.destroy_all
@@ -749,6 +749,10 @@ describe Mongoid::Association::Referenced::BelongsTo::Proxy do
749
749
  person.save!
750
750
  end
751
751
 
752
+ # NOTE: there as a bad interdependency here, with the auto_save_spec.rb
753
+ # file. If auto_save_spec.rb runs before this, the following specs fail
754
+ # with "undefined method `nullify' for an instance of Person".
755
+
752
756
  context "when parent exists" do
753
757
 
754
758
  context "when child is destroyed" do
@@ -1755,43 +1755,6 @@ describe Mongoid::Association::Referenced::HasAndBelongsToMany::Proxy do
1755
1755
  end
1756
1756
  end
1757
1757
 
1758
- describe "#any?" do
1759
-
1760
- let(:person) do
1761
- Person.create!
1762
- end
1763
-
1764
- context "when nothing exists on the relation" do
1765
-
1766
- context "when no document is added" do
1767
-
1768
- let!(:sandwich) do
1769
- Sandwich.create!
1770
- end
1771
-
1772
- it "returns false" do
1773
- expect(sandwich.meats.any?).to be false
1774
- end
1775
- end
1776
-
1777
- context "when the document is destroyed" do
1778
-
1779
- before do
1780
- Meat.create!
1781
- end
1782
-
1783
- let!(:sandwich) do
1784
- Sandwich.create!
1785
- end
1786
-
1787
- it "returns false" do
1788
- sandwich.destroy
1789
- expect(sandwich.meats.any?).to be false
1790
- end
1791
- end
1792
- end
1793
- end
1794
-
1795
1758
  context "when documents have been persisted" do
1796
1759
 
1797
1760
  let!(:preference) do
@@ -3041,6 +3004,34 @@ describe Mongoid::Association::Referenced::HasAndBelongsToMany::Proxy do
3041
3004
  end
3042
3005
  end
3043
3006
 
3007
+ # MONGOID-5844
3008
+ #
3009
+ # Specifically, this tests the case where the association is
3010
+ # initialized with a single element (so that Proxy#push does not take
3011
+ # the `concat` route), which causes `reset_unloaded` to be called, which
3012
+ # sets the `_unloaded` Criteria object to match only the specific element
3013
+ # that was given.
3014
+ #
3015
+ # The issue now is that when the events list is updated to be both events,
3016
+ # _unloaded matches one of them already, and the other has previously been
3017
+ # persisted so `new_record?` won't match it. We need to make sure the
3018
+ # `#size` logic properly accounts for this case.
3019
+ context 'when documents have been previously persisted' do
3020
+ let(:person1) { Person.create! }
3021
+ let(:person2) { Person.create! }
3022
+ let(:event1) { Event.create!(administrators: [ person1 ]) }
3023
+ let(:event2) { Event.create!(administrators: [ person2 ]) }
3024
+
3025
+ before do
3026
+ person1.administrated_events = [ event1, event2 ]
3027
+ end
3028
+
3029
+ it 'returns the number of associated documents [MONGOID-5844]' do
3030
+ expect(person1.administrated_events.to_a.size).to eq(2)
3031
+ expect(person1.administrated_events.size).to eq(2)
3032
+ end
3033
+ end
3034
+
3044
3035
  context "when documents have not been persisted" do
3045
3036
 
3046
3037
  before do
@@ -2395,6 +2395,42 @@ describe Mongoid::Association::Referenced::HasMany::Proxy do
2395
2395
  end
2396
2396
  end
2397
2397
  end
2398
+
2399
+ context 'when invoked with specifying conditions' do
2400
+ let(:other_person) { Person.create! }
2401
+ let(:post) { person.posts.first }
2402
+
2403
+ before do
2404
+ person.posts.create title: 'bumfuzzle'
2405
+ other_person.posts.create title: 'bumbershoot'
2406
+ end
2407
+
2408
+ context 'when the conditions match an associated record' do
2409
+ it 'detects its existence by condition' do
2410
+ expect(person.posts.exists?(title: 'bumfuzzle')).to be true
2411
+ expect(other_person.posts.exists?(title: 'bumbershoot')).to be true
2412
+ end
2413
+
2414
+ it 'detects its existence by id' do
2415
+ expect(person.posts.exists?(post._id)).to be true
2416
+ end
2417
+
2418
+ it 'returns false when given false' do
2419
+ expect(person.posts.exists?(false)).to be false
2420
+ end
2421
+
2422
+ it 'returns false when given nil' do
2423
+ expect(person.posts.exists?(nil)).to be false
2424
+ end
2425
+ end
2426
+
2427
+ context 'when the conditions match an unassociated record' do
2428
+ it 'does not detect its existence' do
2429
+ expect(person.posts.exists?(title: 'bumbershoot')).to be false
2430
+ expect(other_person.posts.exists?(title: 'bumfuzzle')).to be false
2431
+ end
2432
+ end
2433
+ end
2398
2434
  end
2399
2435
 
2400
2436
  context "when documents exist in application but not in database" do
@@ -2465,6 +2501,12 @@ describe Mongoid::Association::Referenced::HasMany::Proxy do
2465
2501
  end
2466
2502
  end
2467
2503
  end
2504
+
2505
+ context 'when invoked with specifying conditions' do
2506
+ it 'returns false' do
2507
+ expect(person.posts.exists?(title: 'hullaballoo')).to be false
2508
+ end
2509
+ end
2468
2510
  end
2469
2511
  end
2470
2512