mongoid 6.4.0 → 6.4.7

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 (76) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Rakefile +26 -0
  5. data/lib/mongoid.rb +1 -1
  6. data/lib/mongoid/clients/sessions.rb +2 -2
  7. data/lib/mongoid/contextual/aggregable/mongo.rb +1 -1
  8. data/lib/mongoid/contextual/map_reduce.rb +4 -4
  9. data/lib/mongoid/contextual/memory.rb +4 -4
  10. data/lib/mongoid/contextual/mongo.rb +3 -3
  11. data/lib/mongoid/criteria/modifiable.rb +12 -2
  12. data/lib/mongoid/criteria/queryable/selectable.rb +34 -7
  13. data/lib/mongoid/document.rb +4 -4
  14. data/lib/mongoid/extensions/big_decimal.rb +1 -1
  15. data/lib/mongoid/extensions/regexp.rb +1 -0
  16. data/lib/mongoid/extensions/string.rb +3 -1
  17. data/lib/mongoid/indexable.rb +4 -4
  18. data/lib/mongoid/matchable.rb +3 -0
  19. data/lib/mongoid/matchable/nor.rb +37 -0
  20. data/lib/mongoid/persistable.rb +1 -1
  21. data/lib/mongoid/persistable/creatable.rb +2 -2
  22. data/lib/mongoid/persistable/deletable.rb +2 -2
  23. data/lib/mongoid/persistable/settable.rb +5 -5
  24. data/lib/mongoid/persistable/updatable.rb +2 -2
  25. data/lib/mongoid/persistable/upsertable.rb +1 -1
  26. data/lib/mongoid/persistence_context.rb +4 -0
  27. data/lib/mongoid/query_cache.rb +21 -10
  28. data/lib/mongoid/railtie.rb +17 -0
  29. data/lib/mongoid/railties/controller_runtime.rb +86 -0
  30. data/lib/mongoid/relations/embedded/batchable.rb +4 -4
  31. data/lib/mongoid/relations/embedded/many.rb +23 -0
  32. data/lib/mongoid/relations/many.rb +2 -2
  33. data/lib/mongoid/relations/referenced/many.rb +1 -1
  34. data/lib/mongoid/relations/touchable.rb +1 -1
  35. data/lib/mongoid/reloadable.rb +1 -1
  36. data/lib/mongoid/scopable.rb +3 -3
  37. data/lib/mongoid/tasks/database.rb +2 -2
  38. data/lib/mongoid/threaded.rb +36 -0
  39. data/lib/mongoid/version.rb +1 -1
  40. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +4 -0
  41. data/spec/app/models/array_field.rb +7 -0
  42. data/spec/app/models/delegating_patient.rb +16 -0
  43. data/spec/integration/document_spec.rb +22 -0
  44. data/spec/mongoid/clients/factory_spec.rb +52 -28
  45. data/spec/mongoid/clients/options_spec.rb +30 -15
  46. data/spec/mongoid/clients/sessions_spec.rb +12 -3
  47. data/spec/mongoid/contextual/geo_near_spec.rb +1 -0
  48. data/spec/mongoid/contextual/mongo_spec.rb +2 -2
  49. data/spec/mongoid/criteria/modifiable_spec.rb +59 -10
  50. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +3 -3
  51. data/spec/mongoid/criteria/queryable/selectable_spec.rb +42 -3
  52. data/spec/mongoid/criteria/queryable/selector_spec.rb +2 -2
  53. data/spec/mongoid/criteria/scopable_spec.rb +81 -0
  54. data/spec/mongoid/criteria_spec.rb +4 -1
  55. data/spec/mongoid/document_spec.rb +54 -0
  56. data/spec/mongoid/extensions/big_decimal_spec.rb +9 -9
  57. data/spec/mongoid/extensions/regexp_spec.rb +23 -0
  58. data/spec/mongoid/extensions/string_spec.rb +35 -7
  59. data/spec/mongoid/fields_spec.rb +1 -1
  60. data/spec/mongoid/findable_spec.rb +1 -1
  61. data/spec/mongoid/matchable/nor_spec.rb +209 -0
  62. data/spec/mongoid/matchable_spec.rb +26 -1
  63. data/spec/mongoid/persistable/incrementable_spec.rb +6 -6
  64. data/spec/mongoid/persistable/settable_spec.rb +35 -1
  65. data/spec/mongoid/query_cache_spec.rb +73 -18
  66. data/spec/mongoid/relations/embedded/many_spec.rb +246 -16
  67. data/spec/mongoid/scopable_spec.rb +13 -0
  68. data/spec/mongoid/threaded_spec.rb +68 -0
  69. data/spec/rails/controller_extension/controller_runtime_spec.rb +110 -0
  70. data/spec/spec_helper.rb +9 -0
  71. data/spec/support/cluster_config.rb +158 -0
  72. data/spec/support/constraints.rb +101 -0
  73. data/spec/support/macros.rb +20 -0
  74. data/spec/support/spec_config.rb +42 -0
  75. metadata +41 -23
  76. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 496715e8829978d85bd903477e9d6d7961fa4ffe8d9529d6720fbfdb2b3ec9a8
4
- data.tar.gz: 907296ce5256555834c738f5fdf8d7277b57d6c09da3c733eb7cb0ba6526d0a2
3
+ metadata.gz: 9b178391288873186489028a588fe8d35be83a44d9bf351846a727af169096cd
4
+ data.tar.gz: ce725bbd596c7ac7df2817826788be6162a55a6ebeacf5553eebc810feb43ee3
5
5
  SHA512:
6
- metadata.gz: 9d16c8cff475066981b0736ed07ebe0b463c8ee4e8c43049087f43ad8ee6789ff88a5c2e73e3fe6a31a52f7732cc6b8614de72d8c76df2dd3c54ee48a5977a41
7
- data.tar.gz: d15deebe3406b501b95fa864306c03cfa7b106f810909dffeda8d7402d14de5aa1dad3d1129685b859ef9d537e656108a015b77f066ea5affd9e32785e018b0a
6
+ metadata.gz: ea99aca6fe69fbe884477398019e761bf7f1274bb75b382b451767cdc6cfa4f6a1ea1ed874a65835d758ebc97c08823734721c6908296a926b1ae37e35c67fda
7
+ data.tar.gz: d9e7087501c49435c51873f901bb2e24a036dd1b2268fcb733f03f3aea6a523339e13997198d8e46ea2a128abf665ded41a1765382004d40523b317f3691e7d6
Binary file
data.tar.gz.sig CHANGED
Binary file
data/Rakefile CHANGED
@@ -1,4 +1,5 @@
1
1
  require "bundler"
2
+ require "bundler/gem_tasks"
2
3
  Bundler.setup
3
4
 
4
5
  require "rake"
@@ -7,6 +8,9 @@ require "rspec/core/rake_task"
7
8
  $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
8
9
  require "mongoid/version"
9
10
 
11
+ tasks = Rake.application.instance_variable_get('@tasks')
12
+ tasks['release:do'] = tasks.delete('release')
13
+
10
14
  task :gem => :build
11
15
  task :build do
12
16
  system "gem build mongoid.gemspec"
@@ -33,3 +37,25 @@ RSpec::Core::RakeTask.new('spec:progress') do |spec|
33
37
  end
34
38
 
35
39
  task :default => :spec
40
+
41
+ desc "Generate all documentation"
42
+ task :docs => 'docs:yard'
43
+
44
+ namespace :docs do
45
+ desc "Generate yard documention"
46
+ task :yard do
47
+ out = File.join('yard-docs', Mongoid::VERSION)
48
+ FileUtils.rm_rf(out)
49
+ system "yardoc -o #{out} --title mongoid-#{Mongoid::VERSION}"
50
+ end
51
+ end
52
+
53
+ namespace :release do
54
+ task :check_private_key do
55
+ unless File.exist?('gem-private_key.pem')
56
+ raise "No private key present, cannot release"
57
+ end
58
+ end
59
+ end
60
+
61
+ task :release => ['release:check_private_key', 'release:do']
@@ -101,5 +101,5 @@ module Mongoid
101
101
  # Mongoid.database = Mongo::Connection.new.db("test")
102
102
  #
103
103
  # @since 1.0.0
104
- delegate(*(Config.public_instance_methods(false) - [ :logger=, :logger ] << { to: Config }))
104
+ delegate(*(Config.public_instance_methods(false) - [ :logger=, :logger ]), to: Config)
105
105
  end
@@ -53,7 +53,7 @@ module Mongoid
53
53
 
54
54
  private
55
55
 
56
- def session
56
+ def _session
57
57
  Threaded.get_session
58
58
  end
59
59
 
@@ -104,7 +104,7 @@ module Mongoid
104
104
 
105
105
  private
106
106
 
107
- def session
107
+ def _session
108
108
  Threaded.get_session
109
109
  end
110
110
  end
@@ -23,7 +23,7 @@ module Mongoid
23
23
  #
24
24
  # @since 3.0.0
25
25
  def aggregates(field)
26
- result = collection.find.aggregate(pipeline(field), session: session).to_a
26
+ result = collection.find.aggregate(pipeline(field), session: _session).to_a
27
27
  if result.empty?
28
28
  { "count" => 0, "sum" => nil, "avg" => nil, "min" => nil, "max" => nil }
29
29
  else
@@ -165,8 +165,8 @@ module Mongoid
165
165
  def raw
166
166
  validate_out!
167
167
  cmd = command
168
- opts = { read: cmd.delete(:read).options } if cmd[:read]
169
- @map_reduce.database.command(cmd, (opts || {}).merge(session: session)).first
168
+ opts = { read: cmd.delete(:read) } if cmd[:read]
169
+ @map_reduce.database.command(cmd, (opts || {}).merge(session: _session)).first
170
170
  end
171
171
  alias :results :raw
172
172
 
@@ -250,8 +250,8 @@ module Mongoid
250
250
  raise Errors::NoMapReduceOutput.new({}) unless @map_reduce.out
251
251
  end
252
252
 
253
- def session
254
- criteria.send(:session)
253
+ def _session
254
+ criteria.send(:_session)
255
255
  end
256
256
  end
257
257
  end
@@ -49,7 +49,7 @@ module Mongoid
49
49
  unless removed.empty?
50
50
  collection.find(selector).update_one(
51
51
  positionally(selector, "$pullAll" => { path => removed }),
52
- session: session
52
+ session: _session
53
53
  )
54
54
  end
55
55
  deleted
@@ -304,7 +304,7 @@ module Mongoid
304
304
  updates["$set"].merge!(doc.atomic_updates["$set"] || {})
305
305
  doc.move_changes
306
306
  end
307
- collection.find(selector).update_one(updates, session: session) unless updates["$set"].empty?
307
+ collection.find(selector).update_one(updates, session: _session) unless updates["$set"].empty?
308
308
  end
309
309
 
310
310
  # Get the limiting value.
@@ -446,8 +446,8 @@ module Mongoid
446
446
  doc.destroyed = true
447
447
  end
448
448
 
449
- def session
450
- @criteria.send(:session)
449
+ def _session
450
+ @criteria.send(:_session)
451
451
  end
452
452
  end
453
453
  end
@@ -341,7 +341,7 @@ module Mongoid
341
341
  @criteria, @klass, @cache = criteria, criteria.klass, criteria.options[:cache]
342
342
  @collection = @klass.collection
343
343
  criteria.send(:merge_type_selection)
344
- @view = collection.find(criteria.selector, session: session)
344
+ @view = collection.find(criteria.selector, session: _session)
345
345
  apply_options
346
346
  end
347
347
 
@@ -706,8 +706,8 @@ module Mongoid
706
706
  documents.push(doc) if cacheable?
707
707
  end
708
708
 
709
- def session
710
- @criteria.send(:session)
709
+ def _session
710
+ @criteria.send(:_session)
711
711
  end
712
712
 
713
713
  def acknowledged_write?
@@ -3,6 +3,10 @@ module Mongoid
3
3
  class Criteria
4
4
  module Modifiable
5
5
 
6
+ # @attribute [r] create_attrs Additional attributes to add to the Document upon creation.
7
+ # @api private
8
+ attr_reader :create_attrs
9
+
6
10
  # Build a document given the selector and return it.
7
11
  # Complex criteria, such as $in and $or operations will get ignored.
8
12
  #
@@ -57,6 +61,9 @@ module Mongoid
57
61
 
58
62
  # Define attributes with which new documents will be created.
59
63
  #
64
+ # Note that if `find_or_create_by` is called after this in a method chain, the attributes in
65
+ # the query will override those from this method.
66
+ #
60
67
  # @example Define attributes to be used when a new document is created.
61
68
  # Person.create_with(job: 'Engineer').find_or_create_by(employer: 'MongoDB')
62
69
  #
@@ -64,7 +71,9 @@ module Mongoid
64
71
  #
65
72
  # @since 5.1.0
66
73
  def create_with(attrs = {})
67
- where(selector.merge(attrs))
74
+ tap do
75
+ (@create_attrs ||= {}).merge!(attrs)
76
+ end
68
77
  end
69
78
 
70
79
  # Find the first +Document+ given the conditions, or creates a new document
@@ -172,7 +181,8 @@ module Mongoid
172
181
  #
173
182
  # @since 3.0.0
174
183
  def create_document(method, attrs = nil, &block)
175
- attributes = selector.reduce(attrs ? attrs.dup : {}) do |hash, (key, value)|
184
+ attrs = (create_attrs || {}).merge(attrs || {})
185
+ attributes = selector.reduce(attrs) do |hash, (key, value)|
176
186
  unless invalid_key?(hash, key) || invalid_embedded_doc?(value)
177
187
  hash[key] = value
178
188
  end
@@ -24,7 +24,7 @@ module Mongoid
24
24
  # @since 2.0.0
25
25
  POLYGON = "Polygon"
26
26
 
27
- # @attribute [rw] negating If the next spression is negated.
27
+ # @attribute [rw] negating If the next expression is negated.
28
28
  # @attribute [rw] selector The query selector.
29
29
  attr_accessor :negating, :selector
30
30
 
@@ -134,13 +134,21 @@ module Mongoid
134
134
  ::Boolean.evolve(value)
135
135
  end
136
136
 
137
- # Add a $geoIntersects or $geoWithin selection. Symbol operators must be used as shown in
138
- # the examples to expand the criteria.
137
+ # Add a $geoIntersects or $geoWithin selection. Symbol operators must
138
+ # be used as shown in the examples to expand the criteria.
139
139
  #
140
140
  # @note The only valid geometry shapes for a $geoIntersects are:
141
141
  # :intersects_line, :intersects_point, and :intersects_polygon.
142
142
  #
143
- # @note The only valid geometry shape for a $geoWithin is :within_polygon
143
+ # @note The only valid options for a $geoWithin query are the geometry
144
+ # shape :within_polygon and the operator :within_box.
145
+ #
146
+ # @note The :within_box operator for the $geoWithin query expects the
147
+ # lower left (south west) coordinate pair as the first argument and
148
+ # the upper right (north east) as the second argument.
149
+ # Important: When latitude and longitude are passed, longitude is
150
+ # expected as the first element of the coordinate pair.
151
+ # Source: https://docs.mongodb.com/manual/reference/operator/query/box/
144
152
  #
145
153
  # @example Add a geo intersect criterion for a line.
146
154
  # query.geo_spacial(:location.intersects_line => [[ 1, 10 ], [ 2, 10 ]])
@@ -154,6 +162,9 @@ module Mongoid
154
162
  # @example Add a geo within criterion for a polygon.
155
163
  # query.geo_spacial(:location.within_polygon => [[ 1, 10 ], [ 2, 10 ], [ 1, 10 ]])
156
164
  #
165
+ # @example Add a geo within criterion for a box.
166
+ # query.geo_spacial(:location.within_box => [[ 1, 10 ], [ 2, 10 ])
167
+ #
157
168
  # @param [ Hash ] criterion The criterion.
158
169
  #
159
170
  # @return [ Selectable ] The cloned selectable.
@@ -174,6 +185,7 @@ module Mongoid
174
185
  key :within_polygon, :override, "$geoWithin", "$geometry" do |value|
175
186
  { "type" => POLYGON, "coordinates" => value }
176
187
  end
188
+ key :within_box, :override, "$geoWithin", "$box"
177
189
 
178
190
  # Add the $gt criterion to the selector.
179
191
  #
@@ -501,6 +513,11 @@ module Mongoid
501
513
  # @example Construct a text search selector with options.
502
514
  # selectable.text_search("testing", :$language => "fr")
503
515
  #
516
+ # @note Per https://docs.mongodb.com/manual/reference/operator/query/text/
517
+ # it is not currently possible to supply multiple text search
518
+ # conditions in a query. Mongoid will build such a query but the
519
+ # server will return an error when trying to execute it.
520
+ #
504
521
  # @param [ String, Symbol ] terms A string of terms that MongoDB parses
505
522
  # and uses to query the text index.
506
523
  # @param [ Hash ] opts Text search options. See MongoDB documentation
@@ -512,9 +529,19 @@ module Mongoid
512
529
  def text_search(terms, opts = nil)
513
530
  clone.tap do |query|
514
531
  if terms
515
- criterion = { :$text => { :$search => terms } }
516
- criterion[:$text].merge!(opts) if opts
517
- query.selector = criterion
532
+ criterion = {'$text' => { '$search' => terms }}
533
+ criterion['$text'].merge!(opts) if opts
534
+ if query.selector['$text']
535
+ # Per https://docs.mongodb.com/manual/reference/operator/query/text/
536
+ # multiple $text expressions are not currently supported by
537
+ # MongoDB server, but build the query correctly instead of
538
+ # overwriting previous text search condition with the currently
539
+ # given one.
540
+ Mongoid.logger.warn('Multiple $text expressions per query are not currently supported by the server')
541
+ query.selector = {'$and' => [query.selector]}.merge(criterion)
542
+ else
543
+ query.selector = query.selector.merge(criterion)
544
+ end
518
545
  end
519
546
  end
520
547
  end
@@ -190,11 +190,11 @@ module Mongoid
190
190
  #
191
191
  # @since 5.1.0
192
192
  def as_json(options = nil)
193
- if options && (options[:compact] == true)
194
- super(options).reject! { |k,v| v.nil? }
195
- else
196
- super(options)
193
+ rv = super
194
+ if options && options[:compact]
195
+ rv = rv.compact
197
196
  end
197
+ rv
198
198
  end
199
199
 
200
200
  # Returns an instance of the specified class with the attributes,
@@ -53,7 +53,7 @@ module Mongoid
53
53
  #
54
54
  # @since 3.0.0
55
55
  def demongoize(object)
56
- object && object.numeric? ? ::BigDecimal.new(object.to_s) : nil
56
+ object && object.numeric? ? BigDecimal(object.to_s) : nil
57
57
  end
58
58
 
59
59
  # Mongoize an object of any type to how it's stored in the db as a String.
@@ -17,6 +17,7 @@ module Mongoid
17
17
  #
18
18
  # @since 3.0.0
19
19
  def mongoize(object)
20
+ return nil if object.nil?
20
21
  ::Regexp.new(object)
21
22
  end
22
23
  end
@@ -82,7 +82,9 @@ module Mongoid
82
82
  #
83
83
  # @since 3.0.0
84
84
  def numeric?
85
- true if Float(self) rescue (self =~ /^NaN|\-?Infinity$/)
85
+ !!Float(self)
86
+ rescue ArgumentError
87
+ (self =~ /\A(?:NaN|-?Infinity)\z/) == 0
86
88
  end
87
89
 
88
90
  # Get the string as a getter string.
@@ -32,10 +32,10 @@ module Mongoid
32
32
  key, options = spec.key, spec.options
33
33
  if database = options[:database]
34
34
  with(database: database) do |klass|
35
- klass.collection.indexes(session: session).create_one(key, options.except(:database))
35
+ klass.collection.indexes(session: _session).create_one(key, options.except(:database))
36
36
  end
37
37
  else
38
- collection.indexes(session: session).create_one(key, options)
38
+ collection.indexes(session: _session).create_one(key, options)
39
39
  end
40
40
  end and true
41
41
  end
@@ -53,9 +53,9 @@ module Mongoid
53
53
  indexed_database_names.each do |database|
54
54
  with(database: database) do |klass|
55
55
  begin
56
- klass.collection.indexes(session: session).each do |spec|
56
+ klass.collection.indexes(session: _session).each do |spec|
57
57
  unless spec["name"] == "_id_"
58
- klass.collection.indexes(session: session).drop_one(spec["key"])
58
+ klass.collection.indexes(session: _session).drop_one(spec["key"])
59
59
  logger.info(
60
60
  "MONGOID: Removed index '#{spec["name"]}' on collection " +
61
61
  "'#{klass.collection.name}' in database '#{database}'."
@@ -11,6 +11,7 @@ require "mongoid/matchable/lte"
11
11
  require "mongoid/matchable/ne"
12
12
  require "mongoid/matchable/nin"
13
13
  require "mongoid/matchable/or"
14
+ require "mongoid/matchable/nor"
14
15
  require "mongoid/matchable/size"
15
16
  require "mongoid/matchable/elem_match"
16
17
  require "mongoid/matchable/regexp"
@@ -40,6 +41,7 @@ module Mongoid
40
41
  "$ne" => Ne,
41
42
  "$nin" => Nin,
42
43
  "$or" => Or,
44
+ "$nor" => Nor,
43
45
  "$size" => Size
44
46
  }.with_indifferent_access.freeze
45
47
 
@@ -124,6 +126,7 @@ module Mongoid
124
126
  case key.to_s
125
127
  when "$or" then Or.new(value, document)
126
128
  when "$and" then And.new(value, document)
129
+ when "$nor" then Nor.new(value, document)
127
130
  else Default.new(extract_attribute(document, key))
128
131
  end
129
132
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+ module Mongoid
4
+ module Matchable
5
+
6
+ # Defines behavior for handling $nor expressions in embedded documents.
7
+ class Nor < Default
8
+
9
+ # Does the supplied query match the attribute?
10
+ #
11
+ # Note: an empty array as an argument to $nor is prohibited by
12
+ # MongoDB server. Mongoid does allow this and returns false in this case.
13
+ #
14
+ # @example Does this match?
15
+ # matcher._matches?("$nor" => [ { field => value } ])
16
+ #
17
+ # @param [ Array ] conditions The or expression.
18
+ #
19
+ # @return [ true, false ] True if matches, false if not.
20
+ #
21
+ # @since 6.4.2/7.0.2/7.1.0
22
+ def _matches?(conditions)
23
+ if conditions.length == 0
24
+ # MongoDB does not allow $nor array to be empty, but
25
+ # Mongoid accepts an empty array for $or which MongoDB also
26
+ # prohibits
27
+ return false
28
+ end
29
+ conditions.none? do |condition|
30
+ condition.all? do |key, value|
31
+ document._matches?(key => value)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -204,7 +204,7 @@ module Mongoid
204
204
  def persist_atomic_operations(operations)
205
205
  if persisted? && operations
206
206
  selector = atomic_selector
207
- _root.collection.find(selector).update_one(positionally(selector, operations), session: session)
207
+ _root.collection.find(selector).update_one(positionally(selector, operations), session: _session)
208
208
  end
209
209
  end
210
210
  end
@@ -63,7 +63,7 @@ module Mongoid
63
63
  selector = _parent.atomic_selector
64
64
  _root.collection.find(selector).update_one(
65
65
  positionally(selector, atomic_inserts),
66
- session: session)
66
+ session: _session)
67
67
  end
68
68
  end
69
69
 
@@ -78,7 +78,7 @@ module Mongoid
78
78
  #
79
79
  # @since 4.0.0
80
80
  def insert_as_root
81
- collection.insert_one(as_attributes, session: session)
81
+ collection.insert_one(as_attributes, session: _session)
82
82
  end
83
83
 
84
84
  # Post process an insert, which sets the new record attribute to false