mongoid 7.4.0 → 8.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (391) 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 +52 -28
  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 +22 -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 -24
  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/cacheable.rb +2 -2
  59. data/lib/mongoid/changeable.rb +42 -7
  60. data/lib/mongoid/clients/options.rb +5 -1
  61. data/lib/mongoid/clients/sessions.rb +2 -14
  62. data/lib/mongoid/clients/validators/storage.rb +3 -3
  63. data/lib/mongoid/config/environment.rb +20 -4
  64. data/lib/mongoid/config/validators/client.rb +6 -6
  65. data/lib/mongoid/config.rb +32 -17
  66. data/lib/mongoid/contextual/aggregable/memory.rb +24 -16
  67. data/lib/mongoid/contextual/aggregable/mongo.rb +5 -5
  68. data/lib/mongoid/contextual/aggregable/none.rb +1 -1
  69. data/lib/mongoid/contextual/atomic.rb +1 -1
  70. data/lib/mongoid/contextual/geo_near.rb +7 -7
  71. data/lib/mongoid/contextual/map_reduce.rb +2 -2
  72. data/lib/mongoid/contextual/memory.rb +180 -21
  73. data/lib/mongoid/contextual/mongo.rb +237 -217
  74. data/lib/mongoid/contextual/none.rb +67 -5
  75. data/lib/mongoid/contextual/queryable.rb +1 -1
  76. data/lib/mongoid/contextual.rb +2 -2
  77. data/lib/mongoid/copyable.rb +32 -8
  78. data/lib/mongoid/criteria/findable.rb +7 -4
  79. data/lib/mongoid/criteria/includable.rb +24 -20
  80. data/lib/mongoid/criteria/marshalable.rb +10 -2
  81. data/lib/mongoid/criteria/permission.rb +1 -1
  82. data/lib/mongoid/criteria/queryable/aggregable.rb +2 -2
  83. data/lib/mongoid/criteria/queryable/extensions/array.rb +2 -13
  84. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +25 -4
  85. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +2 -2
  86. data/lib/mongoid/criteria/queryable/extensions/date.rb +6 -1
  87. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +6 -1
  88. data/lib/mongoid/criteria/queryable/extensions/hash.rb +0 -14
  89. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
  90. data/lib/mongoid/criteria/queryable/extensions/object.rb +2 -1
  91. data/lib/mongoid/criteria/queryable/extensions/range.rb +13 -5
  92. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +3 -3
  93. data/lib/mongoid/criteria/queryable/extensions/set.rb +1 -1
  94. data/lib/mongoid/criteria/queryable/extensions/string.rb +3 -3
  95. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +4 -2
  96. data/lib/mongoid/criteria/queryable/extensions/time.rb +6 -1
  97. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +6 -1
  98. data/lib/mongoid/criteria/queryable/key.rb +3 -3
  99. data/lib/mongoid/criteria/queryable/mergeable.rb +21 -0
  100. data/lib/mongoid/criteria/queryable/optional.rb +5 -11
  101. data/lib/mongoid/criteria/queryable/options.rb +2 -2
  102. data/lib/mongoid/criteria/queryable/pipeline.rb +1 -1
  103. data/lib/mongoid/criteria/queryable/selectable.rb +31 -37
  104. data/lib/mongoid/criteria/queryable/selector.rb +92 -7
  105. data/lib/mongoid/criteria/queryable/smash.rb +40 -7
  106. data/lib/mongoid/criteria/queryable.rb +12 -7
  107. data/lib/mongoid/criteria/scopable.rb +2 -2
  108. data/lib/mongoid/criteria.rb +15 -35
  109. data/lib/mongoid/deprecable.rb +36 -0
  110. data/lib/mongoid/deprecation.rb +25 -0
  111. data/lib/mongoid/document.rb +98 -34
  112. data/lib/mongoid/equality.rb +12 -12
  113. data/lib/mongoid/errors/document_not_found.rb +33 -12
  114. data/lib/mongoid/errors/invalid_config_option.rb +1 -1
  115. data/lib/mongoid/errors/invalid_dependent_strategy.rb +1 -1
  116. data/lib/mongoid/errors/invalid_dot_dollar_assignment.rb +23 -0
  117. data/lib/mongoid/errors/invalid_field.rb +6 -2
  118. data/lib/mongoid/errors/invalid_field_type.rb +26 -0
  119. data/lib/mongoid/errors/invalid_relation.rb +1 -1
  120. data/lib/mongoid/errors/invalid_relation_option.rb +1 -1
  121. data/lib/mongoid/errors/invalid_session_use.rb +1 -1
  122. data/lib/mongoid/errors/invalid_storage_options.rb +1 -1
  123. data/lib/mongoid/errors/mongoid_error.rb +3 -3
  124. data/lib/mongoid/errors/nested_attributes_metadata_not_found.rb +1 -1
  125. data/lib/mongoid/errors/no_client_database.rb +1 -1
  126. data/lib/mongoid/errors/no_client_hosts.rb +1 -1
  127. data/lib/mongoid/errors/readonly_attribute.rb +1 -1
  128. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +1 -1
  129. data/lib/mongoid/errors/unknown_attribute.rb +1 -1
  130. data/lib/mongoid/errors.rb +2 -2
  131. data/lib/mongoid/extensions/array.rb +9 -7
  132. data/lib/mongoid/extensions/big_decimal.rb +33 -10
  133. data/lib/mongoid/extensions/binary.rb +42 -0
  134. data/lib/mongoid/extensions/boolean.rb +8 -2
  135. data/lib/mongoid/extensions/date.rb +26 -20
  136. data/lib/mongoid/extensions/date_time.rb +1 -1
  137. data/lib/mongoid/extensions/false_class.rb +1 -1
  138. data/lib/mongoid/extensions/float.rb +7 -4
  139. data/lib/mongoid/extensions/hash.rb +13 -6
  140. data/lib/mongoid/extensions/integer.rb +7 -4
  141. data/lib/mongoid/extensions/module.rb +1 -1
  142. data/lib/mongoid/extensions/object.rb +8 -6
  143. data/lib/mongoid/extensions/range.rb +41 -10
  144. data/lib/mongoid/extensions/regexp.rb +11 -4
  145. data/lib/mongoid/extensions/set.rb +11 -4
  146. data/lib/mongoid/extensions/string.rb +11 -22
  147. data/lib/mongoid/extensions/symbol.rb +4 -15
  148. data/lib/mongoid/extensions/time.rb +27 -16
  149. data/lib/mongoid/extensions/time_with_zone.rb +1 -2
  150. data/lib/mongoid/extensions/true_class.rb +1 -1
  151. data/lib/mongoid/extensions.rb +1 -0
  152. data/lib/mongoid/factory.rb +42 -7
  153. data/lib/mongoid/fields/foreign_key.rb +11 -4
  154. data/lib/mongoid/fields/localized.rb +9 -4
  155. data/lib/mongoid/fields/standard.rb +7 -7
  156. data/lib/mongoid/fields/validators/macro.rb +3 -9
  157. data/lib/mongoid/fields.rb +201 -35
  158. data/lib/mongoid/findable.rb +34 -13
  159. data/lib/mongoid/indexable/specification.rb +2 -2
  160. data/lib/mongoid/indexable/validators/options.rb +6 -2
  161. data/lib/mongoid/interceptable.rb +73 -13
  162. data/lib/mongoid/matchable.rb +1 -1
  163. data/lib/mongoid/matcher.rb +12 -7
  164. data/lib/mongoid/persistable/creatable.rb +18 -9
  165. data/lib/mongoid/persistable/deletable.rb +1 -1
  166. data/lib/mongoid/persistable/destroyable.rb +1 -1
  167. data/lib/mongoid/persistable/savable.rb +2 -2
  168. data/lib/mongoid/persistable/unsettable.rb +1 -1
  169. data/lib/mongoid/persistable/updatable.rb +19 -12
  170. data/lib/mongoid/persistable/upsertable.rb +2 -2
  171. data/lib/mongoid/persistable.rb +3 -3
  172. data/lib/mongoid/persistence_context.rb +63 -10
  173. data/lib/mongoid/query_cache.rb +8 -260
  174. data/lib/mongoid/railties/controller_runtime.rb +1 -1
  175. data/lib/mongoid/reloadable.rb +7 -3
  176. data/lib/mongoid/scopable.rb +26 -22
  177. data/lib/mongoid/selectable.rb +1 -2
  178. data/lib/mongoid/serializable.rb +10 -6
  179. data/lib/mongoid/stateful.rb +35 -9
  180. data/lib/mongoid/tasks/database.rb +0 -2
  181. data/lib/mongoid/threaded/lifecycle.rb +5 -5
  182. data/lib/mongoid/threaded.rb +12 -12
  183. data/lib/mongoid/timestamps/created.rb +1 -1
  184. data/lib/mongoid/timestamps/updated.rb +2 -2
  185. data/lib/mongoid/touchable.rb +2 -3
  186. data/lib/mongoid/traversable.rb +8 -4
  187. data/lib/mongoid/validatable/localizable.rb +1 -1
  188. data/lib/mongoid/validatable/macros.rb +0 -2
  189. data/lib/mongoid/validatable/presence.rb +2 -2
  190. data/lib/mongoid/validatable/uniqueness.rb +9 -8
  191. data/lib/mongoid/validatable.rb +6 -6
  192. data/lib/mongoid/version.rb +1 -1
  193. data/lib/mongoid/warnings.rb +28 -0
  194. data/lib/mongoid.rb +2 -0
  195. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +11 -5
  196. data/spec/config/mongoid.yml +16 -0
  197. data/spec/config/mongoid_with_schema_map_uuid.yml +27 -0
  198. data/spec/integration/app_spec.rb +28 -26
  199. data/spec/integration/associations/belongs_to_spec.rb +18 -0
  200. data/spec/integration/associations/embedded_dirty_spec.rb +28 -0
  201. data/spec/integration/associations/embedded_spec.rb +15 -0
  202. data/spec/integration/associations/embeds_many_spec.rb +15 -2
  203. data/spec/integration/associations/embeds_one_spec.rb +18 -0
  204. data/spec/integration/associations/foreign_key_spec.rb +9 -0
  205. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +21 -0
  206. data/spec/integration/associations/has_one_spec.rb +97 -1
  207. data/spec/integration/associations/scope_option_spec.rb +1 -1
  208. data/spec/integration/callbacks_models.rb +95 -1
  209. data/spec/integration/callbacks_spec.rb +226 -4
  210. data/spec/integration/criteria/range_spec.rb +95 -1
  211. data/spec/integration/discriminator_key_spec.rb +115 -76
  212. data/spec/integration/dots_and_dollars_spec.rb +277 -0
  213. data/spec/integration/i18n_fallbacks_spec.rb +1 -17
  214. data/spec/integration/matcher_examples_spec.rb +20 -13
  215. data/spec/integration/matcher_operator_data/type_decimal.yml +3 -2
  216. data/spec/integration/matcher_operator_spec.rb +3 -5
  217. data/spec/integration/persistence/range_field_spec.rb +350 -0
  218. data/spec/lite_spec_helper.rb +1 -1
  219. data/spec/mongoid/association/counter_cache_spec.rb +1 -1
  220. data/spec/mongoid/association/depending_spec.rb +9 -9
  221. data/spec/mongoid/association/eager_spec.rb +2 -1
  222. data/spec/mongoid/association/embedded/embedded_in/binding_spec.rb +2 -1
  223. data/spec/mongoid/association/embedded/embedded_in/buildable_spec.rb +54 -0
  224. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +69 -9
  225. data/spec/mongoid/association/embedded/embeds_many/buildable_spec.rb +112 -0
  226. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +219 -8
  227. data/spec/mongoid/association/embedded/embeds_many_models.rb +157 -0
  228. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +12 -0
  229. data/spec/mongoid/association/embedded/embeds_many_spec.rb +68 -0
  230. data/spec/mongoid/association/embedded/embeds_one/buildable_spec.rb +25 -0
  231. data/spec/mongoid/association/embedded/embeds_one_models.rb +19 -0
  232. data/spec/mongoid/association/embedded/embeds_one_spec.rb +28 -0
  233. data/spec/mongoid/association/referenced/belongs_to/binding_spec.rb +2 -1
  234. data/spec/mongoid/association/referenced/belongs_to/buildable_spec.rb +54 -0
  235. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +15 -0
  236. data/spec/mongoid/association/referenced/belongs_to_models.rb +11 -0
  237. data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -2
  238. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +67 -4
  239. data/spec/mongoid/association/referenced/has_and_belongs_to_many_models.rb +25 -0
  240. data/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb +35 -2
  241. data/spec/mongoid/association/referenced/has_many/buildable_spec.rb +109 -0
  242. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +8 -8
  243. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +82 -13
  244. data/spec/mongoid/association/referenced/has_many_models.rb +3 -1
  245. data/spec/mongoid/association/referenced/has_many_spec.rb +25 -0
  246. data/spec/mongoid/association/referenced/has_one/buildable_spec.rb +2 -2
  247. data/spec/mongoid/association/referenced/has_one/proxy_spec.rb +107 -1
  248. data/spec/mongoid/association/referenced/has_one_models.rb +16 -0
  249. data/spec/mongoid/association/syncable_spec.rb +14 -0
  250. data/spec/mongoid/atomic/paths_spec.rb +0 -14
  251. data/spec/mongoid/atomic_spec.rb +22 -0
  252. data/spec/mongoid/attributes/nested_spec.rb +80 -11
  253. data/spec/mongoid/attributes/nested_spec_models.rb +48 -0
  254. data/spec/mongoid/attributes/projector_spec.rb +1 -5
  255. data/spec/mongoid/attributes_spec.rb +524 -27
  256. data/spec/mongoid/cacheable_spec.rb +3 -3
  257. data/spec/mongoid/changeable_spec.rb +130 -13
  258. data/spec/mongoid/clients/factory_spec.rb +34 -42
  259. data/spec/mongoid/clients/options_spec.rb +1 -0
  260. data/spec/mongoid/clients/sessions_spec.rb +0 -38
  261. data/spec/mongoid/clients_spec.rb +57 -2
  262. data/spec/mongoid/config/environment_spec.rb +39 -1
  263. data/spec/mongoid/config_spec.rb +104 -13
  264. data/spec/mongoid/contextual/aggregable/memory_spec.rb +396 -158
  265. data/spec/mongoid/contextual/aggregable/memory_table.yml +88 -0
  266. data/spec/mongoid/contextual/aggregable/memory_table_spec.rb +62 -0
  267. data/spec/mongoid/contextual/map_reduce_spec.rb +2 -16
  268. data/spec/mongoid/contextual/memory_spec.rb +1336 -69
  269. data/spec/mongoid/contextual/mongo_spec.rb +1105 -174
  270. data/spec/mongoid/contextual/none_spec.rb +38 -0
  271. data/spec/mongoid/copyable_spec.rb +451 -1
  272. data/spec/mongoid/criteria/findable_spec.rb +86 -210
  273. data/spec/mongoid/criteria/includable_spec.rb +1492 -0
  274. data/spec/mongoid/criteria/includable_spec_models.rb +54 -0
  275. data/spec/mongoid/criteria/marshalable_spec.rb +18 -1
  276. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +7 -19
  277. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +134 -26
  278. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +11 -0
  279. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +11 -0
  280. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +0 -15
  281. data/spec/mongoid/criteria/queryable/extensions/numeric_spec.rb +73 -7
  282. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +11 -0
  283. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +11 -0
  284. data/spec/mongoid/criteria/queryable/optional_spec.rb +0 -484
  285. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +50 -0
  286. data/spec/mongoid/criteria/queryable/selectable_spec.rb +289 -124
  287. data/spec/mongoid/criteria/queryable/selector_spec.rb +14 -2
  288. data/spec/mongoid/criteria_projection_spec.rb +0 -1
  289. data/spec/mongoid/criteria_spec.rb +475 -1199
  290. data/spec/mongoid/document_fields_spec.rb +173 -24
  291. data/spec/mongoid/document_spec.rb +32 -41
  292. data/spec/mongoid/equality_spec.rb +12 -12
  293. data/spec/mongoid/errors/document_not_found_spec.rb +76 -0
  294. data/spec/mongoid/errors/invalid_field_spec.rb +1 -1
  295. data/spec/mongoid/errors/invalid_field_type_spec.rb +55 -0
  296. data/spec/mongoid/errors/mongoid_error_spec.rb +3 -1
  297. data/spec/mongoid/errors/no_environment_spec.rb +3 -3
  298. data/spec/mongoid/errors/too_many_nested_attribute_records_spec.rb +1 -1
  299. data/spec/mongoid/extensions/array_spec.rb +16 -2
  300. data/spec/mongoid/extensions/big_decimal_spec.rb +712 -212
  301. data/spec/mongoid/extensions/binary_spec.rb +44 -9
  302. data/spec/mongoid/extensions/boolean_spec.rb +68 -82
  303. data/spec/mongoid/extensions/date_class_mongoize_spec.rb +7 -3
  304. data/spec/mongoid/extensions/date_spec.rb +71 -1
  305. data/spec/mongoid/extensions/date_time_spec.rb +15 -9
  306. data/spec/mongoid/extensions/float_spec.rb +53 -74
  307. data/spec/mongoid/extensions/hash_spec.rb +30 -0
  308. data/spec/mongoid/extensions/integer_spec.rb +50 -64
  309. data/spec/mongoid/extensions/range_spec.rb +255 -54
  310. data/spec/mongoid/extensions/regexp_spec.rb +58 -33
  311. data/spec/mongoid/extensions/set_spec.rb +106 -0
  312. data/spec/mongoid/extensions/string_spec.rb +53 -25
  313. data/spec/mongoid/extensions/symbol_spec.rb +18 -25
  314. data/spec/mongoid/extensions/time_spec.rb +634 -66
  315. data/spec/mongoid/extensions/time_with_zone_spec.rb +17 -31
  316. data/spec/mongoid/factory_spec.rb +61 -1
  317. data/spec/mongoid/fields/localized_spec.rb +37 -12
  318. data/spec/mongoid/fields_spec.rb +321 -50
  319. data/spec/mongoid/findable_spec.rb +80 -15
  320. data/spec/mongoid/indexable/specification_spec.rb +2 -2
  321. data/spec/mongoid/indexable_spec.rb +39 -20
  322. data/spec/mongoid/interceptable_spec.rb +584 -5
  323. data/spec/mongoid/interceptable_spec_models.rb +235 -4
  324. data/spec/mongoid/matcher/extract_attribute_spec.rb +1 -5
  325. data/spec/mongoid/mongoizable_spec.rb +285 -0
  326. data/spec/mongoid/persistable/creatable_spec.rb +2 -2
  327. data/spec/mongoid/persistable/deletable_spec.rb +2 -2
  328. data/spec/mongoid/persistable/destroyable_spec.rb +2 -2
  329. data/spec/mongoid/persistable/upsertable_spec.rb +14 -0
  330. data/spec/mongoid/persistence_context_spec.rb +50 -1
  331. data/spec/mongoid/query_cache_middleware_spec.rb +0 -18
  332. data/spec/mongoid/query_cache_spec.rb +0 -154
  333. data/spec/mongoid/reloadable_spec.rb +35 -2
  334. data/spec/mongoid/scopable_spec.rb +54 -16
  335. data/spec/mongoid/shardable_spec.rb +14 -0
  336. data/spec/mongoid/stateful_spec.rb +28 -0
  337. data/spec/mongoid/timestamps_spec.rb +390 -0
  338. data/spec/mongoid/timestamps_spec_models.rb +67 -0
  339. data/spec/mongoid/touchable_spec.rb +116 -0
  340. data/spec/mongoid/touchable_spec_models.rb +12 -8
  341. data/spec/mongoid/traversable_spec.rb +4 -11
  342. data/spec/mongoid/validatable/presence_spec.rb +1 -1
  343. data/spec/mongoid/validatable/uniqueness_spec.rb +59 -31
  344. data/spec/mongoid/warnings_spec.rb +35 -0
  345. data/spec/rails/controller_extension/controller_runtime_spec.rb +2 -2
  346. data/spec/rails/mongoid_spec.rb +4 -16
  347. data/spec/shared/lib/mrss/constraints.rb +8 -16
  348. data/spec/shared/lib/mrss/docker_runner.rb +23 -3
  349. data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
  350. data/spec/shared/lib/mrss/lite_constraints.rb +32 -1
  351. data/spec/shared/share/Dockerfile.erb +34 -48
  352. data/spec/shared/shlib/config.sh +27 -0
  353. data/spec/shared/shlib/server.sh +32 -19
  354. data/spec/shared/shlib/set_env.sh +37 -0
  355. data/spec/support/constraints.rb +24 -0
  356. data/spec/support/macros.rb +55 -0
  357. data/spec/support/models/augmentation.rb +12 -0
  358. data/spec/support/models/band.rb +3 -0
  359. data/spec/support/models/catalog.rb +24 -0
  360. data/spec/support/models/circus.rb +3 -0
  361. data/spec/support/models/code.rb +2 -0
  362. data/spec/support/models/fanatic.rb +8 -0
  363. data/spec/support/models/implant.rb +9 -0
  364. data/spec/support/models/label.rb +2 -0
  365. data/spec/support/models/membership.rb +1 -0
  366. data/spec/support/models/passport.rb +9 -0
  367. data/spec/support/models/person.rb +1 -0
  368. data/spec/support/models/player.rb +2 -0
  369. data/spec/support/models/powerup.rb +12 -0
  370. data/spec/support/models/registry.rb +1 -0
  371. data/spec/support/models/school.rb +14 -0
  372. data/spec/support/models/shield.rb +18 -0
  373. data/spec/support/models/student.rb +14 -0
  374. data/spec/support/models/weapon.rb +12 -0
  375. data/spec/support/schema_maps/schema_map_aws.json +17 -0
  376. data/spec/support/schema_maps/schema_map_aws_key_alt_names.json +12 -0
  377. data/spec/support/schema_maps/schema_map_azure.json +17 -0
  378. data/spec/support/schema_maps/schema_map_azure_key_alt_names.json +12 -0
  379. data/spec/support/schema_maps/schema_map_gcp.json +17 -0
  380. data/spec/support/schema_maps/schema_map_gcp_key_alt_names.json +12 -0
  381. data/spec/support/schema_maps/schema_map_kmip.json +17 -0
  382. data/spec/support/schema_maps/schema_map_kmip_key_alt_names.json +12 -0
  383. data/spec/support/schema_maps/schema_map_local.json +18 -0
  384. data/spec/support/schema_maps/schema_map_local_key_alt_names.json +12 -0
  385. data/spec/support/spec_config.rb +4 -0
  386. data.tar.gz.sig +0 -0
  387. metadata +682 -619
  388. metadata.gz.sig +0 -0
  389. data/lib/mongoid/errors/eager_load.rb +0 -23
  390. data/lib/mongoid/errors/invalid_value.rb +0 -17
  391. 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,16 +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
- !!@cache
46
- end
47
-
48
40
  # Get the number of documents matching the query.
49
41
  #
50
42
  # @example Get the number of matching documents.
@@ -64,7 +56,7 @@ module Mongoid
64
56
  # @return [ Integer ] The number of matches.
65
57
  def count(options = {}, &block)
66
58
  return super(&block) if block_given?
67
- try_cache(:count) { view.count_documents(options) }
59
+ view.count_documents(options)
68
60
  end
69
61
 
70
62
  # Get the estimated number of documents matching the query.
@@ -83,7 +75,7 @@ module Mongoid
83
75
  unless self.criteria.selector.empty?
84
76
  raise Mongoid::Errors::InvalidEstimatedCountCriteria.new(self.klass)
85
77
  end
86
- try_cache(:estimated_count) { view.estimated_document_count(options) }
78
+ view.estimated_document_count(options)
87
79
  end
88
80
 
89
81
  # Delete all documents in the database that match the selector.
@@ -117,7 +109,7 @@ module Mongoid
117
109
  # @example Get the distinct values.
118
110
  # context.distinct(:name)
119
111
  #
120
- # @param [ String, Symbol ] field The name of the field.
112
+ # @param [ String | Symbol ] field The name of the field.
121
113
  #
122
114
  # @return [ Array<Object> ] The distinct values for the field.
123
115
  def distinct(field)
@@ -151,7 +143,6 @@ module Mongoid
151
143
  documents_for_iteration.each do |doc|
152
144
  yield_document(doc, &block)
153
145
  end
154
- @cache_loaded = true
155
146
  self
156
147
  else
157
148
  to_enum
@@ -164,17 +155,11 @@ module Mongoid
164
155
  # context.exists?
165
156
  #
166
157
  # @note We don't use count here since Mongo does not use counted
167
- # b-tree indexes, unless a count is already cached then that is
168
- # used to determine the value.
158
+ # b-tree indexes.
169
159
  #
170
- # @return [ true, false ] If the count is more than zero.
160
+ # @return [ true | false ] If the count is more than zero.
171
161
  def exists?
172
- return !documents.empty? if cached? && cache_loaded?
173
- return @count > 0 if instance_variable_defined?(:@count)
174
-
175
- try_cache(:exists) do
176
- !!(view.projection(_id: 1).limit(1).first)
177
- end
162
+ !!(view.projection(_id: 1).limit(1).first)
178
163
  end
179
164
 
180
165
  # Run an explain on the criteria.
@@ -196,9 +181,9 @@ module Mongoid
196
181
  # @param [ Hash ] update The updates.
197
182
  # @param [ Hash ] options The command options.
198
183
  #
199
- # @option options [ :before, :after ] :return_document Return the updated document
184
+ # @option options [ :before | :after ] :return_document Return the updated document
200
185
  # from before or after update.
201
- # @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.
202
187
  #
203
188
  # @return [ Document ] The result of the command.
204
189
  def find_one_and_update(update, options = {})
@@ -216,9 +201,9 @@ module Mongoid
216
201
  # @param [ Hash ] replacement The replacement.
217
202
  # @param [ Hash ] options The command options.
218
203
  #
219
- # @option options [ :before, :after ] :return_document Return the updated document
204
+ # @option options [ :before | :after ] :return_document Return the updated document
220
205
  # from before or after update.
221
- # @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.
222
207
  #
223
208
  # @return [ Document ] The result of the command.
224
209
  def find_one_and_replace(replacement, options = {})
@@ -248,29 +233,16 @@ module Mongoid
248
233
  # @note Automatically adding a sort on _id when no other sort is
249
234
  # defined on the criteria has the potential to cause bad performance issues.
250
235
  # If you experience unexpected poor performance when using #first or #last
251
- # and have no sort defined on the criteria, use the option { id_sort: :none }.
252
- # 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.
253
238
  #
254
- # @param [ Hash ] opts The options for the query returning the first document.
255
- #
256
- # @option opts [ :none ] :id_sort Don't apply a sort on _id if no other sort
257
- # is defined on the criteria.
239
+ # @param [ Integer ] limit The number of documents to return.
258
240
  #
259
241
  # @return [ Document ] The first document.
260
- def first(opts = {})
261
- return documents.first if cached? && cache_loaded?
262
- try_cache(:first) do
263
- if sort = view.sort || ({ _id: 1 } unless opts[:id_sort] == :none)
264
- if raw_doc = view.sort(sort).limit(1).first
265
- doc = Factory.from_db(klass, raw_doc, criteria)
266
- eager_load([doc]).first
267
- end
268
- else
269
- if raw_doc = view.limit(1).first
270
- doc = Factory.from_db(klass, raw_doc, criteria)
271
- eager_load([doc]).first
272
- end
273
- 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)
274
246
  end
275
247
  end
276
248
  alias :one :first
@@ -279,7 +251,6 @@ module Mongoid
279
251
  #
280
252
  # @api private
281
253
  def find_first
282
- return documents.first if cached? && cache_loaded?
283
254
  if raw_doc = view.first
284
255
  doc = Factory.from_db(klass, raw_doc, criteria)
285
256
  eager_load([doc]).first
@@ -309,29 +280,6 @@ module Mongoid
309
280
  GeoNear.new(collection, criteria, coordinates)
310
281
  end
311
282
 
312
- # Invoke the block for each element of Contextual. Create a new array
313
- # containing the values returned by the block.
314
- #
315
- # If the symbol field name is passed instead of the block, additional
316
- # optimizations would be used.
317
- #
318
- # @example Map by some field.
319
- # context.map(:field1)
320
- #
321
- # @example Map with block.
322
- # context.map(&:field1)
323
- #
324
- # @param [ Symbol ] field The field name.
325
- #
326
- # @return [ Array ] The result of mapping.
327
- def map(field = nil, &block)
328
- if block_given?
329
- super(&block)
330
- else
331
- criteria.pluck(field)
332
- end
333
- end
334
-
335
283
  # Create the new Mongo context. This delegates operations to the
336
284
  # underlying driver.
337
285
  #
@@ -340,7 +288,7 @@ module Mongoid
340
288
  #
341
289
  # @param [ Criteria ] criteria The criteria.
342
290
  def initialize(criteria)
343
- @criteria, @klass, @cache = criteria, criteria.klass, criteria.options[:cache]
291
+ @criteria, @klass = criteria, criteria.klass
344
292
  @collection = @klass.collection
345
293
  criteria.send(:merge_type_selection)
346
294
  @view = collection.find(criteria.selector, session: _session)
@@ -357,32 +305,26 @@ module Mongoid
357
305
  # @note Automatically adding a sort on _id when no other sort is
358
306
  # defined on the criteria has the potential to cause bad performance issues.
359
307
  # If you experience unexpected poor performance when using #first or #last
360
- # and have no sort defined on the criteria, use the option { id_sort: :none }.
361
- # Be aware that #first/#last won't guarantee order in this case.
362
- #
363
- # @param [ Hash ] opts The options for the query returning the first document.
364
- #
365
- # @option opts [ :none ] :id_sort Don't apply a sort on _id if no other sort
366
- # is defined on the criteria.
367
- def last(opts = {})
368
- try_cache(:last) do
369
- with_inverse_sorting(opts) do
370
- if raw_doc = view.limit(1).first
371
- doc = Factory.from_db(klass, raw_doc, criteria)
372
- eager_load([doc]).first
373
- end
374
- end
375
- end
308
+ # and have no sort defined on the criteria, use #take instead.
309
+ # Be aware that #take won't guarantee order.
310
+ #
311
+ # @param [ Integer ] limit The number of documents to return.
312
+ #
313
+ # @return [ Document ] The last document.
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)
376
317
  end
377
318
 
378
- # Get's the number of documents matching the query selector.
319
+ # Returns the number of documents in the database matching
320
+ # the query selector.
379
321
  #
380
322
  # @example Get the length.
381
323
  # context.length
382
324
  #
383
325
  # @return [ Integer ] The number of documents.
384
326
  def length
385
- @length ||= self.count
327
+ self.count
386
328
  end
387
329
  alias :size :length
388
330
 
@@ -398,6 +340,44 @@ module Mongoid
398
340
  @view = view.limit(value) and self
399
341
  end
400
342
 
343
+ # Take the given number of documents from the database.
344
+ #
345
+ # @example Take 10 documents
346
+ # context.take(10)
347
+ #
348
+ # @param [ Integer | nil ] limit The number of documents to return or nil.
349
+ #
350
+ # @return [ Document | Array<Document> ] The list of documents, or one
351
+ # document if no value was given.
352
+ def take(limit = nil)
353
+ if limit
354
+ limit(limit).to_a
355
+ else
356
+ # Do to_a first so that the Mongo#first method is not used and the
357
+ # result is not sorted.
358
+ limit(1).to_a.first
359
+ end
360
+ end
361
+
362
+ # Take one document from the database and raise an error if there are none.
363
+ #
364
+ # @example Take a document
365
+ # context.take!
366
+ #
367
+ # @return [ Document ] The document.
368
+ #
369
+ # @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
370
+ # documents to take.
371
+ def take!
372
+ # Do to_a first so that the Mongo#first method is not used and the
373
+ # result is not sorted.
374
+ if fst = limit(1).to_a.first
375
+ fst
376
+ else
377
+ raise Errors::DocumentNotFound.new(klass, nil, nil)
378
+ end
379
+ end
380
+
401
381
  # Initiate a map/reduce operation from the context.
402
382
  #
403
383
  # @example Initiate a map/reduce.
@@ -417,12 +397,9 @@ module Mongoid
417
397
  # @example Pluck a field.
418
398
  # context.pluck(:_id)
419
399
  #
420
- # @note This method will return the raw db values - it performs no custom
421
- # serialization.
422
- #
423
- # @param [ String, Symbol, Array ] fields Fields to pluck.
400
+ # @param [ String | Symbol ] *fields Field(s) to pluck.
424
401
  #
425
- # @return [ Array<Object, Array> ] The plucked values.
402
+ # @return [ Array<Object> | Array<Array<Object>> ] The plucked values.
426
403
  def pluck(*fields)
427
404
  # Multiple fields can map to the same field name. For example, plucking
428
405
  # a field and its _translations field map to the same field in the database.
@@ -452,6 +429,87 @@ module Mongoid
452
429
  end
453
430
  end
454
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
+
455
513
  # Skips the provided number of documents.
456
514
  #
457
515
  # @example Skip the documents.
@@ -518,22 +576,6 @@ module Mongoid
518
576
 
519
577
  private
520
578
 
521
- # yield the block given or return the cached value
522
- #
523
- # @param [ String, Symbol ] key The instance variable name
524
- #
525
- # @return the result of the block
526
- def try_cache(key, &block)
527
- unless cached?
528
- yield
529
- else
530
- unless ret = instance_variable_get("@#{key}")
531
- instance_variable_set("@#{key}", ret = yield)
532
- end
533
- ret
534
- end
535
- end
536
-
537
579
  # Update the documents for the provided method.
538
580
  #
539
581
  # @api private
@@ -544,7 +586,7 @@ module Mongoid
544
586
  # @param [ Hash ] attributes The updates.
545
587
  # @param [ Symbol ] method The method to use.
546
588
  #
547
- # @return [ true, false ] If the update succeeded.
589
+ # @return [ true | false ] If the update succeeded.
548
590
  def update_documents(attributes, method = :update_one, opts = {})
549
591
  return false unless attributes
550
592
  attributes = Hash[attributes.map { |k, v| [klass.database_field_name(k.to_s), v] }]
@@ -594,55 +636,9 @@ module Mongoid
594
636
  # Map the inverse sort symbols to the correct MongoDB values.
595
637
  #
596
638
  # @api private
597
- #
598
- # @example Apply the inverse sorting params to the given block
599
- # context.with_inverse_sorting
600
- def with_inverse_sorting(opts = {})
601
- begin
602
- if sort = criteria.options[:sort] || ( { _id: 1 } unless opts[:id_sort] == :none )
603
- @view = view.sort(Hash[sort.map{|k, v| [k, -1*v]}])
604
- end
605
- yield
606
- ensure
607
- apply_option(:sort)
608
- end
609
- end
610
-
611
- # Is the cache able to be added to?
612
- #
613
- # @api private
614
- #
615
- # @example Is the context cacheable?
616
- # context.cacheable?
617
- #
618
- # @return [ true, false ] If caching, and the cache isn't loaded.
619
- def cacheable?
620
- cached? && !cache_loaded?
621
- end
622
-
623
- # Is the cache fully loaded? Will be true if caching after one full
624
- # iteration.
625
- #
626
- # @api private
627
- #
628
- # @example Is the cache loaded?
629
- # context.cache_loaded?
630
- #
631
- # @return [ true, false ] If the cache is loaded.
632
- def cache_loaded?
633
- !!@cache_loaded
634
- end
635
-
636
- # Get the documents for cached queries.
637
- #
638
- # @api private
639
- #
640
- # @example Get the cached documents.
641
- # context.documents
642
- #
643
- # @return [ Array<Document> ] The documents.
644
- def documents
645
- @documents ||= []
639
+ def inverse_sorting
640
+ sort = view.sort || { _id: 1 }
641
+ Hash[sort.map{|k, v| [k, -1*v]}]
646
642
  end
647
643
 
648
644
  # Get the documents the context should iterate. This follows 3 rules:
@@ -658,9 +654,8 @@ module Mongoid
658
654
  # @example Get the documents for iteration.
659
655
  # context.documents_for_iteration
660
656
  #
661
- # @return [ Array<Document>, Mongo::Collection::View ] The docs to iterate.
657
+ # @return [ Array<Document> | Mongo::Collection::View ] The docs to iterate.
662
658
  def documents_for_iteration
663
- return documents if cached? && !documents.empty?
664
659
  return view unless eager_loadable?
665
660
  docs = view.map{ |doc| Factory.from_db(klass, doc, criteria) }
666
661
  eager_load(docs)
@@ -680,7 +675,6 @@ module Mongoid
680
675
  doc = document.respond_to?(:_id) ?
681
676
  document : Factory.from_db(klass, document, criteria)
682
677
  yield(doc)
683
- documents.push(doc) if cacheable?
684
678
  end
685
679
 
686
680
  private
@@ -693,6 +687,26 @@ module Mongoid
693
687
  collection.write_concern.nil? || collection.write_concern.acknowledged?
694
688
  end
695
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
+
696
710
  # Extracts the value for the given field name from the given attribute
697
711
  # hash.
698
712
  #
@@ -701,24 +715,18 @@ module Mongoid
701
715
  #
702
716
  # @param [ Object ] The value for the given field name
703
717
  def extract_value(attrs, field_name)
704
- def fetch_and_demongoize(d, meth, klass)
705
- res = d.try(:fetch, meth, nil)
706
- if field = klass.fields[meth]
707
- field.demongoize(res)
708
- else
709
- res.class.demongoize(res)
710
- end
711
- end
718
+ i = 1
719
+ num_meths = field_name.count('.') + 1
720
+ curr = attrs.dup
712
721
 
713
- k = klass
714
- meths = field_name.split('.')
715
- 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
716
724
  is_translation = false
717
- if !k.fields.key?(meth) && !k.relations.key?(meth)
718
- if tr = meth.match(/(.*)_translations\z/)&.captures&.first
719
- is_translation = true
720
- meth = tr
721
- 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
722
730
  end
723
731
 
724
732
  # 1. If curr is an array fetch from all elements in the array.
@@ -731,31 +739,24 @@ module Mongoid
731
739
  # 3. If the meth is an _translations field, do not demongoize the
732
740
  # value so the full hash is returned.
733
741
  # 4. Otherwise, fetch and demongoize the value for the key meth.
734
- if curr.is_a? Array
735
- 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)
736
744
  res.empty? ? nil : res
737
- elsif !is_translation && k.fields[meth]&.localized?
738
- if i < meths.length-1
745
+ elsif !is_translation && field&.localized?
746
+ if i < num_meths
739
747
  curr.try(:fetch, meth, nil)
740
748
  else
741
- fetch_and_demongoize(curr, meth, k)
749
+ fetch_and_demongoize(curr, meth, field)
742
750
  end
743
751
  elsif is_translation
744
752
  curr.try(:fetch, meth, nil)
745
753
  else
746
- fetch_and_demongoize(curr, meth, k)
747
- end.tap do
748
- if as = k.try(:aliased_associations)
749
- if a = as.fetch(meth, nil)
750
- meth = a
751
- end
752
- end
753
-
754
- if relation = k.relations[meth]
755
- k = relation.klass
756
- end
754
+ fetch_and_demongoize(curr, meth, field)
757
755
  end
756
+
757
+ i += 1
758
758
  end
759
+ curr
759
760
  end
760
761
 
761
762
  # Recursively demongoize the given value. This method recursively traverses
@@ -763,35 +764,54 @@ module Mongoid
763
764
  #
764
765
  # @param [ String ] field_name The name of the field to demongoize.
765
766
  # @param [ Object ] value The value to demongoize.
766
- # @param [ Boolean ] is_translation The field we are retrieving is an
767
+ # @param [ true | false ] is_translation The field we are retrieving is an
767
768
  # _translations field.
768
769
  #
769
770
  # @return [ Object ] The demongoized value.
770
771
  def recursive_demongoize(field_name, value, is_translation)
771
- k = klass
772
- field_name.split('.').each do |meth|
773
- if as = k.try(:aliased_associations)
774
- if a = as.fetch(meth, nil)
775
- meth = a.to_s
776
- end
777
- end
772
+ field = klass.traverse_association_tree(field_name)
773
+ demongoize_with_field(field, value, is_translation)
774
+ end
778
775
 
779
- if relation = k.relations[meth]
780
- k = relation.klass
781
- elsif field = k.fields[meth]
782
- # If it's a localized field that's not a hash, don't demongoize
783
- # again, we already have the translation. If it's an _translation
784
- # field, don't demongoize, we want the full hash not just a
785
- # specific translation.
786
- if field.localized? && (!value.is_a?(Hash) || is_translation)
787
- return value.class.demongoize(value)
788
- else
789
- return field.demongoize(value)
790
- 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)
791
797
  else
792
- return value.class.demongoize(value)
798
+ field.demongoize(value)
793
799
  end
800
+ else
801
+ value.class.demongoize(value)
802
+ end
803
+ end
804
+
805
+ # Process the raw documents retrieved for #first/#last.
806
+ #
807
+ # @return [ Array<Document> | Document ] The list of documents or a
808
+ # single document.
809
+ def process_raw_docs(raw_docs, limit)
810
+ docs = raw_docs.map do |d|
811
+ Factory.from_db(klass, d, criteria)
794
812
  end
813
+ docs = eager_load(docs)
814
+ limit ? docs : docs.first
795
815
  end
796
816
  end
797
817
  end