mongoid 7.1.0.rc0 → 7.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG.md +6 -6
  5. data/LICENSE +1 -1
  6. data/README.md +5 -5
  7. data/Rakefile +14 -0
  8. data/lib/config/locales/en.yml +5 -5
  9. data/lib/mongoid.rb +3 -2
  10. data/lib/mongoid/association/accessors.rb +37 -2
  11. data/lib/mongoid/association/embedded/embeds_many.rb +2 -1
  12. data/lib/mongoid/association/embedded/embeds_one.rb +2 -1
  13. data/lib/mongoid/association/many.rb +3 -2
  14. data/lib/mongoid/association/proxy.rb +6 -4
  15. data/lib/mongoid/association/referenced/belongs_to/binding.rb +1 -1
  16. data/lib/mongoid/association/referenced/belongs_to/eager.rb +38 -2
  17. data/lib/mongoid/association/referenced/eager.rb +29 -9
  18. data/lib/mongoid/association/referenced/has_many/enumerable.rb +2 -22
  19. data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -2
  20. data/lib/mongoid/atomic.rb +13 -3
  21. data/lib/mongoid/attributes.rb +28 -20
  22. data/lib/mongoid/clients/factory.rb +2 -2
  23. data/lib/mongoid/clients/options.rb +8 -8
  24. data/lib/mongoid/clients/sessions.rb +20 -4
  25. data/lib/mongoid/clients/storage_options.rb +5 -5
  26. data/lib/mongoid/config.rb +42 -12
  27. data/lib/mongoid/config/options.rb +5 -2
  28. data/lib/mongoid/contextual.rb +5 -4
  29. data/lib/mongoid/contextual/geo_near.rb +3 -2
  30. data/lib/mongoid/contextual/map_reduce.rb +3 -2
  31. data/lib/mongoid/contextual/mongo.rb +2 -1
  32. data/lib/mongoid/criteria.rb +23 -4
  33. data/lib/mongoid/criteria/modifiable.rb +2 -1
  34. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
  35. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +6 -6
  36. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +12 -0
  37. data/lib/mongoid/criteria/queryable/mergeable.rb +75 -8
  38. data/lib/mongoid/criteria/queryable/pipeline.rb +3 -2
  39. data/lib/mongoid/criteria/queryable/selectable.rb +120 -13
  40. data/lib/mongoid/criteria/queryable/storable.rb +104 -99
  41. data/lib/mongoid/errors/eager_load.rb +2 -0
  42. data/lib/mongoid/errors/no_client_config.rb +2 -2
  43. data/lib/mongoid/errors/no_default_client.rb +1 -1
  44. data/lib/mongoid/extensions/hash.rb +4 -2
  45. data/lib/mongoid/extensions/regexp.rb +1 -1
  46. data/lib/mongoid/fields.rb +2 -1
  47. data/lib/mongoid/fields/standard.rb +2 -1
  48. data/lib/mongoid/fields/validators/macro.rb +4 -1
  49. data/lib/mongoid/findable.rb +5 -4
  50. data/lib/mongoid/interceptable.rb +5 -1
  51. data/lib/mongoid/matchable/regexp.rb +2 -2
  52. data/lib/mongoid/persistable/pushable.rb +11 -2
  53. data/lib/mongoid/persistence_context.rb +6 -6
  54. data/lib/mongoid/query_cache.rb +61 -18
  55. data/lib/mongoid/railties/database.rake +7 -0
  56. data/lib/mongoid/serializable.rb +10 -2
  57. data/lib/mongoid/shardable.rb +56 -4
  58. data/lib/mongoid/tasks/database.rake +10 -5
  59. data/lib/mongoid/tasks/database.rb +83 -0
  60. data/lib/mongoid/timestamps/timeless.rb +3 -1
  61. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  62. data/lib/mongoid/version.rb +1 -1
  63. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +32 -23
  64. data/lib/rails/generators/mongoid/model/templates/model.rb.tt +1 -1
  65. data/spec/app/models/coding.rb +4 -0
  66. data/spec/app/models/coding/pull_request.rb +12 -0
  67. data/spec/app/models/delegating_patient.rb +16 -0
  68. data/spec/app/models/passport.rb +1 -0
  69. data/spec/app/models/phone.rb +1 -0
  70. data/spec/app/models/publication.rb +5 -0
  71. data/spec/app/models/publication/encyclopedia.rb +12 -0
  72. data/spec/app/models/publication/review.rb +14 -0
  73. data/spec/integration/app_spec.rb +254 -0
  74. data/spec/integration/associations/embedded_spec.rb +54 -0
  75. data/spec/integration/associations/has_many_spec.rb +34 -0
  76. data/spec/integration/associations/has_one_spec.rb +34 -0
  77. data/spec/integration/bson_regexp_raw_spec.rb +20 -0
  78. data/spec/integration/criteria/date_field_spec.rb +41 -0
  79. data/spec/integration/criteria/logical_spec.rb +13 -0
  80. data/spec/integration/document_spec.rb +22 -0
  81. data/spec/integration/shardable_spec.rb +149 -0
  82. data/spec/lite_spec_helper.rb +15 -4
  83. data/spec/mongoid/association/accessors_spec.rb +238 -63
  84. data/spec/mongoid/association/embedded/embeds_many_models.rb +19 -0
  85. data/spec/mongoid/association/embedded/embeds_many_spec.rb +10 -0
  86. data/spec/mongoid/association/embedded/embeds_one_spec.rb +0 -2
  87. data/spec/mongoid/association/referenced/belongs_to/eager_spec.rb +193 -10
  88. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +140 -1
  89. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +146 -68
  90. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +2 -1
  91. data/spec/mongoid/attributes_spec.rb +19 -7
  92. data/spec/mongoid/changeable_spec.rb +23 -0
  93. data/spec/mongoid/clients/factory_spec.rb +8 -8
  94. data/spec/mongoid/clients/options_spec.rb +11 -11
  95. data/spec/mongoid/clients/sessions_spec.rb +8 -4
  96. data/spec/mongoid/clients/transactions_spec.rb +20 -8
  97. data/spec/mongoid/clients_spec.rb +2 -2
  98. data/spec/mongoid/contextual/atomic_spec.rb +22 -11
  99. data/spec/mongoid/contextual/geo_near_spec.rb +11 -2
  100. data/spec/mongoid/contextual/map_reduce_spec.rb +20 -5
  101. data/spec/mongoid/contextual/mongo_spec.rb +76 -53
  102. data/spec/mongoid/criteria/queryable/extensions/regexp_raw_spec.rb +1 -1
  103. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +7 -7
  104. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +1 -1
  105. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +19 -7
  106. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +28 -1
  107. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +45 -12
  108. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +1051 -392
  109. data/spec/mongoid/criteria/queryable/selectable_spec.rb +52 -0
  110. data/spec/mongoid/criteria/queryable/storable_spec.rb +80 -2
  111. data/spec/mongoid/criteria_spec.rb +36 -2
  112. data/spec/mongoid/document_fields_spec.rb +29 -0
  113. data/spec/mongoid/document_persistence_context_spec.rb +33 -0
  114. data/spec/mongoid/errors/no_client_config_spec.rb +2 -2
  115. data/spec/mongoid/errors/no_client_database_spec.rb +3 -3
  116. data/spec/mongoid/errors/no_client_hosts_spec.rb +3 -3
  117. data/spec/mongoid/fields_spec.rb +24 -1
  118. data/spec/mongoid/indexable_spec.rb +6 -4
  119. data/spec/mongoid/interceptable_spec.rb +62 -0
  120. data/spec/mongoid/interceptable_spec_models.rb +76 -0
  121. data/spec/mongoid/matchable/default_spec.rb +1 -1
  122. data/spec/mongoid/matchable/regexp_spec.rb +2 -2
  123. data/spec/mongoid/matchable_spec.rb +2 -2
  124. data/spec/mongoid/persistable/pushable_spec.rb +55 -1
  125. data/spec/mongoid/query_cache_spec.rb +77 -9
  126. data/spec/mongoid/relations/proxy_spec.rb +1 -1
  127. data/spec/mongoid/scopable_spec.rb +2 -1
  128. data/spec/mongoid/serializable_spec.rb +129 -18
  129. data/spec/mongoid/shardable_models.rb +61 -0
  130. data/spec/mongoid/shardable_spec.rb +69 -16
  131. data/spec/mongoid/tasks/database_rake_spec.rb +13 -13
  132. data/spec/mongoid/tasks/database_spec.rb +1 -1
  133. data/spec/spec_helper.rb +2 -31
  134. data/spec/support/child_process_helper.rb +76 -0
  135. data/spec/support/cluster_config.rb +3 -3
  136. data/spec/support/constraints.rb +26 -10
  137. data/spec/support/expectations.rb +3 -1
  138. data/spec/support/helpers.rb +11 -0
  139. data/spec/support/lite_constraints.rb +22 -0
  140. data/spec/support/session_registry.rb +50 -0
  141. data/spec/support/spec_config.rb +12 -4
  142. metadata +518 -480
  143. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5be07e768fab481565fe3b29a550dffa07427780a9cc6ed55cdab15621464223
4
- data.tar.gz: f89e567cfae14b99e6b4864e0ac36f7a88b7c9aa110024fd4970189be82dec5d
3
+ metadata.gz: '06912d432d08b07e80bad71d5073cf2d5a4924f097cdefed959107c6cad245ef'
4
+ data.tar.gz: d6880896621095c3d3017207d9966f99c17f7aea9a1d7366746e287e4c2cea04
5
5
  SHA512:
6
- metadata.gz: acaef29b0e2a8f2297a9fe64e2422dbb741d068819596da631813cae5ea9ea838db090cc6fcd46fdf7f09c5d9983bdc9446f846903662fe6939fc0e200993581
7
- data.tar.gz: 9fdb5ce28104e6b2cf06ea6f290486c14448181e453522530cb956b43d3dc4699564dd7a618b99ac4d057a6615115012322cba69d47d37559fb0dbfa01c1305f
6
+ metadata.gz: 9b9a2bcc459c5ae8f2d7dd5585e7c0c39869f6a860067ae4ab36c5df64efb524ef688d5ab087e1a52a9d6ea3101df66213ad607c62df7499e5706bcce3f0974c
7
+ data.tar.gz: cafe608ad57c0b91593bd570024c74aa6ed203f14e6ebe61b026601ef7c64ab8bc28d944a42e73aa6c1b1aea1510f83e9a64f869bee15ce549db2ade75e56295
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -1848,18 +1848,18 @@ child elements.
1848
1848
  set a child on a relation without the proper inverse_of definitions
1849
1849
  due to Mongoid not being able to determine it.
1850
1850
 
1851
- class Lush
1851
+ class Car
1852
1852
  include Mongoid::Document
1853
- embeds_one :whiskey, class_name: "Drink"
1853
+ embeds_one :engine, class_name: "Motor"
1854
1854
  end
1855
1855
 
1856
- class Drink
1856
+ class Motor
1857
1857
  include Mongoid::Document
1858
- embedded_in :alcoholic, class_name: "Lush"
1858
+ embedded_in :machine, class_name: "Car"
1859
1859
  end
1860
1860
 
1861
- lush = Lush.new
1862
- lush.whiskey = Drink.new # raises an InverseNotFound error.
1861
+ car = Car.new
1862
+ car.engine = Motor.new # raises an InverseNotFound error.
1863
1863
 
1864
1864
  * \#1680 Polymorphic relations now use `*_type` keys in lookup queries.
1865
1865
 
data/LICENSE CHANGED
@@ -1,5 +1,5 @@
1
1
  Copyright (c) 2009-2016 Durran Jordan
2
- Copyright (c) 2015-2019 MongoDB, Inc.
2
+ Copyright (c) 2015-2020 MongoDB, Inc.
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining
5
5
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -17,9 +17,9 @@ Compatibility
17
17
 
18
18
  Mongoid supports and is tested against:
19
19
 
20
- - MRI 2.2-2.6
21
- - JRuby 9.1-9.2
22
- - MongoDB server 2.6-4.0
20
+ - MRI 2.3-2.7
21
+ - JRuby 9.2
22
+ - MongoDB server 2.6-4.4
23
23
 
24
24
  Issues
25
25
  ------
@@ -31,14 +31,14 @@ Support
31
31
  -------
32
32
 
33
33
  * [Stack Overflow](http://stackoverflow.com/questions/tagged/mongoid)
34
- * [Mongoid Google Group](http://groups.google.com/group/mongoid)
34
+ * [MongoDB Community Forum](https://developer.mongodb.com/community/forums/tags/c/drivers-odms-connectors/7/mongoid-odm)
35
35
  * [#mongoid](http://webchat.freenode.net/?channels=mongoid) on Freenode IRC
36
36
 
37
37
  License
38
38
  -------
39
39
 
40
40
  Copyright (c) 2009-2016 Durran Jordan
41
- Copyright (c) 2015-2019 MongoDB, Inc.
41
+ Copyright (c) 2015-2020 MongoDB, Inc.
42
42
 
43
43
  Permission is hereby granted, free of charge, to any person obtaining
44
44
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler"
4
+ require "bundler/gem_tasks"
4
5
  Bundler.setup
5
6
 
6
7
  require "rake"
@@ -9,6 +10,9 @@ require "rspec/core/rake_task"
9
10
  $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
10
11
  require "mongoid/version"
11
12
 
13
+ tasks = Rake.application.instance_variable_get('@tasks')
14
+ tasks['release:do'] = tasks.delete('release')
15
+
12
16
  task :gem => :build
13
17
  task :build do
14
18
  system "gem build mongoid.gemspec"
@@ -47,3 +51,13 @@ namespace :docs do
47
51
  system "yardoc -o #{out} --title mongoid-#{Mongoid::VERSION}"
48
52
  end
49
53
  end
54
+
55
+ namespace :release do
56
+ task :check_private_key do
57
+ unless File.exist?('gem-private_key.pem')
58
+ raise "No private key present, cannot release"
59
+ end
60
+ end
61
+ end
62
+
63
+ task :release => ['release:check_private_key', 'release:do']
@@ -249,7 +249,7 @@ en:
249
249
  Example:\n
250
250
  \_\_class Band\n
251
251
  \_\_\_\_include Mongoid::Document\n
252
- \_\_\_\_store_in collection: 'artists', database: 'secondary'\n
252
+ \_\_\_\_store_in collection: 'artists', database: 'music'\n
253
253
  \_\_end\n\n"
254
254
  invalid_storage_parent:
255
255
  message: "Invalid store_in call on class %{klass}."
@@ -282,13 +282,13 @@ en:
282
282
  you will need to explicitly tell Mongoid on the association what
283
283
  the inverse is.\n\n
284
284
  Example:\n
285
- \_\_class Lush\n
285
+ \_\_class Car\n
286
286
  \_\_\_\_include Mongoid::Document\n
287
- \_\_\_\_has_one :whiskey, class_name: \"Drink\", inverse_of: :alcoholic\n
287
+ \_\_\_\_has_one :engine, class_name: \"Motor\", inverse_of: :machine\n
288
288
  \_\_end\n\n
289
- \_\_class Drink\n
289
+ \_\_class Motor\n
290
290
  \_\_\_\_include Mongoid::Document\n
291
- \_\_\_\_belongs_to :alcoholic, class_name: \"Lush\", inverse_of: :whiskey\n
291
+ \_\_\_\_belongs_to :machine, class_name: \"Car\", inverse_of: :engine\n
292
292
  \_\_end"
293
293
  invalid_set_polymorphic_relation:
294
294
  message: "The %{name} attribute can't be set to an instance of
@@ -3,7 +3,7 @@
3
3
 
4
4
  require "support/ruby_version"
5
5
 
6
- require "delegate"
6
+ require "forwardable"
7
7
  require "time"
8
8
  require "set"
9
9
 
@@ -36,6 +36,7 @@ end
36
36
  I18n.load_path << File.join(File.dirname(__FILE__), "config", "locales", "en.yml")
37
37
 
38
38
  module Mongoid
39
+ extend Forwardable
39
40
  extend Loggable
40
41
  extend self
41
42
 
@@ -109,5 +110,5 @@ module Mongoid
109
110
  # Mongoid.database = Mongo::Connection.new.db("test")
110
111
  #
111
112
  # @since 1.0.0
112
- delegate(*(Config.public_instance_methods(false) - [ :logger=, :logger ] << { to: Config }))
113
+ def_delegators Config, *(Config.public_instance_methods(false) - [ :logger=, :logger ])
113
114
  end
@@ -131,15 +131,50 @@ module Mongoid
131
131
  # @api private
132
132
  def _mongoid_filter_selected_fields(assoc_key)
133
133
  return nil unless __selected_fields
134
+
135
+ projecting_assoc = false
136
+
134
137
  filtered = {}
135
138
  __selected_fields.each do |k, v|
136
139
  bits = k.split('.')
140
+
141
+ # If we are asked to project an association, we need all of that
142
+ # association's fields. However, we may be asked to project
143
+ # an association *and* its fields in the same query. In this case
144
+ # behavior differs according to server version:
145
+ #
146
+ # 4.2 and lower take the most recent projection specification, meaning
147
+ # projecting foo followed by foo.bar effectively projects foo.bar and
148
+ # projecting foo.bar followed by foo effectively projects foo.
149
+ # To match this behavior we need to track when we are being asked
150
+ # to project the association and when we are asked to project a field,
151
+ # and if we are asked to project the association last we need to
152
+ # remove any field projections.
153
+ #
154
+ # 4.4 (and presumably higher) do not allow projection to be on an
155
+ # association and its field, so it doesn't matter what we do. Hence
156
+ # we just need to handle the 4.2 and lower case correctly.
137
157
  if bits.first == assoc_key
138
- bits.shift
139
- filtered[bits.join('.')] = v
158
+ # Projecting the entire association OR some of its fields
159
+ if bits.length > 1
160
+ # Projecting a field
161
+ bits.shift
162
+ filtered[bits.join('.')] = v
163
+ projecting_assoc = false
164
+ else
165
+ # Projecting the entire association
166
+ projecting_assoc = true
167
+ end
140
168
  end
141
169
  end
142
170
 
171
+ if projecting_assoc
172
+ # The last projection was of the entire association; we may have
173
+ # also been projecting fields, but discard the field projections
174
+ # and return nil indicating we want the entire association.
175
+ return nil
176
+ end
177
+
143
178
  # Positional projection is specified as "foo.$". In this case the
144
179
  # document that the $ is referring to should be retrieved with all
145
180
  # fields. See https://docs.mongodb.com/manual/reference/operator/projection/positional/
@@ -200,7 +200,8 @@ module Mongoid
200
200
  def determine_inverses(other)
201
201
  matches = relation_class.relations.values.select do |rel|
202
202
  relation_complements.include?(rel.class) &&
203
- rel.relation_class_name == inverse_class_name
203
+ # https://jira.mongodb.org/browse/MONGOID-4882
204
+ rel.relation_class_name.sub(/\A::/, '') == inverse_class_name
204
205
  end
205
206
  if matches.size > 1
206
207
  raise Errors::AmbiguousRelationship.new(relation_class, @owner_class, name, matches)
@@ -162,7 +162,8 @@ module Mongoid
162
162
  def determine_inverses(other)
163
163
  matches = relation_class.relations.values.select do |rel|
164
164
  relation_complements.include?(rel.class) &&
165
- rel.relation_class_name == inverse_class_name
165
+ # https://jira.mongodb.org/browse/MONGOID-4882
166
+ rel.relation_class_name.sub(/\A::/, '') == inverse_class_name
166
167
 
167
168
  end
168
169
  if matches.size > 1
@@ -7,10 +7,11 @@ module Mongoid
7
7
  # This is the superclass for all many to one and many to many association
8
8
  # proxies.
9
9
  class Many < Association::Proxy
10
+ extend Forwardable
10
11
  include ::Enumerable
11
12
 
12
- delegate :avg, :max, :min, :sum, to: :criteria
13
- delegate :length, :size, to: :_target
13
+ def_delegators :criteria, :avg, :max, :min, :sum
14
+ def_delegators :_target, :length, :size
14
15
 
15
16
  # Is the association empty?
16
17
  #
@@ -9,12 +9,14 @@ module Mongoid
9
9
  # This class is the superclass for all association proxy objects, and contains
10
10
  # common behavior for all of them.
11
11
  class Proxy
12
+ extend Forwardable
13
+
12
14
  alias :extend_proxy :extend
13
15
 
14
16
  # We undefine most methods to get them sent through to the target.
15
17
  instance_methods.each do |method|
16
18
  undef_method(method) unless
17
- method =~ /^(__.*|send|object_id|equal\?|respond_to\?|tap|public_send|extend_proxy|extend_proxies)$/
19
+ method =~ /\A(__.*|send|object_id|equal\?|respond_to\?|tap|public_send|extend_proxy|extend_proxies)\z/
18
20
  end
19
21
 
20
22
  include Threaded::Lifecycle
@@ -36,9 +38,9 @@ module Mongoid
36
38
  attr_accessor :_target
37
39
 
38
40
  # Backwards compatibility with Mongoid beta releases.
39
- delegate :foreign_key, :inverse_foreign_key, to: :_association
40
- delegate :bind_one, :unbind_one, to: :binding
41
- delegate :collection_name, to: :_base
41
+ def_delegators :_association, :foreign_key, :inverse_foreign_key
42
+ def_delegators :binding, :bind_one, :unbind_one
43
+ def_delegator :_base, :collection_name
42
44
 
43
45
  # Convenience for setting the target and the association metadata properties since
44
46
  # all proxies will need to do this.
@@ -76,7 +76,7 @@ module Mongoid
76
76
  # @since 3.0.0
77
77
  def check_polymorphic_inverses!(doc)
78
78
  inverses = _association.inverses(doc)
79
- if inverses.count > 1 && _base.send(_association.foreign_key).nil?
79
+ if inverses.length > 1 && _base.send(_association.foreign_key).nil?
80
80
  raise Errors::InvalidSetPolymorphicRelation.new(
81
81
  _association.name, _base.class.name, _target.class.name
82
82
  )
@@ -12,8 +12,6 @@ module Mongoid
12
12
  private
13
13
 
14
14
  def preload
15
- raise Errors::EagerLoad.new(@association.name) if @association.polymorphic?
16
-
17
15
  @docs.each do |d|
18
16
  set_relation(d, nil)
19
17
  end
@@ -24,6 +22,44 @@ module Mongoid
24
22
  end
25
23
  end
26
24
 
25
+ # Retrieves the documents referenced by the association, and
26
+ # yields each one sequentially to the provided block. If the
27
+ # association is not polymorphic, all documents are retrieved in
28
+ # a single query. If the association is polymorphic, one query is
29
+ # issued per association target class.
30
+ def each_loaded_document(&block)
31
+ if @association.polymorphic?
32
+ keys_by_type_from_docs.each do |type, keys|
33
+ each_loaded_document_of_class(Object.const_get(type), keys, &block)
34
+ end
35
+ else
36
+ super
37
+ end
38
+ end
39
+
40
+ # Returns a map from association target class name to foreign key
41
+ # values for the documents of that association target class,
42
+ # as referenced by this association.
43
+ def keys_by_type_from_docs
44
+ inverse_type_field = @association.inverse_type
45
+
46
+ @docs.each_with_object({}) do |doc, keys_by_type|
47
+ next unless doc.respond_to?(inverse_type_field) && doc.respond_to?(group_by_key)
48
+ inverse_type_name = doc.send(inverse_type_field)
49
+ # If a particular document does not have a value for this
50
+ # association, inverse_type_name will be nil.
51
+ next if inverse_type_name.nil?
52
+
53
+ key_value = doc.send(group_by_key)
54
+ # If a document has the *_type field set but the corresponding
55
+ # *_id field not set, the key value here will be nil.
56
+ next unless key_value
57
+
58
+ keys_by_type[inverse_type_name] ||= []
59
+ keys_by_type[inverse_type_name].push(key_value)
60
+ end
61
+ end
62
+
27
63
  def group_by_key
28
64
  @association.foreign_key
29
65
  end
@@ -59,17 +59,29 @@ module Mongoid
59
59
  raise NotImplementedError
60
60
  end
61
61
 
62
- # Run the preloader.
63
- #
64
- # @example Iterate over the documents loaded for the current association
65
- # loader.each_loaded_document { |doc| }
62
+ # Retrieves the documents referenced by the association, and
63
+ # yields each one sequentially to the provided block. If the
64
+ # association is not polymorphic, all documents are retrieved in
65
+ # a single query. If the association is polymorphic, one query is
66
+ # issued per association target class.
66
67
  #
67
68
  # @since 4.0.0
68
- def each_loaded_document
69
- doc_keys = keys_from_docs
70
- return @association.klass.none if doc_keys.all?(&:nil?)
69
+ def each_loaded_document(&block)
70
+ each_loaded_document_of_class(@association.klass, keys_from_docs, &block)
71
+ end
71
72
 
72
- criteria = @association.klass.any_in(key => doc_keys)
73
+ # Retrieves the documents of the specified class, that have the
74
+ # foreign key included in the specified list of keys.
75
+ #
76
+ # When the documents are retrieved, the set of inclusions applied
77
+ # is the set of inclusions applied to the host document minus the
78
+ # association that is being eagerly loaded.
79
+ private def each_loaded_document_of_class(cls, keys)
80
+ # Note: keys should not include nil elements.
81
+ # Upstream code is responsible for eliminating nils from keys.
82
+ return cls.none if keys.empty?
83
+
84
+ criteria = cls.any_in(key => keys)
73
85
  criteria.inclusions = criteria.inclusions - [@association]
74
86
  criteria.each do |doc|
75
87
  yield doc
@@ -93,6 +105,9 @@ module Mongoid
93
105
 
94
106
  # Return a hash with the current documents grouped by key.
95
107
  #
108
+ # Documents that do not have a value for the association being loaded
109
+ # are not returned.
110
+ #
96
111
  # @example Return a hash with the current documents grouped by key.
97
112
  # loader.grouped_docs
98
113
  #
@@ -102,10 +117,15 @@ module Mongoid
102
117
  def grouped_docs
103
118
  @grouped_docs[@association.name] ||= @docs.group_by do |doc|
104
119
  doc.send(group_by_key) if doc.respond_to?(group_by_key)
120
+ end.reject do |k, v|
121
+ k.nil?
105
122
  end
106
123
  end
107
124
 
108
- # Group the documents and return the keys
125
+ # Group the documents and return the keys.
126
+ #
127
+ # This method omits nil keys (i.e. keys from documents that do not
128
+ # have a value for the association being loaded).
109
129
  #
110
130
  # @example
111
131
  # loader.keys_from_docs
@@ -11,6 +11,7 @@ module Mongoid
11
11
  # target that can be a criteria or array of _loaded documents. This
12
12
  # handles both cases or a combination of the two.
13
13
  class Enumerable
14
+ extend Forwardable
14
15
  include ::Enumerable
15
16
 
16
17
  # The three main instance variables are collections of documents.
@@ -20,7 +21,7 @@ module Mongoid
20
21
  # @attribute [rw] _unloaded A criteria representing persisted docs.
21
22
  attr_accessor :_added, :_loaded, :_unloaded
22
23
 
23
- delegate :is_a?, :kind_of?, to: []
24
+ def_delegators [], :is_a?, :kind_of?
24
25
 
25
26
  # Check if the enumerable is equal to the other object.
26
27
  #
@@ -215,27 +216,6 @@ module Mongoid
215
216
  end
216
217
  end
217
218
 
218
- # Get an arbitrary document in the enumerable. Will check the persisted
219
- # documents first. Does not load the entire enumerable.
220
- #
221
- # @example Get an arbitrary document.
222
- # enumerable.one
223
- #
224
- # @note Unlike #first, this does not automatically sort the result set.
225
- #
226
- # @param [ Hash ] opts The options for the query returning the first document.
227
- #
228
- # @option opts [ :none ] :id_sort Don't apply a sort on _id.
229
- #
230
- # @return [ Document ] The first document found.
231
- #
232
- # @since 7.1.0
233
- def one(opts = {})
234
- opts = opts.dup
235
- opts[:id_sort] ||= :none
236
- first(opts)
237
- end
238
-
239
219
  # Get the first document in the enumerable. Will check the persisted
240
220
  # documents first. Does not load the entire enumerable.
241
221
  #