mongoid 5.1.5 → 5.2.0

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