mongoid 7.5.4 → 8.1.3

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 (421) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +3 -3
  4. data/README.md +6 -6
  5. data/lib/config/locales/en.yml +92 -43
  6. data/lib/mongoid/association/accessors.rb +40 -11
  7. data/lib/mongoid/association/bindable.rb +50 -2
  8. data/lib/mongoid/association/builders.rb +5 -3
  9. data/lib/mongoid/association/constrainable.rb +0 -1
  10. data/lib/mongoid/association/eager_loadable.rb +29 -7
  11. data/lib/mongoid/association/embedded/batchable.rb +34 -11
  12. data/lib/mongoid/association/embedded/cyclic.rb +1 -1
  13. data/lib/mongoid/association/embedded/embedded_in/binding.rb +24 -2
  14. data/lib/mongoid/association/embedded/embedded_in/buildable.rb +2 -2
  15. data/lib/mongoid/association/embedded/embedded_in/proxy.rb +4 -3
  16. data/lib/mongoid/association/embedded/embedded_in.rb +3 -2
  17. data/lib/mongoid/association/embedded/embeds_many/binding.rb +1 -0
  18. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +4 -3
  19. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +69 -45
  20. data/lib/mongoid/association/embedded/embeds_many.rb +2 -2
  21. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +19 -5
  22. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +24 -5
  23. data/lib/mongoid/association/embedded/embeds_one.rb +3 -3
  24. data/lib/mongoid/association/macros.rb +8 -1
  25. data/lib/mongoid/association/many.rb +11 -7
  26. data/lib/mongoid/association/nested/many.rb +5 -4
  27. data/lib/mongoid/association/nested/nested_buildable.rb +4 -4
  28. data/lib/mongoid/association/nested/one.rb +45 -7
  29. data/lib/mongoid/association/one.rb +2 -2
  30. data/lib/mongoid/association/options.rb +9 -9
  31. data/lib/mongoid/association/proxy.rb +15 -4
  32. data/lib/mongoid/association/referenced/auto_save.rb +4 -3
  33. data/lib/mongoid/association/referenced/belongs_to/binding.rb +1 -0
  34. data/lib/mongoid/association/referenced/belongs_to/buildable.rb +1 -1
  35. data/lib/mongoid/association/referenced/belongs_to/proxy.rb +5 -6
  36. data/lib/mongoid/association/referenced/belongs_to.rb +2 -2
  37. data/lib/mongoid/association/referenced/counter_cache.rb +10 -10
  38. data/lib/mongoid/association/referenced/eager.rb +2 -2
  39. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +70 -13
  40. data/lib/mongoid/association/referenced/has_and_belongs_to_many.rb +6 -3
  41. data/lib/mongoid/association/referenced/has_many/enumerable.rb +22 -30
  42. data/lib/mongoid/association/referenced/has_many/proxy.rb +29 -19
  43. data/lib/mongoid/association/referenced/has_many.rb +3 -3
  44. data/lib/mongoid/association/referenced/has_one/buildable.rb +1 -1
  45. data/lib/mongoid/association/referenced/has_one/nested_builder.rb +5 -5
  46. data/lib/mongoid/association/referenced/has_one/proxy.rb +9 -12
  47. data/lib/mongoid/association/referenced/has_one.rb +3 -3
  48. data/lib/mongoid/association/referenced/syncable.rb +4 -4
  49. data/lib/mongoid/association/reflections.rb +4 -4
  50. data/lib/mongoid/association/relatable.rb +44 -10
  51. data/lib/mongoid/association.rb +5 -5
  52. data/lib/mongoid/atomic/modifiers.rb +2 -2
  53. data/lib/mongoid/atomic.rb +7 -0
  54. data/lib/mongoid/attributes/dynamic.rb +4 -4
  55. data/lib/mongoid/attributes/nested.rb +6 -6
  56. data/lib/mongoid/attributes/processing.rb +37 -6
  57. data/lib/mongoid/attributes/projector.rb +2 -2
  58. data/lib/mongoid/attributes/readonly.rb +3 -3
  59. data/lib/mongoid/attributes.rb +51 -42
  60. data/lib/mongoid/changeable.rb +147 -14
  61. data/lib/mongoid/clients/options.rb +5 -1
  62. data/lib/mongoid/clients/sessions.rb +2 -14
  63. data/lib/mongoid/clients/storage_options.rb +2 -5
  64. data/lib/mongoid/clients/validators/storage.rb +3 -15
  65. data/lib/mongoid/collection_configurable.rb +58 -0
  66. data/lib/mongoid/composable.rb +2 -0
  67. data/lib/mongoid/config/defaults.rb +60 -0
  68. data/lib/mongoid/config/options.rb +3 -0
  69. data/lib/mongoid/config/validators/async_query_executor.rb +24 -0
  70. data/lib/mongoid/config/validators/client.rb +6 -6
  71. data/lib/mongoid/config/validators.rb +1 -0
  72. data/lib/mongoid/config.rb +153 -18
  73. data/lib/mongoid/contextual/aggregable/memory.rb +24 -16
  74. data/lib/mongoid/contextual/aggregable/mongo.rb +5 -5
  75. data/lib/mongoid/contextual/aggregable/none.rb +1 -1
  76. data/lib/mongoid/contextual/atomic.rb +1 -1
  77. data/lib/mongoid/contextual/geo_near.rb +7 -7
  78. data/lib/mongoid/contextual/map_reduce.rb +2 -2
  79. data/lib/mongoid/contextual/memory.rb +285 -58
  80. data/lib/mongoid/contextual/mongo/documents_loader.rb +177 -0
  81. data/lib/mongoid/contextual/mongo.rb +540 -346
  82. data/lib/mongoid/contextual/none.rb +193 -20
  83. data/lib/mongoid/contextual/queryable.rb +1 -1
  84. data/lib/mongoid/contextual.rb +14 -2
  85. data/lib/mongoid/copyable.rb +32 -8
  86. data/lib/mongoid/criteria/findable.rb +8 -5
  87. data/lib/mongoid/criteria/includable.rb +27 -22
  88. data/lib/mongoid/criteria/marshalable.rb +10 -2
  89. data/lib/mongoid/criteria/permission.rb +1 -1
  90. data/lib/mongoid/criteria/queryable/aggregable.rb +2 -2
  91. data/lib/mongoid/criteria/queryable/extensions/array.rb +3 -16
  92. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +25 -4
  93. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +2 -2
  94. data/lib/mongoid/criteria/queryable/extensions/date.rb +6 -1
  95. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +6 -1
  96. data/lib/mongoid/criteria/queryable/extensions/hash.rb +1 -17
  97. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -9
  98. data/lib/mongoid/criteria/queryable/extensions/object.rb +2 -1
  99. data/lib/mongoid/criteria/queryable/extensions/range.rb +13 -5
  100. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +3 -3
  101. data/lib/mongoid/criteria/queryable/extensions/set.rb +1 -1
  102. data/lib/mongoid/criteria/queryable/extensions/string.rb +4 -14
  103. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +4 -12
  104. data/lib/mongoid/criteria/queryable/extensions/time.rb +6 -1
  105. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +6 -1
  106. data/lib/mongoid/criteria/queryable/key.rb +4 -4
  107. data/lib/mongoid/criteria/queryable/mergeable.rb +1 -1
  108. data/lib/mongoid/criteria/queryable/optional.rb +11 -17
  109. data/lib/mongoid/criteria/queryable/options.rb +2 -2
  110. data/lib/mongoid/criteria/queryable/pipeline.rb +1 -1
  111. data/lib/mongoid/criteria/queryable/selectable.rb +47 -38
  112. data/lib/mongoid/criteria/queryable/selector.rb +92 -7
  113. data/lib/mongoid/criteria/queryable/smash.rb +40 -7
  114. data/lib/mongoid/criteria/queryable.rb +12 -7
  115. data/lib/mongoid/criteria/scopable.rb +2 -2
  116. data/lib/mongoid/criteria/translator.rb +45 -0
  117. data/lib/mongoid/criteria.rb +20 -40
  118. data/lib/mongoid/deprecable.rb +37 -0
  119. data/lib/mongoid/deprecation.rb +25 -0
  120. data/lib/mongoid/document.rb +127 -35
  121. data/lib/mongoid/equality.rb +8 -8
  122. data/lib/mongoid/errors/create_collection_failure.rb +33 -0
  123. data/lib/mongoid/errors/document_not_found.rb +10 -6
  124. data/lib/mongoid/errors/drop_collection_failure.rb +27 -0
  125. data/lib/mongoid/errors/immutable_attribute.rb +26 -0
  126. data/lib/mongoid/errors/invalid_async_query_executor.rb +25 -0
  127. data/lib/mongoid/errors/invalid_config_option.rb +1 -1
  128. data/lib/mongoid/errors/invalid_dependent_strategy.rb +1 -1
  129. data/lib/mongoid/errors/invalid_dot_dollar_assignment.rb +23 -0
  130. data/lib/mongoid/errors/invalid_field.rb +6 -2
  131. data/lib/mongoid/errors/invalid_field_type.rb +26 -0
  132. data/lib/mongoid/errors/invalid_global_executor_concurrency.rb +22 -0
  133. data/lib/mongoid/errors/invalid_relation.rb +1 -1
  134. data/lib/mongoid/errors/invalid_relation_option.rb +1 -1
  135. data/lib/mongoid/errors/invalid_session_use.rb +1 -1
  136. data/lib/mongoid/errors/invalid_storage_options.rb +1 -1
  137. data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
  138. data/lib/mongoid/errors/mongoid_error.rb +3 -3
  139. data/lib/mongoid/errors/nested_attributes_metadata_not_found.rb +1 -1
  140. data/lib/mongoid/errors/no_client_database.rb +1 -1
  141. data/lib/mongoid/errors/no_client_hosts.rb +1 -1
  142. data/lib/mongoid/errors/readonly_attribute.rb +1 -1
  143. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +1 -1
  144. data/lib/mongoid/errors/unknown_attribute.rb +1 -1
  145. data/lib/mongoid/errors.rb +6 -3
  146. data/lib/mongoid/extensions/array.rb +9 -7
  147. data/lib/mongoid/extensions/big_decimal.rb +33 -10
  148. data/lib/mongoid/extensions/binary.rb +42 -0
  149. data/lib/mongoid/extensions/boolean.rb +8 -2
  150. data/lib/mongoid/extensions/date.rb +26 -20
  151. data/lib/mongoid/extensions/date_time.rb +1 -1
  152. data/lib/mongoid/extensions/false_class.rb +1 -1
  153. data/lib/mongoid/extensions/float.rb +7 -4
  154. data/lib/mongoid/extensions/hash.rb +37 -8
  155. data/lib/mongoid/extensions/integer.rb +7 -4
  156. data/lib/mongoid/extensions/module.rb +1 -1
  157. data/lib/mongoid/extensions/object.rb +10 -8
  158. data/lib/mongoid/extensions/range.rb +41 -10
  159. data/lib/mongoid/extensions/regexp.rb +11 -4
  160. data/lib/mongoid/extensions/set.rb +11 -4
  161. data/lib/mongoid/extensions/string.rb +11 -22
  162. data/lib/mongoid/extensions/symbol.rb +4 -15
  163. data/lib/mongoid/extensions/time.rb +29 -16
  164. data/lib/mongoid/extensions/time_with_zone.rb +1 -2
  165. data/lib/mongoid/extensions/true_class.rb +1 -1
  166. data/lib/mongoid/extensions.rb +1 -0
  167. data/lib/mongoid/factory.rb +55 -7
  168. data/lib/mongoid/fields/foreign_key.rb +11 -4
  169. data/lib/mongoid/fields/localized.rb +19 -4
  170. data/lib/mongoid/fields/standard.rb +17 -7
  171. data/lib/mongoid/fields/validators/macro.rb +3 -9
  172. data/lib/mongoid/fields.rb +129 -20
  173. data/lib/mongoid/findable.rb +54 -24
  174. data/lib/mongoid/indexable/specification.rb +2 -2
  175. data/lib/mongoid/indexable/validators/options.rb +6 -2
  176. data/lib/mongoid/interceptable.rb +188 -16
  177. data/lib/mongoid/matchable.rb +1 -1
  178. data/lib/mongoid/matcher/eq_impl.rb +1 -1
  179. data/lib/mongoid/matcher/type.rb +1 -1
  180. data/lib/mongoid/matcher.rb +33 -13
  181. data/lib/mongoid/persistable/creatable.rb +19 -9
  182. data/lib/mongoid/persistable/deletable.rb +2 -2
  183. data/lib/mongoid/persistable/destroyable.rb +1 -1
  184. data/lib/mongoid/persistable/savable.rb +14 -2
  185. data/lib/mongoid/persistable/unsettable.rb +2 -2
  186. data/lib/mongoid/persistable/updatable.rb +69 -12
  187. data/lib/mongoid/persistable/upsertable.rb +21 -2
  188. data/lib/mongoid/persistable.rb +6 -3
  189. data/lib/mongoid/persistence_context.rb +6 -4
  190. data/lib/mongoid/query_cache.rb +13 -261
  191. data/lib/mongoid/railties/controller_runtime.rb +1 -1
  192. data/lib/mongoid/railties/database.rake +7 -2
  193. data/lib/mongoid/reloadable.rb +10 -8
  194. data/lib/mongoid/scopable.rb +15 -13
  195. data/lib/mongoid/selectable.rb +1 -2
  196. data/lib/mongoid/serializable.rb +10 -6
  197. data/lib/mongoid/stateful.rb +57 -10
  198. data/lib/mongoid/tasks/database.rake +12 -0
  199. data/lib/mongoid/tasks/database.rb +20 -2
  200. data/lib/mongoid/threaded/lifecycle.rb +5 -5
  201. data/lib/mongoid/threaded.rb +42 -12
  202. data/lib/mongoid/timestamps/created.rb +1 -1
  203. data/lib/mongoid/timestamps/updated.rb +2 -2
  204. data/lib/mongoid/touchable.rb +2 -3
  205. data/lib/mongoid/traversable.rb +5 -4
  206. data/lib/mongoid/utils.rb +22 -0
  207. data/lib/mongoid/validatable/localizable.rb +1 -1
  208. data/lib/mongoid/validatable/macros.rb +5 -7
  209. data/lib/mongoid/validatable/presence.rb +2 -2
  210. data/lib/mongoid/validatable/uniqueness.rb +9 -8
  211. data/lib/mongoid/validatable.rb +9 -6
  212. data/lib/mongoid/version.rb +1 -1
  213. data/lib/mongoid/warnings.rb +19 -4
  214. data/lib/mongoid.rb +17 -3
  215. data/spec/config/mongoid.yml +16 -0
  216. data/spec/integration/app_spec.rb +10 -14
  217. data/spec/integration/associations/belongs_to_spec.rb +18 -0
  218. data/spec/integration/associations/embedded_spec.rb +15 -0
  219. data/spec/integration/associations/embeds_many_spec.rb +15 -2
  220. data/spec/integration/associations/embeds_one_spec.rb +18 -0
  221. data/spec/integration/associations/foreign_key_spec.rb +9 -0
  222. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +21 -0
  223. data/spec/integration/associations/has_one_spec.rb +97 -1
  224. data/spec/integration/associations/scope_option_spec.rb +1 -1
  225. data/spec/integration/callbacks_models.rb +132 -1
  226. data/spec/integration/callbacks_spec.rb +381 -4
  227. data/spec/integration/criteria/range_spec.rb +95 -1
  228. data/spec/integration/discriminator_key_spec.rb +118 -80
  229. data/spec/integration/dots_and_dollars_spec.rb +277 -0
  230. data/spec/integration/i18n_fallbacks_spec.rb +3 -32
  231. data/spec/integration/matcher_examples_spec.rb +20 -13
  232. data/spec/integration/matcher_operator_data/type_decimal.yml +3 -2
  233. data/spec/integration/matcher_operator_spec.rb +3 -5
  234. data/spec/integration/persistence/range_field_spec.rb +350 -0
  235. data/spec/mongoid/association/counter_cache_spec.rb +1 -1
  236. data/spec/mongoid/association/depending_spec.rb +9 -9
  237. data/spec/mongoid/association/eager_spec.rb +2 -1
  238. data/spec/mongoid/association/embedded/embedded_in/binding_spec.rb +2 -1
  239. data/spec/mongoid/association/embedded/embedded_in/buildable_spec.rb +54 -0
  240. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +96 -9
  241. data/spec/mongoid/association/embedded/embeds_many/buildable_spec.rb +112 -0
  242. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +255 -65
  243. data/spec/mongoid/association/embedded/embeds_many_models.rb +37 -0
  244. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +12 -0
  245. data/spec/mongoid/association/embedded/embeds_many_spec.rb +68 -0
  246. data/spec/mongoid/association/embedded/embeds_one/buildable_spec.rb +25 -0
  247. data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
  248. data/spec/mongoid/association/embedded/embeds_one_models.rb +19 -0
  249. data/spec/mongoid/association/embedded/embeds_one_spec.rb +28 -0
  250. data/spec/mongoid/association/referenced/belongs_to/binding_spec.rb +2 -1
  251. data/spec/mongoid/association/referenced/belongs_to/buildable_spec.rb +54 -0
  252. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +15 -0
  253. data/spec/mongoid/association/referenced/belongs_to_models.rb +11 -0
  254. data/spec/mongoid/association/referenced/belongs_to_spec.rb +4 -20
  255. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +186 -229
  256. data/spec/mongoid/association/referenced/has_and_belongs_to_many_models.rb +25 -0
  257. data/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb +35 -2
  258. data/spec/mongoid/association/referenced/has_many/buildable_spec.rb +109 -0
  259. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +2 -56
  260. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +173 -177
  261. data/spec/mongoid/association/referenced/has_many_models.rb +3 -1
  262. data/spec/mongoid/association/referenced/has_many_spec.rb +25 -0
  263. data/spec/mongoid/association/referenced/has_one/buildable_spec.rb +2 -2
  264. data/spec/mongoid/association/referenced/has_one/proxy_spec.rb +107 -1
  265. data/spec/mongoid/association/referenced/has_one_models.rb +16 -0
  266. data/spec/mongoid/association/syncable_spec.rb +15 -1
  267. data/spec/mongoid/atomic/paths_spec.rb +0 -14
  268. data/spec/mongoid/attributes/nested_spec.rb +80 -11
  269. data/spec/mongoid/attributes/nested_spec_models.rb +48 -0
  270. data/spec/mongoid/attributes/projector_spec.rb +1 -5
  271. data/spec/mongoid/attributes_spec.rb +510 -33
  272. data/spec/mongoid/changeable_spec.rb +429 -37
  273. data/spec/mongoid/clients/factory_spec.rb +23 -30
  274. data/spec/mongoid/clients/sessions_spec.rb +0 -38
  275. data/spec/mongoid/clients_spec.rb +149 -15
  276. data/spec/mongoid/collection_configurable_spec.rb +158 -0
  277. data/spec/mongoid/config/defaults_spec.rb +160 -0
  278. data/spec/mongoid/config_spec.rb +214 -31
  279. data/spec/mongoid/contextual/aggregable/memory_spec.rb +396 -158
  280. data/spec/mongoid/contextual/aggregable/memory_table.yml +88 -0
  281. data/spec/mongoid/contextual/aggregable/memory_table_spec.rb +62 -0
  282. data/spec/mongoid/contextual/map_reduce_spec.rb +2 -16
  283. data/spec/mongoid/contextual/memory_spec.rb +850 -88
  284. data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
  285. data/spec/mongoid/contextual/mongo_spec.rb +2309 -1127
  286. data/spec/mongoid/contextual/none_spec.rb +60 -21
  287. data/spec/mongoid/copyable_spec.rb +453 -11
  288. data/spec/mongoid/criteria/findable_spec.rb +86 -210
  289. data/spec/mongoid/criteria/includable_spec.rb +1492 -0
  290. data/spec/mongoid/criteria/includable_spec_models.rb +54 -0
  291. data/spec/mongoid/criteria/marshalable_spec.rb +18 -1
  292. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +7 -19
  293. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +134 -26
  294. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +11 -0
  295. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +11 -0
  296. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +0 -15
  297. data/spec/mongoid/criteria/queryable/extensions/numeric_spec.rb +73 -7
  298. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -69
  299. data/spec/mongoid/criteria/queryable/extensions/symbol_spec.rb +0 -59
  300. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +11 -0
  301. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +11 -0
  302. data/spec/mongoid/criteria/queryable/optional_spec.rb +15 -484
  303. data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
  304. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +469 -0
  305. data/spec/mongoid/criteria/queryable/selectable_spec.rb +78 -86
  306. data/spec/mongoid/criteria/queryable/selector_spec.rb +15 -3
  307. data/spec/mongoid/criteria/translator_spec.rb +132 -0
  308. data/spec/mongoid/criteria_projection_spec.rb +1 -5
  309. data/spec/mongoid/criteria_spec.rb +469 -1205
  310. data/spec/mongoid/document_fields_spec.rb +173 -24
  311. data/spec/mongoid/document_spec.rb +32 -41
  312. data/spec/mongoid/errors/document_not_found_spec.rb +29 -2
  313. data/spec/mongoid/errors/invalid_field_spec.rb +1 -1
  314. data/spec/mongoid/errors/invalid_field_type_spec.rb +55 -0
  315. data/spec/mongoid/errors/mongoid_error_spec.rb +3 -1
  316. data/spec/mongoid/errors/no_environment_spec.rb +3 -3
  317. data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
  318. data/spec/mongoid/errors/too_many_nested_attribute_records_spec.rb +1 -1
  319. data/spec/mongoid/extensions/array_spec.rb +16 -2
  320. data/spec/mongoid/extensions/big_decimal_spec.rb +712 -212
  321. data/spec/mongoid/extensions/binary_spec.rb +44 -9
  322. data/spec/mongoid/extensions/boolean_spec.rb +68 -82
  323. data/spec/mongoid/extensions/date_class_mongoize_spec.rb +7 -3
  324. data/spec/mongoid/extensions/date_spec.rb +71 -1
  325. data/spec/mongoid/extensions/date_time_spec.rb +15 -9
  326. data/spec/mongoid/extensions/float_spec.rb +53 -74
  327. data/spec/mongoid/extensions/hash_spec.rb +33 -3
  328. data/spec/mongoid/extensions/integer_spec.rb +50 -64
  329. data/spec/mongoid/extensions/range_spec.rb +255 -54
  330. data/spec/mongoid/extensions/regexp_spec.rb +58 -33
  331. data/spec/mongoid/extensions/set_spec.rb +106 -0
  332. data/spec/mongoid/extensions/string_spec.rb +53 -25
  333. data/spec/mongoid/extensions/symbol_spec.rb +18 -25
  334. data/spec/mongoid/extensions/time_spec.rb +639 -106
  335. data/spec/mongoid/extensions/time_with_zone_spec.rb +24 -83
  336. data/spec/mongoid/factory_spec.rb +61 -1
  337. data/spec/mongoid/fields/localized_spec.rb +80 -37
  338. data/spec/mongoid/fields_spec.rb +500 -84
  339. data/spec/mongoid/findable_spec.rb +450 -58
  340. data/spec/mongoid/indexable/specification_spec.rb +2 -2
  341. data/spec/mongoid/indexable_spec.rb +55 -30
  342. data/spec/mongoid/interceptable_spec.rb +824 -22
  343. data/spec/mongoid/interceptable_spec_models.rb +235 -4
  344. data/spec/mongoid/matcher/extract_attribute_spec.rb +1 -5
  345. data/spec/mongoid/mongoizable_spec.rb +285 -0
  346. data/spec/mongoid/persistable/creatable_spec.rb +2 -2
  347. data/spec/mongoid/persistable/deletable_spec.rb +28 -8
  348. data/spec/mongoid/persistable/destroyable_spec.rb +28 -8
  349. data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
  350. data/spec/mongoid/persistable/logical_spec.rb +37 -0
  351. data/spec/mongoid/persistable/poppable_spec.rb +36 -0
  352. data/spec/mongoid/persistable/pullable_spec.rb +72 -0
  353. data/spec/mongoid/persistable/pushable_spec.rb +72 -0
  354. data/spec/mongoid/persistable/renamable_spec.rb +36 -0
  355. data/spec/mongoid/persistable/savable_spec.rb +96 -0
  356. data/spec/mongoid/persistable/settable_spec.rb +37 -0
  357. data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
  358. data/spec/mongoid/persistable/updatable_spec.rb +20 -28
  359. data/spec/mongoid/persistable/upsertable_spec.rb +89 -1
  360. data/spec/mongoid/persistence_context_spec.rb +31 -57
  361. data/spec/mongoid/query_cache_middleware_spec.rb +0 -18
  362. data/spec/mongoid/query_cache_spec.rb +56 -215
  363. data/spec/mongoid/reloadable_spec.rb +83 -6
  364. data/spec/mongoid/scopable_spec.rb +91 -1
  365. data/spec/mongoid/serializable_spec.rb +9 -30
  366. data/spec/mongoid/shardable_spec.rb +4 -4
  367. data/spec/mongoid/stateful_spec.rb +150 -8
  368. data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
  369. data/spec/mongoid/tasks/database_spec.rb +127 -0
  370. data/spec/mongoid/timestamps_spec.rb +392 -4
  371. data/spec/mongoid/timestamps_spec_models.rb +67 -0
  372. data/spec/mongoid/touchable_spec.rb +390 -2
  373. data/spec/mongoid/touchable_spec_models.rb +14 -8
  374. data/spec/mongoid/traversable_spec.rb +13 -35
  375. data/spec/mongoid/validatable/presence_spec.rb +1 -1
  376. data/spec/mongoid/validatable/uniqueness_spec.rb +58 -31
  377. data/spec/mongoid/warnings_spec.rb +35 -0
  378. data/spec/mongoid_spec.rb +34 -16
  379. data/spec/rails/controller_extension/controller_runtime_spec.rb +2 -2
  380. data/spec/rails/mongoid_spec.rb +4 -16
  381. data/spec/shared/lib/mrss/docker_runner.rb +8 -0
  382. data/spec/shared/lib/mrss/lite_constraints.rb +2 -2
  383. data/spec/shared/lib/mrss/server_version_registry.rb +16 -23
  384. data/spec/shared/lib/mrss/utils.rb +28 -6
  385. data/spec/shared/share/Dockerfile.erb +36 -40
  386. data/spec/shared/shlib/server.sh +28 -4
  387. data/spec/shared/shlib/set_env.sh +4 -4
  388. data/spec/spec_helper.rb +5 -0
  389. data/spec/support/constraints.rb +24 -0
  390. data/spec/support/immutable_ids.rb +118 -0
  391. data/spec/support/macros.rb +78 -0
  392. data/spec/support/models/artist.rb +0 -1
  393. data/spec/support/models/augmentation.rb +12 -0
  394. data/spec/support/models/band.rb +4 -0
  395. data/spec/support/models/book.rb +1 -0
  396. data/spec/support/models/building.rb +2 -0
  397. data/spec/support/models/catalog.rb +24 -0
  398. data/spec/support/models/circus.rb +3 -0
  399. data/spec/support/models/cover.rb +10 -0
  400. data/spec/support/models/fanatic.rb +8 -0
  401. data/spec/support/models/implant.rb +9 -0
  402. data/spec/support/models/label.rb +2 -0
  403. data/spec/support/models/passport.rb +9 -0
  404. data/spec/support/models/person.rb +2 -0
  405. data/spec/support/models/player.rb +2 -0
  406. data/spec/support/models/powerup.rb +12 -0
  407. data/spec/support/models/product.rb +1 -0
  408. data/spec/support/models/purse.rb +9 -0
  409. data/spec/support/models/registry.rb +1 -0
  410. data/spec/support/models/school.rb +14 -0
  411. data/spec/support/models/shield.rb +18 -0
  412. data/spec/support/models/student.rb +14 -0
  413. data/spec/support/models/weapon.rb +12 -0
  414. data.tar.gz.sig +0 -0
  415. metadata +100 -22
  416. metadata.gz.sig +0 -0
  417. data/lib/mongoid/errors/eager_load.rb +0 -23
  418. data/lib/mongoid/errors/invalid_value.rb +0 -17
  419. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -60
  420. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -60
  421. data/spec/mongoid/errors/eager_load_spec.rb +0 -31
@@ -27,7 +27,7 @@ module Mongoid
27
27
  :before_save,
28
28
  :before_update,
29
29
  :before_upsert,
30
- :before_validation
30
+ :before_validation,
31
31
  ].freeze
32
32
 
33
33
  included do
@@ -37,6 +37,14 @@ module Mongoid
37
37
  define_model_callbacks :build, :find, :initialize, :touch, only: :after
38
38
  define_model_callbacks :create, :destroy, :save, :update, :upsert
39
39
 
40
+ # This callback is used internally by Mongoid to save association
41
+ # targets for referenced associations after the parent model is persisted.
42
+ #
43
+ # @api private
44
+ define_model_callbacks :persist_parent
45
+
46
+ define_model_callbacks :commit, :rollback, only: :after
47
+
40
48
  attr_accessor :before_callback_halted
41
49
  end
42
50
 
@@ -47,7 +55,7 @@ module Mongoid
47
55
  #
48
56
  # @param [ Symbol ] kind The type of callback.
49
57
  #
50
- # @return [ true, false ] If the callback can be executed.
58
+ # @return [ true | false ] If the callback can be executed.
51
59
  def callback_executable?(kind)
52
60
  respond_to?("_#{kind}_callbacks")
53
61
  end
@@ -60,7 +68,7 @@ module Mongoid
60
68
  #
61
69
  # @param [ Symbol ] kind The callback kind.
62
70
  #
63
- # @return [ true, false ] If the document is in a callback state.
71
+ # @return [ true | false ] If the document is in a callback state.
64
72
  def in_callback_state?(kind)
65
73
  [ :create, :destroy ].include?(kind) || new_record? || flagged_for_destroy? || changed?
66
74
  end
@@ -73,7 +81,7 @@ module Mongoid
73
81
  # @example Run only the after save callbacks.
74
82
  # model.run_after_callbacks(:save)
75
83
  #
76
- # @param [ Array<Symbol> ] kinds The events that are occurring.
84
+ # @param [ Symbol... ] *kinds The events that are occurring.
77
85
  #
78
86
  # @return [ Object ] The result of the chain executing.
79
87
  def run_after_callbacks(*kinds)
@@ -90,7 +98,7 @@ module Mongoid
90
98
  # @example Run only the before save callbacks.
91
99
  # model.run_before_callbacks(:save, :create)
92
100
  #
93
- # @param [ Array<Symbol> ] kinds The events that are occurring.
101
+ # @param [ Symbol... ] *kinds The events that are occurring.
94
102
  #
95
103
  # @return [ Object ] The result of the chain executing.
96
104
  def run_before_callbacks(*kinds)
@@ -109,22 +117,167 @@ module Mongoid
109
117
  # end
110
118
  #
111
119
  # @param [ Symbol ] kind The type of callback to execute.
112
- # @param [ Array ] args Any options.
113
- #
114
- # @return [ Document ] The document
115
- ruby2_keywords def run_callbacks(kind, *args, &block)
116
- cascadable_children(kind).each do |child|
117
- if child.run_callbacks(child_callback_type(kind, child), *args) == false
118
- return false
120
+ # @param [ true | false ] with_children Flag specifies whether callbacks of embedded document should be run.
121
+ def run_callbacks(kind, with_children: true, &block)
122
+ if with_children
123
+ cascadable_children(kind).each do |child|
124
+ if child.run_callbacks(child_callback_type(kind, child), with_children: with_children) == false
125
+ return false
126
+ end
119
127
  end
120
128
  end
121
129
  if callback_executable?(kind)
122
- super(kind, *args, &block)
130
+ super(kind, &block)
123
131
  else
124
132
  true
125
133
  end
126
134
  end
127
135
 
136
+ # Run the callbacks for embedded documents.
137
+ #
138
+ # @param [ Symbol ] kind The type of callback to execute.
139
+ # @param [ Array<Document> ] children Children to execute callbacks on. If
140
+ # nil, callbacks will be executed on all cascadable children of
141
+ # the document.
142
+ #
143
+ # @api private
144
+ def _mongoid_run_child_callbacks(kind, children: nil, &block)
145
+ if Mongoid::Config.around_callbacks_for_embeds
146
+ _mongoid_run_child_callbacks_with_around(kind, children: children, &block)
147
+ else
148
+ _mongoid_run_child_callbacks_without_around(kind, children: children, &block)
149
+ end
150
+ end
151
+
152
+ # Execute the callbacks of given kind for embedded documents including
153
+ # around callbacks.
154
+ #
155
+ # @note This method is prone to stack overflow errors if the document
156
+ # has a large number of embedded documents. It is recommended to avoid
157
+ # using around callbacks for embedded documents until a proper solution
158
+ # is implemented.
159
+ #
160
+ # @param [ Symbol ] kind The type of callback to execute.
161
+ # @param [ Array<Document> ] children Children to execute callbacks on. If
162
+ # nil, callbacks will be executed on all cascadable children of
163
+ # the document.
164
+ #
165
+ # @api private
166
+ def _mongoid_run_child_callbacks_with_around(kind, children: nil, &block)
167
+ child, *tail = (children || cascadable_children(kind))
168
+ with_children = !Mongoid::Config.prevent_multiple_calls_of_embedded_callbacks
169
+ if child.nil?
170
+ block&.call
171
+ elsif tail.empty?
172
+ child.run_callbacks(child_callback_type(kind, child), with_children: with_children, &block)
173
+ else
174
+ child.run_callbacks(child_callback_type(kind, child), with_children: with_children) do
175
+ _mongoid_run_child_callbacks_with_around(kind, children: tail, &block)
176
+ end
177
+ end
178
+ end
179
+
180
+ # Execute the callbacks of given kind for embedded documents without
181
+ # around callbacks.
182
+ #
183
+ # @param [ Symbol ] kind The type of callback to execute.
184
+ # @param [ Array<Document> ] children Children to execute callbacks on. If
185
+ # nil, callbacks will be executed on all cascadable children of
186
+ # the document.
187
+ #
188
+ # @api private
189
+ def _mongoid_run_child_callbacks_without_around(kind, children: nil, &block)
190
+ children = (children || cascadable_children(kind))
191
+ callback_list = _mongoid_run_child_before_callbacks(kind, children: children)
192
+ return false if callback_list == false
193
+ value = block&.call
194
+ callback_list.each do |_next_sequence, env|
195
+ env.value &&= value
196
+ end
197
+ return false if _mongoid_run_child_after_callbacks(callback_list: callback_list) == false
198
+
199
+ value
200
+ end
201
+
202
+ # Execute the before callbacks of given kind for embedded documents.
203
+ #
204
+ # @param [ Symbol ] kind The type of callback to execute.
205
+ # @param [ Array<Document> ] children Children to execute callbacks on.
206
+ # @param [ Array<ActiveSupport::Callbacks::CallbackSequence, ActiveSupport::Callbacks::Filters::Environment> ] callback_list List of
207
+ # pairs of callback sequence and environment. This list will be later used
208
+ # to execute after callbacks in reverse order.
209
+ #
210
+ # @api private
211
+ def _mongoid_run_child_before_callbacks(kind, children: [], callback_list: [])
212
+ children.each do |child|
213
+ chain = child.__callbacks[child_callback_type(kind, child)]
214
+ env = ActiveSupport::Callbacks::Filters::Environment.new(child, false, nil)
215
+ next_sequence = compile_callbacks(chain)
216
+ unless next_sequence.final?
217
+ Mongoid.logger.warn("Around callbacks are disabled for embedded documents. Skipping around callbacks for #{child.class.name}.")
218
+ Mongoid.logger.warn("To enable around callbacks for embedded documents, set Mongoid::Config.around_callbacks_for_embeds to true.")
219
+ end
220
+ next_sequence.invoke_before(env)
221
+ return false if env.halted
222
+ env.value = !env.halted
223
+ callback_list << [next_sequence, env]
224
+ if (grandchildren = child.send(:cascadable_children, kind))
225
+ _mongoid_run_child_before_callbacks(kind, children: grandchildren, callback_list: callback_list)
226
+ end
227
+ end
228
+ callback_list
229
+ end
230
+
231
+ # Execute the after callbacks.
232
+ #
233
+ # @param [ Array<ActiveSupport::Callbacks::CallbackSequence, ActiveSupport::Callbacks::Filters::Environment> ] callback_list List of
234
+ # pairs of callback sequence and environment.
235
+ def _mongoid_run_child_after_callbacks(callback_list: [])
236
+ callback_list.reverse_each do |next_sequence, env|
237
+ next_sequence.invoke_after(env)
238
+ return false if env.halted
239
+ end
240
+ end
241
+
242
+ # Returns the stored callbacks to be executed later.
243
+ #
244
+ # @return [ Array<Symbol> ] Method symbols of the stored pending callbacks.
245
+ #
246
+ # @api private
247
+ def pending_callbacks
248
+ @pending_callbacks ||= [].to_set
249
+ end
250
+
251
+ # Stores callbacks to be executed later. A good use case for
252
+ # this is delaying the after_find and after_initialize callbacks until the
253
+ # associations are set on the document. This can also be used to delay
254
+ # applying the defaults on a document.
255
+ #
256
+ # @param [ Array<Symbol> ] value Method symbols of the pending callbacks to store.
257
+ #
258
+ # @return [ Array<Symbol> ] Method symbols of the stored pending callbacks.
259
+ #
260
+ # @api private
261
+ def pending_callbacks=(value)
262
+ @pending_callbacks = value
263
+ end
264
+
265
+ # Run the pending callbacks. If the callback is :apply_defaults, we will apply
266
+ # the defaults for this document. Otherwise, the callback is passed to the
267
+ # run_callbacks function.
268
+ #
269
+ # @api private
270
+ def run_pending_callbacks
271
+ pending_callbacks.each do |cb|
272
+ if [:apply_defaults, :apply_post_processed_defaults].include?(cb)
273
+ send(cb)
274
+ else
275
+ self.run_callbacks(cb, with_children: false)
276
+ end
277
+ end
278
+ pending_callbacks.clear
279
+ end
280
+
128
281
  private
129
282
 
130
283
  # We need to hook into this for autosave, since we don't want it firing if
@@ -135,7 +288,7 @@ module Mongoid
135
288
  # @example Was a before callback halted?
136
289
  # document.before_callback_halted?
137
290
  #
138
- # @return [ true, false ] If a before callback was halted.
291
+ # @return [ true | false ] If a before callback was halted.
139
292
  def before_callback_halted?
140
293
  !!@before_callback_halted
141
294
  end
@@ -175,7 +328,7 @@ module Mongoid
175
328
  # @param [ Symbol ] kind The type of callback.
176
329
  # @param [ Document ] child The child document.
177
330
  #
178
- # @return [ true, false ] If the child should fire the callback.
331
+ # @return [ true | false ] If the child should fire the callback.
179
332
  def cascadable_child?(kind, child, association)
180
333
  return false if kind == :initialize || kind == :find || kind == :touch
181
334
  return false if kind == :validate && association.validate?
@@ -238,7 +391,7 @@ module Mongoid
238
391
  end
239
392
  self.class.send :define_method, name do
240
393
  env = ActiveSupport::Callbacks::Filters::Environment.new(self, false, nil)
241
- sequence = chain.compile
394
+ sequence = compile_callbacks(chain)
242
395
  sequence.invoke_before(env)
243
396
  env.value = !env.halted
244
397
  sequence.invoke_after(env)
@@ -248,5 +401,24 @@ module Mongoid
248
401
  end
249
402
  send(name)
250
403
  end
404
+
405
+ # Compile the callback chain.
406
+ #
407
+ # This method hides the differences between ActiveSupport implementations
408
+ # before and after 7.1.
409
+ #
410
+ # @param [ ActiveSupport::Callbacks::CallbackChain ] chain The callback chain.
411
+ # @param [ Symbol | nil ] type The type of callback chain to compile.
412
+ #
413
+ # @return [ ActiveSupport::Callbacks::CallbackSequence ] The compiled callback sequence.
414
+ def compile_callbacks(chain, type = nil)
415
+ if chain.method(:compile).arity == 0
416
+ # ActiveSupport < 7.1
417
+ chain.compile
418
+ else
419
+ # ActiveSupport >= 7.1
420
+ chain.compile(type)
421
+ end
422
+ end
251
423
  end
252
424
  end
@@ -15,7 +15,7 @@ module Mongoid
15
15
  #
16
16
  # @param [ Hash ] selector The MongoDB selector.
17
17
  #
18
- # @return [ true, false ] True if matches, false if not.
18
+ # @return [ true | false ] True if matches, false if not.
19
19
  def _matches?(selector)
20
20
  Matcher::Expression.matches?(self, selector)
21
21
  end
@@ -46,7 +46,7 @@ module Mongoid
46
46
  end
47
47
  end
48
48
 
49
- # Per https://docs.mongodb.com/ruby-driver/current/tutorials/bson-v4/#time-instances,
49
+ # Per https://www.mongodb.com/docs/ruby-driver/current/tutorials/bson-v4/#time-instances,
50
50
  # > Times in BSON (and MongoDB) can only have millisecond precision. When Ruby Time instances
51
51
  # are serialized to BSON or Extended JSON, the times are floored to the nearest millisecond.
52
52
  #
@@ -1,7 +1,7 @@
1
1
  module Mongoid
2
2
  module Matcher
3
3
 
4
- # @see https://docs.mongodb.com/manual/reference/operator/query/type/
4
+ # @see https://www.mongodb.com/docs/manual/reference/operator/query/type/
5
5
  #
6
6
  # @api private
7
7
  module Type
@@ -2,7 +2,6 @@ module Mongoid
2
2
 
3
3
  # @api private
4
4
  module Matcher
5
-
6
5
  # Extracts field values in the document at the specified key.
7
6
  #
8
7
  # The document can be a Hash or a model instance.
@@ -24,23 +23,28 @@ module Mongoid
24
23
  # an array of values of the `bar` field in each of the hashes in the
25
24
  # `foo` array.
26
25
  #
27
- # The return value is a two-element array. The first element is the value
28
- # retrieved, or an array of values. The second element is a boolean flag
29
- # indicating whether an array was expanded at any point during the key
30
- # traversal (because the respective document field was an array).
26
+ # This method can return an individual field value in some document
27
+ # or an array of values from multiple documents. The array can be returned
28
+ # because a field value in the specified path is an array of primitive
29
+ # values (e.g. integers) or because a field value in the specified path
30
+ # is an array of documents (e.g. a one-to-many embedded association),
31
+ # in which case the leaf value may be a scalar for each individual document.
32
+ # If the leaf value is an array and a one-to-many association was traversed,
33
+ # the return value will be an array of arrays. Note that an individual
34
+ # field value can also be an array and this case is indistinguishable
35
+ # from and behaves identically to association traversal for the purposes
36
+ # of, for example, subsequent array element retrieval.
31
37
  #
32
38
  # @param [ Document | Hash ] document The document to extract from.
33
39
  # @param [ String ] key The key path to extract.
34
40
  #
35
- # @return [ Array<true | false, Object | Array, true | false> ]
36
- # Whether the value existed in the document, the extracted value
37
- # and the array expansion flag.
41
+ # @return [ Object | Array ] Field value or values.
38
42
  module_function def extract_attribute(document, key)
39
43
  if document.respond_to?(:as_attributes, true)
40
44
  # If a document has hash fields, as_attributes would keep those fields
41
45
  # as Hash instances which do not offer indifferent access.
42
46
  # Convert to BSON::Document to get indifferent access on hash fields.
43
- document = BSON::Document.new(document.send(:as_attributes))
47
+ document = document.send(:as_attributes)
44
48
  end
45
49
 
46
50
  current = [document]
@@ -50,8 +54,9 @@ module Mongoid
50
54
  current.each do |doc|
51
55
  case doc
52
56
  when Hash
53
- if doc.key?(field)
54
- new << doc[field]
57
+ actual_key = find_exact_key(doc, field)
58
+ if !actual_key.nil?
59
+ new << doc[actual_key]
55
60
  end
56
61
  when Array
57
62
  if (index = field.to_i).to_s == field
@@ -61,8 +66,9 @@ module Mongoid
61
66
  end
62
67
  doc.each do |subdoc|
63
68
  if Hash === subdoc
64
- if subdoc.key?(field)
65
- new << subdoc[field]
69
+ actual_key = find_exact_key(subdoc, field)
70
+ if !actual_key.nil?
71
+ new << subdoc[actual_key]
66
72
  end
67
73
  end
68
74
  end
@@ -74,6 +80,20 @@ module Mongoid
74
80
 
75
81
  current
76
82
  end
83
+
84
+ # Indifferent string or symbol key lookup, returning the exact key.
85
+ #
86
+ # @param [ Hash ] hash The input hash.
87
+ # @param [ String | Symbol ] key The key to perform indifferent lookups with.
88
+ #
89
+ # @return [ String | Symbol | nil ] The exact key (with the correct type) that exists in the hash, or nil if the key does not exist.
90
+ module_function def find_exact_key(hash, key)
91
+ key_s = key.to_s
92
+ return key_s if hash.key?(key_s)
93
+
94
+ key_sym = key.to_sym
95
+ hash.key?(key_sym) ? key_sym : nil
96
+ end
77
97
  end
78
98
  end
79
99
 
@@ -100,15 +100,25 @@ module Mongoid
100
100
  #
101
101
  # @return [ Document ] The document.
102
102
  def prepare_insert(options = {})
103
+ raise Errors::ReadonlyDocument.new(self.class) if readonly? && !Mongoid.legacy_readonly
103
104
  return self if performing_validations?(options) &&
104
105
  invalid?(options[:context] || :create)
105
- result = run_callbacks(:save) do
106
- run_callbacks(:create) do
107
- yield(self)
108
- post_process_insert
106
+ run_callbacks(:save, with_children: false) do
107
+ run_callbacks(:create, with_children: false) do
108
+ run_callbacks(:persist_parent, with_children: false) do
109
+ _mongoid_run_child_callbacks(:save) do
110
+ _mongoid_run_child_callbacks(:create) do
111
+ result = yield(self)
112
+ if !result.is_a?(Document) || result.errors.empty?
113
+ post_process_insert
114
+ post_process_persist(result, options)
115
+ end
116
+ end
117
+ end
118
+ end
109
119
  end
110
120
  end
111
- post_process_persist(result, options) and self
121
+ self
112
122
  end
113
123
 
114
124
  module ClassMethods
@@ -123,10 +133,10 @@ module Mongoid
123
133
  # @example Create multiple new documents.
124
134
  # Person.create({ title: "Mr" }, { title: "Mrs" })
125
135
  #
126
- # @param [ Hash, Array ] attributes The attributes to create with, or an
136
+ # @param [ Hash | Array ] attributes The attributes to create with, or an
127
137
  # Array of multiple attributes for multiple documents.
128
138
  #
129
- # @return [ Document, Array<Document> ] The newly created document(s).
139
+ # @return [ Document | Array<Document> ] The newly created document(s).
130
140
  def create(attributes = nil, &block)
131
141
  _creating do
132
142
  if attributes.is_a?(::Array)
@@ -150,10 +160,10 @@ module Mongoid
150
160
  # @example Create multiple new documents.
151
161
  # Person.create!({ title: "Mr" }, { title: "Mrs" })
152
162
  #
153
- # @param [ Hash, Array ] attributes The attributes to create with, or an
163
+ # @param [ Hash | Array ] attributes The attributes to create with, or an
154
164
  # Array of multiple attributes for multiple documents.
155
165
  #
156
- # @return [ Document, Array<Document> ] The newly created document(s).
166
+ # @return [ Document | Array<Document> ] The newly created document(s).
157
167
  def create!(attributes = nil, &block)
158
168
  _creating do
159
169
  if attributes.is_a?(::Array)
@@ -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?
@@ -86,7 +85,7 @@ module Mongoid
86
85
  #
87
86
  # @param [ Hash ] options The delete options.
88
87
  #
89
- # @return [ true, false ] If the parent should be notified.
88
+ # @return [ true | false ] If the parent should be notified.
90
89
  def notifying_parent?(options = {})
91
90
  !options.delete(:suppress)
92
91
  end
@@ -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,7 @@ module Mongoid
14
14
  #
15
15
  # @param [ Hash ] options Options to pass to destroy.
16
16
  #
17
- # @return [ true, false ] True if successful, false if not.
17
+ # @return [ true | false ] True if successful, false if not.
18
18
  def destroy(options = nil)
19
19
  raise Errors::ReadonlyDocument.new(self.class) if readonly?
20
20
  self.flagged_for_destroy = 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,10 +37,16 @@ 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
  #
37
- # @return [ true, false ] True if validation passed.
49
+ # @return [ true | false ] True if validation passed.
38
50
  def save!(options = {})
39
51
  unless save(options)
40
52
  fail_due_to_validation! unless errors.empty?
@@ -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)
@@ -13,13 +13,13 @@ module Mongoid
13
13
  # @example Update the attribute.
14
14
  # person.update_attribute(:title, "Sir")
15
15
  #
16
- # @param [ Symbol, String ] name The name of the attribute.
16
+ # @param [ Symbol | String ] name The name of the attribute.
17
17
  # @param [ Object ] value The new value of the attribute.a
18
18
  #
19
19
  # @raise [ Errors::ReadonlyAttribute ] If the field cannot be changed due
20
20
  # to being flagged as read-only.
21
21
  #
22
- # @return [ true, false ] True if save was successful, false if not.
22
+ # @return [ true | false ] True if save was successful, false if not.
23
23
  def update_attribute(name, value)
24
24
  as_writable_attribute!(name, value) do |access|
25
25
  normalized = name.to_s
@@ -35,7 +35,7 @@ module Mongoid
35
35
  #
36
36
  # @param [ Hash ] attributes The attributes to update.
37
37
  #
38
- # @return [ true, false ] True if validation passed, false if not.
38
+ # @return [ true | false ] True if validation passed, false if not.
39
39
  def update(attributes = {})
40
40
  assign_attributes(attributes)
41
41
  save
@@ -53,7 +53,7 @@ module Mongoid
53
53
  # @raise [ Errors::Validations ] If validation failed.
54
54
  # @raise [ Errors::Callbacks ] If a callback returns false.
55
55
  #
56
- # @return [ true, false ] True if validation passed.
56
+ # @return [ true | false ] True if validation passed.
57
57
  def update!(attributes = {})
58
58
  result = update_attributes(attributes)
59
59
  unless result
@@ -91,18 +91,32 @@ module Mongoid
91
91
  #
92
92
  # @param [ Hash ] options The options.
93
93
  #
94
- # @return [ true, false ] The result of the update.
94
+ # @option options [ true | false ] :touch Whether or not the updated_at
95
+ # attribute will be updated with the current time.
96
+ #
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
99
- result = run_callbacks(:save) do
100
- run_callbacks(:update) do
101
- yield(self)
102
- true
104
+ update_children = cascadable_children(:update)
105
+ process_touch_option(options, update_children)
106
+ run_callbacks(:save, with_children: false) do
107
+ run_callbacks(:update, with_children: false) do
108
+ run_callbacks(:persist_parent, with_children: false) do
109
+ _mongoid_run_child_callbacks(:save) do
110
+ _mongoid_run_child_callbacks(:update, children: update_children) do
111
+ result = yield(self)
112
+ self.previously_new_record = false
113
+ post_process_persist(result, options)
114
+ true
115
+ end
116
+ end
117
+ end
103
118
  end
104
119
  end
105
- post_process_persist(result, options) and result
106
120
  end
107
121
 
108
122
  # Update the document in the database.
@@ -112,9 +126,9 @@ module Mongoid
112
126
  #
113
127
  # @param [ Hash ] options Options to pass to update.
114
128
  #
115
- # @option options [ true, false ] :validate Whether or not to validate.
129
+ # @option options [ true | false ] :validate Whether or not to validate.
116
130
  #
117
- # @return [ true, false ] True if succeeded, false if not.
131
+ # @return [ true | false ] True if succeeded, false if not.
118
132
  def update_document(options = {})
119
133
  prepare_update(options) do
120
134
  updates, conflicts = init_atomic_updates
@@ -153,6 +167,49 @@ module Mongoid
153
167
  end
154
168
  end
155
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
156
213
  end
157
214
  end
158
215
  end