mongoid 7.4.0 → 7.4.1

Sign up to get free protection for your applications and to get access to all the features.
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