mongoid 7.5.4 → 8.1.6

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 (442) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -3
  3. data/README.md +6 -6
  4. data/Rakefile +44 -46
  5. data/lib/config/locales/en.yml +92 -43
  6. data/lib/mongoid/association/accessors.rb +44 -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 +85 -46
  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 +40 -21
  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 +16 -7
  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 +15 -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 +38 -9
  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 +142 -28
  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 +186 -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 +3 -4
  205. data/lib/mongoid/traversable.rb +5 -4
  206. data/lib/mongoid/utils.rb +22 -0
  207. data/lib/mongoid/validatable/associated.rb +96 -18
  208. data/lib/mongoid/validatable/localizable.rb +1 -1
  209. data/lib/mongoid/validatable/macros.rb +5 -7
  210. data/lib/mongoid/validatable/presence.rb +2 -2
  211. data/lib/mongoid/validatable/uniqueness.rb +9 -8
  212. data/lib/mongoid/validatable.rb +17 -6
  213. data/lib/mongoid/version.rb +1 -1
  214. data/lib/mongoid/warnings.rb +19 -4
  215. data/lib/mongoid.rb +17 -3
  216. data/spec/config/mongoid.yml +16 -0
  217. data/spec/integration/app_spec.rb +13 -16
  218. data/spec/integration/associations/belongs_to_spec.rb +18 -0
  219. data/spec/integration/associations/embedded_spec.rb +15 -0
  220. data/spec/integration/associations/embeds_many_spec.rb +15 -2
  221. data/spec/integration/associations/embeds_one_spec.rb +18 -0
  222. data/spec/integration/associations/foreign_key_spec.rb +9 -0
  223. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +61 -0
  224. data/spec/integration/associations/has_one_spec.rb +97 -1
  225. data/spec/integration/associations/scope_option_spec.rb +1 -1
  226. data/spec/integration/callbacks_models.rb +132 -1
  227. data/spec/integration/callbacks_spec.rb +381 -4
  228. data/spec/integration/criteria/range_spec.rb +95 -1
  229. data/spec/integration/discriminator_key_spec.rb +118 -80
  230. data/spec/integration/dots_and_dollars_spec.rb +277 -0
  231. data/spec/integration/i18n_fallbacks_spec.rb +3 -32
  232. data/spec/integration/matcher_examples_spec.rb +20 -13
  233. data/spec/integration/matcher_operator_data/type_decimal.yml +3 -2
  234. data/spec/integration/matcher_operator_spec.rb +3 -5
  235. data/spec/integration/persistence/range_field_spec.rb +350 -0
  236. data/spec/mongoid/association/counter_cache_spec.rb +1 -1
  237. data/spec/mongoid/association/depending_spec.rb +9 -9
  238. data/spec/mongoid/association/eager_spec.rb +2 -1
  239. data/spec/mongoid/association/embedded/embedded_in/binding_spec.rb +2 -1
  240. data/spec/mongoid/association/embedded/embedded_in/buildable_spec.rb +54 -0
  241. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +96 -9
  242. data/spec/mongoid/association/embedded/embeds_many/buildable_spec.rb +112 -0
  243. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +290 -65
  244. data/spec/mongoid/association/embedded/embeds_many_models.rb +37 -0
  245. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +16 -0
  246. data/spec/mongoid/association/embedded/embeds_many_spec.rb +68 -0
  247. data/spec/mongoid/association/embedded/embeds_one/buildable_spec.rb +25 -0
  248. data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
  249. data/spec/mongoid/association/embedded/embeds_one_models.rb +19 -0
  250. data/spec/mongoid/association/embedded/embeds_one_spec.rb +28 -0
  251. data/spec/mongoid/association/referenced/belongs_to/binding_spec.rb +2 -1
  252. data/spec/mongoid/association/referenced/belongs_to/buildable_spec.rb +54 -0
  253. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +15 -0
  254. data/spec/mongoid/association/referenced/belongs_to_models.rb +11 -0
  255. data/spec/mongoid/association/referenced/belongs_to_spec.rb +4 -20
  256. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +186 -229
  257. data/spec/mongoid/association/referenced/has_and_belongs_to_many_models.rb +25 -0
  258. data/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb +35 -2
  259. data/spec/mongoid/association/referenced/has_many/buildable_spec.rb +109 -0
  260. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +2 -56
  261. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +215 -177
  262. data/spec/mongoid/association/referenced/has_many_models.rb +3 -1
  263. data/spec/mongoid/association/referenced/has_many_spec.rb +25 -0
  264. data/spec/mongoid/association/referenced/has_one/buildable_spec.rb +2 -2
  265. data/spec/mongoid/association/referenced/has_one/proxy_spec.rb +107 -1
  266. data/spec/mongoid/association/referenced/has_one_models.rb +16 -0
  267. data/spec/mongoid/association/syncable_spec.rb +15 -1
  268. data/spec/mongoid/atomic/paths_spec.rb +0 -14
  269. data/spec/mongoid/attributes/nested_spec.rb +80 -11
  270. data/spec/mongoid/attributes/nested_spec_models.rb +48 -0
  271. data/spec/mongoid/attributes/projector_spec.rb +1 -5
  272. data/spec/mongoid/attributes_spec.rb +526 -33
  273. data/spec/mongoid/changeable_spec.rb +429 -37
  274. data/spec/mongoid/clients/factory_spec.rb +23 -30
  275. data/spec/mongoid/clients/sessions_spec.rb +0 -38
  276. data/spec/mongoid/clients_spec.rb +149 -15
  277. data/spec/mongoid/collection_configurable_spec.rb +158 -0
  278. data/spec/mongoid/config/defaults_spec.rb +160 -0
  279. data/spec/mongoid/config_spec.rb +214 -31
  280. data/spec/mongoid/contextual/aggregable/memory_spec.rb +396 -158
  281. data/spec/mongoid/contextual/aggregable/memory_table.yml +88 -0
  282. data/spec/mongoid/contextual/aggregable/memory_table_spec.rb +62 -0
  283. data/spec/mongoid/contextual/map_reduce_spec.rb +2 -16
  284. data/spec/mongoid/contextual/memory_spec.rb +850 -88
  285. data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
  286. data/spec/mongoid/contextual/mongo_spec.rb +2256 -1005
  287. data/spec/mongoid/contextual/none_spec.rb +60 -21
  288. data/spec/mongoid/copyable_spec.rb +453 -11
  289. data/spec/mongoid/criteria/findable_spec.rb +86 -210
  290. data/spec/mongoid/criteria/includable_spec.rb +1492 -0
  291. data/spec/mongoid/criteria/includable_spec_models.rb +54 -0
  292. data/spec/mongoid/criteria/marshalable_spec.rb +18 -1
  293. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +7 -19
  294. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +134 -26
  295. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +11 -0
  296. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +11 -0
  297. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +0 -15
  298. data/spec/mongoid/criteria/queryable/extensions/numeric_spec.rb +73 -7
  299. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -69
  300. data/spec/mongoid/criteria/queryable/extensions/symbol_spec.rb +0 -59
  301. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +11 -0
  302. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +11 -0
  303. data/spec/mongoid/criteria/queryable/optional_spec.rb +15 -484
  304. data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
  305. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +469 -0
  306. data/spec/mongoid/criteria/queryable/selectable_spec.rb +78 -86
  307. data/spec/mongoid/criteria/queryable/selector_spec.rb +15 -3
  308. data/spec/mongoid/criteria/translator_spec.rb +132 -0
  309. data/spec/mongoid/criteria_projection_spec.rb +1 -5
  310. data/spec/mongoid/criteria_spec.rb +469 -1205
  311. data/spec/mongoid/document_fields_spec.rb +173 -24
  312. data/spec/mongoid/document_spec.rb +32 -41
  313. data/spec/mongoid/errors/document_not_found_spec.rb +29 -2
  314. data/spec/mongoid/errors/invalid_field_spec.rb +1 -1
  315. data/spec/mongoid/errors/invalid_field_type_spec.rb +55 -0
  316. data/spec/mongoid/errors/mongoid_error_spec.rb +3 -1
  317. data/spec/mongoid/errors/no_environment_spec.rb +3 -3
  318. data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
  319. data/spec/mongoid/errors/too_many_nested_attribute_records_spec.rb +1 -1
  320. data/spec/mongoid/extensions/array_spec.rb +16 -2
  321. data/spec/mongoid/extensions/big_decimal_spec.rb +712 -212
  322. data/spec/mongoid/extensions/binary_spec.rb +44 -9
  323. data/spec/mongoid/extensions/boolean_spec.rb +68 -82
  324. data/spec/mongoid/extensions/date_class_mongoize_spec.rb +7 -3
  325. data/spec/mongoid/extensions/date_spec.rb +71 -1
  326. data/spec/mongoid/extensions/date_time_spec.rb +15 -9
  327. data/spec/mongoid/extensions/float_spec.rb +53 -74
  328. data/spec/mongoid/extensions/hash_spec.rb +33 -3
  329. data/spec/mongoid/extensions/integer_spec.rb +50 -64
  330. data/spec/mongoid/extensions/range_spec.rb +255 -54
  331. data/spec/mongoid/extensions/regexp_spec.rb +58 -33
  332. data/spec/mongoid/extensions/set_spec.rb +106 -0
  333. data/spec/mongoid/extensions/string_spec.rb +53 -25
  334. data/spec/mongoid/extensions/symbol_spec.rb +18 -25
  335. data/spec/mongoid/extensions/time_spec.rb +639 -106
  336. data/spec/mongoid/extensions/time_with_zone_spec.rb +24 -83
  337. data/spec/mongoid/factory_spec.rb +61 -1
  338. data/spec/mongoid/fields/localized_spec.rb +80 -37
  339. data/spec/mongoid/fields_spec.rb +503 -87
  340. data/spec/mongoid/findable_spec.rb +450 -58
  341. data/spec/mongoid/indexable/specification_spec.rb +2 -2
  342. data/spec/mongoid/indexable_spec.rb +55 -30
  343. data/spec/mongoid/interceptable_spec.rb +824 -22
  344. data/spec/mongoid/interceptable_spec_models.rb +235 -4
  345. data/spec/mongoid/matcher/extract_attribute_spec.rb +1 -5
  346. data/spec/mongoid/mongoizable_spec.rb +285 -0
  347. data/spec/mongoid/persistable/creatable_spec.rb +2 -2
  348. data/spec/mongoid/persistable/deletable_spec.rb +28 -8
  349. data/spec/mongoid/persistable/destroyable_spec.rb +28 -8
  350. data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
  351. data/spec/mongoid/persistable/logical_spec.rb +37 -0
  352. data/spec/mongoid/persistable/poppable_spec.rb +36 -0
  353. data/spec/mongoid/persistable/pullable_spec.rb +72 -0
  354. data/spec/mongoid/persistable/pushable_spec.rb +72 -0
  355. data/spec/mongoid/persistable/renamable_spec.rb +36 -0
  356. data/spec/mongoid/persistable/savable_spec.rb +96 -0
  357. data/spec/mongoid/persistable/settable_spec.rb +37 -0
  358. data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
  359. data/spec/mongoid/persistable/updatable_spec.rb +20 -28
  360. data/spec/mongoid/persistable/upsertable_spec.rb +89 -1
  361. data/spec/mongoid/persistence_context_spec.rb +31 -57
  362. data/spec/mongoid/query_cache_middleware_spec.rb +0 -18
  363. data/spec/mongoid/query_cache_spec.rb +56 -215
  364. data/spec/mongoid/reloadable_spec.rb +83 -6
  365. data/spec/mongoid/scopable_spec.rb +91 -1
  366. data/spec/mongoid/serializable_spec.rb +25 -39
  367. data/spec/mongoid/shardable_spec.rb +4 -4
  368. data/spec/mongoid/stateful_spec.rb +150 -8
  369. data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
  370. data/spec/mongoid/tasks/database_spec.rb +127 -0
  371. data/spec/mongoid/timestamps_spec.rb +392 -4
  372. data/spec/mongoid/timestamps_spec_models.rb +67 -0
  373. data/spec/mongoid/touchable_spec.rb +390 -2
  374. data/spec/mongoid/touchable_spec_models.rb +14 -8
  375. data/spec/mongoid/traversable_spec.rb +13 -35
  376. data/spec/mongoid/validatable/associated_spec.rb +13 -30
  377. data/spec/mongoid/validatable/presence_spec.rb +1 -1
  378. data/spec/mongoid/validatable/uniqueness_spec.rb +58 -31
  379. data/spec/mongoid/warnings_spec.rb +35 -0
  380. data/spec/mongoid_spec.rb +34 -16
  381. data/spec/rails/controller_extension/controller_runtime_spec.rb +2 -2
  382. data/spec/rails/mongoid_spec.rb +4 -16
  383. data/spec/spec_helper.rb +5 -0
  384. data/spec/support/constraints.rb +24 -0
  385. data/spec/support/immutable_ids.rb +118 -0
  386. data/spec/support/macros.rb +78 -0
  387. data/spec/support/models/artist.rb +0 -1
  388. data/spec/support/models/augmentation.rb +12 -0
  389. data/spec/support/models/band.rb +5 -0
  390. data/spec/support/models/book.rb +1 -0
  391. data/spec/support/models/building.rb +2 -0
  392. data/spec/support/models/catalog.rb +24 -0
  393. data/spec/support/models/circus.rb +3 -0
  394. data/spec/support/models/cover.rb +10 -0
  395. data/spec/support/models/fanatic.rb +8 -0
  396. data/spec/support/models/implant.rb +9 -0
  397. data/spec/support/models/label.rb +2 -0
  398. data/spec/support/models/lat_lng.rb +6 -0
  399. data/spec/support/models/name.rb +10 -0
  400. data/spec/support/models/passport.rb +9 -0
  401. data/spec/support/models/person.rb +2 -0
  402. data/spec/support/models/player.rb +2 -0
  403. data/spec/support/models/powerup.rb +12 -0
  404. data/spec/support/models/product.rb +1 -0
  405. data/spec/support/models/purse.rb +9 -0
  406. data/spec/support/models/registry.rb +1 -0
  407. data/spec/support/models/school.rb +14 -0
  408. data/spec/support/models/shield.rb +18 -0
  409. data/spec/support/models/student.rb +14 -0
  410. data/spec/support/models/weapon.rb +12 -0
  411. metadata +101 -96
  412. checksums.yaml.gz.sig +0 -0
  413. data/lib/mongoid/errors/eager_load.rb +0 -23
  414. data/lib/mongoid/errors/invalid_value.rb +0 -17
  415. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -60
  416. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -60
  417. data/spec/mongoid/errors/eager_load_spec.rb +0 -31
  418. data/spec/shared/LICENSE +0 -20
  419. data/spec/shared/bin/get-mongodb-download-url +0 -17
  420. data/spec/shared/bin/s3-copy +0 -45
  421. data/spec/shared/bin/s3-upload +0 -69
  422. data/spec/shared/lib/mrss/child_process_helper.rb +0 -80
  423. data/spec/shared/lib/mrss/cluster_config.rb +0 -231
  424. data/spec/shared/lib/mrss/constraints.rb +0 -378
  425. data/spec/shared/lib/mrss/docker_runner.rb +0 -291
  426. data/spec/shared/lib/mrss/eg_config_utils.rb +0 -51
  427. data/spec/shared/lib/mrss/event_subscriber.rb +0 -210
  428. data/spec/shared/lib/mrss/lite_constraints.rb +0 -238
  429. data/spec/shared/lib/mrss/server_version_registry.rb +0 -120
  430. data/spec/shared/lib/mrss/session_registry.rb +0 -69
  431. data/spec/shared/lib/mrss/session_registry_legacy.rb +0 -60
  432. data/spec/shared/lib/mrss/spec_organizer.rb +0 -179
  433. data/spec/shared/lib/mrss/utils.rb +0 -15
  434. data/spec/shared/share/Dockerfile.erb +0 -325
  435. data/spec/shared/share/haproxy-1.conf +0 -16
  436. data/spec/shared/share/haproxy-2.conf +0 -17
  437. data/spec/shared/shlib/config.sh +0 -27
  438. data/spec/shared/shlib/distro.sh +0 -74
  439. data/spec/shared/shlib/server.sh +0 -392
  440. data/spec/shared/shlib/set_env.sh +0 -169
  441. data.tar.gz.sig +0 -0
  442. metadata.gz.sig +0 -3
@@ -6,7 +6,15 @@ module Mongoid
6
6
  # document can transition through.
7
7
  module Stateful
8
8
 
9
- attr_writer :destroyed, :flagged_for_destroy, :new_record
9
+ attr_writer :destroyed, :flagged_for_destroy, :previously_new_record
10
+
11
+ def new_record=(new_value)
12
+ @new_record ||= false
13
+ if @new_record && !new_value
14
+ @previously_new_record = true
15
+ end
16
+ @new_record = new_value
17
+ end
10
18
 
11
19
  # Returns true if the +Document+ has not been persisted to the database,
12
20
  # false if it has. This is determined by the variable @new_record
@@ -15,29 +23,47 @@ module Mongoid
15
23
  # @example Is the document new?
16
24
  # person.new_record?
17
25
  #
18
- # @return [ true, false ] True if new, false if not.
26
+ # @return [ true | false ] True if new, false if not.
19
27
  def new_record?
20
28
  @new_record ||= false
21
29
  end
22
30
 
31
+ # Returns true if this document was just created -- that is, prior to the last
32
+ # save, the object didn't exist in the database and new_record? would have
33
+ # returned true.
34
+ #
35
+ # @return [ true | false ] True if was just created, false if not.
36
+ def previously_new_record?
37
+ @previously_new_record ||= false
38
+ end
39
+
23
40
  # Checks if the document has been saved to the database. Returns false
24
41
  # if the document has been destroyed.
25
42
  #
26
43
  # @example Is the document persisted?
27
44
  # person.persisted?
28
45
  #
29
- # @return [ true, false ] True if persisted, false if not.
46
+ # @return [ true | false ] True if persisted, false if not.
30
47
  def persisted?
31
48
  !new_record? && !destroyed?
32
49
  end
33
50
 
51
+ # Checks if the document was previously saved to the database
52
+ # but now it has been deleted.
53
+ #
54
+ # @return [ true | false ] True if was persisted but now destroyed,
55
+ # otherwise false.
56
+ def previously_persisted?
57
+ !new_record? && destroyed?
58
+ end
59
+
34
60
  # Returns whether or not the document has been flagged for deletion, but
35
61
  # not destroyed yet. Used for atomic pulls of child documents.
36
62
  #
37
63
  # @example Is the document flagged?
38
64
  # document.flagged_for_destroy?
39
65
  #
40
- # @return [ true, false ] If the document is flagged.
66
+ # @return [ true | false ] If the document is flagged.
41
67
  def flagged_for_destroy?
42
68
  @flagged_for_destroy ||= false
43
69
  end
@@ -51,7 +77,7 @@ module Mongoid
51
77
  # @example Is the document destroyed?
52
78
  # person.destroyed?
53
79
  #
54
- # @return [ true, false ] True if destroyed, false if not.
80
+ # @return [ true | false ] True if destroyed, false if not.
55
81
  def destroyed?
56
82
  @destroyed ||= false
57
83
  end
@@ -61,7 +87,7 @@ module Mongoid
61
87
  # @example Is this pushable?
62
88
  # person.pushable?
63
89
  #
64
- # @return [ true, false ] Is the document new and embedded?
90
+ # @return [ true | false ] Is the document new and embedded?
65
91
  def pushable?
66
92
  new_record? &&
67
93
  embedded_many? &&
@@ -69,14 +95,35 @@ module Mongoid
69
95
  !_parent.delayed_atomic_sets[atomic_path]
70
96
  end
71
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
+
72
115
  # Is the document readonly?
73
116
  #
74
117
  # @example Is the document readonly?
75
118
  # document.readonly?
76
119
  #
77
- # @return [ true, false ] If the document is readonly.
120
+ # @return [ true | false ] If the document is readonly.
78
121
  def readonly?
79
- __selected_fields != nil
122
+ if Mongoid.legacy_readonly
123
+ __selected_fields != nil
124
+ else
125
+ @readonly ||= false
126
+ end
80
127
  end
81
128
 
82
129
  # Determine if the document can be set.
@@ -84,7 +131,7 @@ module Mongoid
84
131
  # @example Is this settable?
85
132
  # person.settable?
86
133
  #
87
- # @return [ true, false ] Is this document a new embeds one?
134
+ # @return [ true | false ] Is this document a new embeds one?
88
135
  def settable?
89
136
  new_record? && embedded_one? && _parent.persisted?
90
137
  end
@@ -94,7 +141,7 @@ module Mongoid
94
141
  # @example Is the document updateable?
95
142
  # person.updateable?
96
143
  #
97
- # @return [ true, false ] If the document is changed and persisted.
144
+ # @return [ true | false ] If the document is changed and persisted.
98
145
  def updateable?
99
146
  persisted? && changed?
100
147
  end
@@ -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
  #
@@ -36,7 +56,6 @@ module Mongoid
36
56
  # Mongoid::Tasks::Database.undefined_indexes
37
57
  #
38
58
  # @return [ Array<Hash> ] The list of undefined indexes by model.
39
- #
40
59
  def undefined_indexes(models = ::Mongoid.models)
41
60
  undefined_by_model = {}
42
61
 
@@ -90,7 +109,6 @@ module Mongoid
90
109
  # Mongoid::Tasks::Database.remove_indexes
91
110
  #
92
111
  # @return [ Array<Class> ] The un-indexed models.
93
- #
94
112
  def remove_indexes(models = ::Mongoid.models)
95
113
  models.each do |model|
96
114
  next if model.embedded?
@@ -37,7 +37,7 @@ module Mongoid
37
37
  # @example Is the current thread in assigning mode?
38
38
  # proxy._assigning?
39
39
  #
40
- # @return [ true, false ] If the thread is assigning.
40
+ # @return [ true | false ] If the thread is assigning.
41
41
  def _assigning?
42
42
  Threaded.executing?(ASSIGN)
43
43
  end
@@ -62,7 +62,7 @@ module Mongoid
62
62
  # @example Is the current thread in binding mode?
63
63
  # proxy.binding?
64
64
  #
65
- # @return [ true, false ] If the thread is binding.
65
+ # @return [ true | false ] If the thread is binding.
66
66
  def _binding?
67
67
  Threaded.executing?(BIND)
68
68
  end
@@ -87,7 +87,7 @@ module Mongoid
87
87
  # @example Is the current thread in building mode?
88
88
  # proxy._building?
89
89
  #
90
- # @return [ true, false ] If the thread is building.
90
+ # @return [ true | false ] If the thread is building.
91
91
  def _building?
92
92
  Threaded.executing?(BUILD)
93
93
  end
@@ -97,7 +97,7 @@ module Mongoid
97
97
  # @example Is the current thread in creating mode?
98
98
  # proxy.creating?
99
99
  #
100
- # @return [ true, false ] If the thread is creating.
100
+ # @return [ true | false ] If the thread is creating.
101
101
  def _creating?
102
102
  Threaded.executing?(CREATE)
103
103
  end
@@ -122,7 +122,7 @@ module Mongoid
122
122
  # @example Is the current thread in loading mode?
123
123
  # proxy._loading?
124
124
  #
125
- # @return [ true, false ] If the thread is loading.
125
+ # @return [ true | false ] If the thread is loading.
126
126
  def _loading?
127
127
  Threaded.executing?(LOAD)
128
128
  end
@@ -26,6 +26,10 @@ module Mongoid
26
26
  hash[key] = "[mongoid]:#{key}-stack"
27
27
  end
28
28
 
29
+ # The key storing the default value for whether or not callbacks are
30
+ # executed on documents.
31
+ EXECUTE_CALLBACKS = '[mongoid]:execute-callbacks'
32
+
29
33
  extend self
30
34
 
31
35
  # Begin entry into a named thread local stack.
@@ -45,7 +49,7 @@ module Mongoid
45
49
  # @example Get the global database override.
46
50
  # Threaded.database_override
47
51
  #
48
- # @return [ String, Symbol ] The override.
52
+ # @return [ String | Symbol ] The override.
49
53
  def database_override
50
54
  Thread.current[DATABASE_OVERRIDE_KEY]
51
55
  end
@@ -55,9 +59,9 @@ module Mongoid
55
59
  # @example Set the global database override.
56
60
  # Threaded.database_override = :testing
57
61
  #
58
- # @param [ String, Symbol ] name The global override name.
62
+ # @param [ String | Symbol ] name The global override name.
59
63
  #
60
- # @return [ String, Symbol ] The override.
64
+ # @return [ String | Symbol ] The override.
61
65
  def database_override=(name)
62
66
  Thread.current[DATABASE_OVERRIDE_KEY] = name
63
67
  end
@@ -167,7 +171,7 @@ module Mongoid
167
171
  # @example Get the global client override.
168
172
  # Threaded.client_override
169
173
  #
170
- # @return [ String, Symbol ] The override.
174
+ # @return [ String | Symbol ] The override.
171
175
  def client_override
172
176
  Thread.current[CLIENT_OVERRIDE_KEY]
173
177
  end
@@ -177,9 +181,9 @@ module Mongoid
177
181
  # @example Set the global client override.
178
182
  # Threaded.client_override = :testing
179
183
  #
180
- # @param [ String, Symbol ] name The global override name.
184
+ # @param [ String | Symbol ] name The global override name.
181
185
  #
182
- # @return [ String, Symbol ] The override.
186
+ # @return [ String | Symbol ] The override.
183
187
  def client_override=(name)
184
188
  Thread.current[CLIENT_OVERRIDE_KEY] = name
185
189
  end
@@ -255,7 +259,7 @@ module Mongoid
255
259
  #
256
260
  # @param [ Document ] document The document to check.
257
261
  #
258
- # @return [ true, false ] If the document is autosaved.
262
+ # @return [ true | false ] If the document is autosaved.
259
263
  def autosaved?(document)
260
264
  autosaves_for(document.class).include?(document._id)
261
265
  end
@@ -267,7 +271,7 @@ module Mongoid
267
271
  #
268
272
  # @param [ Document ] document The document to check.
269
273
  #
270
- # @return [ true, false ] If the document is validated.
274
+ # @return [ true | false ] If the document is validated.
271
275
  def validated?(document)
272
276
  validations_for(document.class).include?(document._id)
273
277
  end
@@ -322,7 +326,7 @@ module Mongoid
322
326
  #
323
327
  # @param [ Mongo::Session ] session The session to save.
324
328
  def set_session(session)
325
- Thread.current[:session] = session
329
+ Thread.current["[mongoid]:session"] = session
326
330
  end
327
331
 
328
332
  # Get the cached session for this thread.
@@ -330,9 +334,9 @@ module Mongoid
330
334
  # @example Get the session for this thread.
331
335
  # Threaded.get_session
332
336
  #
333
- # @return [ Mongo::Session, nil ] The session cached on this thread or nil.
337
+ # @return [ Mongo::Session | nil ] The session cached on this thread or nil.
334
338
  def get_session
335
- Thread.current[:session]
339
+ Thread.current["[mongoid]:session"]
336
340
  end
337
341
 
338
342
  # Clear the cached session for this thread.
@@ -344,7 +348,33 @@ module Mongoid
344
348
  def clear_session
345
349
  session = get_session
346
350
  session.end_session if session
347
- Thread.current[:session] = nil
351
+ Thread.current["[mongoid]:session"] = nil
352
+ end
353
+
354
+ # Queries whether document callbacks should be executed by default for the
355
+ # current thread.
356
+ #
357
+ # Unless otherwise indicated (by #execute_callbacks=), this will return
358
+ # true.
359
+ #
360
+ # @return [ true | false ] Whether or not document callbacks should be
361
+ # executed by default.
362
+ def execute_callbacks?
363
+ if Thread.current.key?(EXECUTE_CALLBACKS)
364
+ Thread.current[EXECUTE_CALLBACKS]
365
+ else
366
+ true
367
+ end
368
+ end
369
+
370
+ # Indicates whether document callbacks should be invoked by default for
371
+ # the current thread. Individual documents may further override the
372
+ # callback behavior, but this will be used for the default behavior.
373
+ #
374
+ # @param flag [ true | false ] Whether or not document callbacks should be
375
+ # executed by default.
376
+ def execute_callbacks=(flag)
377
+ Thread.current[EXECUTE_CALLBACKS] = flag
348
378
  end
349
379
  end
350
380
  end
@@ -23,7 +23,7 @@ module Mongoid
23
23
  # person.set_created_at
24
24
  def set_created_at
25
25
  if !timeless? && !created_at
26
- time = Time.now.utc
26
+ time = Time.configured.now
27
27
  self.updated_at = time if is_a?(Updated) && !updated_at_changed?
28
28
  self.created_at = time
29
29
  end
@@ -24,7 +24,7 @@ module Mongoid
24
24
  # person.set_updated_at
25
25
  def set_updated_at
26
26
  if able_to_set_updated_at?
27
- self.updated_at = Time.now.utc unless updated_at_changed?
27
+ self.updated_at = Time.configured.now unless updated_at_changed?
28
28
  end
29
29
 
30
30
  clear_timeless_option
@@ -35,7 +35,7 @@ module Mongoid
35
35
  # @example Can the timestamp be set?
36
36
  # document.able_to_set_updated_at?
37
37
  #
38
- # @return [ true, false ] If the timestamp can be set.
38
+ # @return [ true | false ] If the timestamp can be set.
39
39
  def able_to_set_updated_at?
40
40
  !frozen? && !timeless? && (new_record? || changed?)
41
41
  end
@@ -19,13 +19,13 @@ module Mongoid
19
19
  #
20
20
  # @param [ Symbol ] field The name of an additional field to update.
21
21
  #
22
- # @return [ true/false ] false if record is new_record otherwise true.
22
+ # @return [ true/false ] false if document is new_record otherwise true.
23
23
  def touch(field = nil)
24
24
  return false if _root.new_record?
25
- current = Time.now
25
+ current = Time.configured.now
26
26
  field = database_field_name(field)
27
27
  write_attribute(:updated_at, current) if respond_to?("updated_at=")
28
- write_attribute(field, current) if field
28
+ write_attribute(field, current) if field.present?
29
29
 
30
30
  # If the document being touched is embedded, touch its parents
31
31
  # all the way through the composition hierarchy to the root object,
@@ -60,7 +60,6 @@ module Mongoid
60
60
 
61
61
  # Callbacks are invoked on the composition root first and on the
62
62
  # leaf-most embedded document last.
63
- # TODO add tests, see MONGOID-5015.
64
63
  run_callbacks(:touch)
65
64
  true
66
65
  end
@@ -203,7 +203,7 @@ module Mongoid
203
203
  # @example Check if the document is a subclass
204
204
  # Square.new.hereditary?
205
205
  #
206
- # @return [ true, false ] True if hereditary, false if not.
206
+ # @return [ true | false ] True if hereditary, false if not.
207
207
  def hereditary?
208
208
  self.class.hereditary?
209
209
  end
@@ -233,10 +233,11 @@ module Mongoid
233
233
  def remove_child(child)
234
234
  name = child.association_name
235
235
  if child.embedded_one?
236
+ self.attributes.delete(child._association.store_as)
236
237
  remove_ivar(name)
237
238
  else
238
239
  relation = send(name)
239
- relation.send(:delete_one, child)
240
+ relation._remove(child)
240
241
  end
241
242
  end
242
243
 
@@ -282,7 +283,7 @@ module Mongoid
282
283
  # @example Is the document the root?
283
284
  # document._root?
284
285
  #
285
- # @return [ true, false ] If the document is the root.
286
+ # @return [ true | false ] If the document is the root.
286
287
  def _root?
287
288
  _parent ? false : true
288
289
  end
@@ -294,7 +295,7 @@ module Mongoid
294
295
  # @example Check if the document is a subclass.
295
296
  # Square.hereditary?
296
297
  #
297
- # @return [ true, false ] True if hereditary, false if not.
298
+ # @return [ true | false ] True if hereditary, false if not.
298
299
  def hereditary?
299
300
  !!(Mongoid::Document > superclass)
300
301
  end
@@ -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,32 +15,110 @@ module Mongoid
15
15
  #
16
16
  # validates_associated :name, :addresses
17
17
  # end
18
- class AssociatedValidator < ActiveModel::EachValidator
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
19
24
 
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.
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.
23
29
  #
24
- # @example Validate the association.
25
- # validator.validate_each(document, :name, name)
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.
26
46
  #
27
47
  # @param [ Document ] document The document to validate.
28
48
  # @param [ Symbol ] attribute The association to validate.
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?
35
- true
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?
36
75
  else
37
- doc.validated? ? true : doc.valid?
76
+ true
38
77
  end
39
- end.all?
40
- ensure
41
- document.exit_validate
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)
42
98
  end
43
- document.errors.add(attribute, :invalid, **options) unless valid
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)
44
122
  end
45
123
  end
46
124
  end
@@ -12,7 +12,7 @@ module Mongoid
12
12
  # validator.validate_each(model, :name, "value")
13
13
  #
14
14
  # @param [ Document ] document The document.
15
- # @param [ Symbol, String ] attribute The attribute to validate.
15
+ # @param [ Symbol | String ] attribute The attribute to validate.
16
16
  # @param [ Object ] value The attribute value.
17
17
  def validate_each(document, attribute, value)
18
18
  field = document.fields[document.database_field_name(attribute)]
@@ -18,8 +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.
22
- #
21
+ # @param [ Object... ] *args The arguments to pass to the validator.
23
22
  def validates_associated(*args)
24
23
  validates_with(AssociatedValidator, _merge_attributes(args))
25
24
  end
@@ -36,8 +35,7 @@ module Mongoid
36
35
  # validates_uniqueness_of :title
37
36
  # end
38
37
  #
39
- # @param [ Array ] args The arguments to pass to the validator.
40
- #
38
+ # @param [ Object... ] *args The arguments to pass to the validator.
41
39
  def validates_uniqueness_of(*args)
42
40
  validates_with(UniquenessValidator, _merge_attributes(args))
43
41
  end
@@ -52,7 +50,7 @@ module Mongoid
52
50
  # validates_format_of :title, with: /\A[a-z0-9 \-_]*\z/i
53
51
  # end
54
52
  #
55
- # @param [ Array ] args The names of the fields to validate.
53
+ # @param [ Object... ] *args The names of the field(s) to validate.
56
54
  def validates_format_of(*args)
57
55
  validates_with(FormatValidator, _merge_attributes(args))
58
56
  end
@@ -67,7 +65,7 @@ module Mongoid
67
65
  # validates_length_of :title, minimum: 100
68
66
  # end
69
67
  #
70
- # @param [ Array ] args The names of the fields to validate.
68
+ # @param [ Object... ] *args The names of the field(s) to validate.
71
69
  def validates_length_of(*args)
72
70
  validates_with(LengthValidator, _merge_attributes(args))
73
71
  end
@@ -82,7 +80,7 @@ module Mongoid
82
80
  # validates_presence_of :title
83
81
  # end
84
82
  #
85
- # @param [ Array ] args The names of the fields to validate.
83
+ # @param [ Object... ] *args The names of the field(s) to validate.
86
84
  def validates_presence_of(*args)
87
85
  validates_with(PresenceValidator, _merge_attributes(args))
88
86
  end