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
@@ -50,7 +50,7 @@ module Mongoid
50
50
  _unscoped.clear
51
51
  end
52
52
 
53
- # Batch remove the provided documents as a $pullAll.
53
+ # Batch remove the provided documents as a $pullAll or $pull.
54
54
  #
55
55
  # @example Batch remove the documents.
56
56
  # batchable.batch_remove([ doc_one, doc_two ])
@@ -58,13 +58,36 @@ module Mongoid
58
58
  # @param [ Array<Document> ] docs The docs to remove.
59
59
  # @param [ Symbol ] method Delete or destroy.
60
60
  def batch_remove(docs, method = :delete)
61
+ # If the _id is nil, we cannot use $pull and delete by searching for
62
+ # the id. Therefore we have to use pullAll with the documents'
63
+ # attributes.
61
64
  removals = pre_process_batch_remove(docs, method)
65
+ pulls, pull_alls = removals.partition { |o| !o["_id"].nil? }
66
+
67
+ if !_base.persisted?
68
+ post_process_batch_remove(docs, method) unless docs.empty?
69
+ return reindex
70
+ end
71
+
62
72
  if !docs.empty?
63
- collection.find(selector).update_one(
64
- positionally(selector, "$pullAll" => { path => removals }),
73
+ if !pulls.empty?
74
+ collection.find(selector).update_one(
75
+ positionally(selector, "$pull" => { path => { "_id" => { "$in" => pulls.pluck("_id") } } }),
65
76
  session: _session
66
- )
77
+ )
78
+ end
79
+ if !pull_alls.empty?
80
+ collection.find(selector).update_one(
81
+ positionally(selector, "$pullAll" => { path => pull_alls }),
82
+ session: _session
83
+ )
84
+ end
67
85
  post_process_batch_remove(docs, method)
86
+ else
87
+ collection.find(selector).update_one(
88
+ positionally(selector, "$set" => { path => [] }),
89
+ session: _session
90
+ )
68
91
  end
69
92
  reindex
70
93
  end
@@ -74,7 +97,7 @@ module Mongoid
74
97
  # @example Batch replace the documents.
75
98
  # batchable.batch_replace([ doc_one, doc_two ])
76
99
  #
77
- # @param [ Array<Document> ] docs The docs to replace with.
100
+ # @param [ Array<Document> | Array<Hash> ] docs The docs to replace with.
78
101
  #
79
102
  # @return [ Array<Hash> ] The inserts.
80
103
  def batch_replace(docs)
@@ -90,7 +113,7 @@ module Mongoid
90
113
  batch_remove(_target.dup)
91
114
  end
92
115
  elsif _target != docs
93
- _base.delayed_atomic_sets.clear unless _assigning?
116
+ _base.delayed_atomic_sets.delete(path) unless _assigning?
94
117
  docs = normalize_docs(docs).compact
95
118
  _target.clear and _unscoped.clear
96
119
  _base.delayed_atomic_unsets.delete(path)
@@ -173,7 +196,7 @@ module Mongoid
173
196
  # @example Can inserts be performed?
174
197
  # batchable.insertable?
175
198
  #
176
- # @return [ true, false ] If inserts can be performed.
199
+ # @return [ true | false ] If inserts can be performed.
177
200
  def insertable?
178
201
  persistable? && !_assigning? && inserts_valid
179
202
  end
@@ -185,7 +208,7 @@ module Mongoid
185
208
  # @example Are the inserts currently valid.
186
209
  # batchable.inserts_valid
187
210
  #
188
- # @return [ true, false ] If inserts are currently valid.
211
+ # @return [ true | false ] If inserts are currently valid.
189
212
  def inserts_valid
190
213
  @inserts_valid
191
214
  end
@@ -197,9 +220,9 @@ module Mongoid
197
220
  # @example Set the flag.
198
221
  # batchable.inserts_valid = true
199
222
  #
200
- # @param [ true, false ] value The flag.
223
+ # @param [ true | false ] value The flag.
201
224
  #
202
- # @return [ true, false ] The flag.
225
+ # @return [ true | false ] The flag.
203
226
  def inserts_valid=(value)
204
227
  @inserts_valid = value
205
228
  end
@@ -212,7 +235,7 @@ module Mongoid
212
235
  # @example Normalize the docs.
213
236
  # batchable.normalize_docs(docs)
214
237
  #
215
- # @param [ Array<Hash, Document> ] docs The docs to normalize.
238
+ # @param [ Array<Document> | Array<Hash> ] docs The docs to normalize.
216
239
  #
217
240
  # @return [ Array<Document> ] The docs.
218
241
  def normalize_docs(docs)
@@ -88,7 +88,7 @@ module Mongoid
88
88
  # @example Determine the child name.
89
89
  # Role.cyclic_child_name
90
90
  #
91
- # @param [ true, false ] many Is the a many association?
91
+ # @param [ true | false ] many Is the a many association?
92
92
  #
93
93
  # @return [ String ] "child_" plus the class name underscored in
94
94
  # singular or plural form.
@@ -19,12 +19,14 @@ module Mongoid
19
19
  # name.person.bind(:continue => true)
20
20
  # name.person = Person.new
21
21
  def bind_one
22
- _base._association = _association.inverse_association(_target) unless _base._association
23
- _base.parentize(_target)
24
22
  binding do
23
+ check_polymorphic_inverses!(_target)
24
+ _base._association = _association.inverse_association(_target) unless _base._association
25
+ _base.parentize(_target)
25
26
  if _base.embedded_many?
26
27
  _target.do_or_do_not(_association.inverse(_target)).push(_base)
27
28
  else
29
+ remove_associated(_target)
28
30
  _target.do_or_do_not(_association.inverse_setter(_target), _base)
29
31
  end
30
32
  end
@@ -45,6 +47,26 @@ module Mongoid
45
47
  end
46
48
  end
47
49
  end
50
+
51
+ private
52
+
53
+ # Check for problems with multiple inverse definitions.
54
+ #
55
+ # @api private
56
+ #
57
+ # @example Check for inverses errors.
58
+ # binding.check_inverses!(doc)
59
+ #
60
+ # @param [ Document ] doc The document to check.
61
+ def check_polymorphic_inverses!(doc)
62
+ if inverses = _association.inverses(doc)
63
+ if inverses.length > 1
64
+ raise Errors::InvalidSetPolymorphicRelation.new(
65
+ _association.name, _base.class.name, _target.class.name
66
+ )
67
+ end
68
+ end
69
+ end
48
70
  end
49
71
  end
50
72
  end
@@ -15,8 +15,8 @@ module Mongoid
15
15
  # @example Build the document.
16
16
  # Builder.new(meta, attrs).build
17
17
  #
18
- # @param [ Object ] base The object.
19
- # @param [ Object ] object The parent hash or document.
18
+ # @param [ Document ] base The object.
19
+ # @param [ Document | Hash ] object The parent hash or document.
20
20
  # @param [ String ] type Not used in this context.
21
21
  # @param [ Hash ] selected_fields Fields which were retrieved via
22
22
  # #only. If selected_fields are specified, fields not listed in it
@@ -30,9 +30,9 @@ module Mongoid
30
30
  # @example Substitute the new document.
31
31
  # person.name.substitute(new_name)
32
32
  #
33
- # @param [ Document ] replacement A document to replace the target.
33
+ # @param [ Document | Hash ] replacement A document to replace the target.
34
34
  #
35
- # @return [ Document, nil ] The association or nil.
35
+ # @return [ Document | nil ] The association or nil.
36
36
  def substitute(replacement)
37
37
  unbind_one
38
38
  unless replacement
@@ -40,6 +40,7 @@ module Mongoid
40
40
  return nil
41
41
  end
42
42
  _base.new_record = true
43
+ replacement = Factory.build(klass, replacement) if replacement.is_a?(::Hash)
43
44
  self._target = replacement
44
45
  bind_one
45
46
  self
@@ -74,7 +75,7 @@ module Mongoid
74
75
  # @example Can we persist the association?
75
76
  # relation.persistable?
76
77
  #
77
- # @return [ true, false ] If the association is persistable.
78
+ # @return [ true | false ] If the association is persistable.
78
79
  def persistable?
79
80
  _target.persisted? && !_binding? && !_building?
80
81
  end
@@ -75,7 +75,7 @@ module Mongoid
75
75
 
76
76
  # Is this association polymorphic?
77
77
  #
78
- # @return [ true, false ] Whether this association is polymorphic.
78
+ # @return [ true | false ] Whether this association is polymorphic.
79
79
  def polymorphic?
80
80
  !!@options[:polymorphic]
81
81
  end
@@ -114,7 +114,8 @@ module Mongoid
114
114
  rel.as == name &&
115
115
  rel.relation_class_name == inverse_class_name
116
116
  end
117
- matches.collect { |m| m.name }
117
+
118
+ matches.map { |m| m.name }
118
119
  end
119
120
  end
120
121
 
@@ -19,6 +19,7 @@ module Mongoid
19
19
  def bind_one(doc)
20
20
  doc.parentize(_base)
21
21
  binding do
22
+ remove_associated(doc)
22
23
  doc.do_or_do_not(_association.inverse_setter(_target), _base)
23
24
  end
24
25
  end
@@ -17,8 +17,9 @@ module Mongoid
17
17
  # @example Build the documents.
18
18
  # Builder.new(meta, attrs).build
19
19
  #
20
- # @param [ Object ] base The base object.
21
- # @param [ Object ] object The object to use to build the association.
20
+ # @param [ Document ] base The base object.
21
+ # @param [ Array<Document> | Array<Hash> ] object The object to use
22
+ # to build the association.
22
23
  # @param [ String ] type Not used in this context.
23
24
  # @param [ Hash ] selected_fields Fields which were retrieved via
24
25
  # #only. If selected_fields are specified, fields not listed in it
@@ -31,7 +32,7 @@ module Mongoid
31
32
  docs = []
32
33
  object.each do |attrs|
33
34
  if _loading? && base.persisted?
34
- docs.push(Factory.from_db(klass, attrs, nil, selected_fields))
35
+ docs.push(Factory.execute_from_db(klass, attrs, nil, selected_fields, execute_callbacks: false))
35
36
  else
36
37
  docs.push(Factory.build(klass, attrs))
37
38
  end
@@ -19,7 +19,7 @@ module Mongoid
19
19
  # @example Push a document.
20
20
  # person.addresses.push(address)
21
21
  #
22
- # @param [ Document, Array<Document> ] args Any number of documents.
22
+ # @param [ Document... ] *args Any number of documents.
23
23
  def <<(*args)
24
24
  docs = args.flatten
25
25
  return concat(docs) if docs.size > 1
@@ -67,10 +67,11 @@ module Mongoid
67
67
  #
68
68
  # @return [ Document ] The new document.
69
69
  def build(attributes = {}, type = nil)
70
- doc = Factory.build(type || _association.klass, attributes)
70
+ doc = Factory.execute_build(type || _association.klass, attributes, execute_callbacks: false)
71
71
  append(doc)
72
72
  doc.apply_post_processed_defaults
73
73
  yield(doc) if block_given?
74
+ doc.run_pending_callbacks
74
75
  doc.run_callbacks(:build) { doc }
75
76
  _base._reset_memoized_descendants!
76
77
  doc
@@ -94,6 +95,7 @@ module Mongoid
94
95
  # @return [ self ] The empty association.
95
96
  def clear
96
97
  batch_clear(_target.dup)
98
+ update_attributes_hash
97
99
  self
98
100
  end
99
101
 
@@ -115,7 +117,7 @@ module Mongoid
115
117
  # @example Use #persisted? inside block to count persisted documents.
116
118
  # person.addresses.count { |a| a.persisted? && a.country == "FR" }
117
119
  #
118
- # @param [ Object, Array<Object> ] args Args to delegate to the target.
120
+ # @param [ Object... ] *args Args to delegate to the target.
119
121
  #
120
122
  # @return [ Integer ] The total number of persisted embedded docs, as
121
123
  # flagged by the #persisted? method.
@@ -133,22 +135,40 @@ module Mongoid
133
135
  #
134
136
  # @param [ Document ] document The document to be deleted.
135
137
  #
136
- # @return [ Document, nil ] The deleted document or nil if nothing deleted.
138
+ # @return [ Document | nil ] The deleted document or nil if nothing deleted.
137
139
  def delete(document)
138
- execute_callback :before_remove, document
139
- doc = _target.delete_one(document)
140
- if doc && !_binding?
141
- _unscoped.delete_one(doc)
142
- if _assigning?
143
- _base.add_atomic_pull(doc)
144
- else
145
- doc.delete(suppress: true)
146
- unbind_one(doc)
140
+ execute_callbacks_around(:remove, document) do
141
+ doc = _target.delete_one(document)
142
+ if doc && !_binding?
143
+ _unscoped.delete_one(doc)
144
+ if _assigning?
145
+ _base.add_atomic_pull(doc)
146
+ else
147
+ doc.delete(suppress: true)
148
+ unbind_one(doc)
149
+ end
150
+ update_attributes_hash
147
151
  end
152
+ reindex
153
+ doc
148
154
  end
155
+ end
156
+
157
+ # Mongoid::Extensions::Array defines Array#delete_one, so we need
158
+ # to make sure that method behaves reasonably on proxies, too.
159
+ alias delete_one delete
160
+
161
+ # Removes a single document from the collection *in memory only*.
162
+ # It will *not* persist the change.
163
+ #
164
+ # @param [ Document ] document The document to delete.
165
+ #
166
+ # @api private
167
+ def _remove(document)
168
+ _target.delete_one(document)
169
+ _unscoped.delete_one(document)
170
+ update_attributes_hash
149
171
  reindex
150
- execute_callback :after_remove, document
151
- doc
152
172
  end
153
173
 
154
174
  # Delete all the documents in the association without running callbacks.
@@ -173,7 +193,7 @@ module Mongoid
173
193
  # doc.state == "GA"
174
194
  # end
175
195
  #
176
- # @return [ Many, Enumerator ] The association or an enumerator if no
196
+ # @return [ Many | Enumerator ] The association or an enumerator if no
177
197
  # block was provided.
178
198
  def delete_if
179
199
  if block_given?
@@ -207,9 +227,9 @@ module Mongoid
207
227
  # @example Are there persisted documents?
208
228
  # person.posts.exists?
209
229
  #
210
- # @return [ true, false ] True is persisted documents exist, false if not.
230
+ # @return [ true | false ] True is persisted documents exist, false if not.
211
231
  def exists?
212
- count > 0
232
+ _target.any? { |doc| doc.persisted? }
213
233
  end
214
234
 
215
235
  # Finds a document in this association through several different
@@ -232,7 +252,7 @@ module Mongoid
232
252
  # @example Finds the first matching document using a block.
233
253
  # person.addresses.find { |addr| addr.state == 'CA' }
234
254
  #
235
- # @param [ Array<Object> ] args Various arguments.
255
+ # @param [ Object... ] *args Various arguments.
236
256
  # @param [ Proc ] block Optional block to pass.
237
257
  #
238
258
  # @return [ Document | Array<Document> | nil ] A document or matching documents.
@@ -256,6 +276,7 @@ module Mongoid
256
276
  integrate(doc)
257
277
  doc._index = index
258
278
  end
279
+ update_attributes_hash
259
280
  @_unscoped = _target.dup
260
281
  @_target = scope(_target)
261
282
  end
@@ -283,7 +304,7 @@ module Mongoid
283
304
  # @param [ Integer ] count The number of documents to pop, or 1 if not
284
305
  # provided.
285
306
  #
286
- # @return [ Document, Array<Document> ] The popped document(s).
307
+ # @return [ Document | Array<Document> ] The popped document(s).
287
308
  def pop(count = nil)
288
309
  if count
289
310
  if docs = _target[_target.size - count, _target.size]
@@ -291,6 +312,8 @@ module Mongoid
291
312
  end
292
313
  else
293
314
  delete(_target[-1])
315
+ end.tap do
316
+ update_attributes_hash
294
317
  end
295
318
  end
296
319
 
@@ -306,7 +329,7 @@ module Mongoid
306
329
  # @param [ Integer ] count The number of documents to shift, or 1 if not
307
330
  # provided.
308
331
  #
309
- # @return [ Document, Array<Document> ] The shifted document(s).
332
+ # @return [ Document | Array<Document> ] The shifted document(s).
310
333
  def shift(count = nil)
311
334
  if count
312
335
  if _target.size > 0 && docs = _target[0, count]
@@ -314,6 +337,8 @@ module Mongoid
314
337
  end
315
338
  else
316
339
  delete(_target[0])
340
+ end.tap do
341
+ update_attributes_hash
317
342
  end
318
343
  end
319
344
 
@@ -323,11 +348,12 @@ module Mongoid
323
348
  # @example Substitute the association's target.
324
349
  # person.addresses.substitute([ address ])
325
350
  #
326
- # @param [ Array<Document> ] docs The replacement docs.
351
+ # @param [ Array<Document> | Array<Hash> ] docs The replacement docs.
327
352
  #
328
353
  # @return [ Many ] The proxied association.
329
354
  def substitute(docs)
330
355
  batch_replace(docs)
356
+ update_attributes_hash
331
357
  self
332
358
  end
333
359
 
@@ -365,6 +391,7 @@ module Mongoid
365
391
  end
366
392
  _unscoped.push(document)
367
393
  integrate(document)
394
+ update_attributes_hash
368
395
  document._index = _unscoped.size - 1
369
396
  execute_callback :after_add, document
370
397
  end
@@ -387,20 +414,6 @@ module Mongoid
387
414
  _association.criteria(_base, _target)
388
415
  end
389
416
 
390
- # Deletes one document from the target and unscoped.
391
- #
392
- # @api private
393
- #
394
- # @example Delete one document.
395
- # relation.delete_one(doc)
396
- #
397
- # @param [ Document ] document The document to delete.
398
- def delete_one(document)
399
- _target.delete_one(document)
400
- _unscoped.delete_one(document)
401
- reindex
402
- end
403
-
404
417
  # Integrate the document into the association. will set its metadata and
405
418
  # attempt to bind the inverse.
406
419
  #
@@ -418,11 +431,11 @@ module Mongoid
418
431
  #
419
432
  # If the method exists on the array, use the default proxy behavior.
420
433
  #
421
- # @param [ Symbol, String ] name The name of the method.
422
- # @param [ Array ] args The method args
434
+ # @param [ Symbol | String ] name The name of the method.
435
+ # @param [ Object... ] *args The method args.
423
436
  # @param [ Proc ] block Optional block to pass.
424
437
  #
425
- # @return [ Criteria, Object ] A Criteria or return value from the target.
438
+ # @return [ Criteria | Object ] A Criteria or return value from the target.
426
439
  ruby2_keywords def method_missing(name, *args, &block)
427
440
  return super if _target.respond_to?(name)
428
441
  klass.send(:with_scope, criteria) do
@@ -435,7 +448,7 @@ module Mongoid
435
448
  # @example Can we persist the association?
436
449
  # relation.persistable?
437
450
  #
438
- # @return [ true, false ] If the association is persistable.
451
+ # @return [ true | false ] If the association is persistable.
439
452
  def persistable?
440
453
  _base.persisted? && !_binding?
441
454
  end
@@ -479,13 +492,14 @@ module Mongoid
479
492
  # relation.remove_all({ :num => 1 }, true)
480
493
  #
481
494
  # @param [ Hash ] conditions Conditions to filter by.
482
- # @param [ true, false ] method :delete or :destroy.
495
+ # @param [ true | false ] method :delete or :destroy.
483
496
  #
484
497
  # @return [ Integer ] The number of documents removed.
485
498
  def remove_all(conditions = {}, method = :delete)
486
499
  criteria = where(conditions || {})
487
500
  removed = criteria.size
488
501
  batch_remove(criteria, method)
502
+ update_attributes_hash
489
503
  removed
490
504
  end
491
505
 
@@ -511,12 +525,22 @@ module Mongoid
511
525
  @_unscoped = docs
512
526
  end
513
527
 
528
+ # Returns a list of attributes hashes for each document.
529
+ #
530
+ # @return [ Array<Hash> ] The list of attributes hashes
514
531
  def as_attributes
515
- attributes = []
516
- _unscoped.each do |doc|
517
- attributes.push(doc.as_document)
532
+ _unscoped.map { |doc| doc.send(:as_attributes) }
533
+ end
534
+
535
+ # Update the _base's attributes hash with the _target's attributes
536
+ #
537
+ # @api private
538
+ def update_attributes_hash
539
+ if !_target.empty?
540
+ _base.attributes.merge!(_association.store_as => _target.map(&:attributes))
541
+ else
542
+ _base.attributes.delete(_association.store_as)
518
543
  end
519
- attributes
520
544
  end
521
545
 
522
546
  class << self
@@ -92,7 +92,7 @@ module Mongoid
92
92
 
93
93
  # Is this association polymorphic?
94
94
  #
95
- # @return [ true, false ] Whether this association is polymorphic.
95
+ # @return [ true | false ] Whether this association is polymorphic.
96
96
  def polymorphic?
97
97
  @polymorphic ||= !!@options[:as]
98
98
  end
@@ -101,7 +101,7 @@ module Mongoid
101
101
  #
102
102
  # @note Only relevant if the association is polymorphic.
103
103
  #
104
- # @return [ String, nil ] The field for storing the associated object's type.
104
+ # @return [ String | nil ] The field for storing the associated object's type.
105
105
  def type
106
106
  @type ||= "#{as}_type" if polymorphic?
107
107
  end
@@ -17,7 +17,7 @@ module Mongoid
17
17
  # Builder.new(meta, attrs).build
18
18
  #
19
19
  # @param [ Document ] base The document this association hangs off of.
20
- # @param [ Document ] object The related document.
20
+ # @param [ Document | Hash ] object The related document.
21
21
  # @param [ String ] _type Not used in this context.
22
22
  # @param [ Hash ] selected_fields Fields which were retrieved via
23
23
  # #only. If selected_fields are specified, fields not listed in it
@@ -25,11 +25,25 @@ module Mongoid
25
25
  #
26
26
  # @return [ Document ] A single document.
27
27
  def build(base, object, _type = nil, selected_fields = nil)
28
- return object unless object.is_a?(Hash)
29
- if _loading? && base.persisted?
30
- Factory.from_db(klass, object, nil, selected_fields)
28
+ if object.is_a?(Hash)
29
+ if _loading? && base.persisted?
30
+ Factory.execute_from_db(klass, object, nil, selected_fields, execute_callbacks: false)
31
+ else
32
+ Factory.build(klass, object)
33
+ end
31
34
  else
32
- Factory.build(klass, object)
35
+ clear_associated(object)
36
+ object
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def clear_associated(doc)
43
+ if doc && (inv = inverse(doc))
44
+ if associated = doc.ivar(inv)
45
+ associated.substitute(nil)
46
+ end
33
47
  end
34
48
  end
35
49
  end
@@ -31,6 +31,7 @@ module Mongoid
31
31
  characterize_one(_target)
32
32
  bind_one
33
33
  characterize_one(_target)
34
+ update_attributes_hash(_target)
34
35
  _base._reset_memoized_descendants!
35
36
  _target.save if persistable?
36
37
  end
@@ -42,9 +43,9 @@ module Mongoid
42
43
  # @example Substitute the new document.
43
44
  # person.name.substitute(new_name)
44
45
  #
45
- # @param [ Document ] replacement A document to replace the target.
46
+ # @param [ Document | Hash ] replacement A document to replace the target.
46
47
  #
47
- # @return [ Document, nil ] The association or nil.
48
+ # @return [ Document | nil ] The association or nil.
48
49
  def substitute(replacement)
49
50
  if replacement != self
50
51
  if _assigning?
@@ -80,11 +81,15 @@ module Mongoid
80
81
  end
81
82
  end
82
83
  unbind_one
83
- return nil unless replacement
84
+ unless replacement
85
+ update_attributes_hash(replacement)
86
+ return nil
87
+ end
84
88
  replacement = Factory.build(klass, replacement) if replacement.is_a?(::Hash)
85
89
  self._target = replacement
86
- bind_one
87
90
  characterize_one(_target)
91
+ bind_one
92
+ update_attributes_hash(_target)
88
93
  _target.save if persistable?
89
94
  end
90
95
  self
@@ -107,11 +112,25 @@ module Mongoid
107
112
  # @example Can we persist the association?
108
113
  # relation.persistable?
109
114
  #
110
- # @return [ true, false ] If the association is persistable.
115
+ # @return [ true | false ] If the association is persistable.
111
116
  def persistable?
112
117
  _base.persisted? && !_binding? && !_building? && !_assigning?
113
118
  end
114
119
 
120
+ # Update the _base's attributes hash with the _target's attributes
121
+ #
122
+ # @param replacement [ Document | nil ] The doc to use to update the
123
+ # attributes hash.
124
+ #
125
+ # @api private
126
+ def update_attributes_hash(replacement)
127
+ if replacement
128
+ _base.attributes.merge!(_association.store_as => replacement.attributes)
129
+ else
130
+ _base.attributes.delete(_association.store_as)
131
+ end
132
+ end
133
+
115
134
  class << self
116
135
 
117
136
  # Returns true if the association is an embedded one. In this case
@@ -66,7 +66,7 @@ module Mongoid
66
66
  # @example Get the validation default.
67
67
  # Proxy.validation_default
68
68
  #
69
- # @return [ true, false ] The validation default.
69
+ # @return [ true | false ] The validation default.
70
70
  def validation_default; true; end
71
71
 
72
72
  # Does this association type store the foreign key?
@@ -88,7 +88,7 @@ module Mongoid
88
88
 
89
89
  # Is this association polymorphic?
90
90
  #
91
- # @return [ true, false ] Whether this association is polymorphic.
91
+ # @return [ true | false ] Whether this association is polymorphic.
92
92
  def polymorphic?
93
93
  @polymorphic ||= !!@options[:as]
94
94
  end
@@ -97,7 +97,7 @@ module Mongoid
97
97
  #
98
98
  # @note Only relevant if the association is polymorphic.
99
99
  #
100
- # @return [ String, nil ] The field for storing the associated object's type.
100
+ # @return [ String | nil ] The field for storing the associated object's type.
101
101
  def type
102
102
  @type ||= "#{as}_type" if polymorphic?
103
103
  end