mongoid 7.4.0 → 7.4.1

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 (49) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +2 -2
  4. data/lib/mongoid/association/embedded/batchable.rb +20 -3
  5. data/lib/mongoid/association/referenced/has_many/proxy.rb +2 -2
  6. data/lib/mongoid/atomic/paths/embedded/many.rb +19 -0
  7. data/lib/mongoid/config/environment.rb +20 -4
  8. data/lib/mongoid/persistable/upsertable.rb +1 -1
  9. data/lib/mongoid/traversable.rb +4 -1
  10. data/lib/mongoid/version.rb +1 -1
  11. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +7 -2
  12. data/spec/config/mongoid_with_schema_map_uuid.yml +27 -0
  13. data/spec/integration/app_spec.rb +20 -14
  14. data/spec/integration/associations/embedded_dirty_spec.rb +28 -0
  15. data/spec/lite_spec_helper.rb +1 -1
  16. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +21 -0
  17. data/spec/mongoid/association/embedded/embeds_many_models.rb +121 -0
  18. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +8 -0
  19. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +24 -8
  20. data/spec/mongoid/atomic_spec.rb +22 -0
  21. data/spec/mongoid/clients/factory_spec.rb +11 -12
  22. data/spec/mongoid/config/environment_spec.rb +39 -1
  23. data/spec/mongoid/config_spec.rb +40 -0
  24. data/spec/mongoid/errors/mongoid_error_spec.rb +1 -1
  25. data/spec/shared/lib/mrss/constraints.rb +8 -16
  26. data/spec/shared/lib/mrss/docker_runner.rb +23 -3
  27. data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
  28. data/spec/shared/lib/mrss/event_subscriber.rb +15 -5
  29. data/spec/shared/lib/mrss/lite_constraints.rb +32 -1
  30. data/spec/shared/share/Dockerfile.erb +34 -48
  31. data/spec/shared/shlib/config.sh +27 -0
  32. data/spec/shared/shlib/server.sh +32 -19
  33. data/spec/shared/shlib/set_env.sh +37 -0
  34. data/spec/support/macros.rb +9 -0
  35. data/spec/support/models/membership.rb +1 -0
  36. data/spec/support/schema_maps/schema_map_aws.json +17 -0
  37. data/spec/support/schema_maps/schema_map_aws_key_alt_names.json +12 -0
  38. data/spec/support/schema_maps/schema_map_azure.json +17 -0
  39. data/spec/support/schema_maps/schema_map_azure_key_alt_names.json +12 -0
  40. data/spec/support/schema_maps/schema_map_gcp.json +17 -0
  41. data/spec/support/schema_maps/schema_map_gcp_key_alt_names.json +12 -0
  42. data/spec/support/schema_maps/schema_map_kmip.json +17 -0
  43. data/spec/support/schema_maps/schema_map_kmip_key_alt_names.json +12 -0
  44. data/spec/support/schema_maps/schema_map_local.json +18 -0
  45. data/spec/support/schema_maps/schema_map_local_key_alt_names.json +12 -0
  46. data/spec/support/spec_config.rb +4 -0
  47. data.tar.gz.sig +0 -0
  48. metadata +28 -2
  49. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4fe0d8621e8481c3b8a6e3c80b42940c0e31b12618947c1b6ac8b854eaa52b4e
4
- data.tar.gz: 7a6efa1cccc8c103b3ee0519e7ba90b84f895c2f38e36deac5af79c6b49a160b
3
+ metadata.gz: d6d3533b504ce95ac9e04f10e414e9ec7cad374f7e2be0d5af2de6bcaf3d7938
4
+ data.tar.gz: 80f128f6d7fa5f62974412ad1b61b094e7bb04c3528f488b8b44d8819ef4c1c0
5
5
  SHA512:
6
- metadata.gz: c671b0e7dd7262c7c041dd420e4f1baaaa2a243d4e223809595af008c314e0a66196b620aa1e71a02300ede2ef613aab4e41fdcb38aacbb87b47b9b1b0de929b
7
- data.tar.gz: 925dd1da4a7f473bf16bcae71adf87fcb87cfa69637b548e3f002072a19df6b26b868346f68f842d9ddfdf0de6f3071273fe0ed1a2dbbafdb0097db7dd5a642d
6
+ metadata.gz: ad9539a86be88240aa2cad5ef04c0cf0ff1b3aa42b576406153783601d7a75ff6077088efaba8607ddf53db7c58f8cf24d667861ce675dafae62ce7f899e9d3c
7
+ data.tar.gz: 1ab3af654053050249a3a9b8207c5e1b6969b4f6a031062ca433dc085598cc6590222445638e2cf2b5389e19212dec1c1c51ad4b66044528c7b265a84ecd554e
checksums.yaml.gz.sig CHANGED
Binary file
data/README.md CHANGED
@@ -19,9 +19,9 @@ Compatibility
19
19
 
20
20
  Mongoid supports and is tested against:
21
21
 
22
- - MRI 2.5-3.0
22
+ - MRI 2.5 - 3.1
23
23
  - JRuby 9.2
24
- - MongoDB server 2.6-4.4
24
+ - MongoDB server 2.6 - 6.0
25
25
 
26
26
  Issues
27
27
  ------
@@ -80,7 +80,8 @@ module Mongoid
80
80
  def batch_replace(docs)
81
81
  if docs.blank?
82
82
  if _assigning? && !empty?
83
- _base.delayed_atomic_sets.clear
83
+ _base.delayed_atomic_sets.delete(path)
84
+ clear_atomic_path_cache
84
85
  _base.add_atomic_unset(first)
85
86
  target_duplicate = _target.dup
86
87
  pre_process_batch_remove(target_duplicate, :delete)
@@ -92,7 +93,8 @@ module Mongoid
92
93
  _base.delayed_atomic_sets.clear unless _assigning?
93
94
  docs = normalize_docs(docs).compact
94
95
  _target.clear and _unscoped.clear
95
- _base.delayed_atomic_unsets.clear
96
+ _base.delayed_atomic_unsets.delete(path)
97
+ clear_atomic_path_cache
96
98
  inserts = execute_batch_set(docs)
97
99
  add_atomic_sets(inserts)
98
100
  end
@@ -234,7 +236,22 @@ module Mongoid
234
236
  #
235
237
  # @return [ String ] The atomic path.
236
238
  def path
237
- @path ||= _unscoped.first.atomic_path
239
+ @path ||= if _unscoped.empty?
240
+ Mongoid::Atomic::Paths::Embedded::Many.position_without_document(_base, _association)
241
+ else
242
+ _unscoped.first.atomic_path
243
+ end
244
+ end
245
+
246
+ # Clear the cache for path and atomic_paths. This method is used when
247
+ # the path method is used, and the association has not been set on the
248
+ # document yet, which can cause path and atomic_paths to be calculated
249
+ # incorrectly later.
250
+ #
251
+ # @api private
252
+ def clear_atomic_path_cache
253
+ self.path = nil
254
+ _base.instance_variable_set("@atomic_paths", nil)
238
255
  end
239
256
 
240
257
  # Set the atomic path.
@@ -469,8 +469,8 @@ module Mongoid
469
469
  selector = conditions || {}
470
470
  removed = klass.send(method, selector.merge!(criteria.selector))
471
471
  _target.delete_if do |doc|
472
- if doc._matches?(selector)
473
- unbind_one(doc) and true
472
+ doc._matches?(selector).tap do |b|
473
+ unbind_one(doc) if b
474
474
  end
475
475
  end
476
476
  removed
@@ -34,6 +34,25 @@ module Mongoid
34
34
  locator = document.new_record? ? "" : ".#{document._index}"
35
35
  "#{pos}#{"." unless pos.blank?}#{document._association.store_as}#{locator}"
36
36
  end
37
+
38
+ class << self
39
+
40
+ # Get the position of where the document would go for the given
41
+ # association. The use case for this function is when trying to
42
+ # persist an empty list for an embedded association. All of the
43
+ # existing functions for getting the position to store a document
44
+ # require passing in a document to store, which we don't have when
45
+ # trying to store the empty list.
46
+ #
47
+ # @param [ Document ] parent The parent document to store in.
48
+ # @param [ Association ] association The association.
49
+ #
50
+ # @return [ String ] The position string.
51
+ def position_without_document(parent, association)
52
+ pos = parent.atomic_position
53
+ "#{pos}#{"." unless pos.blank?}#{association.store_as}"
54
+ end
55
+ end
37
56
  end
38
57
  end
39
58
  end
@@ -44,21 +44,37 @@ module Mongoid
44
44
  # override the current Mongoid environment.
45
45
  #
46
46
  # @return [ Hash ] The settings.
47
+ #
47
48
  # @api private
48
49
  def load_yaml(path, environment = nil)
49
50
  env = environment ? environment.to_s : env_name
50
- contents = File.new(path).read
51
+
52
+ contents = File.read(path)
51
53
  if contents.empty?
52
54
  raise Mongoid::Errors::EmptyConfigFile.new(path)
53
55
  end
54
- data = if RUBY_VERSION.start_with?("2.5")
55
- YAML.safe_load(ERB.new(contents).result, [Symbol], [], true)
56
+
57
+ # These are the classes that can be used in a Mongoid
58
+ # configuration file in addition to standard YAML types.
59
+ permitted_classes = [
60
+ # Symbols occur as values for read preference, for example.
61
+ Symbol,
62
+ # BSON::Binary occur as keyId values for FLE (more precisely,
63
+ # the keyIds are UUIDs).
64
+ BSON::Binary,
65
+ ]
66
+
67
+ result = ERB.new(contents).result
68
+ data = if RUBY_VERSION < '2.6'
69
+ YAML.safe_load(result, permitted_classes, [], true)
56
70
  else
57
- YAML.safe_load(ERB.new(contents).result, permitted_classes: [Symbol], aliases: true)
71
+ YAML.safe_load(result, permitted_classes: permitted_classes, aliases: true)
58
72
  end
73
+
59
74
  unless data.is_a?(Hash)
60
75
  raise Mongoid::Errors::InvalidConfigFile.new(path)
61
76
  end
77
+
62
78
  data[env]
63
79
  end
64
80
  end
@@ -18,7 +18,7 @@ module Mongoid
18
18
  # @return [ true ] True.
19
19
  def upsert(options = {})
20
20
  prepare_upsert(options) do
21
- collection.find(atomic_selector).update_one(
21
+ collection.find(atomic_selector).replace_one(
22
22
  as_attributes, upsert: true, session: _session)
23
23
  end
24
24
  end
@@ -178,7 +178,10 @@ module Mongoid
178
178
  to_expand = []
179
179
  expanding.each do |child|
180
180
  next if expanded[child]
181
- expanded[child] = true
181
+ # Don't mark expanded if _id is nil, since documents are compared by
182
+ # their _ids, multiple embedded documents with nil ids will compare
183
+ # equally, and some documents will not be expanded.
184
+ expanded[child] = true if child._id
182
185
  children << child
183
186
  to_expand += child._children
184
187
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mongoid
4
- VERSION = "7.4.0"
4
+ VERSION = "7.4.1"
5
5
  end
@@ -80,8 +80,13 @@ development:
80
80
  # (default: 10)
81
81
  # connect_timeout: 10
82
82
 
83
- # The timeout to wait to execute operations on a socket before raising an error.
84
- # (default: 5)
83
+ # How long to wait for a response for each operation sent to the
84
+ # server. This timeout should be set to a value larger than the
85
+ # processing time for the longest operation that will be executed
86
+ # by the application. Note that this is a client-side timeout;
87
+ # the server may continue executing an operation after the client
88
+ # aborts it with the SocketTimeout exception.
89
+ # (default: nil, meaning no timeout)
85
90
  # socket_timeout: 5
86
91
 
87
92
  # The name of the replica set to connect to. Servers provided as seeds that do
@@ -0,0 +1,27 @@
1
+ test:
2
+ clients:
3
+ default:
4
+ database: mongoid_test
5
+ hosts:
6
+ <% SpecConfig.instance.addresses.each do |address| %>
7
+ - <%= address %>
8
+ <% end %>
9
+ options:
10
+ auto_encryption_options:
11
+ key_vault_namespace: 'admin.datakeys'
12
+ kms_providers:
13
+ local:
14
+ key: "z7iYiYKLuYymEWtk4kfny1ESBwwFdA58qMqff96A8ghiOcIK75lJGPUIocku8LOFjQuEgeIP4xlln3s7r93FV9J5sAE7zg8U"
15
+ schema_map:
16
+ blog_development.comments:
17
+ properties:
18
+ message:
19
+ encrypt:
20
+ keyId:
21
+ - !ruby/object:BSON::Binary
22
+ data: !binary |-
23
+ R/AgNcxASFiiJWKXqWGo5w==
24
+ type: :uuid
25
+ bsonType: "string"
26
+ algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
27
+ bsonType: "object"
@@ -5,6 +5,11 @@ require 'spec_helper'
5
5
  BASE = File.join(File.dirname(__FILE__), '../..')
6
6
  TMP_BASE = File.join(BASE, 'tmp')
7
7
 
8
+ def check_call(cmd, **opts)
9
+ puts "Executing #{cmd.join(' ')}"
10
+ Mrss::ChildProcessHelper.check_call(cmd, **opts)
11
+ end
12
+
8
13
  describe 'Mongoid application tests' do
9
14
  before(:all) do
10
15
  unless SpecConfig.instance.app_tests?
@@ -87,14 +92,14 @@ describe 'Mongoid application tests' do
87
92
 
88
93
  Dir.chdir(TMP_BASE) do
89
94
  FileUtils.rm_rf('mongoid-test')
90
- Mrss::ChildProcessHelper.check_call(%w(rails new mongoid-test --skip-spring --skip-active-record), env: clean_env)
95
+ check_call(%w(rails new mongoid-test --skip-spring --skip-active-record), env: clean_env)
91
96
 
92
97
  Dir.chdir('mongoid-test') do
93
98
  adjust_app_gemfile
94
- Mrss::ChildProcessHelper.check_call(%w(bundle install), env: clean_env)
99
+ check_call(%w(bundle install), env: clean_env)
95
100
 
96
- Mrss::ChildProcessHelper.check_call(%w(rails g model post), env: clean_env)
97
- Mrss::ChildProcessHelper.check_call(%w(rails g model comment post:belongs_to), env: clean_env)
101
+ check_call(%w(rails g model post), env: clean_env)
102
+ check_call(%w(rails g model comment post:belongs_to), env: clean_env)
98
103
 
99
104
  # https://jira.mongodb.org/browse/MONGOID-4885
100
105
  comment_text = File.read('app/models/comment.rb')
@@ -109,16 +114,16 @@ describe 'Mongoid application tests' do
109
114
 
110
115
  Dir.chdir(TMP_BASE) do
111
116
  FileUtils.rm_rf('mongoid-test-config')
112
- Mrss::ChildProcessHelper.check_call(%w(rails new mongoid-test-config --skip-spring --skip-active-record), env: clean_env)
117
+ check_call(%w(rails new mongoid-test-config --skip-spring --skip-active-record), env: clean_env)
113
118
 
114
119
  Dir.chdir('mongoid-test-config') do
115
120
  adjust_app_gemfile
116
- Mrss::ChildProcessHelper.check_call(%w(bundle install), env: clean_env)
121
+ check_call(%w(bundle install), env: clean_env)
117
122
 
118
123
  mongoid_config_file = File.join(TMP_BASE,'mongoid-test-config/config/mongoid.yml')
119
124
 
120
125
  File.exist?(mongoid_config_file).should be false
121
- Mrss::ChildProcessHelper.check_call(%w(rails g mongoid:config), env: clean_env)
126
+ check_call(%w(rails g mongoid:config), env: clean_env)
122
127
  File.exist?(mongoid_config_file).should be true
123
128
 
124
129
  config_text = File.read(mongoid_config_file)
@@ -130,10 +135,11 @@ describe 'Mongoid application tests' do
130
135
  end
131
136
 
132
137
  def install_rails
133
- Mrss::ChildProcessHelper.check_call(%w(gem uni rails -a))
138
+ check_call(%w(gem uni rails -a))
134
139
  if (rails_version = SpecConfig.instance.rails_version) == 'master'
135
140
  else
136
- Mrss::ChildProcessHelper.check_call(%w(gem install rails --no-document -v) + [rails_version])
141
+ check_call(%w(gem list))
142
+ check_call(%w(gem install rails --no-document -v) + ["~> #{rails_version}.0"])
137
143
  end
138
144
  end
139
145
 
@@ -157,7 +163,7 @@ describe 'Mongoid application tests' do
157
163
  before do
158
164
  Dir.chdir(APP_PATH) do
159
165
  remove_bundler_req
160
- Mrss::ChildProcessHelper.check_call(%w(bundle install), env: env)
166
+ check_call(%w(bundle install), env: env)
161
167
  write_mongoid_yml
162
168
  end
163
169
 
@@ -171,7 +177,7 @@ describe 'Mongoid application tests' do
171
177
  end
172
178
  index.should be nil
173
179
 
174
- Mrss::ChildProcessHelper.check_call(%w(bundle exec rake db:mongoid:create_indexes),
180
+ check_call(%w(bundle exec rake db:mongoid:create_indexes -t),
175
181
  cwd: APP_PATH, env: env)
176
182
 
177
183
  index = client['posts'].indexes.detect do |index|
@@ -189,11 +195,11 @@ describe 'Mongoid application tests' do
189
195
  def clone_application(repo_url, subdir: nil)
190
196
  Dir.chdir(TMP_BASE) do
191
197
  FileUtils.rm_rf(File.basename(repo_url))
192
- Mrss::ChildProcessHelper.check_call(%w(git clone) + [repo_url])
198
+ check_call(%w(git clone) + [repo_url])
193
199
  Dir.chdir(File.join(*[File.basename(repo_url), subdir].compact)) do
194
200
  adjust_app_gemfile
195
201
  adjust_rails_defaults
196
- Mrss::ChildProcessHelper.check_call(%w(bundle install), env: clean_env)
202
+ check_call(%w(bundle install), env: clean_env)
197
203
  puts `git diff`
198
204
 
199
205
  write_mongoid_yml
@@ -316,7 +322,7 @@ describe 'Mongoid application tests' do
316
322
  # in `initialize': too long unix socket path (126bytes given but 108bytes max) (ArgumentError)
317
323
  # Is it trying to create unix sockets in current directory?
318
324
  # https://stackoverflow.com/questions/30302021/rails-runner-without-spring
319
- Mrss::ChildProcessHelper.check_call(%w(bin/spring binstub --remove --all), env: clean_env)
325
+ check_call(%w(bin/spring binstub --remove --all), env: clean_env)
320
326
  end
321
327
 
322
328
  def clean_env
@@ -24,6 +24,34 @@ describe 'embedded associations' do
24
24
  it 'performs dirty tracking efficiently' do
25
25
  subject.changed?.should be false
26
26
  end
27
+
28
+ it 'calculates the descendants properly' do
29
+ expect(subject._descendants.length).to eq(40)
30
+ end
31
+ end
32
+
33
+ context 'when association is cyclic and the _id is nil' do
34
+ before do
35
+ # create deeply nested record
36
+ a = EmmOuter.create(level: 0)
37
+ level = 1
38
+ iter = a.inners.create(id: nil, level: level)
39
+ loop do
40
+ iter.friends.create(id: nil, level: (level += 1))
41
+ iter = iter.friends[0]
42
+ break if level == 40
43
+ end
44
+ end
45
+
46
+ let(:subject) { EmmOuter.first }
47
+
48
+ it 'performs dirty tracking efficiently' do
49
+ subject.changed?.should be false
50
+ end
51
+
52
+ it 'calculates the descendants properly' do
53
+ expect(subject._descendants.length).to eq(40)
54
+ end
27
55
  end
28
56
  end
29
57
  end
@@ -43,7 +43,7 @@ end
43
43
 
44
44
  require 'mongoid'
45
45
 
46
- if SpecConfig.instance.mri?
46
+ if SpecConfig.instance.mri? && !SpecConfig.instance.windows?
47
47
  require 'timeout_interrupt'
48
48
  else
49
49
  require 'timeout'
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "spec_helper"
4
+ require_relative '../embeds_many_models.rb'
4
5
 
5
6
  describe Mongoid::Association::Embedded::EmbedsMany::Proxy do
6
7
 
@@ -4649,4 +4650,24 @@ describe Mongoid::Association::Embedded::EmbedsMany::Proxy do
4649
4650
  end
4650
4651
  end
4651
4652
  end
4653
+
4654
+ context "when using assign_attributes with an already populated array" do
4655
+ let(:post) { EmmPost.create! }
4656
+
4657
+ before do
4658
+ post.assign_attributes(company_tags: [{id: BSON::ObjectId.new, title: 'a'}],
4659
+ user_tags: [{id: BSON::ObjectId.new, title: 'b'}])
4660
+ post.save!
4661
+ post.reload
4662
+ post.assign_attributes(company_tags: [{id: BSON::ObjectId.new, title: 'c'}],
4663
+ user_tags: [])
4664
+ post.save!
4665
+ post.reload
4666
+ end
4667
+
4668
+ it "has the correct embedded documents" do
4669
+ expect(post.company_tags.length).to eq(1)
4670
+ expect(post.company_tags.first.title).to eq("c")
4671
+ end
4672
+ end
4652
4673
  end
@@ -67,3 +67,124 @@ class EmmOuter
67
67
 
68
68
  field :level, :type => Integer
69
69
  end
70
+
71
+ class EmmCustomerAddress
72
+ include Mongoid::Document
73
+
74
+ embedded_in :addressable, polymorphic: true, inverse_of: :work_address
75
+ end
76
+
77
+ class EmmFriend
78
+ include Mongoid::Document
79
+
80
+ embedded_in :befriendable, polymorphic: true
81
+ end
82
+
83
+ class EmmCustomer
84
+ include Mongoid::Document
85
+
86
+ embeds_one :home_address, class_name: 'EmmCustomerAddress', as: :addressable
87
+ embeds_one :work_address, class_name: 'EmmCustomerAddress', as: :addressable
88
+
89
+ embeds_many :close_friends, class_name: 'EmmFriend', as: :befriendable
90
+ embeds_many :acquaintances, class_name: 'EmmFriend', as: :befriendable
91
+ end
92
+
93
+ class EmmUser
94
+ include Mongoid::Document
95
+ include Mongoid::Timestamps
96
+
97
+ embeds_many :orders, class_name: 'EmmOrder'
98
+ end
99
+
100
+ class EmmOrder
101
+ include Mongoid::Document
102
+
103
+ field :amount, type: Integer
104
+
105
+ embedded_in :user, class_name: 'EmmUser'
106
+ end
107
+
108
+ module EmmSpec
109
+ # There is also a top-level Car class defined.
110
+ class Car
111
+ include Mongoid::Document
112
+
113
+ embeds_many :doors
114
+ end
115
+
116
+ class Door
117
+ include Mongoid::Document
118
+
119
+ embedded_in :car
120
+ end
121
+
122
+ class Tank
123
+ include Mongoid::Document
124
+
125
+ embeds_many :guns
126
+ embeds_many :emm_turrets
127
+ # This association references a model that is not in our module,
128
+ # and it does not define class_name hence Mongoid will not be able to
129
+ # figure out the inverse for this association.
130
+ embeds_many :emm_hatches
131
+
132
+ # class_name is intentionally unqualified, references a class in the
133
+ # same module. Rails permits class_name to be unqualified like this.
134
+ embeds_many :launchers, class_name: 'Launcher'
135
+ end
136
+
137
+ class Gun
138
+ include Mongoid::Document
139
+
140
+ embedded_in :tank
141
+ end
142
+
143
+ class Launcher
144
+ include Mongoid::Document
145
+
146
+ # class_name is intentionally unqualified.
147
+ embedded_in :tank, class_name: 'Tank'
148
+ end
149
+ end
150
+
151
+ # This is intentionally on top level.
152
+ class EmmTurret
153
+ include Mongoid::Document
154
+
155
+ embedded_in :tank, class_name: 'EmmSpec::Tank'
156
+ end
157
+
158
+ # This is intentionally on top level.
159
+ class EmmHatch
160
+ include Mongoid::Document
161
+
162
+ # No :class_name option on this association intentionally.
163
+ embedded_in :tank
164
+ end
165
+
166
+ class EmmPost
167
+ include Mongoid::Document
168
+
169
+ embeds_many :company_tags, class_name: "EmmCompanyTag"
170
+ embeds_many :user_tags, class_name: "EmmUserTag"
171
+ end
172
+
173
+
174
+ class EmmCompanyTag
175
+ include Mongoid::Document
176
+
177
+ field :title, type: String
178
+
179
+ embedded_in :post, class_name: "EmmPost"
180
+ end
181
+
182
+
183
+ class EmmUserTag
184
+ include Mongoid::Document
185
+
186
+ field :title, type: String
187
+
188
+ embedded_in :post, class_name: "EmmPost"
189
+ end
190
+
@@ -2394,6 +2394,10 @@ describe Mongoid::Association::Referenced::HasAndBelongsToMany::Proxy do
2394
2394
  it "removes the ids from the foreign key" do
2395
2395
  expect(person.preference_ids).to eq([ preference_two.id ])
2396
2396
  end
2397
+
2398
+ it "sets the association locally" do
2399
+ expect(person.preferences).to eq([preference_two])
2400
+ end
2397
2401
  end
2398
2402
 
2399
2403
  context "when conditions are not provided" do
@@ -2420,6 +2424,10 @@ describe Mongoid::Association::Referenced::HasAndBelongsToMany::Proxy do
2420
2424
  it "returns the number of documents deleted" do
2421
2425
  expect(deleted).to eq(2)
2422
2426
  end
2427
+
2428
+ it "sets the association locally" do
2429
+ expect(person.preferences).to eq([])
2430
+ end
2423
2431
  end
2424
2432
  end
2425
2433
  end
@@ -2239,10 +2239,8 @@ describe Mongoid::Association::Referenced::HasMany::Proxy do
2239
2239
  Person.create!(username: 'durran')
2240
2240
  end
2241
2241
 
2242
- before do
2243
- person.posts.create!(title: "Testing")
2244
- person.posts.create!(title: "Test")
2245
- end
2242
+ let!(:post1) { person.posts.create!(title: "Testing") }
2243
+ let!(:post2) { person.posts.create!(title: "Test") }
2246
2244
 
2247
2245
  it "removes the correct posts" do
2248
2246
  person.posts.send(method, { title: "Testing" })
@@ -2258,6 +2256,11 @@ describe Mongoid::Association::Referenced::HasMany::Proxy do
2258
2256
  it "returns the number of documents deleted" do
2259
2257
  expect(person.posts.send(method, { title: "Testing" })).to eq(1)
2260
2258
  end
2259
+
2260
+ it "sets the association locally" do
2261
+ person.posts.send(method, { title: "Testing" })
2262
+ expect(person.posts).to eq([post2])
2263
+ end
2261
2264
  end
2262
2265
 
2263
2266
  context "when conditions are not provided" do
@@ -2284,6 +2287,11 @@ describe Mongoid::Association::Referenced::HasMany::Proxy do
2284
2287
  it "returns the number of documents deleted" do
2285
2288
  expect(person.posts.send(method)).to eq(2)
2286
2289
  end
2290
+
2291
+ it "sets the association locally" do
2292
+ person.posts.send(method)
2293
+ expect(person.posts).to eq([])
2294
+ end
2287
2295
  end
2288
2296
  end
2289
2297
 
@@ -2295,10 +2303,8 @@ describe Mongoid::Association::Referenced::HasMany::Proxy do
2295
2303
  Movie.create!(title: "Bladerunner")
2296
2304
  end
2297
2305
 
2298
- before do
2299
- movie.ratings.create!(value: 1)
2300
- movie.ratings.create!(value: 2)
2301
- end
2306
+ let!(:rating1) { movie.ratings.create!(value: 1) }
2307
+ let!(:rating2) { movie.ratings.create!(value: 2) }
2302
2308
 
2303
2309
  it "removes the correct ratings" do
2304
2310
  movie.ratings.send(method, { value: 1 })
@@ -2313,6 +2319,11 @@ describe Mongoid::Association::Referenced::HasMany::Proxy do
2313
2319
  it "returns the number of documents deleted" do
2314
2320
  expect(movie.ratings.send(method, { value: 1 })).to eq(1)
2315
2321
  end
2322
+
2323
+ it "sets the association locally" do
2324
+ movie.ratings.send(method, { value: 1 })
2325
+ expect(movie.ratings).to eq([rating2])
2326
+ end
2316
2327
  end
2317
2328
 
2318
2329
  context "when conditions are not provided" do
@@ -2339,6 +2350,11 @@ describe Mongoid::Association::Referenced::HasMany::Proxy do
2339
2350
  it "returns the number of documents deleted" do
2340
2351
  expect(movie.ratings.send(method)).to eq(2)
2341
2352
  end
2353
+
2354
+ it "sets the association locally" do
2355
+ movie.ratings.send(method)
2356
+ expect(movie.ratings).to eq([])
2357
+ end
2342
2358
  end
2343
2359
  end
2344
2360
  end
@@ -386,5 +386,27 @@ describe Mongoid::Atomic do
386
386
  end
387
387
  end
388
388
  end
389
+
390
+ context "when adding embedded documents with nil ids" do
391
+ let(:account) { Account.create!(name: "acc") }
392
+
393
+ before do
394
+ account.memberships.build(id: nil, name: "m1")
395
+ account.memberships.build(id: nil, name: "m2")
396
+ end
397
+
398
+ it "has the correct updates" do
399
+ account.atomic_updates.should == {
400
+ "$push" => {
401
+ "memberships" => {
402
+ "$each" => [
403
+ { "_id" => nil, "name" => "m1" },
404
+ { "_id" => nil, "name" => "m2" }
405
+ ]
406
+ }
407
+ }
408
+ }
409
+ end
410
+ end
389
411
  end
390
412
  end