mongoid 7.0.2 → 7.0.3

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/mongoid/association/referenced/eager.rb +4 -1
  4. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +3 -1
  5. data/lib/mongoid/association/referenced/has_many/proxy.rb +2 -2
  6. data/lib/mongoid/association/relatable.rb +90 -10
  7. data/lib/mongoid/clients/options.rb +6 -4
  8. data/lib/mongoid/copyable.rb +2 -2
  9. data/lib/mongoid/criteria/options.rb +2 -2
  10. data/lib/mongoid/criteria/queryable/selectable.rb +33 -6
  11. data/lib/mongoid/document.rb +11 -4
  12. data/lib/mongoid/extensions/big_decimal.rb +1 -1
  13. data/lib/mongoid/extensions/regexp.rb +1 -0
  14. data/lib/mongoid/matchable.rb +3 -1
  15. data/lib/mongoid/matchable/eq.rb +22 -0
  16. data/lib/mongoid/matchable/ne.rb +1 -1
  17. data/lib/mongoid/persistence_context.rb +20 -5
  18. data/lib/mongoid/query_cache.rb +8 -4
  19. data/lib/mongoid/railtie.rb +17 -0
  20. data/lib/mongoid/railties/controller_runtime.rb +86 -0
  21. data/lib/mongoid/scopable.rb +3 -3
  22. data/lib/mongoid/threaded.rb +36 -0
  23. data/lib/mongoid/version.rb +1 -1
  24. data/spec/app/models/minim.rb +7 -0
  25. data/spec/app/models/store_as_dup_test3.rb +7 -0
  26. data/spec/app/models/store_as_dup_test4.rb +7 -0
  27. data/spec/config/mongoid.yml +12 -3
  28. data/spec/integration/associations/belongs_to_spec.rb +13 -0
  29. data/spec/lite_spec_helper.rb +56 -0
  30. data/spec/mongoid/association/referenced/belongs_to/eager_spec.rb +24 -5
  31. data/spec/mongoid/association/referenced/belongs_to_spec.rb +46 -3
  32. data/spec/mongoid/association/referenced/has_and_belongs_to_many/eager_spec.rb +21 -2
  33. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_persistence_spec.rb +56 -0
  34. data/spec/mongoid/association/referenced/has_and_belongs_to_many_models.rb +26 -0
  35. data/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb +3 -3
  36. data/spec/mongoid/association/referenced/has_many/proxy_query_spec.rb +23 -0
  37. data/spec/mongoid/association/referenced/has_many_models.rb +37 -0
  38. data/spec/mongoid/association/referenced/has_many_spec.rb +3 -3
  39. data/spec/mongoid/association/referenced/has_one_models.rb +48 -0
  40. data/spec/mongoid/association/referenced/has_one_spec.rb +51 -4
  41. data/spec/mongoid/clients/factory_spec.rb +24 -18
  42. data/spec/mongoid/clients/options_spec.rb +40 -37
  43. data/spec/mongoid/clients_spec.rb +68 -8
  44. data/spec/mongoid/config_spec.rb +3 -1
  45. data/spec/mongoid/contextual/mongo_spec.rb +5 -2
  46. data/spec/mongoid/copyable_spec.rb +40 -6
  47. data/spec/mongoid/copyable_spec_models.rb +17 -0
  48. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +3 -3
  49. data/spec/mongoid/criteria/queryable/selectable_spec.rb +42 -3
  50. data/spec/mongoid/criteria/queryable/selector_spec.rb +2 -2
  51. data/spec/mongoid/criteria/scopable_spec.rb +81 -0
  52. data/spec/mongoid/criteria_spec.rb +18 -3
  53. data/spec/mongoid/document_spec.rb +81 -2
  54. data/spec/mongoid/extensions/big_decimal_spec.rb +9 -9
  55. data/spec/mongoid/extensions/regexp_spec.rb +23 -0
  56. data/spec/mongoid/fields_spec.rb +1 -1
  57. data/spec/mongoid/findable_spec.rb +1 -1
  58. data/spec/mongoid/matchable/eq_spec.rb +48 -0
  59. data/spec/mongoid/persistable/incrementable_spec.rb +6 -6
  60. data/spec/mongoid/persistence_context_spec.rb +1 -1
  61. data/spec/mongoid/query_cache_spec.rb +59 -6
  62. data/spec/mongoid/scopable_spec.rb +13 -0
  63. data/spec/mongoid/threaded_spec.rb +68 -0
  64. data/spec/rails/controller_extension/controller_runtime_spec.rb +110 -0
  65. data/spec/spec_helper.rb +35 -25
  66. data/spec/support/constraints.rb +101 -0
  67. data/spec/support/macros.rb +20 -0
  68. data/spec/support/spec_config.rb +39 -0
  69. metadata +471 -460
  70. checksums.yaml.gz.sig +0 -0
  71. data.tar.gz.sig +0 -2
  72. metadata.gz.sig +0 -0
@@ -10,7 +10,7 @@ describe Mongoid::Findable do
10
10
  end
11
11
 
12
12
  it "returns the distinct values for the field" do
13
- expect(Band.distinct(:name)).to eq([ "Tool", "Photek" ])
13
+ expect(Band.distinct(:name).sort).to eq([ "Photek", "Tool" ])
14
14
  end
15
15
  end
16
16
 
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Mongoid::Matchable::Eq do
6
+
7
+ let(:matcher) do
8
+ described_class.new("first")
9
+ end
10
+
11
+ describe "#_matches?" do
12
+
13
+ context "when the values are equal" do
14
+
15
+ it "returns true" do
16
+ expect(matcher._matches?("$eq" => "first")).to be true
17
+ end
18
+ end
19
+
20
+ context "when the values are not equal" do
21
+
22
+ it "returns false" do
23
+ expect(matcher._matches?("$eq" => "second")).to be false
24
+ end
25
+ end
26
+
27
+ context "when the value is an array" do
28
+
29
+ let(:array_matcher) do
30
+ described_class.new([ "first" ])
31
+ end
32
+
33
+ context "when the value is in the array" do
34
+
35
+ it "returns true" do
36
+ expect(array_matcher._matches?("$eq" => "first")).to be true
37
+ end
38
+ end
39
+
40
+ context "when the value is not in the array" do
41
+
42
+ it "returns false" do
43
+ expect(array_matcher._matches?("$eq" => "second")).to be false
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -66,15 +66,15 @@ describe Mongoid::Persistable::Incrementable do
66
66
  context "when providing big decimal values" do
67
67
 
68
68
  let(:five) do
69
- BigDecimal.new("5.0")
69
+ BigDecimal("5.0")
70
70
  end
71
71
 
72
72
  let(:neg_ten) do
73
- BigDecimal.new("-10.0")
73
+ BigDecimal("-10.0")
74
74
  end
75
75
 
76
76
  let(:thirty) do
77
- BigDecimal.new("30.0")
77
+ BigDecimal("30.0")
78
78
  end
79
79
 
80
80
  let!(:inc) do
@@ -151,15 +151,15 @@ describe Mongoid::Persistable::Incrementable do
151
151
  context "when providing big decimal values" do
152
152
 
153
153
  let(:five) do
154
- BigDecimal.new("5.0")
154
+ BigDecimal("5.0")
155
155
  end
156
156
 
157
157
  let(:neg_ten) do
158
- BigDecimal.new("-10.0")
158
+ BigDecimal("-10.0")
159
159
  end
160
160
 
161
161
  let(:thirty) do
162
- BigDecimal.new("30.0")
162
+ BigDecimal("30.0")
163
163
  end
164
164
 
165
165
  let!(:inc) do
@@ -536,7 +536,7 @@ describe Mongoid::PersistenceContext do
536
536
  end
537
537
 
538
538
  before do
539
- Mongoid.clients[:alternative] = { database: :mongoid_test, hosts: [ "#{HOST}:#{PORT}" ] }
539
+ Mongoid.clients[:alternative] = { database: :mongoid_test, hosts: SpecConfig.instance.addresses }
540
540
  end
541
541
 
542
542
  after do
@@ -123,6 +123,36 @@ describe Mongoid::QueryCache do
123
123
  end
124
124
  end
125
125
 
126
+ context 'querying all documents after a single document' do
127
+ before do
128
+ 3.times do
129
+ Person.create
130
+ end
131
+ end
132
+
133
+ it 'returns all documents' do
134
+ expect(Person.all.to_a.count).to eq(3)
135
+ Person.first
136
+ expect(Person.all.to_a.count).to eq(3)
137
+ end
138
+
139
+ context 'with conditions specified' do
140
+ it 'returns all documents' do
141
+ expect(Person.gt(age: 0).to_a.count).to eq(3)
142
+ Person.gt(age: 0).first
143
+ expect(Person.gt(age: 0).to_a.count).to eq(3)
144
+ end
145
+ end
146
+
147
+ context 'with order specified' do
148
+ it 'returns all documents' do
149
+ expect(Person.order_by(name: 1).to_a.count).to eq(3)
150
+ Person.order_by(name: 1).first
151
+ expect(Person.order_by(name: 1).to_a.count).to eq(3)
152
+ end
153
+ end
154
+ end
155
+
126
156
  context "when querying in the same collection" do
127
157
 
128
158
  before do
@@ -411,18 +441,18 @@ describe Mongoid::QueryCache do
411
441
  end
412
442
  end
413
443
 
414
- context "when querying a very large collection" do
444
+ context "when querying collection smaller than the batch size" do
415
445
 
416
446
  before do
417
- 123.times { Band.create! }
447
+ 99.times { Band.create! }
418
448
  end
419
449
 
420
450
  it "returns the right number of records" do
421
- expect(Band.all.to_a.length).to eq(123)
451
+ expect(Band.all.to_a.length).to eq(99)
422
452
  end
423
453
 
424
454
  it "#pluck returns the same count of objects" do
425
- expect(Band.pluck(:name).length).to eq(123)
455
+ expect(Band.pluck(:name).length).to eq(99)
426
456
  end
427
457
 
428
458
  context "when loading all the documents" do
@@ -433,12 +463,12 @@ describe Mongoid::QueryCache do
433
463
 
434
464
  it "caches the complete result of the query" do
435
465
  expect_no_queries do
436
- expect(Band.all.to_a.length).to eq(123)
466
+ expect(Band.all.to_a.length).to eq(99)
437
467
  end
438
468
  end
439
469
 
440
470
  it "returns the same count of objects when using #pluck" do
441
- expect(Band.pluck(:name).length).to eq(123)
471
+ expect(Band.pluck(:name).length).to eq(99)
442
472
  end
443
473
  end
444
474
  end
@@ -450,6 +480,29 @@ describe Mongoid::QueryCache do
450
480
  Band.collection.indexes.create_one(name: 1)
451
481
  end
452
482
  end
483
+
484
+ context 'when the initial query does not exhaust the results' do
485
+ before do
486
+ Mongoid::QueryCache.enabled = true
487
+ 10.times { Band.create! }
488
+
489
+ Band.batch_size(4).all.any?
490
+ end
491
+
492
+ it 'does not cache the result' do
493
+ expect(Band.all.map(&:id).size).to eq(10)
494
+ end
495
+
496
+ context 'when a batch size smaller than the result set is specified' do
497
+ let(:batch_size) do
498
+ 4
499
+ end
500
+
501
+ it 'does not cache the result' do
502
+ expect(Band.batch_size(batch_size).all.map(&:id).size).to eq(10)
503
+ end
504
+ end
505
+ end
453
506
  end
454
507
 
455
508
  describe Mongoid::QueryCache::Middleware do
@@ -1130,6 +1130,19 @@ describe Mongoid::Scopable do
1130
1130
  it "sets the threading options" do
1131
1131
  Band.without_default_scope do
1132
1132
  expect(Mongoid::Threaded).to be_executing(:without_default_scope)
1133
+ expect(Mongoid::Threaded.without_default_scope?(Band)).to be(true)
1134
+ end
1135
+ end
1136
+
1137
+ it "suppresses default scope on the given model within the given block" do
1138
+ Appointment.without_default_scope do
1139
+ expect(Appointment.all.selector).to be_empty
1140
+ end
1141
+ end
1142
+
1143
+ it "does not affect other models' default scopes within the given block" do
1144
+ Appointment.without_default_scope do
1145
+ expect(Audio.all.selector).not_to be_empty
1133
1146
  end
1134
1147
  end
1135
1148
  end
@@ -233,4 +233,72 @@ describe Mongoid::Threaded do
233
233
  end
234
234
  end
235
235
  end
236
+
237
+ describe "#begin_without_default_scope" do
238
+
239
+ let(:klass) do
240
+ Appointment
241
+ end
242
+
243
+ after do
244
+ described_class.exit_without_default_scope(klass)
245
+ end
246
+
247
+ it "adds the given class to the without_default_scope stack" do
248
+ described_class.begin_without_default_scope(klass)
249
+
250
+ expect(described_class.stack(:without_default_scope)).to include(klass)
251
+ end
252
+ end
253
+
254
+ describe "#exit_without_default_scope" do
255
+
256
+ let(:klass) do
257
+ Appointment
258
+ end
259
+
260
+ before do
261
+ described_class.begin_without_default_scope(klass)
262
+ end
263
+
264
+ it "removes the given class from the without_default_scope stack" do
265
+ described_class.exit_without_default_scope(klass)
266
+
267
+ expect(described_class.stack(:without_default_scope)).not_to include(klass)
268
+ end
269
+ end
270
+
271
+ describe "#without_default_scope?" do
272
+
273
+ let(:klass) do
274
+ Appointment
275
+ end
276
+
277
+ context "when klass has begun without_default_scope" do
278
+
279
+ before do
280
+ described_class.begin_without_default_scope(klass)
281
+ end
282
+
283
+ after do
284
+ described_class.exit_without_default_scope(klass)
285
+ end
286
+
287
+ it "returns true" do
288
+ expect(described_class.without_default_scope?(klass)).to be(true)
289
+ end
290
+ end
291
+
292
+ context "when klass has exited without_default_scope" do
293
+
294
+ before do
295
+ described_class.begin_without_default_scope(klass)
296
+ described_class.exit_without_default_scope(klass)
297
+ end
298
+
299
+ it "returns false" do
300
+ expect(described_class.without_default_scope?(klass)).to be(false)
301
+ end
302
+ end
303
+ end
236
304
  end
@@ -0,0 +1,110 @@
1
+ require "spec_helper"
2
+ require "mongoid/railties/controller_runtime"
3
+
4
+ describe "Mongoid::Railties::ControllerRuntime" do
5
+ controller_runtime = Mongoid::Railties::ControllerRuntime
6
+ collector = controller_runtime::Collector
7
+
8
+ def set_metric value
9
+ Thread.current["Mongoid.controller_runtime"] = value
10
+ end
11
+
12
+ def clear_metric!
13
+ set_metric 0
14
+ end
15
+
16
+ describe "Collector" do
17
+
18
+ it "stores the metric in thread-safe manner" do
19
+ clear_metric!
20
+ expect(collector.runtime).to eq(0)
21
+ set_metric 42
22
+ expect(collector.runtime).to eq(42)
23
+ end
24
+
25
+ it "sets metric on both succeeded and failed" do
26
+ instance = collector.new
27
+ event_payload = OpenStruct.new duration: 42
28
+
29
+ clear_metric!
30
+ instance.succeeded event_payload
31
+ expect(collector.runtime).to eq(42)
32
+
33
+ clear_metric!
34
+ instance.failed event_payload
35
+ expect(collector.runtime).to eq(42)
36
+ end
37
+
38
+ it "resets the metric and returns the value" do
39
+ clear_metric!
40
+ expect(collector.reset_runtime).to eq(0)
41
+ set_metric 42
42
+ expect(collector.reset_runtime).to eq(42)
43
+ expect(collector.runtime).to eq(0)
44
+ end
45
+
46
+ end
47
+
48
+ reference_controller_class = Class.new do
49
+ def process_action *_
50
+ @process_action = true
51
+ end
52
+
53
+ def cleanup_view_runtime *_
54
+ @cleanup_view_runtime.call
55
+ end
56
+
57
+ def append_info_to_payload *_
58
+ @append_info_to_payload = true
59
+ end
60
+
61
+ def self.log_process_action *_
62
+ @log_process_action.call
63
+ end
64
+ end
65
+
66
+ controller_class = Class.new reference_controller_class do
67
+ include controller_runtime::ControllerExtension
68
+ end
69
+
70
+ let(:controller){ controller_class.new }
71
+
72
+ it "resets the metric before each action" do
73
+ set_metric 42
74
+ controller.send(:process_action, 'foo')
75
+ expect(collector.runtime).to be(0)
76
+ expect(controller.instance_variable_get "@process_action").to be(true)
77
+ end
78
+
79
+ it "strips the metric of other sources of the runtime" do
80
+ set_metric 1
81
+ controller.instance_variable_set "@cleanup_view_runtime", ->{
82
+ controller.instance_variable_set "@cleanup_view_runtime", true
83
+ set_metric 13
84
+ 42
85
+ }
86
+ returned = controller.send :cleanup_view_runtime
87
+ expect(controller.instance_variable_get "@cleanup_view_runtime").to be(true)
88
+ expect(controller.mongoid_runtime).to eq(14)
89
+ expect(returned).to be(29)
90
+ end
91
+
92
+ it "appends the metric to payload" do
93
+ payload = {}
94
+ set_metric 42
95
+ controller.send :append_info_to_payload, payload
96
+ expect(controller.instance_variable_get "@append_info_to_payload").to be(true)
97
+ expect(payload[:mongoid_runtime]).to eq(42)
98
+ end
99
+
100
+ it "adds metric to log message" do
101
+ controller_class.instance_variable_set "@log_process_action", ->{
102
+ controller_class.instance_variable_set "@log_process_action", true
103
+ []
104
+ }
105
+ messages = controller_class.log_process_action mongoid_runtime: 42.101
106
+ expect(controller_class.instance_variable_get "@log_process_action").to be(true)
107
+ expect(messages).to eq(["MongoDB: 42.1ms"])
108
+ end
109
+
110
+ end
@@ -1,24 +1,20 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__))
2
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
1
+ # frozen_string_literal: true
2
+
3
+ require 'lite_spec_helper'
3
4
 
4
5
  MODELS = File.join(File.dirname(__FILE__), "app/models")
5
6
  $LOAD_PATH.unshift(MODELS)
6
7
 
7
8
  require "action_controller"
8
- require "mongoid"
9
- require "rspec"
10
-
11
- # These environment variables can be set if wanting to test against a database
12
- # that is not on the local machine.
13
- ENV["MONGOID_SPEC_HOST"] ||= "127.0.0.1"
14
- ENV["MONGOID_SPEC_PORT"] ||= "27017"
15
-
16
- # These are used when creating any connection in the test suite.
17
- HOST = ENV["MONGOID_SPEC_HOST"]
18
- PORT = ENV["MONGOID_SPEC_PORT"].to_i
9
+ require 'rspec/retry'
19
10
 
20
- Mongo::Logger.logger.level = Logger::INFO
21
- # Mongoid.logger.level = Logger::DEBUG
11
+ if SpecConfig.instance.client_debug?
12
+ Mongoid.logger.level = Logger::DEBUG
13
+ Mongo::Logger.logger.level = Logger::DEBUG
14
+ else
15
+ Mongoid.logger.level = Logger::INFO
16
+ Mongo::Logger.logger.level = Logger::INFO
17
+ end
22
18
 
23
19
  # When testing locally we use the database named mongoid_test. However when
24
20
  # tests are running in parallel on Travis we need to use different database
@@ -34,11 +30,13 @@ end
34
30
 
35
31
  require 'support/authorization'
36
32
  require 'support/expectations'
33
+ require 'support/macros'
34
+ require 'support/constraints'
37
35
 
38
36
  # Give MongoDB time to start up on the travis ci environment.
39
37
  if (ENV['CI'] == 'travis' || ENV['CI'] == 'evergreen')
40
38
  starting = true
41
- client = Mongo::Client.new(['127.0.0.1:27017'])
39
+ client = Mongo::Client.new(SpecConfig.instance.addresses)
42
40
  while starting
43
41
  begin
44
42
  client.command(Mongo::Server::Monitor::Connection::ISMASTER)
@@ -54,10 +52,10 @@ CONFIG = {
54
52
  clients: {
55
53
  default: {
56
54
  database: database_id,
57
- hosts: [ "#{HOST}:#{PORT}" ],
55
+ hosts: SpecConfig.instance.addresses,
58
56
  options: {
59
- server_selection_timeout: 0.5,
60
- wait_queue_timeout: 5,
57
+ server_selection_timeout: 3.42,
58
+ wait_queue_timeout: 1,
61
59
  max_pool_size: 5,
62
60
  heartbeat_frequency: 180,
63
61
  user: MONGOID_ROOT_USER.name,
@@ -67,7 +65,12 @@ CONFIG = {
67
65
  }
68
66
  },
69
67
  options: {
70
- belongs_to_required_by_default: false
68
+ belongs_to_required_by_default: false,
69
+ log_level: if SpecConfig.instance.client_debug?
70
+ :debug
71
+ else
72
+ :info
73
+ end,
71
74
  }
72
75
  }
73
76
 
@@ -89,6 +92,12 @@ def array_filters_supported?
89
92
  end
90
93
  alias :sessions_supported? :array_filters_supported?
91
94
 
95
+ def testing_geo_near?
96
+ $geo_near_enabled ||= (Mongoid::Clients.default
97
+ .command(serverStatus: 1)
98
+ .first['version'] < '4.1')
99
+ end
100
+
92
101
  def transactions_supported?
93
102
  Mongoid::Clients.default.cluster.next_primary.features.transactions_enabled?
94
103
  end
@@ -138,9 +147,11 @@ I18n.config.enforce_available_locales = false
138
147
  RSpec.configure do |config|
139
148
  config.raise_errors_for_deprecations!
140
149
  config.include(Mongoid::Expectations)
150
+ config.extend(Constraints)
151
+ config.extend(Mongoid::Macros)
141
152
 
142
153
  config.before(:suite) do
143
- client = Mongo::Client.new(["#{HOST}:#{PORT}"])
154
+ client = Mongo::Client.new(SpecConfig.instance.addresses, server_selection_timeout: 3.03)
144
155
  begin
145
156
  # Create the root user administrator as the first user to be added to the
146
157
  # database. This user will need to be authenticated in order to add any
@@ -153,14 +164,13 @@ RSpec.configure do |config|
153
164
 
154
165
  # Drop all collections and clear the identity map before each spec.
155
166
  config.before(:each) do
167
+ unless Mongoid.default_client.cluster.connected?
168
+ Mongoid.default_client.reconnect
169
+ end
156
170
  Mongoid.default_client.collections.each do |coll|
157
171
  coll.delete_many
158
172
  end
159
173
  end
160
-
161
- config.after(:suite) do
162
- Mongoid.purge!
163
- end
164
174
  end
165
175
 
166
176
  # A subscriber to be used with the Ruby driver for testing.