mongoid 8.0.8 → 8.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +3 -3
  4. data/README.md +3 -3
  5. data/Rakefile +0 -25
  6. data/lib/config/locales/en.yml +46 -14
  7. data/lib/mongoid/association/accessors.rb +2 -2
  8. data/lib/mongoid/association/builders.rb +1 -1
  9. data/lib/mongoid/association/embedded/batchable.rb +2 -2
  10. data/lib/mongoid/association/embedded/embedded_in/buildable.rb +2 -2
  11. data/lib/mongoid/association/embedded/embedded_in/proxy.rb +2 -1
  12. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +3 -2
  13. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +6 -6
  14. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +1 -1
  15. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +1 -1
  16. data/lib/mongoid/association/macros.rb +0 -6
  17. data/lib/mongoid/association/nested/one.rb +40 -2
  18. data/lib/mongoid/association/proxy.rb +1 -1
  19. data/lib/mongoid/association/referenced/counter_cache.rb +2 -2
  20. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +1 -1
  21. data/lib/mongoid/association/referenced/has_many/enumerable.rb +2 -2
  22. data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -3
  23. data/lib/mongoid/association/reflections.rb +2 -2
  24. data/lib/mongoid/atomic.rb +7 -16
  25. data/lib/mongoid/attributes/dynamic.rb +1 -1
  26. data/lib/mongoid/attributes/nested.rb +2 -2
  27. data/lib/mongoid/attributes/processing.rb +5 -29
  28. data/lib/mongoid/attributes/projector.rb +1 -1
  29. data/lib/mongoid/attributes/readonly.rb +1 -1
  30. data/lib/mongoid/attributes.rb +8 -2
  31. data/lib/mongoid/changeable.rb +107 -5
  32. data/lib/mongoid/clients/storage_options.rb +2 -5
  33. data/lib/mongoid/clients/validators/storage.rb +1 -13
  34. data/lib/mongoid/collection_configurable.rb +58 -0
  35. data/lib/mongoid/composable.rb +2 -0
  36. data/lib/mongoid/config/defaults.rb +60 -0
  37. data/lib/mongoid/config/options.rb +0 -3
  38. data/lib/mongoid/config/validators/async_query_executor.rb +24 -0
  39. data/lib/mongoid/config/validators.rb +1 -0
  40. data/lib/mongoid/config.rb +99 -28
  41. data/lib/mongoid/contextual/atomic.rb +1 -1
  42. data/lib/mongoid/contextual/memory.rb +233 -33
  43. data/lib/mongoid/contextual/mongo/documents_loader.rb +177 -0
  44. data/lib/mongoid/contextual/mongo.rb +370 -133
  45. data/lib/mongoid/contextual/none.rb +162 -7
  46. data/lib/mongoid/contextual.rb +12 -0
  47. data/lib/mongoid/criteria/findable.rb +2 -2
  48. data/lib/mongoid/criteria/includable.rb +4 -3
  49. data/lib/mongoid/criteria/queryable/key.rb +1 -1
  50. data/lib/mongoid/criteria/queryable/mergeable.rb +1 -1
  51. data/lib/mongoid/criteria/queryable/optional.rb +8 -8
  52. data/lib/mongoid/criteria/queryable/selectable.rb +43 -12
  53. data/lib/mongoid/criteria/queryable/selector.rb +1 -1
  54. data/lib/mongoid/criteria/queryable/storable.rb +1 -1
  55. data/lib/mongoid/criteria.rb +6 -5
  56. data/lib/mongoid/deprecable.rb +1 -2
  57. data/lib/mongoid/deprecation.rb +3 -3
  58. data/lib/mongoid/errors/create_collection_failure.rb +33 -0
  59. data/lib/mongoid/errors/drop_collection_failure.rb +27 -0
  60. data/lib/mongoid/errors/immutable_attribute.rb +26 -0
  61. data/lib/mongoid/errors/invalid_async_query_executor.rb +25 -0
  62. data/lib/mongoid/errors/invalid_global_executor_concurrency.rb +22 -0
  63. data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
  64. data/lib/mongoid/errors.rb +4 -1
  65. data/lib/mongoid/extensions/hash.rb +2 -24
  66. data/lib/mongoid/extensions/object.rb +2 -2
  67. data/lib/mongoid/extensions/time.rb +2 -0
  68. data/lib/mongoid/fields/localized.rb +10 -0
  69. data/lib/mongoid/fields/standard.rb +10 -0
  70. data/lib/mongoid/fields.rb +53 -24
  71. data/lib/mongoid/findable.rb +27 -3
  72. data/lib/mongoid/interceptable.rb +10 -118
  73. data/lib/mongoid/matcher/eq_impl.rb +1 -1
  74. data/lib/mongoid/matcher/type.rb +1 -1
  75. data/lib/mongoid/persistable/creatable.rb +1 -0
  76. data/lib/mongoid/persistable/deletable.rb +1 -1
  77. data/lib/mongoid/persistable/savable.rb +13 -1
  78. data/lib/mongoid/persistable/unsettable.rb +2 -2
  79. data/lib/mongoid/persistable/updatable.rb +51 -1
  80. data/lib/mongoid/persistable/upsertable.rb +20 -1
  81. data/lib/mongoid/persistable.rb +3 -0
  82. data/lib/mongoid/query_cache.rb +5 -1
  83. data/lib/mongoid/railties/database.rake +7 -2
  84. data/lib/mongoid/reloadable.rb +5 -3
  85. data/lib/mongoid/stateful.rb +22 -1
  86. data/lib/mongoid/tasks/database.rake +12 -0
  87. data/lib/mongoid/tasks/database.rb +20 -0
  88. data/lib/mongoid/utils.rb +22 -0
  89. data/lib/mongoid/validatable/associated.rb +18 -96
  90. data/lib/mongoid/validatable/macros.rb +5 -5
  91. data/lib/mongoid/validatable.rb +4 -9
  92. data/lib/mongoid/version.rb +1 -1
  93. data/lib/mongoid/warnings.rb +17 -1
  94. data/lib/mongoid.rb +16 -3
  95. data/spec/integration/app_spec.rb +2 -2
  96. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +0 -40
  97. data/spec/integration/callbacks_models.rb +37 -0
  98. data/spec/integration/callbacks_spec.rb +126 -12
  99. data/spec/integration/discriminator_key_spec.rb +4 -5
  100. data/spec/integration/i18n_fallbacks_spec.rb +3 -2
  101. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +27 -0
  102. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +20 -25
  103. data/spec/mongoid/association/embedded/embeds_many_models.rb +1 -0
  104. data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
  105. data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -18
  106. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +5 -27
  107. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +9 -50
  108. data/spec/mongoid/association/syncable_spec.rb +1 -1
  109. data/spec/mongoid/attributes_spec.rb +3 -33
  110. data/spec/mongoid/changeable_spec.rb +299 -24
  111. data/spec/mongoid/clients_spec.rb +122 -13
  112. data/spec/mongoid/collection_configurable_spec.rb +158 -0
  113. data/spec/mongoid/config/defaults_spec.rb +160 -0
  114. data/spec/mongoid/config_spec.rb +154 -27
  115. data/spec/mongoid/contextual/memory_spec.rb +332 -76
  116. data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
  117. data/spec/mongoid/contextual/mongo_spec.rb +1009 -125
  118. data/spec/mongoid/contextual/none_spec.rb +49 -2
  119. data/spec/mongoid/copyable_spec.rb +2 -10
  120. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -10
  121. data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
  122. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +419 -0
  123. data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -1
  124. data/spec/mongoid/criteria/queryable/selector_spec.rb +3 -76
  125. data/spec/mongoid/criteria/queryable/storable_spec.rb +0 -72
  126. data/spec/mongoid/criteria_projection_spec.rb +1 -4
  127. data/spec/mongoid/criteria_spec.rb +5 -9
  128. data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
  129. data/spec/mongoid/extensions/hash_spec.rb +3 -3
  130. data/spec/mongoid/extensions/time_spec.rb +8 -43
  131. data/spec/mongoid/extensions/time_with_zone_spec.rb +7 -52
  132. data/spec/mongoid/fields/localized_spec.rb +46 -28
  133. data/spec/mongoid/fields_spec.rb +136 -77
  134. data/spec/mongoid/findable_spec.rb +391 -34
  135. data/spec/mongoid/indexable_spec.rb +16 -10
  136. data/spec/mongoid/interceptable_spec.rb +173 -362
  137. data/spec/mongoid/persistable/deletable_spec.rb +26 -6
  138. data/spec/mongoid/persistable/destroyable_spec.rb +26 -6
  139. data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
  140. data/spec/mongoid/persistable/logical_spec.rb +37 -0
  141. data/spec/mongoid/persistable/poppable_spec.rb +36 -0
  142. data/spec/mongoid/persistable/pullable_spec.rb +72 -0
  143. data/spec/mongoid/persistable/pushable_spec.rb +72 -0
  144. data/spec/mongoid/persistable/renamable_spec.rb +36 -0
  145. data/spec/mongoid/persistable/savable_spec.rb +96 -0
  146. data/spec/mongoid/persistable/settable_spec.rb +37 -0
  147. data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
  148. data/spec/mongoid/persistable/updatable_spec.rb +20 -28
  149. data/spec/mongoid/persistable/upsertable_spec.rb +80 -6
  150. data/spec/mongoid/persistence_context_spec.rb +7 -57
  151. data/spec/mongoid/query_cache_spec.rb +56 -61
  152. data/spec/mongoid/reloadable_spec.rb +24 -28
  153. data/spec/mongoid/scopable_spec.rb +70 -0
  154. data/spec/mongoid/serializable_spec.rb +9 -30
  155. data/spec/mongoid/stateful_spec.rb +122 -8
  156. data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
  157. data/spec/mongoid/tasks/database_spec.rb +127 -0
  158. data/spec/mongoid/timestamps_spec.rb +9 -11
  159. data/spec/mongoid/touchable_spec.rb +277 -5
  160. data/spec/mongoid/touchable_spec_models.rb +3 -1
  161. data/spec/mongoid/traversable_spec.rb +9 -24
  162. data/spec/mongoid/validatable/associated_spec.rb +30 -13
  163. data/spec/mongoid/validatable/uniqueness_spec.rb +2 -3
  164. data/spec/mongoid_spec.rb +36 -10
  165. data/spec/spec_helper.rb +5 -0
  166. data/spec/support/immutable_ids.rb +118 -0
  167. data/spec/support/macros.rb +47 -15
  168. data/spec/support/models/artist.rb +0 -1
  169. data/spec/support/models/band.rb +1 -0
  170. data/spec/support/models/book.rb +1 -0
  171. data/spec/support/models/building.rb +2 -0
  172. data/spec/support/models/cover.rb +10 -0
  173. data/spec/support/models/name.rb +0 -10
  174. data/spec/support/models/person.rb +0 -1
  175. data/spec/support/models/product.rb +1 -0
  176. data.tar.gz.sig +0 -0
  177. metadata +698 -664
  178. metadata.gz.sig +0 -0
  179. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -60
  180. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -60
  181. data/spec/support/models/purse.rb +0 -9
@@ -16,7 +16,6 @@ module Mongoid
16
16
  #
17
17
  # @return [ TrueClass ] True.
18
18
  def delete(options = {})
19
- raise Errors::ReadonlyDocument.new(self.class) if readonly?
20
19
  prepare_delete do
21
20
  unless options[:persist] == false
22
21
  if embedded?
@@ -102,6 +101,7 @@ module Mongoid
102
101
  #
103
102
  # @return [ Object ] The result of the block.
104
103
  def prepare_delete
104
+ raise Errors::ReadonlyDocument.new(self.class) if readonly?
105
105
  yield(self)
106
106
  freeze
107
107
  self.destroyed = true
@@ -14,7 +14,13 @@ module Mongoid
14
14
  #
15
15
  # @param [ Hash ] options Options to pass to the save.
16
16
  #
17
- # @return [ true | false ] True is success, false if not.
17
+ # @option options [ true | false ] :touch Whether or not the updated_at
18
+ # attribute will be updated with the current time. When this option is
19
+ # false, none of the embedded documents will be touched. This option is
20
+ # ignored when saving a new document, and the created_at and updated_at
21
+ # will be set to the current time.
22
+ #
23
+ # @return [ true | false ] True if success, false if not.
18
24
  def save(options = {})
19
25
  if new_record?
20
26
  !insert(options).new_record?
@@ -31,6 +37,12 @@ module Mongoid
31
37
  #
32
38
  # @param [ Hash ] options Options to pass to the save.
33
39
  #
40
+ # @option options [ true | false ] :touch Whether or not the updated_at
41
+ # attribute will be updated with the current time. When this option is
42
+ # false, none of the embedded documents will be touched.This option is
43
+ # ignored when saving a new document, and the created_at and updated_at
44
+ # will be set to the current time.
45
+ #
34
46
  # @raise [ Errors::Validations ] If validation failed.
35
47
  # @raise [ Errors::Callback ] If a callback returns false.
36
48
  #
@@ -13,8 +13,8 @@ module Mongoid
13
13
  # @example Unset the values.
14
14
  # document.unset(:first_name, :last_name, :middle)
15
15
  #
16
- # @param [ Array<String | Symbol> ] fields The names of the fields to
17
- # unset.
16
+ # @param [ [ String | Symbol | Array<String | Symbol>]... ] *fields
17
+ # The names of the field(s) to unset.
18
18
  #
19
19
  # @return [ Document ] The document.
20
20
  def unset(*fields)
@@ -91,16 +91,23 @@ module Mongoid
91
91
  #
92
92
  # @param [ Hash ] options The options.
93
93
  #
94
+ # @option options [ true | false ] :touch Whether or not the updated_at
95
+ # attribute will be updated with the current time.
96
+ #
94
97
  # @return [ true | false ] The result of the update.
95
98
  def prepare_update(options = {})
99
+ raise Errors::ReadonlyDocument.new(self.class) if readonly? && !Mongoid.legacy_readonly
100
+ enforce_immutability_of_id_field!
96
101
  return false if performing_validations?(options) &&
97
102
  invalid?(options[:context] || :update)
98
103
  process_flagged_destroys
104
+ update_children = cascadable_children(:update)
105
+ process_touch_option(options, update_children)
99
106
  run_callbacks(:save, with_children: false) do
100
107
  run_callbacks(:update, with_children: false) do
101
108
  run_callbacks(:persist_parent, with_children: false) do
102
109
  _mongoid_run_child_callbacks(:save) do
103
- _mongoid_run_child_callbacks(:update) do
110
+ _mongoid_run_child_callbacks(:update, children: update_children) do
104
111
  result = yield(self)
105
112
  self.previously_new_record = false
106
113
  post_process_persist(result, options)
@@ -160,6 +167,49 @@ module Mongoid
160
167
  end
161
168
  end
162
169
  end
170
+
171
+ # If there is a touch option and it is false, this method will call the
172
+ # timeless method so that the updated_at attribute is not updated. It
173
+ # will call the timeless method on all of the cascadable children as
174
+ # well. Note that timeless is cleared in the before_update callback.
175
+ #
176
+ # @param [ Hash ] options The options.
177
+ # @param [ Array<Document> ] children The children that the :update
178
+ # callbacks will be executed on.
179
+ #
180
+ # @option options [ true | false ] :touch Whether or not the updated_at
181
+ # attribute will be updated with the current time.
182
+ def process_touch_option(options, children)
183
+ unless options.fetch(:touch, true)
184
+ timeless
185
+ children.each(&:timeless)
186
+ end
187
+ end
188
+
189
+ # Checks to see if the _id field has been modified. If it has, and if
190
+ # the document has already been persisted, this is an error. Otherwise,
191
+ # returns without side-effects.
192
+ #
193
+ # Note that if `Mongoid::Config.immutable_ids` is false, this will do
194
+ # nothing.
195
+ #
196
+ # @raise [ Errors::ImmutableAttribute ] if _id has changed, and document
197
+ # has been persisted.
198
+ def enforce_immutability_of_id_field!
199
+ # special case here: we *do* allow the _id to be mutated if it was
200
+ # previously nil. This addresses an odd case exposed in
201
+ # has_one/proxy_spec.rb where `person.create_address` would
202
+ # (somehow?) create the address with a nil _id first, before then
203
+ # saving it *again* with the correct _id.
204
+
205
+ if _id_changed? && !_id_was.nil? && persisted?
206
+ if Mongoid::Config.immutable_ids
207
+ raise Errors::ImmutableAttribute.new(:_id, _id)
208
+ else
209
+ Mongoid::Warnings.warn_mutable_ids
210
+ end
211
+ end
212
+ end
163
213
  end
164
214
  end
165
215
  end
@@ -10,16 +10,32 @@ module Mongoid
10
10
  # database, then Mongo will insert a new one, otherwise the fields will get
11
11
  # overwritten with new values on the existing document.
12
12
  #
13
+ # If the replace option is true, unspecified attributes will be dropped,
14
+ # and if it is false, unspecified attributes will be maintained. The
15
+ # replace option defaults to true in Mongoid 8.1 and earlier. The default
16
+ # will be flipped to false in Mongoid 9.
17
+ #
13
18
  # @example Upsert the document.
14
19
  # document.upsert
15
20
  #
21
+ # @example Upsert the document without replace.
22
+ # document.upsert(replace: false)
23
+ #
16
24
  # @param [ Hash ] options The validation options.
17
25
  #
26
+ # @option options [ true | false ] :validate Whether or not to validate.
27
+ # @option options [ true | false ] :replace Whether or not to replace the document on upsert.
28
+ #
18
29
  # @return [ true ] True.
19
30
  def upsert(options = {})
20
31
  prepare_upsert(options) do
21
- collection.find(atomic_selector).replace_one(
32
+ if options.fetch(:replace, true)
33
+ collection.find(atomic_selector).replace_one(
22
34
  as_attributes, upsert: true, session: _session)
35
+ else
36
+ collection.find(atomic_selector).update_one(
37
+ { "$set" => as_attributes }, upsert: true, session: _session)
38
+ end
23
39
  end
24
40
  end
25
41
 
@@ -36,8 +52,11 @@ module Mongoid
36
52
  #
37
53
  # @param [ Hash ] options The options hash.
38
54
  #
55
+ # @option options [ true | false ] :validate Whether or not to validate.
56
+ #
39
57
  # @return [ true | false ] If the operation succeeded.
40
58
  def prepare_upsert(options = {})
59
+ raise Errors::ReadonlyDocument.new(self.class) if readonly? && !Mongoid.legacy_readonly
41
60
  return false if performing_validations?(options) && invalid?(:upsert)
42
61
  result = run_callbacks(:upsert) do
43
62
  yield(self)
@@ -161,6 +161,8 @@ module Mongoid
161
161
  # @param [ Object ] result The result of the operation.
162
162
  # @param [ Hash ] options The options.
163
163
  #
164
+ # @option options [ true | false ] :validate Whether or not to validate.
165
+ #
164
166
  # @return [ true ] true.
165
167
  def post_process_persist(result, options = {})
166
168
  post_persist unless result == false
@@ -180,6 +182,7 @@ module Mongoid
180
182
  #
181
183
  # @return [ Object ] The result of the operation.
182
184
  def prepare_atomic_operation
185
+ raise Errors::ReadonlyDocument.new(self.class) if readonly? && !Mongoid.legacy_readonly
183
186
  operations = yield({})
184
187
  persist_or_delay_atomic_operation(operations)
185
188
  self
@@ -14,6 +14,7 @@ module Mongoid
14
14
  #
15
15
  # @return [ nil ] Always nil.
16
16
  def clear_cache
17
+ Mongoid::Warnings.warn_mongoid_query_cache_clear
17
18
  Mongo::QueryCache.clear
18
19
  end
19
20
 
@@ -24,6 +25,7 @@ module Mongoid
24
25
  #
25
26
  # @param [ true | false ] value The enabled value.
26
27
  def enabled=(value)
28
+ Mongoid::Warnings.warn_mongoid_query_cache
27
29
  Mongo::QueryCache.enabled = value
28
30
  end
29
31
 
@@ -34,6 +36,7 @@ module Mongoid
34
36
  #
35
37
  # @return [ true | false ] If the cache is enabled.
36
38
  def enabled?
39
+ Mongoid::Warnings.warn_mongoid_query_cache
37
40
  Mongo::QueryCache.enabled?
38
41
  end
39
42
 
@@ -44,6 +47,7 @@ module Mongoid
44
47
  #
45
48
  # @return [ Object ] The result of the block.
46
49
  def cache(&block)
50
+ Mongoid::Warnings.warn_mongoid_query_cache
47
51
  Mongo::QueryCache.cache(&block)
48
52
  end
49
53
 
@@ -54,6 +58,7 @@ module Mongoid
54
58
  #
55
59
  # @return [ Object ] The result of the block.
56
60
  def uncached(&block)
61
+ Mongoid::Warnings.warn_mongoid_query_cache
57
62
  Mongo::QueryCache.uncached(&block)
58
63
  end
59
64
  end
@@ -61,4 +66,3 @@ module Mongoid
61
66
  Middleware = Mongo::QueryCache::Middleware
62
67
  end
63
68
  end
64
-
@@ -25,7 +25,7 @@ namespace :db do
25
25
 
26
26
  unless Rake::Task.task_defined?("db:setup")
27
27
  desc "Create the database, and initialize with the seed data"
28
- task :setup => [ "db:create", "mongoid:create_indexes", "db:seed" ]
28
+ task :setup => [ "db:create", "mongoid:create_collections", "mongoid:create_indexes", "db:seed" ]
29
29
  end
30
30
 
31
31
  unless Rake::Task.task_defined?("db:reset")
@@ -55,10 +55,15 @@ namespace :db do
55
55
 
56
56
  unless Rake::Task.task_defined?("db:test:prepare")
57
57
  namespace :test do
58
- task :prepare => "mongoid:create_indexes"
58
+ task :prepare => ["mongoid:create_collections", "mongoid:create_indexes"]
59
59
  end
60
60
  end
61
61
 
62
+ unless Rake::Task.task_defined?("db:create_collections")
63
+ desc "Create collections specified in Mongoid models"
64
+ task :create_collections => "mongoid:create_collections"
65
+ end
66
+
62
67
  unless Rake::Task.task_defined?("db:create_indexes")
63
68
  desc "Create indexes specified in Mongoid models"
64
69
  task :create_indexes => "mongoid:create_indexes"
@@ -16,14 +16,16 @@ module Mongoid
16
16
  #
17
17
  # @return [ Document ] The document, reloaded.
18
18
  def reload
19
+ if @atomic_selector
20
+ # Clear atomic_selector cache for sharded clusters. MONGOID-5076
21
+ remove_instance_variable('@atomic_selector')
22
+ end
23
+
19
24
  reloaded = _reload
20
25
  if Mongoid.raise_not_found_error && (reloaded.nil? || reloaded.empty?)
21
26
  shard_keys = atomic_selector.with_indifferent_access.slice(*shard_key_fields, :_id)
22
27
  raise Errors::DocumentNotFound.new(self.class, _id, shard_keys)
23
28
  end
24
-
25
- reset_atomic_updates!
26
-
27
29
  @attributes = reloaded
28
30
  @attributes_before_type_cast = @attributes.dup
29
31
  @changed_attributes = {}
@@ -95,6 +95,23 @@ module Mongoid
95
95
  !_parent.delayed_atomic_sets[atomic_path]
96
96
  end
97
97
 
98
+ # Flags the document as readonly. Will cause a ReadonlyDocument error to be
99
+ # raised if the document is attempted to be saved, updated or destroyed.
100
+ #
101
+ # @example Flag the document as readonly.
102
+ # document.readonly!
103
+ #
104
+ # @return [ true | false ] true if the document was successfully marked
105
+ # readonly, false otherwise.
106
+ def readonly!
107
+ if Mongoid.legacy_readonly
108
+ Mongoid::Warnings.warn_legacy_readonly
109
+ false
110
+ else
111
+ @readonly = true
112
+ end
113
+ end
114
+
98
115
  # Is the document readonly?
99
116
  #
100
117
  # @example Is the document readonly?
@@ -102,7 +119,11 @@ module Mongoid
102
119
  #
103
120
  # @return [ true | false ] If the document is readonly.
104
121
  def readonly?
105
- __selected_fields != nil
122
+ if Mongoid.legacy_readonly
123
+ __selected_fields != nil
124
+ else
125
+ @readonly ||= false
126
+ end
106
127
  end
107
128
 
108
129
  # Determine if the document can be set.
@@ -5,6 +5,11 @@ namespace :db do
5
5
  task :load_models do
6
6
  end
7
7
 
8
+ desc "Create collections for Mongoid models"
9
+ task :create_collections => [:environment, :load_models] do
10
+ ::Mongoid::Tasks::Database.create_collections
11
+ end
12
+
8
13
  desc "Create indexes specified in Mongoid models"
9
14
  task :create_indexes => [:environment, :load_models] do
10
15
  ::Mongoid::Tasks::Database.create_indexes
@@ -34,5 +39,12 @@ namespace :db do
34
39
  task :purge => :environment do
35
40
  ::Mongoid.purge!
36
41
  end
42
+
43
+ namespace :create_collections do
44
+ desc "Drop and create collections for Mongoid models"
45
+ task :force => [:environment, :load_models] do
46
+ ::Mongoid::Tasks::Database.create_collections(force: true)
47
+ end
48
+ end
37
49
  end
38
50
  end
@@ -5,6 +5,26 @@ module Mongoid
5
5
  module Database
6
6
  extend self
7
7
 
8
+ # Create collections for each model given the provided globs and the class is
9
+ # not embedded.
10
+ #
11
+ # @param [ Array<Mongoid::Document> ] models. Array of document classes for
12
+ # which collections should be created. Defaulted to all document classes
13
+ # in the application.
14
+ # @param [ true | false ] force If true, the method will drop existing
15
+ # collections before creating new ones. If false, the method will create
16
+ # only new collection (that do not exist in the database).
17
+ def create_collections(models = ::Mongoid.models, force: false)
18
+ models.each do |model|
19
+ if !model.embedded? || model.cyclic?
20
+ model.create_collection(force: force)
21
+ logger.info("MONGOID: Created collection for #{model}:")
22
+ else
23
+ logger.info("MONGOID: collection options ignored on: #{model}, please define in the root model.")
24
+ end
25
+ end
26
+ end
27
+
8
28
  # Create indexes for each model given the provided globs and the class is
9
29
  # not embedded.
10
30
  #
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongoid
4
+
5
+ # @api private
6
+ module Utils
7
+
8
+ # This function should be used if you need to measure time.
9
+ # @example Calculate elapsed time.
10
+ # starting = Utils.monotonic_time
11
+ # # do something time consuming
12
+ # ending = Utils.monotonic_time
13
+ # puts "It took #{(ending - starting).to_i} seconds"
14
+ #
15
+ # @see https://blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way/
16
+ #
17
+ # @return [Float] seconds according to monotonic clock
18
+ module_function def monotonic_time
19
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
20
+ end
21
+ end
22
+ end
@@ -15,110 +15,32 @@ module Mongoid
15
15
  #
16
16
  # validates_associated :name, :addresses
17
17
  # end
18
- class AssociatedValidator < ActiveModel::Validator
19
- # Required by `validates_with` so that the validator
20
- # gets added to the correct attributes.
21
- def attributes
22
- options[:attributes]
23
- end
18
+ class AssociatedValidator < ActiveModel::EachValidator
24
19
 
25
- # Checks that the named associations of the given record
26
- # (`attributes`) are valid. This does NOT load the associations
27
- # from the database, and will only validate records that are dirty
28
- # or unpersisted.
20
+ # Validates that the associations provided are either all nil or all
21
+ # valid. If neither is true then the appropriate errors will be added to
22
+ # the parent document.
29
23
  #
30
- # If anything is not valid, appropriate errors will be added to
31
- # the `document` parameter.
32
- #
33
- # @param [ Mongoid::Document ] document the document with the
34
- # associations to validate.
35
- def validate(document)
36
- options[:attributes].each do |attr_name|
37
- validate_association(document, attr_name)
38
- end
39
- end
40
-
41
- private
42
-
43
- # Validates that the given association provided is either nil,
44
- # persisted and unchanged, or invalid. Otherwise, the appropriate errors
45
- # will be added to the parent document.
24
+ # @example Validate the association.
25
+ # validator.validate_each(document, :name, name)
46
26
  #
47
27
  # @param [ Document ] document The document to validate.
48
28
  # @param [ Symbol ] attribute The association to validate.
49
- def validate_association(document, attribute)
50
- # grab the proxy from the instance variable directly; we don't want
51
- # any loading logic to run; we just want to see if it's already
52
- # been loaded.
53
- proxy = document.ivar(attribute)
54
- return unless proxy
55
-
56
- # if the variable exists, now we see if it is a proxy, or an actual
57
- # document. It might be a literal document instead of a proxy if this
58
- # document was created with a Document instance as a provided attribute,
59
- # e.g. "Post.new(message: Message.new)".
60
- target = proxy.respond_to?(:_target) ? proxy._target : proxy
61
-
62
- # Now, fetch the list of documents from the target. Target may be a
63
- # single value, or a list of values, and in the case of HasMany,
64
- # might be a rather complex collection. We need to do this without
65
- # triggering a load, so it's a bit of a delicate dance.
66
- list = get_target_documents(target)
67
-
68
- valid = document.validating do
69
- # Now, treating the target as an array, look at each element
70
- # and see if it is valid, but only if it has already been
71
- # persisted, or changed, and hasn't been flagged for destroy.
72
- list.all? do |value|
73
- if value && !value.flagged_for_destroy? && (!value.persisted? || value.changed?)
74
- value.validated? ? true : value.valid?
75
- else
29
+ # @param [ Object ] value The value of the association.
30
+ def validate_each(document, attribute, value)
31
+ begin
32
+ document.begin_validate
33
+ valid = Array.wrap(value).collect do |doc|
34
+ if doc.nil? || doc.flagged_for_destroy?
76
35
  true
36
+ else
37
+ doc.validated? ? true : doc.valid?
77
38
  end
78
- end
79
- end
80
-
81
- document.errors.add(attribute, :invalid) unless valid
82
- end
83
-
84
- private
85
-
86
- # Examine the given target object and return an array of
87
- # documents (possibly empty) that the target represents.
88
- #
89
- # @param [ Array | Mongoid::Document | Mongoid::Association::Proxy | HasMany::Enumerable ] target
90
- # the target object to examine.
91
- #
92
- # @return [ Array<Mongoid::Document> ] the list of documents
93
- def get_target_documents(target)
94
- if target.respond_to?(:_loaded?)
95
- get_target_documents_for_has_many(target)
96
- else
97
- get_target_documents_for_other(target)
39
+ end.all?
40
+ ensure
41
+ document.exit_validate
98
42
  end
99
- end
100
-
101
- # Returns the list of all currently in-memory values held by
102
- # the target. The target will not be loaded.
103
- #
104
- # @param [ HasMany::Enumerable ] target the target that will
105
- # be examined for in-memory documents.
106
- #
107
- # @return [ Array<Mongoid::Document> ] the in-memory documents
108
- # held by the target.
109
- def get_target_documents_for_has_many(target)
110
- [ *target._loaded.values, *target._added.values ]
111
- end
112
-
113
- # Returns the target as an array. If the target represents a single
114
- # value, it is wrapped in an array.
115
- #
116
- # @param [ Array | Mongoid::Document | Mongoid::Association::Proxy ] target
117
- # the target to return.
118
- #
119
- # @return [ Array<Mongoid::Document> ] the target, as an array.
120
- def get_target_documents_for_other(target)
121
- Array.wrap(target)
43
+ document.errors.add(attribute, :invalid, **options) unless valid
122
44
  end
123
45
  end
124
46
  end
@@ -18,7 +18,7 @@ module Mongoid
18
18
  # validates_associated :name, :addresses
19
19
  # end
20
20
  #
21
- # @param [ Array ] args The arguments to pass to the validator.
21
+ # @param [ Object... ] *args The arguments to pass to the validator.
22
22
  def validates_associated(*args)
23
23
  validates_with(AssociatedValidator, _merge_attributes(args))
24
24
  end
@@ -35,7 +35,7 @@ module Mongoid
35
35
  # validates_uniqueness_of :title
36
36
  # end
37
37
  #
38
- # @param [ Array ] args The arguments to pass to the validator.
38
+ # @param [ Object... ] *args The arguments to pass to the validator.
39
39
  def validates_uniqueness_of(*args)
40
40
  validates_with(UniquenessValidator, _merge_attributes(args))
41
41
  end
@@ -50,7 +50,7 @@ module Mongoid
50
50
  # validates_format_of :title, with: /\A[a-z0-9 \-_]*\z/i
51
51
  # end
52
52
  #
53
- # @param [ Array ] args The names of the fields to validate.
53
+ # @param [ Object... ] *args The names of the field(s) to validate.
54
54
  def validates_format_of(*args)
55
55
  validates_with(FormatValidator, _merge_attributes(args))
56
56
  end
@@ -65,7 +65,7 @@ module Mongoid
65
65
  # validates_length_of :title, minimum: 100
66
66
  # end
67
67
  #
68
- # @param [ Array ] args The names of the fields to validate.
68
+ # @param [ Object... ] *args The names of the field(s) to validate.
69
69
  def validates_length_of(*args)
70
70
  validates_with(LengthValidator, _merge_attributes(args))
71
71
  end
@@ -80,7 +80,7 @@ module Mongoid
80
80
  # validates_presence_of :title
81
81
  # end
82
82
  #
83
- # @param [ Array ] args The names of the fields to validate.
83
+ # @param [ Object... ] *args The names of the field(s) to validate.
84
84
  def validates_presence_of(*args)
85
85
  validates_with(PresenceValidator, _merge_attributes(args))
86
86
  end
@@ -37,14 +37,6 @@ module Mongoid
37
37
  Threaded.exit_validate(self)
38
38
  end
39
39
 
40
- # Perform a validation within the associated block.
41
- def validating
42
- begin_validate
43
- yield
44
- ensure
45
- exit_validate
46
- end
47
-
48
40
  # Given the provided options, are we performing validations?
49
41
  #
50
42
  # @example Are we performing validations?
@@ -52,6 +44,8 @@ module Mongoid
52
44
  #
53
45
  # @param [ Hash ] options The options to check.
54
46
  #
47
+ # @option options [ true | false ] :validate Whether or not to validate.
48
+ #
55
49
  # @return [ true | false ] If we are validating.
56
50
  def performing_validations?(options = {})
57
51
  options[:validate].nil? ? true : options[:validate]
@@ -137,7 +131,8 @@ module Mongoid
137
131
  # @example Validate with a specific validator.
138
132
  # validates_with MyValidator, on: :create
139
133
  #
140
- # @param [ Class<Array> | Hash ] args The validator classes and options.
134
+ # @param [ ActiveModel::Validator..., Hash ] *args The validator classes
135
+ # and options hash.
141
136
  #
142
137
  # @note See ActiveModel::Validations::With for full options. This is
143
138
  # overridden to add autosave functionality when presence validation is
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mongoid
4
- VERSION = "8.0.8"
4
+ VERSION = "8.1.0"
5
5
  end
@@ -23,6 +23,22 @@ module Mongoid
23
23
 
24
24
  warning :geo_haystack_deprecated, 'The geoHaystack type is deprecated.'
25
25
  warning :as_json_compact_deprecated, '#as_json :compact option is deprecated. Please call #compact on the returned Hash object instead.'
26
- warning :symbol_type_deprecated, 'The BSON Symbol type is deprecated by MongoDB. Please use String or StringifiedSymbol field types instead of the Symbol field type'
26
+ warning :symbol_type_deprecated, 'The BSON Symbol type is deprecated by MongoDB. Please use String or StringifiedSymbol field types instead of the Symbol field type.'
27
+ warning :legacy_readonly, 'The readonly! method will only mark the document readonly when the legacy_readonly feature flag is switched off.'
28
+ warning :use_activesupport_time_zone_deprecated, 'Config option :use_activesupport_time_zone is deprecated and should be removed from your config. It will always be true beginning in Mongoid 9.0.'
29
+ warning :broken_aggregables_deprecated, 'Config option :broken_aggregables is deprecated. It will always be false beginning in Mongoid 9.0. Please use load_defaults for Mongoid 8.0 or later, then remove it from your config.'
30
+ warning :broken_alias_handling_deprecated, 'Config option :broken_alias_handling is deprecated. It will always be false beginning in Mongoid 9.0. Please use load_defaults for Mongoid 8.0 or later, then remove it from your config.'
31
+ warning :broken_and_deprecated, 'Config option :broken_and is deprecated. It will always be false beginning in Mongoid 9.0. Please use load_defaults for Mongoid 8.0 or later, then remove it from your config.'
32
+ warning :broken_scoping_deprecated, 'Config option :broken_scoping is deprecated. It will always be false beginning in Mongoid 9.0. Please use load_defaults for Mongoid 8.0 or later, then remove it from your config.'
33
+ warning :broken_updates_deprecated, 'Config option :broken_updates is deprecated. It will always be false beginning in Mongoid 9.0. Please use load_defaults for Mongoid 8.0 or later, then remove it from your config.'
34
+ warning :compare_time_by_ms_deprecated, 'Config option :compare_time_by_ms is deprecated. It will always be true beginning in Mongoid 9.0. Please use load_defaults for Mongoid 8.0 or later, then remove it from your config.'
35
+ warning :legacy_attributes_deprecated, 'Config option :legacy_attributes is deprecated. It will always be false beginning in Mongoid 9.0. Please use load_defaults for Mongoid 8.0 or later, then remove it from your config.'
36
+ warning :legacy_pluck_distinct_deprecated, 'Config option :legacy_pluck_distinct is deprecated. It will always be false beginning in Mongoid 9.0. Please use load_defaults for Mongoid 8.0 or later, then remove it from your config.'
37
+ warning :legacy_triple_equals_deprecated, 'Config option :legacy_triple_equals is deprecated. It will always be false beginning in Mongoid 9.0. Please use load_defaults for Mongoid 8.0 or later, then remove it from your config.'
38
+ warning :object_id_as_json_oid_deprecated, 'Config option :object_id_as_json_oid is deprecated. It will always be false beginning in Mongoid 9.0. Please use load_defaults for Mongoid 8.0 or later, then remove it from your config.'
39
+ warning :overwrite_chained_operators_deprecated, 'Config option :overwrite_chained_operators is deprecated. It will always be false beginning in Mongoid 9.0. Please use load_defaults for Mongoid 8.0 or later, then remove it from your config.'
40
+ warning :mutable_ids, 'In Mongoid 9.0 the _id field will be immutable. In earlier versions of 8.x, mutating the _id field was supported inconsistently. Prepare your code for 9.0 by setting Mongoid::Config.immutable_ids to true.'
41
+ warning :mongoid_query_cache, 'In Mongoid 9.0, Mongoid::QueryCache will be removed. Please replace it with Mongo::QueryCache.'
42
+ warning :mongoid_query_cache_clear, 'In Mongoid 9.0, Mongoid::QueryCache#clear_cache should be replaced it with Mongo::QueryCache#clear.'
27
43
  end
28
44
  end