mongoid 7.5.0 → 8.0.2

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 (358) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +3 -3
  4. data/lib/config/locales/en.yml +47 -30
  5. data/lib/mongoid/association/accessors.rb +38 -9
  6. data/lib/mongoid/association/bindable.rb +50 -2
  7. data/lib/mongoid/association/builders.rb +4 -2
  8. data/lib/mongoid/association/constrainable.rb +0 -1
  9. data/lib/mongoid/association/eager_loadable.rb +29 -7
  10. data/lib/mongoid/association/embedded/batchable.rb +53 -13
  11. data/lib/mongoid/association/embedded/cyclic.rb +1 -1
  12. data/lib/mongoid/association/embedded/embedded_in/binding.rb +24 -2
  13. data/lib/mongoid/association/embedded/embedded_in/proxy.rb +2 -2
  14. data/lib/mongoid/association/embedded/embedded_in.rb +3 -2
  15. data/lib/mongoid/association/embedded/embeds_many/binding.rb +1 -0
  16. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +1 -1
  17. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +50 -28
  18. data/lib/mongoid/association/embedded/embeds_many.rb +2 -2
  19. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +18 -4
  20. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +23 -4
  21. data/lib/mongoid/association/embedded/embeds_one.rb +3 -3
  22. data/lib/mongoid/association/macros.rb +2 -1
  23. data/lib/mongoid/association/many.rb +11 -7
  24. data/lib/mongoid/association/nested/many.rb +5 -4
  25. data/lib/mongoid/association/nested/nested_buildable.rb +4 -4
  26. data/lib/mongoid/association/nested/one.rb +5 -5
  27. data/lib/mongoid/association/one.rb +2 -2
  28. data/lib/mongoid/association/options.rb +9 -9
  29. data/lib/mongoid/association/proxy.rb +14 -3
  30. data/lib/mongoid/association/referenced/auto_save.rb +4 -3
  31. data/lib/mongoid/association/referenced/belongs_to/binding.rb +1 -0
  32. data/lib/mongoid/association/referenced/belongs_to/buildable.rb +1 -1
  33. data/lib/mongoid/association/referenced/belongs_to/proxy.rb +5 -6
  34. data/lib/mongoid/association/referenced/belongs_to.rb +2 -2
  35. data/lib/mongoid/association/referenced/counter_cache.rb +10 -10
  36. data/lib/mongoid/association/referenced/eager.rb +2 -2
  37. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +66 -13
  38. data/lib/mongoid/association/referenced/has_and_belongs_to_many.rb +6 -3
  39. data/lib/mongoid/association/referenced/has_many/enumerable.rb +20 -28
  40. data/lib/mongoid/association/referenced/has_many/proxy.rb +24 -18
  41. data/lib/mongoid/association/referenced/has_many.rb +3 -3
  42. data/lib/mongoid/association/referenced/has_one/buildable.rb +1 -1
  43. data/lib/mongoid/association/referenced/has_one/nested_builder.rb +5 -5
  44. data/lib/mongoid/association/referenced/has_one/proxy.rb +9 -12
  45. data/lib/mongoid/association/referenced/has_one.rb +3 -3
  46. data/lib/mongoid/association/referenced/syncable.rb +4 -4
  47. data/lib/mongoid/association/reflections.rb +2 -2
  48. data/lib/mongoid/association/relatable.rb +44 -10
  49. data/lib/mongoid/association.rb +5 -5
  50. data/lib/mongoid/atomic/modifiers.rb +2 -2
  51. data/lib/mongoid/atomic/paths/embedded/many.rb +19 -0
  52. data/lib/mongoid/attributes/dynamic.rb +3 -3
  53. data/lib/mongoid/attributes/nested.rb +5 -5
  54. data/lib/mongoid/attributes/processing.rb +10 -3
  55. data/lib/mongoid/attributes/projector.rb +1 -1
  56. data/lib/mongoid/attributes/readonly.rb +2 -2
  57. data/lib/mongoid/attributes.rb +43 -40
  58. data/lib/mongoid/changeable.rb +42 -7
  59. data/lib/mongoid/clients/options.rb +5 -1
  60. data/lib/mongoid/clients/sessions.rb +2 -14
  61. data/lib/mongoid/clients/validators/storage.rb +3 -3
  62. data/lib/mongoid/config/validators/client.rb +6 -6
  63. data/lib/mongoid/config.rb +27 -17
  64. data/lib/mongoid/contextual/aggregable/memory.rb +24 -16
  65. data/lib/mongoid/contextual/aggregable/mongo.rb +5 -5
  66. data/lib/mongoid/contextual/aggregable/none.rb +1 -1
  67. data/lib/mongoid/contextual/atomic.rb +1 -1
  68. data/lib/mongoid/contextual/geo_near.rb +7 -7
  69. data/lib/mongoid/contextual/map_reduce.rb +2 -2
  70. data/lib/mongoid/contextual/memory.rb +59 -32
  71. data/lib/mongoid/contextual/mongo.rb +184 -256
  72. data/lib/mongoid/contextual/none.rb +34 -16
  73. data/lib/mongoid/contextual/queryable.rb +1 -1
  74. data/lib/mongoid/contextual.rb +2 -2
  75. data/lib/mongoid/copyable.rb +32 -8
  76. data/lib/mongoid/criteria/findable.rb +7 -4
  77. data/lib/mongoid/criteria/includable.rb +24 -20
  78. data/lib/mongoid/criteria/marshalable.rb +10 -2
  79. data/lib/mongoid/criteria/permission.rb +1 -1
  80. data/lib/mongoid/criteria/queryable/aggregable.rb +2 -2
  81. data/lib/mongoid/criteria/queryable/extensions/array.rb +2 -15
  82. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +25 -4
  83. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +2 -2
  84. data/lib/mongoid/criteria/queryable/extensions/date.rb +6 -1
  85. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +6 -1
  86. data/lib/mongoid/criteria/queryable/extensions/hash.rb +0 -16
  87. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
  88. data/lib/mongoid/criteria/queryable/extensions/object.rb +2 -1
  89. data/lib/mongoid/criteria/queryable/extensions/range.rb +13 -5
  90. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +3 -3
  91. data/lib/mongoid/criteria/queryable/extensions/set.rb +1 -1
  92. data/lib/mongoid/criteria/queryable/extensions/string.rb +3 -3
  93. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +4 -2
  94. data/lib/mongoid/criteria/queryable/extensions/time.rb +6 -1
  95. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +6 -1
  96. data/lib/mongoid/criteria/queryable/key.rb +3 -3
  97. data/lib/mongoid/criteria/queryable/optional.rb +5 -11
  98. data/lib/mongoid/criteria/queryable/options.rb +2 -2
  99. data/lib/mongoid/criteria/queryable/pipeline.rb +1 -1
  100. data/lib/mongoid/criteria/queryable/selectable.rb +5 -27
  101. data/lib/mongoid/criteria/queryable/selector.rb +92 -7
  102. data/lib/mongoid/criteria/queryable/smash.rb +40 -7
  103. data/lib/mongoid/criteria/queryable.rb +12 -7
  104. data/lib/mongoid/criteria/scopable.rb +2 -2
  105. data/lib/mongoid/criteria.rb +15 -37
  106. data/lib/mongoid/deprecable.rb +36 -0
  107. data/lib/mongoid/deprecation.rb +25 -0
  108. data/lib/mongoid/document.rb +98 -36
  109. data/lib/mongoid/equality.rb +12 -12
  110. data/lib/mongoid/errors/document_not_found.rb +10 -6
  111. data/lib/mongoid/errors/invalid_config_option.rb +1 -1
  112. data/lib/mongoid/errors/invalid_dependent_strategy.rb +1 -1
  113. data/lib/mongoid/errors/invalid_dot_dollar_assignment.rb +23 -0
  114. data/lib/mongoid/errors/invalid_field.rb +6 -2
  115. data/lib/mongoid/errors/invalid_field_type.rb +26 -0
  116. data/lib/mongoid/errors/invalid_relation.rb +1 -1
  117. data/lib/mongoid/errors/invalid_relation_option.rb +1 -1
  118. data/lib/mongoid/errors/invalid_session_use.rb +1 -1
  119. data/lib/mongoid/errors/invalid_storage_options.rb +1 -1
  120. data/lib/mongoid/errors/mongoid_error.rb +3 -3
  121. data/lib/mongoid/errors/nested_attributes_metadata_not_found.rb +1 -1
  122. data/lib/mongoid/errors/no_client_database.rb +1 -1
  123. data/lib/mongoid/errors/no_client_hosts.rb +1 -1
  124. data/lib/mongoid/errors/readonly_attribute.rb +1 -1
  125. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +1 -1
  126. data/lib/mongoid/errors/unknown_attribute.rb +1 -1
  127. data/lib/mongoid/errors.rb +2 -2
  128. data/lib/mongoid/extensions/array.rb +9 -7
  129. data/lib/mongoid/extensions/big_decimal.rb +29 -10
  130. data/lib/mongoid/extensions/binary.rb +42 -0
  131. data/lib/mongoid/extensions/boolean.rb +8 -2
  132. data/lib/mongoid/extensions/date.rb +26 -20
  133. data/lib/mongoid/extensions/date_time.rb +1 -1
  134. data/lib/mongoid/extensions/false_class.rb +1 -1
  135. data/lib/mongoid/extensions/float.rb +4 -5
  136. data/lib/mongoid/extensions/hash.rb +13 -6
  137. data/lib/mongoid/extensions/integer.rb +4 -5
  138. data/lib/mongoid/extensions/module.rb +1 -1
  139. data/lib/mongoid/extensions/object.rb +8 -6
  140. data/lib/mongoid/extensions/range.rb +41 -10
  141. data/lib/mongoid/extensions/regexp.rb +11 -4
  142. data/lib/mongoid/extensions/set.rb +11 -4
  143. data/lib/mongoid/extensions/string.rb +11 -22
  144. data/lib/mongoid/extensions/symbol.rb +4 -15
  145. data/lib/mongoid/extensions/time.rb +27 -16
  146. data/lib/mongoid/extensions/time_with_zone.rb +1 -2
  147. data/lib/mongoid/extensions/true_class.rb +1 -1
  148. data/lib/mongoid/extensions.rb +1 -0
  149. data/lib/mongoid/factory.rb +42 -7
  150. data/lib/mongoid/fields/foreign_key.rb +11 -4
  151. data/lib/mongoid/fields/localized.rb +2 -2
  152. data/lib/mongoid/fields/standard.rb +7 -7
  153. data/lib/mongoid/fields/validators/macro.rb +3 -9
  154. data/lib/mongoid/fields.rb +57 -15
  155. data/lib/mongoid/findable.rb +28 -22
  156. data/lib/mongoid/indexable/specification.rb +2 -2
  157. data/lib/mongoid/indexable/validators/options.rb +6 -2
  158. data/lib/mongoid/interceptable.rb +73 -13
  159. data/lib/mongoid/matchable.rb +1 -1
  160. data/lib/mongoid/matcher.rb +12 -7
  161. data/lib/mongoid/persistable/creatable.rb +18 -9
  162. data/lib/mongoid/persistable/deletable.rb +1 -1
  163. data/lib/mongoid/persistable/destroyable.rb +1 -1
  164. data/lib/mongoid/persistable/savable.rb +2 -2
  165. data/lib/mongoid/persistable/unsettable.rb +1 -1
  166. data/lib/mongoid/persistable/updatable.rb +19 -12
  167. data/lib/mongoid/persistable/upsertable.rb +1 -1
  168. data/lib/mongoid/persistable.rb +3 -3
  169. data/lib/mongoid/persistence_context.rb +22 -5
  170. data/lib/mongoid/query_cache.rb +8 -260
  171. data/lib/mongoid/railties/controller_runtime.rb +1 -1
  172. data/lib/mongoid/reloadable.rb +7 -3
  173. data/lib/mongoid/scopable.rb +17 -15
  174. data/lib/mongoid/selectable.rb +1 -2
  175. data/lib/mongoid/serializable.rb +10 -6
  176. data/lib/mongoid/stateful.rb +35 -9
  177. data/lib/mongoid/tasks/database.rb +0 -2
  178. data/lib/mongoid/threaded/lifecycle.rb +5 -5
  179. data/lib/mongoid/threaded.rb +9 -9
  180. data/lib/mongoid/timestamps/created.rb +1 -1
  181. data/lib/mongoid/timestamps/updated.rb +2 -2
  182. data/lib/mongoid/touchable.rb +2 -3
  183. data/lib/mongoid/traversable.rb +4 -3
  184. data/lib/mongoid/validatable/localizable.rb +1 -1
  185. data/lib/mongoid/validatable/macros.rb +0 -2
  186. data/lib/mongoid/validatable/presence.rb +2 -2
  187. data/lib/mongoid/validatable/uniqueness.rb +9 -8
  188. data/lib/mongoid/validatable.rb +6 -6
  189. data/lib/mongoid/version.rb +1 -1
  190. data/lib/mongoid/warnings.rb +3 -4
  191. data/lib/mongoid.rb +1 -0
  192. data/spec/config/mongoid.yml +16 -0
  193. data/spec/integration/app_spec.rb +8 -12
  194. data/spec/integration/associations/belongs_to_spec.rb +18 -0
  195. data/spec/integration/associations/embedded_spec.rb +15 -0
  196. data/spec/integration/associations/embeds_many_spec.rb +15 -2
  197. data/spec/integration/associations/embeds_one_spec.rb +18 -0
  198. data/spec/integration/associations/foreign_key_spec.rb +9 -0
  199. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +21 -0
  200. data/spec/integration/associations/has_one_spec.rb +97 -1
  201. data/spec/integration/associations/scope_option_spec.rb +1 -1
  202. data/spec/integration/callbacks_models.rb +95 -1
  203. data/spec/integration/callbacks_spec.rb +226 -4
  204. data/spec/integration/criteria/range_spec.rb +95 -1
  205. data/spec/integration/discriminator_key_spec.rb +115 -76
  206. data/spec/integration/dots_and_dollars_spec.rb +277 -0
  207. data/spec/integration/i18n_fallbacks_spec.rb +1 -15
  208. data/spec/integration/matcher_examples_spec.rb +20 -13
  209. data/spec/integration/matcher_operator_data/type_decimal.yml +3 -2
  210. data/spec/integration/matcher_operator_spec.rb +3 -5
  211. data/spec/integration/persistence/range_field_spec.rb +350 -0
  212. data/spec/mongoid/association/counter_cache_spec.rb +1 -1
  213. data/spec/mongoid/association/depending_spec.rb +9 -9
  214. data/spec/mongoid/association/eager_spec.rb +2 -1
  215. data/spec/mongoid/association/embedded/embedded_in/binding_spec.rb +2 -1
  216. data/spec/mongoid/association/embedded/embedded_in/buildable_spec.rb +54 -0
  217. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +69 -9
  218. data/spec/mongoid/association/embedded/embeds_many/buildable_spec.rb +112 -0
  219. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +219 -8
  220. data/spec/mongoid/association/embedded/embeds_many_models.rb +157 -0
  221. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +12 -0
  222. data/spec/mongoid/association/embedded/embeds_many_spec.rb +68 -0
  223. data/spec/mongoid/association/embedded/embeds_one/buildable_spec.rb +25 -0
  224. data/spec/mongoid/association/embedded/embeds_one_models.rb +19 -0
  225. data/spec/mongoid/association/embedded/embeds_one_spec.rb +28 -0
  226. data/spec/mongoid/association/referenced/belongs_to/binding_spec.rb +2 -1
  227. data/spec/mongoid/association/referenced/belongs_to/buildable_spec.rb +54 -0
  228. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +15 -0
  229. data/spec/mongoid/association/referenced/belongs_to_models.rb +11 -0
  230. data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -2
  231. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +67 -4
  232. data/spec/mongoid/association/referenced/has_and_belongs_to_many_models.rb +25 -0
  233. data/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb +35 -2
  234. data/spec/mongoid/association/referenced/has_many/buildable_spec.rb +109 -0
  235. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +2 -56
  236. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +82 -13
  237. data/spec/mongoid/association/referenced/has_many_models.rb +3 -1
  238. data/spec/mongoid/association/referenced/has_many_spec.rb +25 -0
  239. data/spec/mongoid/association/referenced/has_one/buildable_spec.rb +2 -2
  240. data/spec/mongoid/association/referenced/has_one/proxy_spec.rb +107 -1
  241. data/spec/mongoid/association/referenced/has_one_models.rb +16 -0
  242. data/spec/mongoid/association/syncable_spec.rb +14 -0
  243. data/spec/mongoid/atomic/paths_spec.rb +0 -14
  244. data/spec/mongoid/attributes/nested_spec.rb +80 -11
  245. data/spec/mongoid/attributes/nested_spec_models.rb +48 -0
  246. data/spec/mongoid/attributes/projector_spec.rb +1 -5
  247. data/spec/mongoid/attributes_spec.rb +524 -27
  248. data/spec/mongoid/changeable_spec.rb +130 -13
  249. data/spec/mongoid/clients/factory_spec.rb +23 -30
  250. data/spec/mongoid/clients/sessions_spec.rb +0 -38
  251. data/spec/mongoid/clients_spec.rb +32 -2
  252. data/spec/mongoid/config_spec.rb +58 -13
  253. data/spec/mongoid/contextual/aggregable/memory_spec.rb +396 -158
  254. data/spec/mongoid/contextual/aggregable/memory_table.yml +88 -0
  255. data/spec/mongoid/contextual/aggregable/memory_table_spec.rb +62 -0
  256. data/spec/mongoid/contextual/map_reduce_spec.rb +2 -16
  257. data/spec/mongoid/contextual/memory_spec.rb +521 -14
  258. data/spec/mongoid/contextual/mongo_spec.rb +564 -394
  259. data/spec/mongoid/contextual/none_spec.rb +11 -19
  260. data/spec/mongoid/copyable_spec.rb +451 -1
  261. data/spec/mongoid/criteria/findable_spec.rb +86 -210
  262. data/spec/mongoid/criteria/includable_spec.rb +1492 -0
  263. data/spec/mongoid/criteria/includable_spec_models.rb +54 -0
  264. data/spec/mongoid/criteria/marshalable_spec.rb +18 -1
  265. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +7 -19
  266. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +134 -26
  267. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +11 -0
  268. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +11 -0
  269. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +0 -15
  270. data/spec/mongoid/criteria/queryable/extensions/numeric_spec.rb +73 -7
  271. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +11 -0
  272. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +11 -0
  273. data/spec/mongoid/criteria/queryable/optional_spec.rb +0 -484
  274. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +50 -0
  275. data/spec/mongoid/criteria/queryable/selectable_spec.rb +77 -85
  276. data/spec/mongoid/criteria/queryable/selector_spec.rb +14 -2
  277. data/spec/mongoid/criteria_spec.rb +469 -1201
  278. data/spec/mongoid/document_fields_spec.rb +173 -24
  279. data/spec/mongoid/document_spec.rb +32 -41
  280. data/spec/mongoid/equality_spec.rb +12 -12
  281. data/spec/mongoid/errors/document_not_found_spec.rb +29 -2
  282. data/spec/mongoid/errors/invalid_field_spec.rb +1 -1
  283. data/spec/mongoid/errors/invalid_field_type_spec.rb +55 -0
  284. data/spec/mongoid/errors/mongoid_error_spec.rb +3 -1
  285. data/spec/mongoid/errors/no_environment_spec.rb +3 -3
  286. data/spec/mongoid/errors/too_many_nested_attribute_records_spec.rb +1 -1
  287. data/spec/mongoid/extensions/array_spec.rb +16 -2
  288. data/spec/mongoid/extensions/big_decimal_spec.rb +697 -212
  289. data/spec/mongoid/extensions/binary_spec.rb +44 -9
  290. data/spec/mongoid/extensions/boolean_spec.rb +68 -82
  291. data/spec/mongoid/extensions/date_class_mongoize_spec.rb +7 -3
  292. data/spec/mongoid/extensions/date_spec.rb +71 -1
  293. data/spec/mongoid/extensions/date_time_spec.rb +15 -9
  294. data/spec/mongoid/extensions/float_spec.rb +48 -76
  295. data/spec/mongoid/extensions/hash_spec.rb +30 -0
  296. data/spec/mongoid/extensions/integer_spec.rb +45 -66
  297. data/spec/mongoid/extensions/range_spec.rb +255 -54
  298. data/spec/mongoid/extensions/regexp_spec.rb +58 -33
  299. data/spec/mongoid/extensions/set_spec.rb +106 -0
  300. data/spec/mongoid/extensions/string_spec.rb +53 -25
  301. data/spec/mongoid/extensions/symbol_spec.rb +18 -25
  302. data/spec/mongoid/extensions/time_spec.rb +634 -66
  303. data/spec/mongoid/extensions/time_with_zone_spec.rb +17 -31
  304. data/spec/mongoid/factory_spec.rb +61 -1
  305. data/spec/mongoid/fields_spec.rb +321 -50
  306. data/spec/mongoid/findable_spec.rb +64 -29
  307. data/spec/mongoid/indexable/specification_spec.rb +2 -2
  308. data/spec/mongoid/indexable_spec.rb +39 -20
  309. data/spec/mongoid/interceptable_spec.rb +584 -5
  310. data/spec/mongoid/interceptable_spec_models.rb +235 -4
  311. data/spec/mongoid/matcher/extract_attribute_spec.rb +1 -5
  312. data/spec/mongoid/mongoizable_spec.rb +285 -0
  313. data/spec/mongoid/persistable/creatable_spec.rb +2 -2
  314. data/spec/mongoid/persistable/deletable_spec.rb +2 -2
  315. data/spec/mongoid/persistable/destroyable_spec.rb +2 -2
  316. data/spec/mongoid/persistable/upsertable_spec.rb +14 -0
  317. data/spec/mongoid/persistence_context_spec.rb +50 -1
  318. data/spec/mongoid/query_cache_middleware_spec.rb +0 -18
  319. data/spec/mongoid/query_cache_spec.rb +0 -154
  320. data/spec/mongoid/reloadable_spec.rb +35 -2
  321. data/spec/mongoid/scopable_spec.rb +54 -16
  322. data/spec/mongoid/shardable_spec.rb +14 -0
  323. data/spec/mongoid/stateful_spec.rb +28 -0
  324. data/spec/mongoid/timestamps_spec.rb +390 -0
  325. data/spec/mongoid/timestamps_spec_models.rb +67 -0
  326. data/spec/mongoid/touchable_spec.rb +116 -0
  327. data/spec/mongoid/touchable_spec_models.rb +12 -8
  328. data/spec/mongoid/traversable_spec.rb +4 -11
  329. data/spec/mongoid/validatable/presence_spec.rb +1 -1
  330. data/spec/mongoid/validatable/uniqueness_spec.rb +60 -31
  331. data/spec/mongoid/warnings_spec.rb +35 -0
  332. data/spec/rails/controller_extension/controller_runtime_spec.rb +2 -2
  333. data/spec/rails/mongoid_spec.rb +4 -16
  334. data/spec/shared/lib/mrss/event_subscriber.rb +5 -15
  335. data/spec/support/constraints.rb +24 -0
  336. data/spec/support/macros.rb +30 -0
  337. data/spec/support/models/augmentation.rb +12 -0
  338. data/spec/support/models/band.rb +3 -0
  339. data/spec/support/models/catalog.rb +24 -0
  340. data/spec/support/models/circus.rb +3 -0
  341. data/spec/support/models/fanatic.rb +8 -0
  342. data/spec/support/models/implant.rb +9 -0
  343. data/spec/support/models/label.rb +2 -0
  344. data/spec/support/models/passport.rb +9 -0
  345. data/spec/support/models/person.rb +1 -0
  346. data/spec/support/models/player.rb +2 -0
  347. data/spec/support/models/powerup.rb +12 -0
  348. data/spec/support/models/registry.rb +1 -0
  349. data/spec/support/models/school.rb +14 -0
  350. data/spec/support/models/shield.rb +18 -0
  351. data/spec/support/models/student.rb +14 -0
  352. data/spec/support/models/weapon.rb +12 -0
  353. data.tar.gz.sig +0 -0
  354. metadata +49 -13
  355. metadata.gz.sig +0 -0
  356. data/lib/mongoid/errors/eager_load.rb +0 -23
  357. data/lib/mongoid/errors/invalid_value.rb +0 -17
  358. data/spec/mongoid/errors/eager_load_spec.rb +0 -31
@@ -17,6 +17,8 @@ module Mongoid
17
17
  include Association::EagerLoadable
18
18
  include Queryable
19
19
 
20
+ Mongoid.deprecate(self, :geo_near)
21
+
20
22
  # Options constant.
21
23
  OPTIONS = [ :hint,
22
24
  :limit,
@@ -35,17 +37,6 @@ module Mongoid
35
37
  # @attribute [r] view The Mongo collection view.
36
38
  attr_reader :view
37
39
 
38
- # Is the context cached?
39
- #
40
- # @example Is the context cached?
41
- # context.cached?
42
- #
43
- # @return [ true, false ] If the context is cached.
44
- def cached?
45
- Mongoid::Warnings.warn_criteria_cache_deprecated
46
- !!@cache
47
- end
48
-
49
40
  # Get the number of documents matching the query.
50
41
  #
51
42
  # @example Get the number of matching documents.
@@ -65,7 +56,7 @@ module Mongoid
65
56
  # @return [ Integer ] The number of matches.
66
57
  def count(options = {}, &block)
67
58
  return super(&block) if block_given?
68
- try_cache(:count) { view.count_documents(options) }
59
+ view.count_documents(options)
69
60
  end
70
61
 
71
62
  # Get the estimated number of documents matching the query.
@@ -84,7 +75,7 @@ module Mongoid
84
75
  unless self.criteria.selector.empty?
85
76
  raise Mongoid::Errors::InvalidEstimatedCountCriteria.new(self.klass)
86
77
  end
87
- try_cache(:estimated_count) { view.estimated_document_count(options) }
78
+ view.estimated_document_count(options)
88
79
  end
89
80
 
90
81
  # Delete all documents in the database that match the selector.
@@ -118,7 +109,7 @@ module Mongoid
118
109
  # @example Get the distinct values.
119
110
  # context.distinct(:name)
120
111
  #
121
- # @param [ String, Symbol ] field The name of the field.
112
+ # @param [ String | Symbol ] field The name of the field.
122
113
  #
123
114
  # @return [ Array<Object> ] The distinct values for the field.
124
115
  def distinct(field)
@@ -152,7 +143,6 @@ module Mongoid
152
143
  documents_for_iteration.each do |doc|
153
144
  yield_document(doc, &block)
154
145
  end
155
- @cache_loaded = true
156
146
  self
157
147
  else
158
148
  to_enum
@@ -165,17 +155,11 @@ module Mongoid
165
155
  # context.exists?
166
156
  #
167
157
  # @note We don't use count here since Mongo does not use counted
168
- # b-tree indexes, unless a count is already cached then that is
169
- # used to determine the value.
158
+ # b-tree indexes.
170
159
  #
171
- # @return [ true, false ] If the count is more than zero.
160
+ # @return [ true | false ] If the count is more than zero.
172
161
  def exists?
173
- return !documents.empty? if cached? && cache_loaded?
174
- return @count > 0 if instance_variable_defined?(:@count)
175
-
176
- try_cache(:exists) do
177
- !!(view.projection(_id: 1).limit(1).first)
178
- end
162
+ !!(view.projection(_id: 1).limit(1).first)
179
163
  end
180
164
 
181
165
  # Run an explain on the criteria.
@@ -197,9 +181,9 @@ module Mongoid
197
181
  # @param [ Hash ] update The updates.
198
182
  # @param [ Hash ] options The command options.
199
183
  #
200
- # @option options [ :before, :after ] :return_document Return the updated document
184
+ # @option options [ :before | :after ] :return_document Return the updated document
201
185
  # from before or after update.
202
- # @option options [ true, false ] :upsert Create the document if it doesn't exist.
186
+ # @option options [ true | false ] :upsert Create the document if it doesn't exist.
203
187
  #
204
188
  # @return [ Document ] The result of the command.
205
189
  def find_one_and_update(update, options = {})
@@ -217,9 +201,9 @@ module Mongoid
217
201
  # @param [ Hash ] replacement The replacement.
218
202
  # @param [ Hash ] options The command options.
219
203
  #
220
- # @option options [ :before, :after ] :return_document Return the updated document
204
+ # @option options [ :before | :after ] :return_document Return the updated document
221
205
  # from before or after update.
222
- # @option options [ true, false ] :upsert Create the document if it doesn't exist.
206
+ # @option options [ true | false ] :upsert Create the document if it doesn't exist.
223
207
  #
224
208
  # @return [ Document ] The result of the command.
225
209
  def find_one_and_replace(replacement, options = {})
@@ -249,32 +233,16 @@ module Mongoid
249
233
  # @note Automatically adding a sort on _id when no other sort is
250
234
  # defined on the criteria has the potential to cause bad performance issues.
251
235
  # If you experience unexpected poor performance when using #first or #last
252
- # and have no sort defined on the criteria, use the option { id_sort: :none }.
253
- # Be aware that #first/#last won't guarantee order in this case.
236
+ # and have no sort defined on the criteria, use #take instead.
237
+ # Be aware that #take won't guarantee order.
254
238
  #
255
- # @param [ Integer | Hash ] limit_or_opts The number of documents to
256
- # return, or a hash of options.
257
- #
258
- # @option limit_or_opts [ :none ] :id_sort This option is deprecated.
259
- # Don't apply a sort on _id if no other sort is defined on the criteria.
239
+ # @param [ Integer ] limit The number of documents to return.
260
240
  #
261
241
  # @return [ Document ] The first document.
262
- def first(limit_or_opts = nil)
263
- limit = limit_or_opts unless limit_or_opts.is_a?(Hash)
264
- if cached? && cache_loaded?
265
- return limit ? documents.first(limit) : documents.first
266
- end
267
- try_numbered_cache(:first, limit) do
268
- if limit_or_opts.try(:key?, :id_sort)
269
- Mongoid::Warnings.warn_id_sort_deprecated
270
- end
271
- sorted_view = view
272
- if sort = view.sort || ({ _id: 1 } unless limit_or_opts.try(:fetch, :id_sort) == :none)
273
- sorted_view = view.sort(sort)
274
- end
275
- if raw_docs = sorted_view.limit(limit || 1).to_a
276
- process_raw_docs(raw_docs, limit)
277
- end
242
+ def first(limit = nil)
243
+ sort = view.sort || { _id: 1 }
244
+ if raw_docs = view.sort(sort).limit(limit || 1).to_a
245
+ process_raw_docs(raw_docs, limit)
278
246
  end
279
247
  end
280
248
  alias :one :first
@@ -283,7 +251,6 @@ module Mongoid
283
251
  #
284
252
  # @api private
285
253
  def find_first
286
- return documents.first if cached? && cache_loaded?
287
254
  if raw_doc = view.first
288
255
  doc = Factory.from_db(klass, raw_doc, criteria)
289
256
  eager_load([doc]).first
@@ -313,33 +280,6 @@ module Mongoid
313
280
  GeoNear.new(collection, criteria, coordinates)
314
281
  end
315
282
 
316
- # Invoke the block for each element of Contextual. Create a new array
317
- # containing the values returned by the block.
318
- #
319
- # If the symbol field name is passed instead of the block, additional
320
- # optimizations would be used.
321
- #
322
- # @example Map by some field.
323
- # context.map(:field1)
324
- #
325
- # @example Map with block.
326
- # context.map(&:field1)
327
- #
328
- # @param [ Symbol ] field The field name.
329
- #
330
- # @return [ Array ] The result of mapping.
331
- def map(field = nil, &block)
332
- if !field.nil?
333
- Mongoid::Warnings.warn_map_field_deprecated
334
- end
335
-
336
- if block_given?
337
- super(&block)
338
- else
339
- criteria.pluck(field)
340
- end
341
- end
342
-
343
283
  # Create the new Mongo context. This delegates operations to the
344
284
  # underlying driver.
345
285
  #
@@ -348,7 +288,7 @@ module Mongoid
348
288
  #
349
289
  # @param [ Criteria ] criteria The criteria.
350
290
  def initialize(criteria)
351
- @criteria, @klass, @cache = criteria, criteria.klass, criteria.options[:cache]
291
+ @criteria, @klass = criteria, criteria.klass
352
292
  @collection = @klass.collection
353
293
  criteria.send(:merge_type_selection)
354
294
  @view = collection.find(criteria.selector, session: _session)
@@ -365,39 +305,26 @@ module Mongoid
365
305
  # @note Automatically adding a sort on _id when no other sort is
366
306
  # defined on the criteria has the potential to cause bad performance issues.
367
307
  # If you experience unexpected poor performance when using #first or #last
368
- # and have no sort defined on the criteria, use the option { id_sort: :none }.
369
- # Be aware that #first/#last won't guarantee order in this case.
308
+ # and have no sort defined on the criteria, use #take instead.
309
+ # Be aware that #take won't guarantee order.
370
310
  #
371
- # @param [ Integer | Hash ] limit_or_opts The number of documents to
372
- # return, or a hash of options.
373
- #
374
- # @option limit_or_opts [ :none ] :id_sort This option is deprecated.
375
- # Don't apply a sort on _id if no other sort is defined on the criteria.
311
+ # @param [ Integer ] limit The number of documents to return.
376
312
  #
377
313
  # @return [ Document ] The last document.
378
- def last(limit_or_opts = nil)
379
- limit = limit_or_opts unless limit_or_opts.is_a?(Hash)
380
- if cached? && cache_loaded?
381
- return limit ? documents.last(limit) : documents.last
382
- end
383
- res = try_numbered_cache(:last, limit) do
384
- with_inverse_sorting(limit_or_opts) do
385
- if raw_docs = view.limit(limit || 1).to_a
386
- process_raw_docs(raw_docs, limit)
387
- end
388
- end
389
- end
390
- res.is_a?(Array) ? res.reverse : res
314
+ def last(limit = nil)
315
+ raw_docs = view.sort(inverse_sorting).limit(limit || 1).to_a.reverse
316
+ process_raw_docs(raw_docs, limit)
391
317
  end
392
318
 
393
- # Get's the number of documents matching the query selector.
319
+ # Returns the number of documents in the database matching
320
+ # the query selector.
394
321
  #
395
322
  # @example Get the length.
396
323
  # context.length
397
324
  #
398
325
  # @return [ Integer ] The number of documents.
399
326
  def length
400
- @length ||= self.count
327
+ self.count
401
328
  end
402
329
  alias :size :length
403
330
 
@@ -470,12 +397,9 @@ module Mongoid
470
397
  # @example Pluck a field.
471
398
  # context.pluck(:_id)
472
399
  #
473
- # @note This method will return the raw db values - it performs no custom
474
- # serialization.
475
- #
476
- # @param [ String, Symbol, Array ] fields Fields to pluck.
400
+ # @param [ String | Symbol ] *fields Field(s) to pluck.
477
401
  #
478
- # @return [ Array<Object, Array> ] The plucked values.
402
+ # @return [ Array<Object> | Array<Array<Object>> ] The plucked values.
479
403
  def pluck(*fields)
480
404
  # Multiple fields can map to the same field name. For example, plucking
481
405
  # a field and its _translations field map to the same field in the database.
@@ -505,6 +429,87 @@ module Mongoid
505
429
  end
506
430
  end
507
431
 
432
+ # Pick the single field values from the database.
433
+ #
434
+ # @example Pick a field.
435
+ # context.pick(:_id)
436
+ #
437
+ # @param [ String | Symbol ] *fields Field(s) to pick.
438
+ #
439
+ # @return [ Object | Array<Object> ] The picked values.
440
+ def pick(*fields)
441
+ limit(1).pluck(*fields).first
442
+ end
443
+
444
+ # Get a hash of counts for the values of a single field. For example,
445
+ # if the following documents were in the database:
446
+ #
447
+ # { _id: 1, age: 21 }
448
+ # { _id: 2, age: 21 }
449
+ # { _id: 3, age: 22 }
450
+ #
451
+ # Model.tally("age")
452
+ #
453
+ # would yield the following result:
454
+ #
455
+ # { 21 => 2, 22 => 1 }
456
+ #
457
+ # When tallying a field inside an array or embeds_many association:
458
+ #
459
+ # { _id: 1, array: [ { x: 1 }, { x: 2 } ] }
460
+ # { _id: 2, array: [ { x: 1 }, { x: 2 } ] }
461
+ # { _id: 3, array: [ { x: 1 }, { x: 3 } ] }
462
+ #
463
+ # Model.tally("array.x")
464
+ #
465
+ # The keys of the resulting hash are arrays:
466
+ #
467
+ # { [ 1, 2 ] => 2, [ 1, 3 ] => 1 }
468
+ #
469
+ # Note that if tallying an element in an array of hashes, and the key
470
+ # doesn't exist in some of the hashes, tally will not include those
471
+ # nil keys in the resulting hash:
472
+ #
473
+ # { _id: 1, array: [ { x: 1 }, { x: 2 }, { y: 3 } ] }
474
+ #
475
+ # Model.tally("array.x")
476
+ # # => { [ 1, 2 ] => 1 }
477
+ #
478
+ # @param [ String | Symbol ] field The field name.
479
+ #
480
+ # @return [ Hash ] The hash of counts.
481
+ def tally(field)
482
+ name = klass.cleanse_localized_field_names(field)
483
+
484
+ fld = klass.traverse_association_tree(name)
485
+ pipeline = [ { "$group" => { _id: "$#{name}", counts: { "$sum": 1 } } } ]
486
+ pipeline.unshift("$match" => view.filter) unless view.filter.blank?
487
+
488
+ collection.aggregate(pipeline).reduce({}) do |tallies, doc|
489
+ is_translation = "#{name}_translations" == field.to_s
490
+ val = doc["_id"]
491
+
492
+ key = if val.is_a?(Array)
493
+ val.map do |v|
494
+ demongoize_with_field(fld, v, is_translation)
495
+ end
496
+ else
497
+ demongoize_with_field(fld, val, is_translation)
498
+ end
499
+
500
+ # The only time where a key will already exist in the tallies hash
501
+ # is when the values are stored differently in the database, but
502
+ # demongoize to the same value. A good example of when this happens
503
+ # is when using localized fields. While the server query won't group
504
+ # together hashes that have other values in different languages, the
505
+ # demongoized value is just the translation in the current locale,
506
+ # which can be the same across multiple of those unequal hashes.
507
+ tallies[key] ||= 0
508
+ tallies[key] += doc["counts"]
509
+ tallies
510
+ end
511
+ end
512
+
508
513
  # Skips the provided number of documents.
509
514
  #
510
515
  # @example Skip the documents.
@@ -571,47 +576,6 @@ module Mongoid
571
576
 
572
577
  private
573
578
 
574
- # yield the block given or return the cached value
575
- #
576
- # @param [ String, Symbol ] key The instance variable name
577
- #
578
- # @return the result of the block
579
- def try_cache(key, &block)
580
- unless cached?
581
- yield
582
- else
583
- unless ret = instance_variable_get("@#{key}")
584
- instance_variable_set("@#{key}", ret = yield)
585
- end
586
- ret
587
- end
588
- end
589
-
590
- # yield the block given or return the cached value
591
- #
592
- # @param [ String, Symbol ] key The instance variable name
593
- # @param [ Integer | nil ] n The number of documents requested or nil
594
- # if none is requested.
595
- #
596
- # @return [ Object ] The result of the block.
597
- def try_numbered_cache(key, n, &block)
598
- unless cached?
599
- yield if block_given?
600
- else
601
- len = n || 1
602
- ret = instance_variable_get("@#{key}")
603
- if !ret || ret.length < len
604
- instance_variable_set("@#{key}", ret = Array.wrap(yield))
605
- elsif !n
606
- ret.is_a?(Array) ? ret.first : ret
607
- elsif ret.length > len
608
- ret.first(n)
609
- else
610
- ret
611
- end
612
- end
613
- end
614
-
615
579
  # Update the documents for the provided method.
616
580
  #
617
581
  # @api private
@@ -622,7 +586,7 @@ module Mongoid
622
586
  # @param [ Hash ] attributes The updates.
623
587
  # @param [ Symbol ] method The method to use.
624
588
  #
625
- # @return [ true, false ] If the update succeeded.
589
+ # @return [ true | false ] If the update succeeded.
626
590
  def update_documents(attributes, method = :update_one, opts = {})
627
591
  return false unless attributes
628
592
  attributes = Hash[attributes.map { |k, v| [klass.database_field_name(k.to_s), v] }]
@@ -672,57 +636,9 @@ module Mongoid
672
636
  # Map the inverse sort symbols to the correct MongoDB values.
673
637
  #
674
638
  # @api private
675
- #
676
- # @example Apply the inverse sorting params to the given block
677
- # context.with_inverse_sorting
678
- def with_inverse_sorting(opts = {})
679
- Mongoid::Warnings.warn_id_sort_deprecated if opts.try(:key?, :id_sort)
680
-
681
- begin
682
- if sort = criteria.options[:sort] || ( { _id: 1 } unless opts.try(:fetch, :id_sort) == :none )
683
- @view = view.sort(Hash[sort.map{|k, v| [k, -1*v]}])
684
- end
685
- yield
686
- ensure
687
- apply_option(:sort)
688
- end
689
- end
690
-
691
- # Is the cache able to be added to?
692
- #
693
- # @api private
694
- #
695
- # @example Is the context cacheable?
696
- # context.cacheable?
697
- #
698
- # @return [ true, false ] If caching, and the cache isn't loaded.
699
- def cacheable?
700
- cached? && !cache_loaded?
701
- end
702
-
703
- # Is the cache fully loaded? Will be true if caching after one full
704
- # iteration.
705
- #
706
- # @api private
707
- #
708
- # @example Is the cache loaded?
709
- # context.cache_loaded?
710
- #
711
- # @return [ true, false ] If the cache is loaded.
712
- def cache_loaded?
713
- !!@cache_loaded
714
- end
715
-
716
- # Get the documents for cached queries.
717
- #
718
- # @api private
719
- #
720
- # @example Get the cached documents.
721
- # context.documents
722
- #
723
- # @return [ Array<Document> ] The documents.
724
- def documents
725
- @documents ||= []
639
+ def inverse_sorting
640
+ sort = view.sort || { _id: 1 }
641
+ Hash[sort.map{|k, v| [k, -1*v]}]
726
642
  end
727
643
 
728
644
  # Get the documents the context should iterate. This follows 3 rules:
@@ -738,9 +654,8 @@ module Mongoid
738
654
  # @example Get the documents for iteration.
739
655
  # context.documents_for_iteration
740
656
  #
741
- # @return [ Array<Document>, Mongo::Collection::View ] The docs to iterate.
657
+ # @return [ Array<Document> | Mongo::Collection::View ] The docs to iterate.
742
658
  def documents_for_iteration
743
- return documents if cached? && !documents.empty?
744
659
  return view unless eager_loadable?
745
660
  docs = view.map{ |doc| Factory.from_db(klass, doc, criteria) }
746
661
  eager_load(docs)
@@ -760,7 +675,6 @@ module Mongoid
760
675
  doc = document.respond_to?(:_id) ?
761
676
  document : Factory.from_db(klass, document, criteria)
762
677
  yield(doc)
763
- documents.push(doc) if cacheable?
764
678
  end
765
679
 
766
680
  private
@@ -773,6 +687,26 @@ module Mongoid
773
687
  collection.write_concern.nil? || collection.write_concern.acknowledged?
774
688
  end
775
689
 
690
+ # Fetch the element from the given hash and demongoize it using the
691
+ # given field. If the obj is an array, map over it and call this method
692
+ # on all of its elements.
693
+ #
694
+ # @param [ Hash | Array<Hash> ] obj The hash or array of hashes to fetch from.
695
+ # @param [ String ] meth The key to fetch from the hash.
696
+ # @param [ Field ] field The field to use for demongoization.
697
+ #
698
+ # @return [ Object ] The demongoized value.
699
+ #
700
+ # @api private
701
+ def fetch_and_demongoize(obj, meth, field)
702
+ if obj.is_a?(Array)
703
+ obj.map { |doc| fetch_and_demongoize(doc, meth, field) }
704
+ else
705
+ res = obj.try(:fetch, meth, nil)
706
+ field ? field.demongoize(res) : res.class.demongoize(res)
707
+ end
708
+ end
709
+
776
710
  # Extracts the value for the given field name from the given attribute
777
711
  # hash.
778
712
  #
@@ -781,24 +715,18 @@ module Mongoid
781
715
  #
782
716
  # @param [ Object ] The value for the given field name
783
717
  def extract_value(attrs, field_name)
784
- def fetch_and_demongoize(d, meth, klass)
785
- res = d.try(:fetch, meth, nil)
786
- if field = klass.fields[meth]
787
- field.demongoize(res)
788
- else
789
- res.class.demongoize(res)
790
- end
791
- end
718
+ i = 1
719
+ num_meths = field_name.count('.') + 1
720
+ curr = attrs.dup
792
721
 
793
- k = klass
794
- meths = field_name.split('.')
795
- meths.each_with_index.inject(attrs) do |curr, (meth, i)|
722
+ klass.traverse_association_tree(field_name) do |meth, obj, is_field|
723
+ field = obj if is_field
796
724
  is_translation = false
797
- if !k.fields.key?(meth) && !k.relations.key?(meth)
798
- if tr = meth.match(/(.*)_translations\z/)&.captures&.first
799
- is_translation = true
800
- meth = tr
801
- end
725
+ # If no association or field was found, check if the meth is an
726
+ # _translations field.
727
+ if obj.nil? & tr = meth.match(/(.*)_translations\z/)&.captures&.first
728
+ is_translation = true
729
+ meth = tr
802
730
  end
803
731
 
804
732
  # 1. If curr is an array fetch from all elements in the array.
@@ -811,31 +739,24 @@ module Mongoid
811
739
  # 3. If the meth is an _translations field, do not demongoize the
812
740
  # value so the full hash is returned.
813
741
  # 4. Otherwise, fetch and demongoize the value for the key meth.
814
- if curr.is_a? Array
815
- res = curr.map { |x| fetch_and_demongoize(x, meth, k) }
742
+ curr = if curr.is_a? Array
743
+ res = fetch_and_demongoize(curr, meth, field)
816
744
  res.empty? ? nil : res
817
- elsif !is_translation && k.fields[meth]&.localized?
818
- if i < meths.length-1
745
+ elsif !is_translation && field&.localized?
746
+ if i < num_meths
819
747
  curr.try(:fetch, meth, nil)
820
748
  else
821
- fetch_and_demongoize(curr, meth, k)
749
+ fetch_and_demongoize(curr, meth, field)
822
750
  end
823
751
  elsif is_translation
824
752
  curr.try(:fetch, meth, nil)
825
753
  else
826
- fetch_and_demongoize(curr, meth, k)
827
- end.tap do
828
- if as = k.try(:aliased_associations)
829
- if a = as.fetch(meth, nil)
830
- meth = a
831
- end
832
- end
833
-
834
- if relation = k.relations[meth]
835
- k = relation.klass
836
- end
754
+ fetch_and_demongoize(curr, meth, field)
837
755
  end
756
+
757
+ i += 1
838
758
  end
759
+ curr
839
760
  end
840
761
 
841
762
  # Recursively demongoize the given value. This method recursively traverses
@@ -843,34 +764,41 @@ module Mongoid
843
764
  #
844
765
  # @param [ String ] field_name The name of the field to demongoize.
845
766
  # @param [ Object ] value The value to demongoize.
846
- # @param [ Boolean ] is_translation The field we are retrieving is an
767
+ # @param [ true | false ] is_translation The field we are retrieving is an
847
768
  # _translations field.
848
769
  #
849
770
  # @return [ Object ] The demongoized value.
850
771
  def recursive_demongoize(field_name, value, is_translation)
851
- k = klass
852
- field_name.split('.').each do |meth|
853
- if as = k.try(:aliased_associations)
854
- if a = as.fetch(meth, nil)
855
- meth = a.to_s
856
- end
857
- end
772
+ field = klass.traverse_association_tree(field_name)
773
+ demongoize_with_field(field, value, is_translation)
774
+ end
858
775
 
859
- if relation = k.relations[meth]
860
- k = relation.klass
861
- elsif field = k.fields[meth]
862
- # If it's a localized field that's not a hash, don't demongoize
863
- # again, we already have the translation. If it's an _translation
864
- # field, don't demongoize, we want the full hash not just a
865
- # specific translation.
866
- if field.localized? && (!value.is_a?(Hash) || is_translation)
867
- return value.class.demongoize(value)
868
- else
869
- return field.demongoize(value)
870
- end
776
+ # Demongoize the value for the given field. If the field is nil or the
777
+ # field is a translations field, the value is demongoized using its class.
778
+ #
779
+ # @param [ Field ] field The field to use to demongoize.
780
+ # @param [ Object ] value The value to demongoize.
781
+ # @param [ true | false ] is_translation The field we are retrieving is an
782
+ # _translations field.
783
+ #
784
+ # @return [ Object ] The demongoized value.
785
+ #
786
+ # @api private
787
+ def demongoize_with_field(field, value, is_translation)
788
+ if field
789
+ # If it's a localized field that's not a hash, don't demongoize
790
+ # again, we already have the translation. If it's an _translations
791
+ # field, don't demongoize, we want the full hash not just a
792
+ # specific translation.
793
+ # If it is a hash, and it's not a translations field, we need to
794
+ # demongoize to get the correct translation.
795
+ if field.localized? && (!value.is_a?(Hash) || is_translation)
796
+ value.class.demongoize(value)
871
797
  else
872
- return value.class.demongoize(value)
798
+ field.demongoize(value)
873
799
  end
800
+ else
801
+ value.class.demongoize(value)
874
802
  end
875
803
  end
876
804