mongoid 5.1.5 → 5.2.0

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 (57) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/config/locales/en.yml +15 -0
  5. data/lib/mongoid.rb +5 -0
  6. data/lib/mongoid/attributes/dynamic.rb +3 -3
  7. data/lib/mongoid/clients/factory.rb +2 -0
  8. data/lib/mongoid/config.rb +1 -0
  9. data/lib/mongoid/config/options.rb +1 -1
  10. data/lib/mongoid/contextual/aggregable/mongo.rb +0 -1
  11. data/lib/mongoid/contextual/map_reduce.rb +20 -97
  12. data/lib/mongoid/contextual/memory.rb +1 -0
  13. data/lib/mongoid/contextual/mongo.rb +20 -15
  14. data/lib/mongoid/criteria.rb +2 -0
  15. data/lib/mongoid/document.rb +1 -0
  16. data/lib/mongoid/errors.rb +1 -0
  17. data/lib/mongoid/errors/in_memory_collation_not_supported.rb +20 -0
  18. data/lib/mongoid/errors/mongoid_error.rb +1 -1
  19. data/lib/mongoid/extensions.rb +1 -0
  20. data/lib/mongoid/extensions/decimal128.rb +39 -0
  21. data/lib/mongoid/fields/localized.rb +8 -3
  22. data/lib/mongoid/indexable/validators/options.rb +2 -1
  23. data/lib/mongoid/persistable/deletable.rb +3 -7
  24. data/lib/mongoid/persistable/settable.rb +3 -1
  25. data/lib/mongoid/query_cache.rb +24 -2
  26. data/lib/mongoid/relations/accessors.rb +1 -1
  27. data/lib/mongoid/relations/builders.rb +2 -2
  28. data/lib/mongoid/relations/eager.rb +2 -2
  29. data/lib/mongoid/relations/reflections.rb +5 -3
  30. data/lib/mongoid/relations/touchable.rb +1 -1
  31. data/lib/mongoid/scopable.rb +1 -1
  32. data/lib/mongoid/version.rb +1 -1
  33. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +6 -2
  34. data/spec/app/models/band.rb +1 -0
  35. data/spec/config/mongoid.yml +5 -0
  36. data/spec/mongoid/clients/factory_spec.rb +8 -0
  37. data/spec/mongoid/clients_spec.rb +78 -1
  38. data/spec/mongoid/config_spec.rb +31 -0
  39. data/spec/mongoid/contextual/atomic_spec.rb +342 -76
  40. data/spec/mongoid/contextual/map_reduce_spec.rb +111 -119
  41. data/spec/mongoid/contextual/memory_spec.rb +316 -56
  42. data/spec/mongoid/contextual/mongo_spec.rb +391 -11
  43. data/spec/mongoid/criteria_spec.rb +21 -2
  44. data/spec/mongoid/extensions/decimal128_spec.rb +44 -0
  45. data/spec/mongoid/extensions/time_spec.rb +2 -2
  46. data/spec/mongoid/fields/localized_spec.rb +91 -0
  47. data/spec/mongoid/indexable_spec.rb +44 -0
  48. data/spec/mongoid/persistable/settable_spec.rb +44 -0
  49. data/spec/mongoid/query_cache_spec.rb +86 -0
  50. data/spec/mongoid/relations/cyclic_spec.rb +22 -0
  51. data/spec/mongoid/relations/referenced/many_spec.rb +11 -0
  52. data/spec/mongoid/relations/referenced/many_to_many_spec.rb +11 -0
  53. data/spec/mongoid/relations/reflections_spec.rb +9 -9
  54. data/spec/mongoid/scopable_spec.rb +12 -0
  55. data/spec/spec_helper.rb +9 -0
  56. metadata +26 -16
  57. metadata.gz.sig +0 -0
@@ -30,6 +30,7 @@ require "mongoid/extensions/big_decimal"
30
30
  require "mongoid/extensions/boolean"
31
31
  require "mongoid/extensions/date"
32
32
  require "mongoid/extensions/date_time"
33
+ require "mongoid/extensions/decimal128"
33
34
  require "mongoid/extensions/false_class"
34
35
  require "mongoid/extensions/float"
35
36
  require "mongoid/extensions/hash"
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ module Extensions
4
+ module Decimal128
5
+
6
+ # Evolve the decimal128.
7
+ #
8
+ # @example Evolve the decimal128.
9
+ # decimal128.__evolve_decimal128__
10
+ #
11
+ # @return [ BSON::Decimal128 ] self.
12
+ #
13
+ # @since 5.2.0
14
+ def __evolve_decimal128__
15
+ self
16
+ end
17
+
18
+ module ClassMethods
19
+
20
+ # Evolve the object into a mongo-friendly value to query with.
21
+ #
22
+ # @example Evolve the object.
23
+ # Decimal128.evolve(dec)
24
+ #
25
+ # @param [ Object ] object The object to evolve.
26
+ #
27
+ # @return [ BSON::Decimal128 ] The decimal128.
28
+ #
29
+ # @since 5.2.0
30
+ def evolve(object)
31
+ object.__evolve_decimal128__
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ BSON::Decimal128.__send__(:include, Mongoid::Extensions::Decimal128)
39
+ BSON::Decimal128.extend(Mongoid::Extensions::Decimal128::ClassMethods)
@@ -77,9 +77,14 @@ module Mongoid
77
77
  # @since 3.0.0
78
78
  def lookup(object)
79
79
  locale = ::I18n.locale
80
- if value = object[locale.to_s]
81
- value
82
- elsif fallbacks? && ::I18n.respond_to?(:fallbacks)
80
+
81
+ value = if object.key?(locale.to_s)
82
+ object[locale.to_s]
83
+ elsif object.key?(locale)
84
+ object[locale]
85
+ end
86
+ return value unless value.nil?
87
+ if fallbacks? && ::I18n.respond_to?(:fallbacks)
83
88
  object[::I18n.fallbacks[locale].map(&:to_s).find{ |loc| object.has_key?(loc) }]
84
89
  end
85
90
  end
@@ -27,7 +27,8 @@ module Mongoid
27
27
  :sphere_version,
28
28
  :text_version,
29
29
  :version,
30
- :partial_filter_expression
30
+ :partial_filter_expression,
31
+ :collation
31
32
  ]
32
33
 
33
34
  VALID_TYPES = [
@@ -135,13 +135,9 @@ module Mongoid
135
135
  # @return [ Integer ] The number of documents deleted.
136
136
  #
137
137
  # @since 1.0.0
138
- def delete_all(conditions = nil)
139
- selector = conditions || {}
140
- selector.merge!(_type: name) if hereditary?
141
- coll = collection
142
- deleted = coll.find(selector).count
143
- coll.find(selector).delete_many
144
- deleted
138
+ def delete_all(conditions = {})
139
+ selector = hereditary? ? conditions.merge(_type: name) : conditions
140
+ collection.find(selector).delete_many.deleted_count
145
141
  end
146
142
  end
147
143
  end
@@ -27,7 +27,9 @@ module Mongoid
27
27
  field = field_and_value_hash.keys.first.to_s
28
28
 
29
29
  if fields[field] && fields[field].type == Hash && attributes.key?(field)
30
- process_attribute(field.to_s, attributes[field].merge(field_and_value_hash[field]))
30
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
31
+ value = attributes[field].merge(field_and_value_hash[field], &merger)
32
+ process_attribute(field.to_s, value)
31
33
  else
32
34
  process_attribute(field.to_s, field_and_value_hash[field])
33
35
  end
@@ -70,6 +70,20 @@ module Mongoid
70
70
  ensure
71
71
  QueryCache.enabled = enabled
72
72
  end
73
+
74
+ # Execute the block with the query cache disabled.
75
+ #
76
+ # @example Execute without the cache.
77
+ # QueryCache.uncached { collection.find }
78
+ #
79
+ # @return [ Object ] The result of the block.
80
+ def uncached
81
+ enabled = QueryCache.enabled?
82
+ QueryCache.enabled = false
83
+ yield
84
+ ensure
85
+ QueryCache.enabled = enabled
86
+ end
73
87
  end
74
88
 
75
89
  # The middleware to be added to a rack application in order to activate the
@@ -222,7 +236,7 @@ module Mongoid
222
236
 
223
237
  def cached_cursor
224
238
  if limit
225
- key = [ collection.namespace, selector, nil, skip, projection ]
239
+ key = [ collection.namespace, selector, nil, skip, sort, projection, collation ]
226
240
  cursor = QueryCache.cache_table[key]
227
241
  if cursor
228
242
  limited_docs = cursor.to_a[0...limit.abs]
@@ -233,7 +247,7 @@ module Mongoid
233
247
  end
234
248
 
235
249
  def cache_key
236
- [ collection.namespace, selector, limit, skip, projection ]
250
+ [ collection.namespace, selector, limit, skip, sort, projection, collation ]
237
251
  end
238
252
 
239
253
  def system_collection?
@@ -252,8 +266,16 @@ module Mongoid
252
266
  alias_query_cache_clear :insert_one, :insert_many
253
267
  end
254
268
  end
269
+
270
+ # Bypass the query cache when reloading a document.
271
+ module Document
272
+ def reload
273
+ QueryCache.uncached { super }
274
+ end
275
+ end
255
276
  end
256
277
  end
257
278
 
258
279
  Mongo::Collection.__send__(:include, Mongoid::QueryCache::Collection)
259
280
  Mongo::Collection::View.__send__(:include, Mongoid::QueryCache::View)
281
+ Mongoid::Document.__send__(:include, Mongoid::QueryCache::Document)
@@ -160,7 +160,7 @@ module Mongoid
160
160
  #
161
161
  # @since 3.0.0
162
162
  def existence_check(name)
163
- module_eval <<-END
163
+ module_eval <<-END, __FILE__, __LINE__ + 1
164
164
  def #{name}?
165
165
  without_autobuild { !__send__(:#{name}).blank? }
166
166
  end
@@ -64,7 +64,7 @@ module Mongoid
64
64
  # @since 2.0.0.rc.1
65
65
  def builder(name, metadata)
66
66
  re_define_method("build_#{name}") do |*args|
67
- attributes, options = parse_args(*args)
67
+ attributes, _options = parse_args(*args)
68
68
  document = Factory.build(metadata.klass, attributes)
69
69
  _building do
70
70
  child = send("#{name}=", document)
@@ -89,7 +89,7 @@ module Mongoid
89
89
  # @since 2.0.0.rc.1
90
90
  def creator(name, metadata)
91
91
  re_define_method("create_#{name}") do |*args|
92
- attributes, options = parse_args(*args)
92
+ attributes, _options = parse_args(*args)
93
93
  document = Factory.build(metadata.klass, attributes)
94
94
  doc = _assigning do
95
95
  send("#{name}=", document)
@@ -14,9 +14,9 @@ module Mongoid
14
14
  end
15
15
 
16
16
  def eager_load(docs)
17
- docs.tap do |docs|
17
+ docs.tap do |d|
18
18
  if eager_loadable?
19
- preload(criteria.inclusions, docs)
19
+ preload(criteria.inclusions, d)
20
20
  end
21
21
  end
22
22
  end
@@ -24,7 +24,7 @@ module Mongoid
24
24
  # @example Find multiple relation metadata by macro.
25
25
  # person.reflect_on_all_associations(:embeds_many)
26
26
  #
27
- # @param [ Array<String, Symbol> ] *macros The relation macros.
27
+ # @param [ Array<Symbol> ] *macros The relation macros.
28
28
  #
29
29
  # @return [ Array<Metadata> ] The matching relation metadata.
30
30
  def reflect_on_all_associations(*macros)
@@ -50,11 +50,13 @@ module Mongoid
50
50
  # @example Find multiple relation metadata by macro.
51
51
  # Person.reflect_on_all_associations(:embeds_many)
52
52
  #
53
- # @param [ Array<String, Symbol> ] *macros The relation macros.
53
+ # @param [ Array<Symbol> ] *macros The relation macros.
54
54
  #
55
55
  # @return [ Array<Metadata> ] The matching relation metadata.
56
56
  def reflect_on_all_associations(*macros)
57
- relations.values.select { |meta| macros.include?(meta.macro) }
57
+ association_reflections = relations.values
58
+ association_reflections.select! { |reflection| macros.include?(reflection.macro) } unless macros.empty?
59
+ association_reflections
58
60
  end
59
61
  end
60
62
  end
@@ -78,7 +78,7 @@ module Mongoid
78
78
  # @return [ Symbol ] The method name.
79
79
  def define_relation_touch_method(name)
80
80
  method_name = "touch_#{name}_after_create_or_destroy"
81
- class_eval <<-TOUCH
81
+ class_eval <<-TOUCH, __FILE__, __LINE__ + 1
82
82
  def #{method_name}
83
83
  without_autobuild do
84
84
  relation = __send__(:#{name})
@@ -117,7 +117,7 @@ module Mongoid
117
117
  # @since 3.0.0
118
118
  def queryable
119
119
  crit = Threaded.current_scope(self) || Criteria.new(self)
120
- crit.embedded = true if crit.klass.embedded?
120
+ crit.embedded = true if (crit.klass.embedded? && !crit.klass.cyclic?)
121
121
  crit
122
122
  end
123
123
 
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid
3
- VERSION = "5.1.5"
3
+ VERSION = "5.2.0"
4
4
  end
@@ -50,8 +50,8 @@ development:
50
50
  # via ismaster commands. (default: 10)
51
51
  # heartbeat_frequency: 10
52
52
 
53
- # The time in seconds for selecting servers for a near read preference. (default: 5)
54
- # local_threshold: 5
53
+ # The time in seconds for selecting servers for a near read preference. (default: 0.015)
54
+ # local_threshold: 0.015
55
55
 
56
56
  # The timeout in seconds for selecting a server for an operation. (default: 30)
57
57
  # server_selection_timeout: 30
@@ -130,6 +130,10 @@ development:
130
130
  # environment. The Mongoid logger will be set to the Rails logger
131
131
  # otherwise.(default: :info)
132
132
  # log_level: :info
133
+
134
+ # Application name that is printed to the mongodb logs upon establishing a
135
+ # connection in server versions >= 3.4. Note that the name cannot exceed 128 bytes.
136
+ # app_name: MyApplicationName
133
137
  test:
134
138
  clients:
135
139
  default:
@@ -13,6 +13,7 @@ class Band
13
13
  field :upserted, type: Mongoid::Boolean, default: false
14
14
  field :created, type: DateTime
15
15
  field :sales, type: BigDecimal
16
+ field :decimal, type: BSON::Decimal128
16
17
  field :y, as: :years, type: Integer
17
18
  field :founded, type: Date
18
19
  field :deleted, type: Boolean
@@ -13,6 +13,10 @@ test:
13
13
  tag_sets:
14
14
  - use: web
15
15
  max_pool_size: 1
16
+ reports:
17
+ database: reports
18
+ hosts:
19
+ - <%=ENV["MONGOID_SPEC_HOST"]%>:<%=ENV["MONGOID_SPEC_PORT"]%>
16
20
  options:
17
21
  include_root_in_json: false
18
22
  include_type_for_serialization: false
@@ -22,3 +26,4 @@ test:
22
26
  use_activesupport_time_zone: true
23
27
  use_utc: false
24
28
  log_level: :warn
29
+ app_name: 'testing'
@@ -36,6 +36,10 @@ describe Mongoid::Clients::Factory do
36
36
  it "sets the cluster's seeds" do
37
37
  expect(cluster.addresses.first.to_s).to eq("127.0.0.1:27017")
38
38
  end
39
+
40
+ it "sets the platform to Mongoid's platform constant" do
41
+ expect(client.options[:platform]).to eq(Mongoid::PLATFORM_DETAILS)
42
+ end
39
43
  end
40
44
 
41
45
  context "when the configuration has no ports" do
@@ -280,5 +284,9 @@ describe Mongoid::Clients::Factory do
280
284
  it "sets the write concern" do
281
285
  expect(client.write_concern).to be_a(Mongo::WriteConcern::Acknowledged)
282
286
  end
287
+
288
+ it "sets the platform to Mongoid's platform constant" do
289
+ expect(client.options[:platform]).to eq(Mongoid::PLATFORM_DETAILS)
290
+ end
283
291
  end
284
292
  end
@@ -408,6 +408,14 @@ describe Mongoid::Clients do
408
408
  it "returns the default client" do
409
409
  expect(mongo_client.options[:database].to_s).to eq(database_id)
410
410
  end
411
+
412
+ it "sets the platform to Mongoid's platform constant" do
413
+ expect(mongo_client.options[:platform]).to eq(Mongoid::PLATFORM_DETAILS)
414
+ end
415
+
416
+ it "sets the app_name to the config value" do
417
+ expect(mongo_client.options[:app_name]).to eq('testing')
418
+ end
411
419
  end
412
420
 
413
421
  context "when no client exists with the key" do
@@ -416,6 +424,10 @@ describe Mongoid::Clients do
416
424
  Band.store_in(client: :nonexistent)
417
425
  end
418
426
 
427
+ after do
428
+ Band.reset_storage_options!
429
+ end
430
+
419
431
  let(:band) do
420
432
  Band.new
421
433
  end
@@ -426,6 +438,63 @@ describe Mongoid::Clients do
426
438
  }.to raise_error(Mongoid::Errors::NoClientConfig)
427
439
  end
428
440
  end
441
+
442
+ context "when getting a client by name", if: testing_locally? do
443
+
444
+ let(:file) do
445
+ File.join(File.dirname(__FILE__), "..", "config", "mongoid.yml")
446
+ end
447
+
448
+ before do
449
+ described_class.clear
450
+ Mongoid.load!(file, :test)
451
+ Band.store_in(client: :reports)
452
+ end
453
+
454
+ after do
455
+ mongo_client.close
456
+ Mongoid::Config.reset
457
+ Band.reset_storage_options!
458
+ end
459
+
460
+ let!(:band) do
461
+ Band.store_in(client: :reports)
462
+ end
463
+
464
+ let!(:mongo_client) do
465
+ Band.new.mongo_client
466
+ end
467
+
468
+ it "uses the reports client" do
469
+ expect(mongo_client.options[:database].to_s).to eq('reports')
470
+ end
471
+
472
+ it "sets the platform to Mongoid's platform constant" do
473
+ expect(mongo_client.options[:platform]).to eq(Mongoid::PLATFORM_DETAILS)
474
+ end
475
+
476
+ it "sets the app_name to the config value" do
477
+ expect(mongo_client.options[:app_name]).to eq('testing')
478
+ end
479
+ end
480
+
481
+ context 'when the app_name is not set in the config' do
482
+
483
+ before do
484
+ Mongoid::Config.reset
485
+ Mongoid.configure do |config|
486
+ config.load_configuration(CONFIG)
487
+ end
488
+ end
489
+
490
+ let(:mongo_client) do
491
+ Band.new.mongo_client
492
+ end
493
+
494
+ it 'does not set the Mongoid.app_name option' do
495
+ expect(mongo_client.options.has_key?(:app_name)).to be(false)
496
+ end
497
+ end
429
498
  end
430
499
 
431
500
  describe ".mongo_client", if: non_legacy_server? do
@@ -457,13 +526,21 @@ describe Mongoid::Clients do
457
526
  Mongoid.clients[:default][:database] = database_id
458
527
  end
459
528
 
460
- let!(:mongo_client) do
529
+ let(:mongo_client) do
461
530
  Band.mongo_client
462
531
  end
463
532
 
464
533
  it "returns the default client" do
465
534
  expect(mongo_client.options[:database].to_s).to eq(database_id)
466
535
  end
536
+
537
+ it "sets the platform to Mongoid's platform constant" do
538
+ expect(mongo_client.options[:platform]).to eq(Mongoid::PLATFORM_DETAILS)
539
+ end
540
+
541
+ it "sets the app_name to the config value" do
542
+ expect(mongo_client.options[:app_name]).to eq('testing')
543
+ end
467
544
  end
468
545
 
469
546
  context "when no client exists with the key" do
@@ -82,6 +82,37 @@ describe Mongoid::Config do
82
82
  end
83
83
  end
84
84
 
85
+ context 'when the app_name is set in the config' do
86
+
87
+ let(:conf) do
88
+ CONFIG.merge(options: {app_name: 'admin-reporting'})
89
+ end
90
+
91
+ before do
92
+ Mongoid.configure do |config|
93
+ config.load_configuration(conf)
94
+ end
95
+ end
96
+
97
+ it 'sets the Mongoid.app_name to the provided value' do
98
+ expect(Mongoid.app_name).to eq('admin-reporting')
99
+ end
100
+ end
101
+
102
+ context 'when the app_name is not set in the config' do
103
+
104
+ before do
105
+ Mongoid::Config.reset
106
+ Mongoid.configure do |config|
107
+ config.load_configuration(CONFIG)
108
+ end
109
+ end
110
+
111
+ it 'does not set the Mongoid.app_name option' do
112
+ expect(Mongoid.app_name).to be_nil
113
+ end
114
+ end
115
+
85
116
  describe "#load!" do
86
117
 
87
118
  before(:all) do