mongoid 7.0.6 → 7.0.12

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 (91) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/README.md +1 -1
  5. data/Rakefile +14 -5
  6. data/lib/mongoid.rb +1 -1
  7. data/lib/mongoid/association/embedded/embeds_many.rb +2 -1
  8. data/lib/mongoid/association/embedded/embeds_one.rb +2 -1
  9. data/lib/mongoid/association/proxy.rb +1 -1
  10. data/lib/mongoid/atomic.rb +13 -3
  11. data/lib/mongoid/clients/sessions.rb +20 -4
  12. data/lib/mongoid/criteria.rb +7 -1
  13. data/lib/mongoid/criteria/modifiable.rb +2 -1
  14. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
  15. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +3 -3
  16. data/lib/mongoid/criteria/queryable/extensions/time.rb +1 -1
  17. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +12 -0
  18. data/lib/mongoid/document.rb +3 -2
  19. data/lib/mongoid/extensions/hash.rb +4 -2
  20. data/lib/mongoid/extensions/regexp.rb +1 -1
  21. data/lib/mongoid/fields.rb +2 -1
  22. data/lib/mongoid/interceptable.rb +3 -1
  23. data/lib/mongoid/matchable/regexp.rb +2 -2
  24. data/lib/mongoid/persistable/pushable.rb +11 -2
  25. data/lib/mongoid/persistence_context.rb +6 -6
  26. data/lib/mongoid/query_cache.rb +61 -18
  27. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  28. data/lib/mongoid/version.rb +1 -1
  29. data/lib/rails/generators/mongoid/model/templates/model.rb.tt +1 -1
  30. data/spec/app/models/customer.rb +11 -0
  31. data/spec/app/models/customer_address.rb +12 -0
  32. data/spec/app/models/delegating_patient.rb +16 -0
  33. data/spec/integration/app_spec.rb +192 -0
  34. data/spec/integration/associations/embedded_spec.rb +62 -0
  35. data/spec/integration/callbacks_models.rb +49 -0
  36. data/spec/integration/callbacks_spec.rb +216 -0
  37. data/spec/integration/criteria/date_field_spec.rb +41 -0
  38. data/spec/integration/document_spec.rb +22 -0
  39. data/spec/lite_spec_helper.rb +12 -4
  40. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +50 -0
  41. data/spec/mongoid/association/embedded/embeds_many_models.rb +53 -0
  42. data/spec/mongoid/association/embedded/embeds_many_spec.rb +10 -0
  43. data/spec/mongoid/association/embedded/embeds_one_spec.rb +0 -2
  44. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +140 -1
  45. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +105 -0
  46. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +2 -1
  47. data/spec/mongoid/atomic/paths_spec.rb +41 -0
  48. data/spec/mongoid/clients/options_spec.rb +4 -4
  49. data/spec/mongoid/clients/sessions_spec.rb +8 -4
  50. data/spec/mongoid/clients/transactions_spec.rb +20 -8
  51. data/spec/mongoid/clients_spec.rb +2 -2
  52. data/spec/mongoid/contextual/atomic_spec.rb +20 -10
  53. data/spec/mongoid/contextual/geo_near_spec.rb +11 -2
  54. data/spec/mongoid/contextual/map_reduce_spec.rb +20 -5
  55. data/spec/mongoid/contextual/mongo_spec.rb +76 -53
  56. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +7 -7
  57. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +1 -1
  58. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +19 -7
  59. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +28 -1
  60. data/spec/mongoid/criteria_spec.rb +4 -2
  61. data/spec/mongoid/document_persistence_context_spec.rb +33 -0
  62. data/spec/mongoid/indexable_spec.rb +6 -4
  63. data/spec/mongoid/matchable/default_spec.rb +1 -1
  64. data/spec/mongoid/matchable/regexp_spec.rb +2 -2
  65. data/spec/mongoid/matchable_spec.rb +2 -2
  66. data/spec/mongoid/persistable/pushable_spec.rb +55 -1
  67. data/spec/mongoid/query_cache_spec.rb +77 -9
  68. data/spec/mongoid/relations/proxy_spec.rb +1 -1
  69. data/spec/mongoid/scopable_spec.rb +2 -1
  70. data/spec/mongoid/tasks/database_rake_spec.rb +13 -13
  71. data/spec/mongoid/tasks/database_spec.rb +1 -1
  72. data/spec/shared/LICENSE +20 -0
  73. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  74. data/spec/shared/lib/mrss/cluster_config.rb +211 -0
  75. data/spec/shared/lib/mrss/constraints.rb +330 -0
  76. data/spec/shared/lib/mrss/docker_runner.rb +262 -0
  77. data/spec/shared/lib/mrss/lite_constraints.rb +175 -0
  78. data/spec/shared/lib/mrss/server_version_registry.rb +69 -0
  79. data/spec/shared/lib/mrss/spec_organizer.rb +149 -0
  80. data/spec/shared/share/Dockerfile.erb +229 -0
  81. data/spec/shared/shlib/distro.sh +73 -0
  82. data/spec/shared/shlib/server.sh +270 -0
  83. data/spec/shared/shlib/set_env.sh +128 -0
  84. data/spec/spec_helper.rb +0 -31
  85. data/spec/support/child_process_helper.rb +76 -0
  86. data/spec/support/cluster_config.rb +3 -3
  87. data/spec/support/constraints.rb +201 -30
  88. data/spec/support/session_registry.rb +50 -0
  89. data/spec/support/spec_config.rb +12 -4
  90. metadata +510 -461
  91. metadata.gz.sig +2 -2
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # describe Mongoid::Relations::Proxy do
4
4
  #
5
- # describe '#with', if: non_legacy_server? do
5
+ # describe '#with' do
6
6
  #
7
7
  # let(:circus) do
8
8
  # Circus.new
@@ -227,7 +227,8 @@ describe Mongoid::Scopable do
227
227
 
228
228
  context "when provided a criteria" do
229
229
 
230
- context 'when a collation is defined on the criteria', if: collation_supported? do
230
+ context 'when a collation is defined on the criteria' do
231
+ min_server_version '3.4'
231
232
 
232
233
  before do
233
234
  Band.scope(:tests, ->{ Band.where(name: 'TESTING').collation(locale: 'en_US', strength: 2) })
@@ -45,7 +45,7 @@ shared_context "rails rake task" do
45
45
  end
46
46
  end
47
47
 
48
- describe "db:drop", if: non_legacy_server? do
48
+ describe "db:drop" do
49
49
  include_context "rake task"
50
50
  include_context "rails rake task"
51
51
 
@@ -58,7 +58,7 @@ describe "db:drop", if: non_legacy_server? do
58
58
  end
59
59
  end
60
60
 
61
- describe "db:purge", if: non_legacy_server? do
61
+ describe "db:purge" do
62
62
  include_context "rake task"
63
63
  include_context "rails rake task"
64
64
 
@@ -71,7 +71,7 @@ describe "db:purge", if: non_legacy_server? do
71
71
  end
72
72
  end
73
73
 
74
- describe "db:seed", if: non_legacy_server? do
74
+ describe "db:seed" do
75
75
  include_context "rake task"
76
76
  include_context "rails rake task"
77
77
 
@@ -85,7 +85,7 @@ describe "db:seed", if: non_legacy_server? do
85
85
  end
86
86
  end
87
87
 
88
- describe "db:setup", if: non_legacy_server? do
88
+ describe "db:setup" do
89
89
  include_context "rake task"
90
90
  include_context "rails rake task"
91
91
 
@@ -117,7 +117,7 @@ describe "db:setup", if: non_legacy_server? do
117
117
  end
118
118
  end
119
119
 
120
- describe "db:reset", if: non_legacy_server? do
120
+ describe "db:reset" do
121
121
  include_context "rake task"
122
122
  include_context "rails rake task"
123
123
 
@@ -135,7 +135,7 @@ describe "db:reset", if: non_legacy_server? do
135
135
  end
136
136
  end
137
137
 
138
- describe "db:create", if: non_legacy_server? do
138
+ describe "db:create" do
139
139
  include_context "rake task"
140
140
  include_context "rails rake task"
141
141
 
@@ -144,7 +144,7 @@ describe "db:create", if: non_legacy_server? do
144
144
  end
145
145
  end
146
146
 
147
- describe "db:migrate", if: non_legacy_server? do
147
+ describe "db:migrate" do
148
148
  include_context "rake task"
149
149
  include_context "rails rake task"
150
150
 
@@ -153,7 +153,7 @@ describe "db:migrate", if: non_legacy_server? do
153
153
  end
154
154
  end
155
155
 
156
- describe "db:test:prepare", if: non_legacy_server? do
156
+ describe "db:test:prepare" do
157
157
  include_context "rake task"
158
158
  include_context "rails rake task"
159
159
 
@@ -174,7 +174,7 @@ describe "db:test:prepare", if: non_legacy_server? do
174
174
  end
175
175
  end
176
176
 
177
- describe "db:mongoid:create_indexes", if: non_legacy_server? do
177
+ describe "db:mongoid:create_indexes" do
178
178
  include_context "rake task"
179
179
 
180
180
  it_behaves_like "create_indexes"
@@ -198,7 +198,7 @@ describe "db:mongoid:create_indexes", if: non_legacy_server? do
198
198
  end
199
199
  end
200
200
 
201
- describe "db:mongoid:remove_undefined_indexes", if: non_legacy_server? do
201
+ describe "db:mongoid:remove_undefined_indexes" do
202
202
  include_context "rake task"
203
203
 
204
204
  it "receives remove_undefined_indexes" do
@@ -224,7 +224,7 @@ describe "db:mongoid:remove_undefined_indexes", if: non_legacy_server? do
224
224
  end
225
225
  end
226
226
 
227
- describe "db:mongoid:remove_indexes", if: non_legacy_server? do
227
+ describe "db:mongoid:remove_indexes" do
228
228
  include_context "rake task"
229
229
 
230
230
  it "receives remove_indexes" do
@@ -250,7 +250,7 @@ describe "db:mongoid:remove_indexes", if: non_legacy_server? do
250
250
  end
251
251
  end
252
252
 
253
- describe "db:mongoid:drop", if: non_legacy_server? do
253
+ describe "db:mongoid:drop" do
254
254
  include_context "rake task"
255
255
 
256
256
  it "works" do
@@ -266,7 +266,7 @@ describe "db:mongoid:drop", if: non_legacy_server? do
266
266
  end
267
267
  end
268
268
 
269
- describe "db:mongoid:purge", if: non_legacy_server? do
269
+ describe "db:mongoid:purge" do
270
270
  include_context "rake task"
271
271
 
272
272
  it "receives a purge" do
@@ -133,7 +133,7 @@ describe "Mongoid::Tasks::Database" do
133
133
  expect(removed_indexes).to be_empty
134
134
  end
135
135
 
136
- context 'when the index is a text index', if: non_legacy_server? do
136
+ context 'when the index is a text index' do
137
137
 
138
138
  before do
139
139
  class Band
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2020 MongoDB, Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ autoload :ChildProcess, 'childprocess'
5
+ autoload :Tempfile, 'tempfile'
6
+
7
+ module Mrss
8
+ module ChildProcessHelper
9
+ class SpawnError < StandardError; end
10
+
11
+ module_function def call(cmd, env: nil, cwd: nil)
12
+ process = ChildProcess.new(*cmd)
13
+ process.io.inherit!
14
+ if cwd
15
+ process.cwd = cwd
16
+ end
17
+ if env
18
+ env.each do |k, v|
19
+ process.environment[k.to_s] = v
20
+ end
21
+ end
22
+ process.start
23
+ process.wait
24
+ process
25
+ end
26
+
27
+ module_function def check_call(cmd, env: nil, cwd: nil)
28
+ process = call(cmd, env: env, cwd: cwd)
29
+ unless process.exit_code == 0
30
+ raise SpawnError, "Failed to execute: #{cmd}"
31
+ end
32
+ end
33
+
34
+ module_function def get_output(cmd, env: nil, cwd: nil)
35
+ process = ChildProcess.new(*cmd)
36
+ process.io.inherit!
37
+ if cwd
38
+ process.cwd = cwd
39
+ end
40
+ if env
41
+ env.each do |k, v|
42
+ process.environment[k.to_s] = v
43
+ end
44
+ end
45
+
46
+ output = ''
47
+ r, w = IO.pipe
48
+
49
+ begin
50
+ process.io.stdout = w
51
+ process.start
52
+ w.close
53
+
54
+ thread = Thread.new do
55
+ begin
56
+ loop do
57
+ output << r.readpartial(16384)
58
+ end
59
+ rescue EOFError
60
+ end
61
+ end
62
+
63
+ process.wait
64
+ thread.join
65
+ ensure
66
+ r.close
67
+ end
68
+
69
+ [process, output]
70
+ end
71
+
72
+ module_function def check_output(*args)
73
+ process, output = get_output(*args)
74
+ unless process.exit_code == 0
75
+ raise SpawnError,"Failed to execute: #{args}"
76
+ end
77
+ output
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,211 @@
1
+ # ClusterConfig requires ClientRegistry class provided by the host project.
2
+
3
+ require 'singleton'
4
+
5
+ module Mrss
6
+ class ClusterConfig
7
+ include Singleton
8
+ include RSpec::Core::Pending
9
+
10
+ def single_server?
11
+ determine_cluster_config
12
+ @single_server
13
+ end
14
+
15
+ def replica_set_name
16
+ determine_cluster_config
17
+ @replica_set_name
18
+ end
19
+
20
+ def server_version
21
+ determine_cluster_config
22
+ @server_version
23
+ end
24
+
25
+ def enterprise?
26
+ determine_cluster_config
27
+ @enterprise
28
+ end
29
+
30
+ def short_server_version
31
+ server_version.split('.')[0..1].join('.')
32
+ end
33
+
34
+ def fcv
35
+ determine_cluster_config
36
+ @fcv
37
+ end
38
+
39
+ # Per https://jira.mongodb.org/browse/SERVER-39052, working with FCV
40
+ # in sharded topologies is annoying. Also, FCV doesn't exist in servers
41
+ # less than 3.4. This method returns FCV on 3.4+ servers when in single
42
+ # or RS topologies, and otherwise returns the major.minor server version.
43
+ def fcv_ish
44
+ if server_version.nil?
45
+ raise "Deployment server version not known - check that connection to deployment succeeded"
46
+ end
47
+
48
+ if server_version >= '3.4' && topology != :sharded
49
+ fcv
50
+ else
51
+ if short_server_version == '4.1'
52
+ '4.2'
53
+ else
54
+ short_server_version
55
+ end
56
+ end
57
+ end
58
+
59
+ # @return [ Mongo::Address ] The address of the primary in the deployment.
60
+ def primary_address
61
+ determine_cluster_config
62
+ @primary_address
63
+ end
64
+
65
+ def primary_address_str
66
+ determine_cluster_config
67
+ @primary_address.seed
68
+ end
69
+
70
+ def primary_address_host
71
+ both = primary_address_str
72
+ both.split(':').first
73
+ end
74
+
75
+ def primary_address_port
76
+ both = primary_address_str
77
+ both.split(':')[1] || 27017
78
+ end
79
+
80
+ def primary_description
81
+ determine_cluster_config
82
+ @primary_description
83
+ end
84
+
85
+ # Try running a command on the admin database to see if the mongod was
86
+ # started with auth.
87
+ def auth_enabled?
88
+ if @auth_enabled.nil?
89
+ @auth_enabled = begin
90
+ basic_client.use(:admin).command(getCmdLineOpts: 1).first["argv"].include?("--auth")
91
+ rescue => e
92
+ e.message =~ /(not authorized)|(unauthorized)|(no users authenticated)|(requires authentication)/
93
+ end
94
+ end
95
+ @auth_enabled
96
+ end
97
+
98
+ def topology
99
+ determine_cluster_config
100
+ @topology
101
+ end
102
+
103
+ def storage_engine
104
+ @storage_engine ||= begin
105
+ # 2.6 does not have wired tiger
106
+ if short_server_version == '2.6'
107
+ :mmapv1
108
+ else
109
+ client = ClientRegistry.instance.global_client('root_authorized')
110
+ if topology == :sharded
111
+ shards = client.use(:admin).command(listShards: 1).first
112
+ if shards['shards'].empty?
113
+ raise 'Shards are empty'
114
+ end
115
+ shard = shards['shards'].first
116
+ address_str = shard['host'].sub(/^.*\//, '').sub(/,.*/, '')
117
+ client = ClusterTools.instance.direct_client(address_str,
118
+ SpecConfig.instance.test_options.merge(SpecConfig.instance.auth_options).merge(connect: :direct))
119
+ end
120
+ rv = client.use(:admin).command(serverStatus: 1).first
121
+ rv = rv['storageEngine']['name']
122
+ rv_map = {
123
+ 'wiredTiger' => :wired_tiger,
124
+ 'mmapv1' => :mmapv1,
125
+ }
126
+ rv_map[rv] || rv
127
+ end
128
+ end
129
+ end
130
+
131
+ # This method returns an alternate address for connecting to the configured
132
+ # deployment. For example, if the replica set is configured with nodes at
133
+ # of localhost:27017 and so on, this method will return 127.0.0.:27017.
134
+ #
135
+ # Note that the "alternate" refers to replica set configuration, not the
136
+ # addresses specified in test suite configuration. If the deployment topology
137
+ # is not a replica set, "alternate" refers to test suite configuration as
138
+ # this is the only configuration available.
139
+ def alternate_address
140
+ @alternate_address ||= begin
141
+ address = primary_address_host
142
+ str = case address
143
+ when '127.0.0.1'
144
+ 'localhost'
145
+ when /^(\d+\.){3}\d+$/
146
+ skip 'This test requires a hostname or 127.0.0.1 as address'
147
+ else
148
+ # We don't know if mongod is listening on ipv4 or ipv6, in principle.
149
+ # Our tests use ipv4, so hardcode that for now.
150
+ # To support both we need to try both addresses which will make this
151
+ # test more complicated.
152
+ #
153
+ # JRuby chokes on primary_address_port as the port (e.g. 27017).
154
+ # Since the port does not actually matter, use a common port like 80.
155
+ resolved_address = Addrinfo.getaddrinfo(address, 80, Socket::PF_INET).first.ip_address
156
+ if resolved_address.include?(':')
157
+ "[#{resolved_address}]"
158
+ else
159
+ resolved_address
160
+ end
161
+ end + ":#{primary_address_port}"
162
+ Mongo::Address.new(str)
163
+ end
164
+ end
165
+
166
+ private
167
+
168
+ def determine_cluster_config
169
+ return if @primary_address
170
+
171
+ # Run all commands to figure out the cluster configuration from the same
172
+ # client. This is somewhat wasteful when running a single test, but reduces
173
+ # test runtime for the suite overall because all commands are sent on the
174
+ # same connection rather than each command connecting to the cluster by
175
+ # itself.
176
+ client = ClientRegistry.instance.global_client('root_authorized')
177
+
178
+ primary = client.cluster.next_primary
179
+ @primary_address = primary.address
180
+ @primary_description = primary.description
181
+ @replica_set_name = client.cluster.topology.replica_set_name
182
+
183
+ @topology ||= begin
184
+ topology = client.cluster.topology.class.name.sub(/.*::/, '')
185
+ topology = topology.gsub(/([A-Z])/) { |match| '_' + match.downcase }.sub(/^_/, '')
186
+ if topology =~ /^replica_set/
187
+ topology = 'replica_set'
188
+ end
189
+ topology.to_sym
190
+ end
191
+
192
+ @single_server = client.cluster.servers_list.length == 1
193
+
194
+ build_info = client.database.command(buildInfo: 1).first
195
+
196
+ @server_version = build_info['version']
197
+ @enterprise = build_info['modules'] && build_info['modules'].include?('enterprise')
198
+
199
+ if @topology != :sharded && short_server_version >= '3.4'
200
+ rv = client.use(:admin).command(getParameter: 1, featureCompatibilityVersion: 1).first['featureCompatibilityVersion']
201
+ @fcv = rv['version'] || rv
202
+ end
203
+ end
204
+
205
+ def basic_client
206
+ # Do not cache the result here so that if the client gets closed,
207
+ # client registry reconnects it in subsequent tests
208
+ ClientRegistry.instance.global_client('basic')
209
+ end
210
+ end
211
+ end