mongoid 7.4.3 → 8.0.7

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 (383) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +3 -3
  4. data/Rakefile +25 -0
  5. data/lib/config/locales/en.yml +52 -28
  6. data/lib/mongoid/association/accessors.rb +38 -9
  7. data/lib/mongoid/association/bindable.rb +50 -2
  8. data/lib/mongoid/association/builders.rb +4 -2
  9. data/lib/mongoid/association/constrainable.rb +0 -1
  10. data/lib/mongoid/association/eager_loadable.rb +29 -7
  11. data/lib/mongoid/association/embedded/batchable.rb +33 -10
  12. data/lib/mongoid/association/embedded/cyclic.rb +1 -1
  13. data/lib/mongoid/association/embedded/embedded_in/binding.rb +24 -2
  14. data/lib/mongoid/association/embedded/embedded_in/proxy.rb +2 -2
  15. data/lib/mongoid/association/embedded/embedded_in.rb +3 -2
  16. data/lib/mongoid/association/embedded/embeds_many/binding.rb +1 -0
  17. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +1 -1
  18. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +65 -41
  19. data/lib/mongoid/association/embedded/embeds_many.rb +2 -2
  20. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +18 -4
  21. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +23 -4
  22. data/lib/mongoid/association/embedded/embeds_one.rb +3 -3
  23. data/lib/mongoid/association/macros.rb +28 -1
  24. data/lib/mongoid/association/many.rb +11 -7
  25. data/lib/mongoid/association/nested/many.rb +5 -4
  26. data/lib/mongoid/association/nested/nested_buildable.rb +4 -4
  27. data/lib/mongoid/association/nested/one.rb +5 -5
  28. data/lib/mongoid/association/one.rb +2 -2
  29. data/lib/mongoid/association/options.rb +9 -9
  30. data/lib/mongoid/association/proxy.rb +14 -3
  31. data/lib/mongoid/association/referenced/auto_save.rb +4 -3
  32. data/lib/mongoid/association/referenced/belongs_to/binding.rb +1 -0
  33. data/lib/mongoid/association/referenced/belongs_to/buildable.rb +1 -1
  34. data/lib/mongoid/association/referenced/belongs_to/proxy.rb +5 -6
  35. data/lib/mongoid/association/referenced/belongs_to.rb +2 -2
  36. data/lib/mongoid/association/referenced/counter_cache.rb +10 -10
  37. data/lib/mongoid/association/referenced/eager.rb +2 -2
  38. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +70 -13
  39. data/lib/mongoid/association/referenced/has_and_belongs_to_many.rb +6 -3
  40. data/lib/mongoid/association/referenced/has_many/enumerable.rb +20 -24
  41. data/lib/mongoid/association/referenced/has_many/proxy.rb +26 -16
  42. data/lib/mongoid/association/referenced/has_many.rb +3 -3
  43. data/lib/mongoid/association/referenced/has_one/buildable.rb +1 -1
  44. data/lib/mongoid/association/referenced/has_one/nested_builder.rb +5 -5
  45. data/lib/mongoid/association/referenced/has_one/proxy.rb +9 -12
  46. data/lib/mongoid/association/referenced/has_one.rb +3 -3
  47. data/lib/mongoid/association/referenced/syncable.rb +4 -4
  48. data/lib/mongoid/association/reflections.rb +2 -2
  49. data/lib/mongoid/association/relatable.rb +44 -10
  50. data/lib/mongoid/association.rb +5 -5
  51. data/lib/mongoid/atomic/modifiers.rb +2 -2
  52. data/lib/mongoid/atomic.rb +7 -0
  53. data/lib/mongoid/attributes/dynamic.rb +3 -3
  54. data/lib/mongoid/attributes/nested.rb +5 -5
  55. data/lib/mongoid/attributes/processing.rb +37 -6
  56. data/lib/mongoid/attributes/projector.rb +1 -1
  57. data/lib/mongoid/attributes/readonly.rb +2 -2
  58. data/lib/mongoid/attributes.rb +43 -40
  59. data/lib/mongoid/cacheable.rb +2 -2
  60. data/lib/mongoid/changeable.rb +43 -10
  61. data/lib/mongoid/clients/options.rb +5 -1
  62. data/lib/mongoid/clients/sessions.rb +2 -14
  63. data/lib/mongoid/clients/validators/storage.rb +3 -3
  64. data/lib/mongoid/config/options.rb +3 -0
  65. data/lib/mongoid/config/validators/client.rb +6 -6
  66. data/lib/mongoid/config.rb +62 -17
  67. data/lib/mongoid/contextual/aggregable/memory.rb +24 -16
  68. data/lib/mongoid/contextual/aggregable/mongo.rb +5 -5
  69. data/lib/mongoid/contextual/aggregable/none.rb +1 -1
  70. data/lib/mongoid/contextual/atomic.rb +1 -1
  71. data/lib/mongoid/contextual/geo_near.rb +7 -7
  72. data/lib/mongoid/contextual/map_reduce.rb +2 -2
  73. data/lib/mongoid/contextual/memory.rb +180 -21
  74. data/lib/mongoid/contextual/mongo.rb +260 -217
  75. data/lib/mongoid/contextual/none.rb +67 -5
  76. data/lib/mongoid/contextual/queryable.rb +1 -1
  77. data/lib/mongoid/contextual.rb +2 -2
  78. data/lib/mongoid/copyable.rb +32 -8
  79. data/lib/mongoid/criteria/findable.rb +7 -4
  80. data/lib/mongoid/criteria/includable.rb +24 -20
  81. data/lib/mongoid/criteria/marshalable.rb +10 -2
  82. data/lib/mongoid/criteria/permission.rb +1 -1
  83. data/lib/mongoid/criteria/queryable/aggregable.rb +2 -2
  84. data/lib/mongoid/criteria/queryable/extensions/array.rb +3 -14
  85. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +25 -4
  86. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +2 -2
  87. data/lib/mongoid/criteria/queryable/extensions/date.rb +6 -1
  88. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +6 -1
  89. data/lib/mongoid/criteria/queryable/extensions/hash.rb +1 -15
  90. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -9
  91. data/lib/mongoid/criteria/queryable/extensions/object.rb +2 -1
  92. data/lib/mongoid/criteria/queryable/extensions/range.rb +13 -5
  93. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +3 -3
  94. data/lib/mongoid/criteria/queryable/extensions/set.rb +1 -1
  95. data/lib/mongoid/criteria/queryable/extensions/string.rb +4 -14
  96. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +4 -12
  97. data/lib/mongoid/criteria/queryable/extensions/time.rb +6 -1
  98. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +6 -1
  99. data/lib/mongoid/criteria/queryable/key.rb +3 -3
  100. data/lib/mongoid/criteria/queryable/mergeable.rb +21 -0
  101. data/lib/mongoid/criteria/queryable/optional.rb +5 -11
  102. data/lib/mongoid/criteria/queryable/options.rb +2 -2
  103. data/lib/mongoid/criteria/queryable/pipeline.rb +1 -1
  104. data/lib/mongoid/criteria/queryable/selectable.rb +31 -37
  105. data/lib/mongoid/criteria/queryable/selector.rb +93 -8
  106. data/lib/mongoid/criteria/queryable/smash.rb +40 -7
  107. data/lib/mongoid/criteria/queryable/storable.rb +1 -1
  108. data/lib/mongoid/criteria/queryable.rb +12 -7
  109. data/lib/mongoid/criteria/scopable.rb +2 -2
  110. data/lib/mongoid/criteria/translator.rb +45 -0
  111. data/lib/mongoid/criteria.rb +16 -35
  112. data/lib/mongoid/deprecable.rb +37 -0
  113. data/lib/mongoid/deprecation.rb +25 -0
  114. data/lib/mongoid/document.rb +135 -34
  115. data/lib/mongoid/equality.rb +8 -8
  116. data/lib/mongoid/errors/document_not_found.rb +33 -12
  117. data/lib/mongoid/errors/invalid_config_option.rb +1 -1
  118. data/lib/mongoid/errors/invalid_dependent_strategy.rb +1 -1
  119. data/lib/mongoid/errors/invalid_dot_dollar_assignment.rb +23 -0
  120. data/lib/mongoid/errors/invalid_field.rb +6 -2
  121. data/lib/mongoid/errors/invalid_field_type.rb +26 -0
  122. data/lib/mongoid/errors/invalid_relation.rb +1 -1
  123. data/lib/mongoid/errors/invalid_relation_option.rb +1 -1
  124. data/lib/mongoid/errors/invalid_session_use.rb +1 -1
  125. data/lib/mongoid/errors/invalid_storage_options.rb +1 -1
  126. data/lib/mongoid/errors/mongoid_error.rb +3 -3
  127. data/lib/mongoid/errors/nested_attributes_metadata_not_found.rb +1 -1
  128. data/lib/mongoid/errors/no_client_database.rb +1 -1
  129. data/lib/mongoid/errors/no_client_hosts.rb +1 -1
  130. data/lib/mongoid/errors/readonly_attribute.rb +1 -1
  131. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +1 -1
  132. data/lib/mongoid/errors/unknown_attribute.rb +1 -1
  133. data/lib/mongoid/errors.rb +2 -2
  134. data/lib/mongoid/extensions/array.rb +9 -7
  135. data/lib/mongoid/extensions/big_decimal.rb +33 -10
  136. data/lib/mongoid/extensions/binary.rb +42 -0
  137. data/lib/mongoid/extensions/boolean.rb +8 -2
  138. data/lib/mongoid/extensions/date.rb +26 -20
  139. data/lib/mongoid/extensions/date_time.rb +1 -1
  140. data/lib/mongoid/extensions/false_class.rb +1 -1
  141. data/lib/mongoid/extensions/float.rb +7 -4
  142. data/lib/mongoid/extensions/hash.rb +37 -8
  143. data/lib/mongoid/extensions/integer.rb +7 -4
  144. data/lib/mongoid/extensions/module.rb +1 -1
  145. data/lib/mongoid/extensions/object.rb +8 -6
  146. data/lib/mongoid/extensions/range.rb +41 -10
  147. data/lib/mongoid/extensions/regexp.rb +11 -4
  148. data/lib/mongoid/extensions/set.rb +11 -4
  149. data/lib/mongoid/extensions/string.rb +11 -22
  150. data/lib/mongoid/extensions/symbol.rb +4 -15
  151. data/lib/mongoid/extensions/time.rb +27 -16
  152. data/lib/mongoid/extensions/time_with_zone.rb +1 -2
  153. data/lib/mongoid/extensions/true_class.rb +1 -1
  154. data/lib/mongoid/extensions.rb +1 -0
  155. data/lib/mongoid/factory.rb +55 -7
  156. data/lib/mongoid/fields/foreign_key.rb +11 -4
  157. data/lib/mongoid/fields/localized.rb +9 -4
  158. data/lib/mongoid/fields/standard.rb +7 -7
  159. data/lib/mongoid/fields/validators/macro.rb +3 -9
  160. data/lib/mongoid/fields.rb +233 -40
  161. data/lib/mongoid/findable.rb +34 -13
  162. data/lib/mongoid/indexable/specification.rb +2 -2
  163. data/lib/mongoid/indexable/validators/options.rb +6 -2
  164. data/lib/mongoid/interceptable.rb +185 -16
  165. data/lib/mongoid/matchable.rb +1 -1
  166. data/lib/mongoid/matcher.rb +33 -13
  167. data/lib/mongoid/persistable/creatable.rb +18 -9
  168. data/lib/mongoid/persistable/deletable.rb +1 -1
  169. data/lib/mongoid/persistable/destroyable.rb +1 -1
  170. data/lib/mongoid/persistable/savable.rb +2 -2
  171. data/lib/mongoid/persistable/unsettable.rb +1 -1
  172. data/lib/mongoid/persistable/updatable.rb +19 -12
  173. data/lib/mongoid/persistable/upsertable.rb +1 -1
  174. data/lib/mongoid/persistable.rb +3 -3
  175. data/lib/mongoid/persistence_context.rb +63 -10
  176. data/lib/mongoid/query_cache.rb +8 -260
  177. data/lib/mongoid/railties/controller_runtime.rb +1 -1
  178. data/lib/mongoid/reloadable.rb +10 -8
  179. data/lib/mongoid/scopable.rb +26 -22
  180. data/lib/mongoid/selectable.rb +1 -2
  181. data/lib/mongoid/serializable.rb +10 -6
  182. data/lib/mongoid/shardable.rb +35 -11
  183. data/lib/mongoid/stateful.rb +35 -9
  184. data/lib/mongoid/tasks/database.rb +0 -2
  185. data/lib/mongoid/threaded/lifecycle.rb +5 -5
  186. data/lib/mongoid/threaded.rb +42 -12
  187. data/lib/mongoid/timestamps/created.rb +1 -1
  188. data/lib/mongoid/timestamps/updated.rb +2 -2
  189. data/lib/mongoid/touchable.rb +2 -3
  190. data/lib/mongoid/traversable.rb +5 -4
  191. data/lib/mongoid/validatable/localizable.rb +1 -1
  192. data/lib/mongoid/validatable/macros.rb +0 -2
  193. data/lib/mongoid/validatable/presence.rb +2 -2
  194. data/lib/mongoid/validatable/uniqueness.rb +9 -8
  195. data/lib/mongoid/validatable.rb +6 -6
  196. data/lib/mongoid/version.rb +1 -1
  197. data/lib/mongoid/warnings.rb +28 -0
  198. data/lib/mongoid.rb +2 -0
  199. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +4 -3
  200. data/spec/config/mongoid.yml +16 -0
  201. data/spec/integration/app_spec.rb +8 -12
  202. data/spec/integration/associations/belongs_to_spec.rb +18 -0
  203. data/spec/integration/associations/embedded_spec.rb +15 -0
  204. data/spec/integration/associations/embeds_many_spec.rb +15 -2
  205. data/spec/integration/associations/embeds_one_spec.rb +18 -0
  206. data/spec/integration/associations/foreign_key_spec.rb +9 -0
  207. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +21 -0
  208. data/spec/integration/associations/has_one_spec.rb +97 -1
  209. data/spec/integration/associations/scope_option_spec.rb +1 -1
  210. data/spec/integration/callbacks_models.rb +95 -1
  211. data/spec/integration/callbacks_spec.rb +246 -4
  212. data/spec/integration/criteria/range_spec.rb +95 -1
  213. data/spec/integration/discriminator_key_spec.rb +115 -76
  214. data/spec/integration/dots_and_dollars_spec.rb +277 -0
  215. data/spec/integration/i18n_fallbacks_spec.rb +1 -17
  216. data/spec/integration/matcher_examples_spec.rb +20 -13
  217. data/spec/integration/matcher_operator_data/type_decimal.yml +3 -2
  218. data/spec/integration/matcher_operator_spec.rb +3 -5
  219. data/spec/integration/persistence/range_field_spec.rb +350 -0
  220. data/spec/mongoid/association/counter_cache_spec.rb +1 -1
  221. data/spec/mongoid/association/depending_spec.rb +9 -9
  222. data/spec/mongoid/association/eager_spec.rb +2 -1
  223. data/spec/mongoid/association/embedded/embedded_in/binding_spec.rb +2 -1
  224. data/spec/mongoid/association/embedded/embedded_in/buildable_spec.rb +54 -0
  225. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +69 -9
  226. data/spec/mongoid/association/embedded/embeds_many/buildable_spec.rb +112 -0
  227. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +235 -40
  228. data/spec/mongoid/association/embedded/embeds_many_models.rb +36 -0
  229. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +12 -0
  230. data/spec/mongoid/association/embedded/embeds_many_spec.rb +68 -0
  231. data/spec/mongoid/association/embedded/embeds_one/buildable_spec.rb +25 -0
  232. data/spec/mongoid/association/embedded/embeds_one_models.rb +19 -0
  233. data/spec/mongoid/association/embedded/embeds_one_spec.rb +28 -0
  234. data/spec/mongoid/association/referenced/belongs_to/binding_spec.rb +2 -1
  235. data/spec/mongoid/association/referenced/belongs_to/buildable_spec.rb +54 -0
  236. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +15 -0
  237. data/spec/mongoid/association/referenced/belongs_to_models.rb +11 -0
  238. data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -2
  239. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +202 -201
  240. data/spec/mongoid/association/referenced/has_and_belongs_to_many_models.rb +25 -0
  241. data/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb +35 -2
  242. data/spec/mongoid/association/referenced/has_many/buildable_spec.rb +109 -0
  243. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +8 -8
  244. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +160 -119
  245. data/spec/mongoid/association/referenced/has_many_models.rb +3 -1
  246. data/spec/mongoid/association/referenced/has_many_spec.rb +25 -0
  247. data/spec/mongoid/association/referenced/has_one/buildable_spec.rb +2 -2
  248. data/spec/mongoid/association/referenced/has_one/proxy_spec.rb +107 -1
  249. data/spec/mongoid/association/referenced/has_one_models.rb +16 -0
  250. data/spec/mongoid/association/syncable_spec.rb +14 -0
  251. data/spec/mongoid/atomic/paths_spec.rb +0 -14
  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 +551 -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 +23 -30
  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_spec.rb +78 -18
  263. data/spec/mongoid/contextual/aggregable/memory_spec.rb +396 -158
  264. data/spec/mongoid/contextual/aggregable/memory_table.yml +88 -0
  265. data/spec/mongoid/contextual/aggregable/memory_table_spec.rb +62 -0
  266. data/spec/mongoid/contextual/map_reduce_spec.rb +2 -16
  267. data/spec/mongoid/contextual/memory_spec.rb +1336 -69
  268. data/spec/mongoid/contextual/mongo_spec.rb +1253 -247
  269. data/spec/mongoid/contextual/none_spec.rb +38 -0
  270. data/spec/mongoid/copyable_spec.rb +451 -1
  271. data/spec/mongoid/criteria/findable_spec.rb +86 -210
  272. data/spec/mongoid/criteria/includable_spec.rb +1492 -0
  273. data/spec/mongoid/criteria/includable_spec_models.rb +54 -0
  274. data/spec/mongoid/criteria/marshalable_spec.rb +18 -1
  275. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +7 -19
  276. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +134 -26
  277. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +11 -0
  278. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +11 -0
  279. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +0 -15
  280. data/spec/mongoid/criteria/queryable/extensions/numeric_spec.rb +73 -7
  281. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +0 -59
  282. data/spec/mongoid/criteria/queryable/extensions/symbol_spec.rb +0 -59
  283. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +11 -0
  284. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +11 -0
  285. data/spec/mongoid/criteria/queryable/optional_spec.rb +15 -484
  286. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +50 -0
  287. data/spec/mongoid/criteria/queryable/selectable_spec.rb +289 -124
  288. data/spec/mongoid/criteria/queryable/selector_spec.rb +89 -4
  289. data/spec/mongoid/criteria/queryable/storable_spec.rb +72 -0
  290. data/spec/mongoid/criteria/translator_spec.rb +132 -0
  291. data/spec/mongoid/criteria_projection_spec.rb +0 -1
  292. data/spec/mongoid/criteria_spec.rb +475 -1199
  293. data/spec/mongoid/document_fields_spec.rb +173 -24
  294. data/spec/mongoid/document_spec.rb +32 -41
  295. data/spec/mongoid/errors/document_not_found_spec.rb +76 -0
  296. data/spec/mongoid/errors/invalid_field_spec.rb +1 -1
  297. data/spec/mongoid/errors/invalid_field_type_spec.rb +55 -0
  298. data/spec/mongoid/errors/mongoid_error_spec.rb +3 -1
  299. data/spec/mongoid/errors/no_environment_spec.rb +3 -3
  300. data/spec/mongoid/errors/too_many_nested_attribute_records_spec.rb +1 -1
  301. data/spec/mongoid/extensions/array_spec.rb +16 -2
  302. data/spec/mongoid/extensions/big_decimal_spec.rb +712 -212
  303. data/spec/mongoid/extensions/binary_spec.rb +44 -9
  304. data/spec/mongoid/extensions/boolean_spec.rb +68 -82
  305. data/spec/mongoid/extensions/date_class_mongoize_spec.rb +7 -3
  306. data/spec/mongoid/extensions/date_spec.rb +71 -1
  307. data/spec/mongoid/extensions/date_time_spec.rb +15 -9
  308. data/spec/mongoid/extensions/float_spec.rb +53 -74
  309. data/spec/mongoid/extensions/hash_spec.rb +33 -3
  310. data/spec/mongoid/extensions/integer_spec.rb +50 -64
  311. data/spec/mongoid/extensions/range_spec.rb +255 -54
  312. data/spec/mongoid/extensions/regexp_spec.rb +58 -33
  313. data/spec/mongoid/extensions/set_spec.rb +106 -0
  314. data/spec/mongoid/extensions/string_spec.rb +53 -25
  315. data/spec/mongoid/extensions/symbol_spec.rb +18 -25
  316. data/spec/mongoid/extensions/time_spec.rb +634 -66
  317. data/spec/mongoid/extensions/time_with_zone_spec.rb +17 -31
  318. data/spec/mongoid/factory_spec.rb +61 -1
  319. data/spec/mongoid/fields/localized_spec.rb +37 -12
  320. data/spec/mongoid/fields_spec.rb +364 -50
  321. data/spec/mongoid/findable_spec.rb +80 -15
  322. data/spec/mongoid/indexable/specification_spec.rb +2 -2
  323. data/spec/mongoid/indexable_spec.rb +39 -20
  324. data/spec/mongoid/interceptable_spec.rb +807 -27
  325. data/spec/mongoid/interceptable_spec_models.rb +235 -4
  326. data/spec/mongoid/matcher/extract_attribute_spec.rb +1 -5
  327. data/spec/mongoid/mongoizable_spec.rb +285 -0
  328. data/spec/mongoid/persistable/creatable_spec.rb +2 -2
  329. data/spec/mongoid/persistable/deletable_spec.rb +2 -2
  330. data/spec/mongoid/persistable/destroyable_spec.rb +2 -2
  331. data/spec/mongoid/persistable/upsertable_spec.rb +14 -0
  332. data/spec/mongoid/persistence_context_spec.rb +50 -1
  333. data/spec/mongoid/query_cache_middleware_spec.rb +0 -18
  334. data/spec/mongoid/query_cache_spec.rb +0 -154
  335. data/spec/mongoid/reloadable_spec.rb +59 -2
  336. data/spec/mongoid/scopable_spec.rb +54 -16
  337. data/spec/mongoid/shardable_models.rb +14 -0
  338. data/spec/mongoid/shardable_spec.rb +157 -51
  339. data/spec/mongoid/stateful_spec.rb +28 -0
  340. data/spec/mongoid/timestamps_spec.rb +390 -0
  341. data/spec/mongoid/timestamps_spec_models.rb +67 -0
  342. data/spec/mongoid/touchable_spec.rb +116 -0
  343. data/spec/mongoid/touchable_spec_models.rb +12 -8
  344. data/spec/mongoid/traversable_spec.rb +4 -11
  345. data/spec/mongoid/validatable/presence_spec.rb +1 -1
  346. data/spec/mongoid/validatable/uniqueness_spec.rb +59 -31
  347. data/spec/mongoid/warnings_spec.rb +35 -0
  348. data/spec/mongoid_spec.rb +1 -1
  349. data/spec/rails/controller_extension/controller_runtime_spec.rb +2 -2
  350. data/spec/rails/mongoid_spec.rb +4 -16
  351. data/spec/shared/lib/mrss/docker_runner.rb +7 -0
  352. data/spec/shared/lib/mrss/lite_constraints.rb +10 -2
  353. data/spec/shared/lib/mrss/server_version_registry.rb +16 -23
  354. data/spec/shared/lib/mrss/utils.rb +28 -6
  355. data/spec/shared/share/Dockerfile.erb +36 -40
  356. data/spec/shared/shlib/server.sh +32 -8
  357. data/spec/shared/shlib/set_env.sh +4 -4
  358. data/spec/support/constraints.rb +24 -0
  359. data/spec/support/macros.rb +46 -0
  360. data/spec/support/models/augmentation.rb +12 -0
  361. data/spec/support/models/band.rb +3 -0
  362. data/spec/support/models/catalog.rb +24 -0
  363. data/spec/support/models/circus.rb +3 -0
  364. data/spec/support/models/code.rb +2 -0
  365. data/spec/support/models/fanatic.rb +8 -0
  366. data/spec/support/models/implant.rb +9 -0
  367. data/spec/support/models/label.rb +2 -0
  368. data/spec/support/models/passport.rb +9 -0
  369. data/spec/support/models/person.rb +2 -0
  370. data/spec/support/models/player.rb +2 -0
  371. data/spec/support/models/powerup.rb +12 -0
  372. data/spec/support/models/purse.rb +9 -0
  373. data/spec/support/models/registry.rb +1 -0
  374. data/spec/support/models/school.rb +14 -0
  375. data/spec/support/models/shield.rb +18 -0
  376. data/spec/support/models/student.rb +14 -0
  377. data/spec/support/models/weapon.rb +12 -0
  378. data.tar.gz.sig +4 -1
  379. metadata +682 -635
  380. metadata.gz.sig +0 -0
  381. data/lib/mongoid/errors/eager_load.rb +0 -23
  382. data/lib/mongoid/errors/invalid_value.rb +0 -17
  383. data/spec/mongoid/errors/eager_load_spec.rb +0 -31
@@ -379,36 +379,77 @@ describe Mongoid::Contextual::Memory do
379
379
 
380
380
  describe "#distinct" do
381
381
 
382
- let(:hobrecht) do
383
- Address.new(street: "hobrecht")
384
- end
382
+ context "when legacy_pluck_distinct is true" do
383
+ config_override :legacy_pluck_distinct, true
385
384
 
386
- let(:friedel) do
387
- Address.new(street: "friedel")
388
- end
385
+ let(:hobrecht) do
386
+ Address.new(street: "hobrecht")
387
+ end
388
+
389
+ let(:friedel) do
390
+ Address.new(street: "friedel")
391
+ end
389
392
 
390
- context "when limiting the result set" do
393
+ context "when limiting the result set" do
391
394
 
392
- let(:criteria) do
393
- Address.where(street: "hobrecht").tap do |crit|
394
- crit.documents = [ hobrecht, hobrecht, friedel ]
395
+ let(:criteria) do
396
+ Address.where(street: "hobrecht").tap do |crit|
397
+ crit.documents = [ hobrecht, hobrecht, friedel ]
398
+ end
399
+ end
400
+
401
+ let(:context) do
402
+ described_class.new(criteria)
403
+ end
404
+
405
+ it "returns the distinct field values" do
406
+ expect(context.distinct(:street)).to eq([ "hobrecht" ])
395
407
  end
396
408
  end
397
409
 
398
- let(:context) do
399
- described_class.new(criteria)
410
+ context "when not limiting the result set" do
411
+
412
+ let(:criteria) do
413
+ Address.all.tap do |crit|
414
+ crit.documents = [ hobrecht, friedel, friedel ]
415
+ end
416
+ end
417
+
418
+ let(:context) do
419
+ described_class.new(criteria)
420
+ end
421
+
422
+ it "returns the distinct field values" do
423
+ expect(context.distinct(:street)).to eq([ "hobrecht", "friedel" ])
424
+ end
400
425
  end
401
426
 
402
- it "returns the distinct field values" do
403
- expect(context.distinct(:street)).to eq([ "hobrecht" ])
427
+ context 'when there is a collation on the criteria' do
428
+
429
+ let(:criteria) do
430
+ Address.where(street: "hobrecht").tap do |crit|
431
+ crit.documents = [ hobrecht, hobrecht, friedel ]
432
+ end.collation(locale: 'en_US', strength: 2)
433
+ end
434
+
435
+ it "raises an exception" do
436
+ expect {
437
+ context.distinct(:street)
438
+ }.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
439
+ end
404
440
  end
405
441
  end
406
442
 
407
- context "when not limiting the result set" do
443
+ context "when legacy_pluck_distinct is false" do
444
+ config_override :legacy_pluck_distinct, false
445
+
446
+ let(:depeche) { Band.create!(name: "Depeche Mode", years: 30, sales: "1E2") }
447
+ let(:new_order) { Band.create!(name: "New Order", years: 25, sales: "2E3") }
448
+ let(:maniacs) { Band.create!(name: "10,000 Maniacs", years: 20, sales: "1E2") }
408
449
 
409
450
  let(:criteria) do
410
- Address.all.tap do |crit|
411
- crit.documents = [ hobrecht, friedel, friedel ]
451
+ Band.all.tap do |crit|
452
+ crit.documents = [ depeche, new_order, maniacs ]
412
453
  end
413
454
  end
414
455
 
@@ -416,23 +457,180 @@ describe Mongoid::Contextual::Memory do
416
457
  described_class.new(criteria)
417
458
  end
418
459
 
419
- it "returns the distinct field values" do
420
- expect(context.distinct(:street)).to eq([ "hobrecht", "friedel" ])
460
+ context "when limiting the result set" do
461
+
462
+ let(:criteria) do
463
+ Band.where(name: "Depeche Mode").tap do |crit|
464
+ crit.documents = [ depeche ]
465
+ end
466
+ end
467
+
468
+ it "returns the distinct matching fields" do
469
+ expect(context.distinct(:name)).to eq([ "Depeche Mode" ])
470
+ end
421
471
  end
422
- end
423
472
 
424
- context 'when there is a collation on the criteria' do
473
+ context "when not limiting the result set" do
425
474
 
426
- let(:criteria) do
427
- Address.where(street: "hobrecht").tap do |crit|
428
- crit.documents = [ hobrecht, hobrecht, friedel ]
429
- end.collation(locale: 'en_US', strength: 2)
475
+ it "returns the distinct field values" do
476
+ expect(context.distinct(:name).sort).to eq([ "10,000 Maniacs", "Depeche Mode", "New Order" ].sort)
477
+ end
430
478
  end
431
479
 
432
- it "raises an exception" do
433
- expect {
434
- context.distinct(:street)
435
- }.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
480
+ context "when providing an aliased field" do
481
+
482
+ it "returns the distinct field values" do
483
+ expect(context.distinct(:years).sort).to eq([ 20, 25, 30 ])
484
+ end
485
+ end
486
+
487
+ context "when providing a demongoizable field" do
488
+
489
+ it "returns the non-demongoized distinct field values" do
490
+ expect(context.distinct(:sales).sort).to eq([ BigDecimal("1E2"), BigDecimal("2E3") ])
491
+ end
492
+ end
493
+
494
+ context "when getting a localized field" do
495
+ before do
496
+ I18n.locale = :en
497
+ d = Dictionary.create!(description: 'english-text')
498
+ I18n.locale = :de
499
+ d.description = 'deutsch-text'
500
+ d.save!
501
+ end
502
+
503
+ after do
504
+ I18n.locale = :en
505
+ end
506
+
507
+ let(:criteria) do
508
+ Dictionary.all.tap do |crit|
509
+ crit.documents = [ Dictionary.first ]
510
+ end
511
+ end
512
+
513
+ context "when getting the field without _translations" do
514
+ it "gets the demongoized localized field" do
515
+ expect(context.distinct(:description)).to eq([ 'deutsch-text' ])
516
+ end
517
+ end
518
+
519
+ context "when getting the field with _translations" do
520
+
521
+ it "gets the full hash" do
522
+ expect(context.distinct(:description_translations)).to eq([ { "de" => "deutsch-text", "en" => "english-text" } ])
523
+ end
524
+ end
525
+
526
+ context 'when plucking a specific locale' do
527
+
528
+ let(:distinct) do
529
+ context.distinct(:'description.de')
530
+ end
531
+
532
+ it 'returns the specific translation' do
533
+ expect(distinct).to eq([ "deutsch-text" ])
534
+ end
535
+ end
536
+
537
+ context 'when plucking a specific locale from _translations field' do
538
+
539
+ let(:distinct) do
540
+ context.distinct(:'description_translations.de')
541
+ end
542
+
543
+ it 'returns the specific translations' do
544
+ expect(distinct).to eq(['deutsch-text'])
545
+ end
546
+ end
547
+
548
+ context 'when fallbacks are enabled with a locale list' do
549
+ with_i18n_fallbacks
550
+
551
+ around(:all) do |example|
552
+ prev_fallbacks = I18n.fallbacks.dup
553
+ I18n.fallbacks[:he] = [ :en ]
554
+ example.run
555
+ I18n.fallbacks = prev_fallbacks
556
+ end
557
+
558
+ after do
559
+ I18n.locale = :en
560
+ end
561
+
562
+ let(:distinct) do
563
+ context.distinct(:description).first
564
+ end
565
+
566
+ it "correctly uses the fallback" do
567
+ I18n.locale = :en
568
+ Dictionary.create!(description: 'english-text')
569
+ I18n.locale = :he
570
+ distinct.should == "english-text"
571
+ end
572
+ end
573
+
574
+ context "when the localized field is embedded" do
575
+ let(:person) do
576
+ p = Passport.new
577
+ I18n.locale = :en
578
+ p.name = "Neil"
579
+ I18n.locale = :he
580
+ p.name = "Nissim"
581
+
582
+ Person.create!(passport: p, employer_id: 12345)
583
+ end
584
+
585
+ after do
586
+ I18n.locale = :en
587
+ end
588
+
589
+ let(:criteria) do
590
+ Person.where(employer_id: 12345).tap do |crit|
591
+ crit.documents = [ person ]
592
+ end
593
+ end
594
+
595
+ let(:distinct) do
596
+ context.distinct("pass.name").first
597
+ end
598
+
599
+ let(:distinct_translations) do
600
+ context.distinct("pass.name_translations").first
601
+ end
602
+
603
+ let(:distinct_translations_field) do
604
+ context.distinct("pass.name_translations.en").first
605
+ end
606
+
607
+ it "returns the translation for the current locale" do
608
+ expect(distinct).to eq("Nissim")
609
+ end
610
+
611
+ it "returns the full _translation hash" do
612
+ expect(distinct_translations).to eq({ "en" => "Neil", "he" => "Nissim" })
613
+ end
614
+
615
+ it "returns the translation for the requested locale" do
616
+ expect(distinct_translations_field).to eq("Neil")
617
+ end
618
+ end
619
+ end
620
+
621
+ context "when getting an embedded field" do
622
+
623
+ let(:label) { Label.new(sales: "1E2") }
624
+ let!(:band) { Band.create!(label: label) }
625
+ let(:criteria) do
626
+ Band.where(_id: band.id).tap do |crit|
627
+ crit.documents = [ band ]
628
+ end
629
+ end
630
+
631
+ it "returns the distinct matching fields" do
632
+ expect(context.distinct("label.sales")).to eq([ BigDecimal("1E2") ])
633
+ end
436
634
  end
437
635
  end
438
636
  end
@@ -624,6 +822,14 @@ describe Mongoid::Contextual::Memory do
624
822
  expect(context.send(method)).to eq(hobrecht)
625
823
  end
626
824
 
825
+ it "returns a list when passing a limit" do
826
+ expect(context.send(method, 2)).to eq([ hobrecht, friedel ])
827
+ end
828
+
829
+ it "returns a list when passing 1" do
830
+ expect(context.send(method, 1)).to eq([ hobrecht ])
831
+ end
832
+
627
833
  context 'when there is a collation on the criteria' do
628
834
 
629
835
  let(:criteria) do
@@ -641,6 +847,108 @@ describe Mongoid::Contextual::Memory do
641
847
  end
642
848
  end
643
849
 
850
+ describe "#take" do
851
+
852
+ let(:hobrecht) do
853
+ Address.new(street: "hobrecht")
854
+ end
855
+
856
+ let(:friedel) do
857
+ Address.new(street: "friedel")
858
+ end
859
+
860
+ let(:criteria) do
861
+ Address.where(:street.in => [ "hobrecht", "friedel" ]).tap do |crit|
862
+ crit.documents = [ hobrecht, friedel ]
863
+ end
864
+ end
865
+
866
+ let(:context) do
867
+ described_class.new(criteria)
868
+ end
869
+
870
+ it "returns the first matching document" do
871
+ expect(context.take).to eq(hobrecht)
872
+ end
873
+
874
+ it "returns an array when passing a limit" do
875
+ expect(context.take(2)).to eq([ hobrecht, friedel ])
876
+ end
877
+
878
+ it "returns an array when passing a limit as 1" do
879
+ expect(context.take(1)).to eq([ hobrecht ])
880
+ end
881
+
882
+ context 'when there is a collation on the criteria' do
883
+
884
+ let(:criteria) do
885
+ Address.where(:street.in => [ "hobrecht", "friedel" ]).tap do |crit|
886
+ crit.documents = [ hobrecht, friedel ]
887
+ end.collation(locale: 'en_US', strength: 2)
888
+ end
889
+
890
+ it "raises an exception" do
891
+ expect {
892
+ context.take
893
+ }.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
894
+ end
895
+ end
896
+ end
897
+
898
+ describe "#take!" do
899
+
900
+ let(:hobrecht) do
901
+ Address.new(street: "hobrecht")
902
+ end
903
+
904
+ let(:friedel) do
905
+ Address.new(street: "friedel")
906
+ end
907
+
908
+ let(:criteria) do
909
+ Address.where(:street.in => [ "hobrecht", "friedel" ]).tap do |crit|
910
+ crit.documents = [ hobrecht, friedel ]
911
+ end
912
+ end
913
+
914
+ let(:context) do
915
+ described_class.new(criteria)
916
+ end
917
+
918
+ it "returns the first matching document" do
919
+ expect(context.take!).to eq(hobrecht)
920
+ end
921
+
922
+ context "when the criteria is empty" do
923
+ let(:criteria) do
924
+ Address.where(street: "bogus").tap do |crit|
925
+ crit.documents = []
926
+ end
927
+ end
928
+
929
+ it "raise an error" do
930
+ expect do
931
+ context.take!
932
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Address./)
933
+ end
934
+ end
935
+
936
+ context 'when there is a collation on the criteria' do
937
+
938
+ let(:criteria) do
939
+ Address.where(:street.in => [ "hobrecht", "friedel" ]).tap do |crit|
940
+ crit.documents = [ hobrecht, friedel ]
941
+ end.collation(locale: 'en_US', strength: 2)
942
+ end
943
+
944
+ it "raises an exception" do
945
+ expect {
946
+ context.take
947
+ }.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
948
+ end
949
+ end
950
+ end
951
+
644
952
  describe "#initialize" do
645
953
 
646
954
  context "when the criteria has no options" do
@@ -766,6 +1074,14 @@ describe Mongoid::Contextual::Memory do
766
1074
  expect(context.last).to eq(friedel)
767
1075
  end
768
1076
 
1077
+ it "returns a list when a limit is passed" do
1078
+ expect(context.last(2)).to eq([ hobrecht, friedel ])
1079
+ end
1080
+
1081
+ it "returns a list when the limit is 1" do
1082
+ expect(context.last(1)).to eq([ friedel ])
1083
+ end
1084
+
769
1085
  context 'when there is a collation on the criteria' do
770
1086
 
771
1087
  let(:criteria) do
@@ -919,72 +1235,1023 @@ describe Mongoid::Contextual::Memory do
919
1235
 
920
1236
  describe "#pluck" do
921
1237
 
922
- let(:hobrecht) do
923
- Address.new(street: "hobrecht")
924
- end
925
-
926
- let(:friedel) do
927
- Address.new(street: "friedel")
928
- end
929
-
930
- let(:criteria) do
931
- Address.all.tap do |crit|
932
- crit.documents = [ hobrecht, friedel ]
933
- end
934
- end
935
-
936
1238
  let(:context) do
937
1239
  described_class.new(criteria)
938
1240
  end
939
1241
 
940
- context "when plucking" do
1242
+ context "when legacy_pluck_distinct is true" do
1243
+ config_override :legacy_pluck_distinct, true
941
1244
 
942
- let!(:plucked) do
943
- context.pluck(:street)
1245
+ let(:hobrecht) do
1246
+ Address.new(street: "hobrecht")
944
1247
  end
945
1248
 
946
- it "returns the values" do
947
- expect(plucked).to eq([ "hobrecht", "friedel" ])
1249
+ let(:friedel) do
1250
+ Address.new(street: "friedel")
948
1251
  end
949
- end
950
1252
 
951
- context "when plucking a field that doesnt exist" do
1253
+ let(:criteria) do
1254
+ Address.all.tap do |crit|
1255
+ crit.documents = [ hobrecht, friedel ]
1256
+ end
1257
+ end
952
1258
 
953
- context "when pluck one field" do
1259
+ context "when plucking" do
954
1260
 
955
- let(:plucked) do
956
- context.pluck(:foo)
1261
+ let!(:plucked) do
1262
+ context.pluck(:street)
957
1263
  end
958
1264
 
959
- it "returns a empty array" do
960
- expect(plucked).to eq([])
1265
+ it "returns the values" do
1266
+ expect(plucked).to eq([ "hobrecht", "friedel" ])
961
1267
  end
962
1268
  end
963
1269
 
964
- context "when pluck multiple fields" do
1270
+ context "when plucking a mix of empty and non-empty values" do
965
1271
 
966
- let(:plucked) do
967
- context.pluck(:foo, :bar)
1272
+ let(:empty_doc) do
1273
+ Address.new(street: nil)
968
1274
  end
969
1275
 
970
- it "returns a empty array" do
971
- expect(plucked).to eq([[], []])
972
- end
1276
+ let(:criteria) do
1277
+ Address.all.tap do |crit|
1278
+ crit.documents = [ hobrecht, friedel, empty_doc ]
1279
+ end
1280
+ end
1281
+
1282
+ let!(:plucked) do
1283
+ context.pluck(:street)
1284
+ end
1285
+
1286
+ it "returns the values" do
1287
+ expect(plucked).to eq([ "hobrecht", "friedel", nil ])
1288
+ end
1289
+ end
1290
+
1291
+ context "when plucking a field that doesnt exist" do
1292
+
1293
+ context "when pluck one field" do
1294
+
1295
+ let(:plucked) do
1296
+ context.pluck(:foo)
1297
+ end
1298
+
1299
+ it "returns a empty array" do
1300
+ expect(plucked).to eq([nil, nil])
1301
+ end
1302
+ end
1303
+
1304
+ context "when pluck multiple fields" do
1305
+
1306
+ let(:plucked) do
1307
+ context.pluck(:foo, :bar)
1308
+ end
1309
+
1310
+ it "returns a empty array" do
1311
+ expect(plucked).to eq([[nil, nil], [nil, nil]])
1312
+ end
1313
+ end
1314
+ end
1315
+
1316
+ context 'when there is a collation on the criteria' do
1317
+
1318
+ let(:criteria) do
1319
+ Address.all.tap do |crit|
1320
+ crit.documents = [ hobrecht, friedel ]
1321
+ end.collation(locale: 'en_US', strength: 2)
1322
+ end
1323
+
1324
+ it "raises an exception" do
1325
+ expect {
1326
+ context.pluck(:foo, :bar)
1327
+ }.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
1328
+ end
973
1329
  end
974
1330
  end
975
1331
 
976
- context 'when there is a collation on the criteria' do
1332
+ context "when legacy_pluck_distinct is false" do
1333
+ config_override :legacy_pluck_distinct, false
1334
+
1335
+ let!(:depeche) do
1336
+ Band.create!(name: "Depeche Mode", likes: 3)
1337
+ end
1338
+
1339
+ let!(:tool) do
1340
+ Band.create!(name: "Tool", likes: 3)
1341
+ end
1342
+
1343
+ let!(:photek) do
1344
+ Band.create!(name: "Photek", likes: 1)
1345
+ end
1346
+
1347
+ let!(:maniacs) do
1348
+ Band.create!(name: "10,000 Maniacs", likes: 1, sales: "1E2")
1349
+ end
977
1350
 
978
1351
  let(:criteria) do
979
- Address.all.tap do |crit|
980
- crit.documents = [ hobrecht, friedel ]
981
- end.collation(locale: 'en_US', strength: 2)
1352
+ Band.all.tap do |crit|
1353
+ crit.documents = [ depeche, tool, photek, maniacs ]
1354
+ end
982
1355
  end
983
1356
 
984
- it "raises an exception" do
985
- expect {
986
- context.pluck(:foo, :bar)
987
- }.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
1357
+ context "when the field is aliased" do
1358
+
1359
+ let!(:expensive) do
1360
+ Product.create!(price: 100000)
1361
+ end
1362
+
1363
+ let!(:cheap) do
1364
+ Product.create!(price: 1)
1365
+ end
1366
+
1367
+ let(:criteria) do
1368
+ Product.all.tap do |crit|
1369
+ crit.documents = [ expensive, cheap ]
1370
+ end
1371
+ end
1372
+
1373
+ context "when using alias_attribute" do
1374
+
1375
+ let(:plucked) do
1376
+ context.pluck(:price)
1377
+ end
1378
+
1379
+ it "uses the aliases" do
1380
+ expect(plucked).to eq([ 100000, 1 ])
1381
+ end
1382
+ end
1383
+ end
1384
+
1385
+ context "when the criteria matches" do
1386
+
1387
+ context "when there are no duplicate values" do
1388
+
1389
+ let!(:plucked) do
1390
+ context.pluck(:name)
1391
+ end
1392
+
1393
+ it "returns the values" do
1394
+ expect(plucked).to contain_exactly("10,000 Maniacs", "Depeche Mode", "Tool", "Photek")
1395
+ end
1396
+
1397
+ context "when subsequently executing the criteria without a pluck" do
1398
+
1399
+ it "does not limit the fields" do
1400
+ expect(context.first.likes).to eq(3)
1401
+ end
1402
+ end
1403
+
1404
+ context 'when the field is a subdocument' do
1405
+
1406
+ context 'when a top-level field and a subdocument field are plucked' do
1407
+ let(:criteria) do
1408
+ Band.where(name: 'FKA Twigs').tap do |crit|
1409
+ crit.documents = [
1410
+ Band.create!(name: 'FKA Twigs'),
1411
+ Band.create!(name: 'FKA Twigs', records: [ Record.new(name: 'LP1') ])
1412
+ ]
1413
+ end
1414
+ end
1415
+
1416
+ let(:embedded_pluck) do
1417
+ context.pluck(:name, 'records.name')
1418
+ end
1419
+
1420
+ let(:expected) do
1421
+ [
1422
+ ["FKA Twigs", []],
1423
+ ['FKA Twigs', ["LP1"]]
1424
+ ]
1425
+ end
1426
+
1427
+ it 'returns the list of top-level field and subdocument values' do
1428
+ expect(embedded_pluck).to eq(expected)
1429
+ end
1430
+ end
1431
+
1432
+ context 'when only a subdocument field is plucked' do
1433
+
1434
+ let(:criteria) do
1435
+ Band.where(name: 'FKA Twigs').tap do |crit|
1436
+ crit.documents = [
1437
+ Band.create!(name: 'FKA Twigs'),
1438
+ Band.create!(name: 'FKA Twigs', records: [ Record.new(name: 'LP1') ])
1439
+ ]
1440
+ end
1441
+ end
1442
+
1443
+ let(:embedded_pluck) do
1444
+ context.pluck('records.name')
1445
+ end
1446
+
1447
+ let(:expected) do
1448
+ [
1449
+ [],
1450
+ ["LP1"]
1451
+ ]
1452
+ end
1453
+
1454
+ it 'returns the list of subdocument values' do
1455
+ expect(embedded_pluck).to eq(expected)
1456
+ end
1457
+ end
1458
+ end
1459
+ end
1460
+
1461
+ context "when plucking multi-fields" do
1462
+
1463
+ let(:plucked) do
1464
+ context.pluck(:name, :likes)
1465
+ end
1466
+
1467
+ it "returns the values" do
1468
+ expect(plucked).to contain_exactly(["10,000 Maniacs", 1], ["Depeche Mode", 3], ["Tool", 3], ["Photek", 1])
1469
+ end
1470
+ end
1471
+
1472
+ context "when there are duplicate values" do
1473
+
1474
+ let(:plucked) do
1475
+ context.pluck(:likes)
1476
+ end
1477
+
1478
+ it "returns the duplicates" do
1479
+ expect(plucked).to contain_exactly(1, 3, 3, 1)
1480
+ end
1481
+ end
1482
+ end
1483
+
1484
+ context "when plucking an aliased field" do
1485
+
1486
+ let(:plucked) do
1487
+ context.pluck(:id)
1488
+ end
1489
+
1490
+ it "returns the field values" do
1491
+ expect(plucked).to eq([ depeche.id, tool.id, photek.id, maniacs.id ])
1492
+ end
1493
+ end
1494
+
1495
+ context "when plucking existent and non-existent fields" do
1496
+
1497
+ let(:plucked) do
1498
+ context.pluck(:id, :fooz)
1499
+ end
1500
+
1501
+ it "returns nil for the field that doesnt exist" do
1502
+ expect(plucked).to eq([[depeche.id, nil], [tool.id, nil], [photek.id, nil], [maniacs.id, nil] ])
1503
+ end
1504
+ end
1505
+
1506
+ context "when plucking a field that doesnt exist" do
1507
+
1508
+ context "when pluck one field" do
1509
+
1510
+ let(:plucked) do
1511
+ context.pluck(:foo)
1512
+ end
1513
+
1514
+ it "returns an array with nil values" do
1515
+ expect(plucked).to eq([nil, nil, nil, nil])
1516
+ end
1517
+ end
1518
+
1519
+ context "when pluck multiple fields" do
1520
+
1521
+ let(:plucked) do
1522
+ context.pluck(:foo, :bar)
1523
+ end
1524
+
1525
+ it "returns an array of arrays with nil values" do
1526
+ expect(plucked).to eq([[nil, nil], [nil, nil], [nil, nil], [nil, nil]])
1527
+ end
1528
+ end
1529
+ end
1530
+
1531
+ context 'when plucking a localized field' do
1532
+
1533
+ before do
1534
+ I18n.locale = :en
1535
+ d = Dictionary.create!(description: 'english-text')
1536
+ I18n.locale = :de
1537
+ d.description = 'deutsch-text'
1538
+ d.save!
1539
+ end
1540
+
1541
+ after do
1542
+ I18n.locale = :en
1543
+ end
1544
+
1545
+ let(:criteria) do
1546
+ Dictionary.all.tap do |crit|
1547
+ crit.documents = [ Dictionary.first ]
1548
+ end
1549
+ end
1550
+
1551
+ context 'when plucking the entire field' do
1552
+
1553
+ let(:plucked) do
1554
+ context.pluck(:description)
1555
+ end
1556
+
1557
+ let(:plucked_translations) do
1558
+ context.pluck(:description_translations)
1559
+ end
1560
+
1561
+ let(:plucked_translations_both) do
1562
+ context.pluck(:description_translations, :description)
1563
+ end
1564
+
1565
+ it 'returns the demongoized translations' do
1566
+ expect(plucked.first).to eq('deutsch-text')
1567
+ end
1568
+
1569
+ it 'returns the full translations hash to _translations' do
1570
+ expect(plucked_translations.first).to eq({"de"=>"deutsch-text", "en"=>"english-text"})
1571
+ end
1572
+
1573
+ it 'returns both' do
1574
+ expect(plucked_translations_both.first).to eq([{"de"=>"deutsch-text", "en"=>"english-text"}, "deutsch-text"])
1575
+ end
1576
+ end
1577
+
1578
+ context 'when plucking a specific locale' do
1579
+
1580
+ let(:plucked) do
1581
+ context.pluck(:'description.de')
1582
+ end
1583
+
1584
+ it 'returns the specific translations' do
1585
+ expect(plucked.first).to eq('deutsch-text')
1586
+ end
1587
+ end
1588
+
1589
+ context 'when plucking a specific locale from _translations field' do
1590
+
1591
+ let(:plucked) do
1592
+ context.pluck(:'description_translations.de')
1593
+ end
1594
+
1595
+ it 'returns the specific translations' do
1596
+ expect(plucked.first).to eq('deutsch-text')
1597
+ end
1598
+ end
1599
+
1600
+ context 'when fallbacks are enabled with a locale list' do
1601
+ with_i18n_fallbacks
1602
+
1603
+ around(:all) do |example|
1604
+ prev_fallbacks = I18n.fallbacks.dup
1605
+ I18n.fallbacks[:he] = [ :en ]
1606
+ example.run
1607
+ I18n.fallbacks = prev_fallbacks
1608
+ end
1609
+
1610
+ after do
1611
+ I18n.locale = :en
1612
+ end
1613
+
1614
+ let(:plucked) do
1615
+ context.pluck(:description).first
1616
+ end
1617
+
1618
+ it "correctly uses the fallback" do
1619
+ I18n.locale = :en
1620
+ Dictionary.create!(description: 'english-text')
1621
+ I18n.locale = :he
1622
+ plucked.should == "english-text"
1623
+ end
1624
+ end
1625
+
1626
+ context "when the localized field is embedded" do
1627
+ before do
1628
+ p = Passport.new
1629
+ I18n.locale = :en
1630
+ p.name = "Neil"
1631
+ I18n.locale = :he
1632
+ p.name = "Nissim"
1633
+
1634
+ Person.create!(passport: p, employer_id: 12345)
1635
+ end
1636
+
1637
+ after do
1638
+ I18n.locale = :en
1639
+ end
1640
+
1641
+ let(:plucked) do
1642
+ Person.where(employer_id: 12345).pluck("pass.name").first
1643
+ end
1644
+
1645
+ let(:plucked_translations) do
1646
+ Person.where(employer_id: 12345).pluck("pass.name_translations").first
1647
+ end
1648
+
1649
+ let(:plucked_translations_field) do
1650
+ Person.where(employer_id: 12345).pluck("pass.name_translations.en").first
1651
+ end
1652
+
1653
+ it "returns the translation for the current locale" do
1654
+ expect(plucked).to eq("Nissim")
1655
+ end
1656
+
1657
+ it "returns the full _translation hash" do
1658
+ expect(plucked_translations).to eq({ "en" => "Neil", "he" => "Nissim" })
1659
+ end
1660
+
1661
+ it "returns the translation for the requested locale" do
1662
+ expect(plucked_translations_field).to eq("Neil")
1663
+ end
1664
+ end
1665
+ end
1666
+
1667
+ context 'when plucking a field to be demongoized' do
1668
+
1669
+ let(:plucked) do
1670
+ Band.where(name: maniacs.name).pluck(:sales)
1671
+ end
1672
+
1673
+ with_config_values :map_big_decimal_to_decimal128, true, false do
1674
+
1675
+ it "demongoizes the field" do
1676
+ expect(plucked.first).to be_a(BigDecimal)
1677
+ expect(plucked.first).to eq(BigDecimal("1E2"))
1678
+ end
1679
+ end
1680
+ end
1681
+
1682
+ context "when plucking an embedded field" do
1683
+ let(:label) { Label.new(sales: "1E2") }
1684
+ let!(:band) { Band.create!(label: label) }
1685
+
1686
+ let(:plucked) do
1687
+ Band.where(_id: band.id).tap do |crit|
1688
+ crit.documents = [ band ]
1689
+ end.pluck("label.sales")
1690
+ end
1691
+
1692
+ it "demongoizes the field" do
1693
+ expect(plucked).to eq([ BigDecimal("1E2") ])
1694
+ end
1695
+ end
1696
+
1697
+ context "when plucking an embeds_many field" do
1698
+ let(:label) { Label.new(sales: "1E2") }
1699
+ let!(:band) { Band.create!(labels: [label]) }
1700
+
1701
+ let(:plucked) { Band.where(_id: band.id).pluck("labels.sales") }
1702
+
1703
+ it "demongoizes the field" do
1704
+ expect(plucked.first).to eq([ BigDecimal("1E2") ])
1705
+ end
1706
+ end
1707
+
1708
+ context "when plucking a nonexistent embedded field" do
1709
+ let(:label) { Label.new(sales: "1E2") }
1710
+ let!(:band) { Band.create!(label: label) }
1711
+
1712
+ let(:plucked) do
1713
+ Band.where(_id: band.id).tap do |crit|
1714
+ crit.documents = [ band ]
1715
+ end.pluck("label.qwerty")
1716
+ end
1717
+
1718
+ it "returns nil" do
1719
+ expect(plucked.first).to eq(nil)
1720
+ end
1721
+ end
1722
+
1723
+ context "when tallying deeply nested arrays/embedded associations" do
1724
+
1725
+ let(:criteria) do
1726
+ Person.all.tap do |crit|
1727
+ crit.documents = [
1728
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ]),
1729
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ]),
1730
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))) ]),
1731
+ ]
1732
+ end
1733
+ end
1734
+
1735
+ let(:plucked) do
1736
+ context.pluck("addresses.code.deepest.array.y.z")
1737
+ end
1738
+
1739
+ it "returns the correct hash" do
1740
+ expect(plucked).to eq([
1741
+ [ [ 1, 2 ] ], [ [ 1, 2 ] ], [ [ 1, 3 ] ]
1742
+ ])
1743
+ end
1744
+ end
1745
+ end
1746
+ end
1747
+
1748
+ describe "#pick" do
1749
+
1750
+ let(:depeche) do
1751
+ Band.create!(name: "Depeche Mode", likes: 3)
1752
+ end
1753
+
1754
+ let(:tool) do
1755
+ Band.create!(name: "Tool", likes: 3)
1756
+ end
1757
+
1758
+ let(:criteria) do
1759
+ Band.all.tap do |crit|
1760
+ crit.documents = [ depeche, tool ]
1761
+ end
1762
+ end
1763
+
1764
+ let(:context) do
1765
+ described_class.new(criteria)
1766
+ end
1767
+
1768
+ context "when picking a field" do
1769
+
1770
+ let(:picked) do
1771
+ context.pick(:name)
1772
+ end
1773
+
1774
+ it "returns one element" do
1775
+ expect(picked).to eq("Depeche Mode")
1776
+ end
1777
+ end
1778
+
1779
+ context "when picking multiple fields" do
1780
+
1781
+ let(:picked) do
1782
+ context.pick(:name, :likes)
1783
+ end
1784
+
1785
+ it "returns an array" do
1786
+ expect(picked).to eq([ "Depeche Mode", 3 ])
1787
+ end
1788
+ end
1789
+
1790
+ context "when no documents to pick" do
1791
+
1792
+ let(:criteria) do
1793
+ Band.all.tap do |crit|
1794
+ crit.documents = []
1795
+ end
1796
+ end
1797
+
1798
+ let(:picked) do
1799
+ context.pick(:name)
1800
+ end
1801
+
1802
+ it "returns nil" do
1803
+ expect(picked).to be_nil
1804
+ end
1805
+ end
1806
+ end
1807
+
1808
+ describe "#tally" do
1809
+ let(:fans1) { [ Fanatic.new(age:1), Fanatic.new(age:2) ] }
1810
+ let(:fans2) { [ Fanatic.new(age:1), Fanatic.new(age:2) ] }
1811
+ let(:fans3) { [ Fanatic.new(age:1), Fanatic.new(age:3) ] }
1812
+
1813
+ let(:genres1) { [ { x: 1, y: { z: 1 } }, { x: 2, y: { z: 2 } }, { y: 3 } ]}
1814
+ let(:genres2) { [ { x: 1, y: { z: 1 } }, { x: 2, y: { z: 2 } }, { y: 4 } ]}
1815
+ let(:genres3) { [ { x: 1, y: { z: 1 } }, { x: 3, y: { z: 3 } }, { y: 5 } ]}
1816
+
1817
+ let(:label1) { Label.new(name: "Atlantic") }
1818
+ let(:label2) { Label.new(name: "Atlantic") }
1819
+ let(:label3) { Label.new(name: "Columbia") }
1820
+
1821
+
1822
+ let(:band1) { Band.new(origin: "tally", name: "Depeche Mode", years: 30, sales: "1E2", label: label1, genres: genres1) }
1823
+ let(:band2) { Band.new(origin: "tally", name: "New Order", years: 30, sales: "2E3", label: label2, genres: genres2) }
1824
+ let(:band3) { Band.new(origin: "tally", name: "10,000 Maniacs", years: 30, sales: "1E2", label: label3, genres: genres3) }
1825
+ let(:band4) { Band.new(origin: "tally2", fanatics: fans1, genres: [1, 2]) }
1826
+ let(:band5) { Band.new(origin: "tally2", fanatics: fans2, genres: [1, 2]) }
1827
+ let(:band6) { Band.new(origin: "tally2", fanatics: fans3, genres: [1, 3]) }
1828
+
1829
+ let(:criteria) do
1830
+ Band.where(origin: "tally").all.tap do |crit|
1831
+ crit.documents = [ band1, band2, band3 ]
1832
+ end
1833
+ end
1834
+
1835
+ let(:criteria2) do
1836
+ Band.where(origin: "tally2").tap do |crit|
1837
+ crit.documents = [ band4, band5, band6 ]
1838
+ end
1839
+ end
1840
+
1841
+ let(:context) do
1842
+ described_class.new(criteria)
1843
+ end
1844
+
1845
+ let(:context2) do
1846
+ described_class.new(criteria2)
1847
+ end
1848
+
1849
+ context "when tallying a string" do
1850
+ let(:tally) do
1851
+ context.tally(:name)
1852
+ end
1853
+
1854
+ it "returns the correct hash" do
1855
+ expect(tally).to eq("Depeche Mode" => 1, "New Order" => 1, "10,000 Maniacs" => 1)
1856
+ end
1857
+ end
1858
+
1859
+ context "using an aliased field" do
1860
+ let(:tally) do
1861
+ context.tally(:years)
1862
+ end
1863
+
1864
+ it "returns the correct hash" do
1865
+ expect(tally).to eq(30 => 3)
1866
+ end
1867
+ end
1868
+
1869
+ context "when tallying a demongoizable field" do
1870
+ let(:tally) do
1871
+ context.tally(:sales)
1872
+ end
1873
+
1874
+ it "returns the correct hash" do
1875
+ expect(tally).to eq(BigDecimal("1E2") => 2, BigDecimal("2E3") => 1)
1876
+ end
1877
+ end
1878
+
1879
+ context "when tallying a localized field" do
1880
+ let(:d1) { Dictionary.new(description: 'en1') }
1881
+ let(:d2) { Dictionary.new(description: 'en1') }
1882
+ let(:d3) { Dictionary.new(description: 'en1') }
1883
+ let(:d4) { Dictionary.new(description: 'en2') }
1884
+
1885
+ before do
1886
+ I18n.locale = :en
1887
+ d1
1888
+ d2
1889
+ d3
1890
+ d4
1891
+ I18n.locale = :de
1892
+ d1.description = 'de1'
1893
+ d2.description = 'de1'
1894
+ d3.description = 'de2'
1895
+ d4.description = 'de3'
1896
+ I18n.locale = :en
1897
+ end
1898
+
1899
+ let(:criteria) do
1900
+ Dictionary.all.tap do |crit|
1901
+ crit.documents = [ d1, d2, d3, d4 ]
1902
+ end
1903
+ end
1904
+
1905
+ context "when getting the demongoized field" do
1906
+ let(:tallied) do
1907
+ context.tally(:description)
1908
+ end
1909
+
1910
+ it "returns the translation for the current locale" do
1911
+ expect(tallied).to eq("en1" => 3, "en2" => 1)
1912
+ end
1913
+ end
1914
+
1915
+ context "when getting a specific locale" do
1916
+ let(:tallied) do
1917
+ context.tally("description.de")
1918
+ end
1919
+
1920
+ it "returns the translation for the the specific locale" do
1921
+ expect(tallied).to eq("de1" => 2, "de2" => 1, "de3" => 1)
1922
+ end
1923
+ end
1924
+
1925
+ context "when getting the full hash" do
1926
+ let(:tallied) do
1927
+ context.tally("description_translations")
1928
+ end
1929
+
1930
+ it "returns the correct hash" do
1931
+ expect(tallied).to eq(
1932
+ {"de" => "de1", "en" => "en1" } => 2,
1933
+ {"de" => "de2", "en" => "en1" } => 1,
1934
+ {"de" => "de3", "en" => "en2" } => 1
1935
+ )
1936
+ end
1937
+ end
1938
+ end
1939
+
1940
+ context "when tallying an embedded localized field" do
1941
+
1942
+ let(:person1) { Person.create!(addresses: [ address1a, address1b ]) }
1943
+ let(:person2) { Person.create!(addresses: [ address2a, address2b ]) }
1944
+
1945
+ let(:address1a) { Address.new(name: "en1") }
1946
+ let(:address1b) { Address.new(name: "en2") }
1947
+ let(:address2a) { Address.new(name: "en1") }
1948
+ let(:address2b) { Address.new(name: "en3") }
1949
+
1950
+ before do
1951
+ I18n.locale = :en
1952
+ address1a
1953
+ address1b
1954
+ address2a
1955
+ address2b
1956
+ I18n.locale = :de
1957
+ address1a.name = "de1"
1958
+ address1b.name = "de2"
1959
+ address2a.name = "de1"
1960
+ address2b.name = "de3"
1961
+ person1
1962
+ person2
1963
+ I18n.locale = :en
1964
+ end
1965
+
1966
+ let(:criteria) do
1967
+ Person.all.tap do |crit|
1968
+ crit.documents = [ person1, person2 ]
1969
+ end
1970
+ end
1971
+
1972
+ context "when getting the demongoized field" do
1973
+ let(:tallied) do
1974
+ context.tally("addresses.name")
1975
+ end
1976
+
1977
+ it "returns the translation for the current locale" do
1978
+ expect(tallied).to eq(
1979
+ [ "en1", "en2" ] => 1,
1980
+ [ "en1", "en3" ] => 1,
1981
+ )
1982
+ end
1983
+ end
1984
+
1985
+ context "when getting a specific locale" do
1986
+ let(:tallied) do
1987
+ context.tally("addresses.name.de")
1988
+ end
1989
+
1990
+ it "returns the translation for the the specific locale" do
1991
+ expect(tallied).to eq(
1992
+ [ "de1", "de2" ] => 1,
1993
+ [ "de1", "de3" ] => 1,
1994
+ )
1995
+ end
1996
+ end
1997
+
1998
+ context "when getting the full hash" do
1999
+ let(:tallied) do
2000
+ context.tally("addresses.name_translations")
2001
+ end
2002
+
2003
+ it "returns the correct hash" do
2004
+ expect(tallied).to eq(
2005
+ [{ "de" => "de1", "en" => "en1" }, { "de" => "de2", "en" => "en2" }] => 1,
2006
+ [{ "de" => "de1", "en" => "en1" }, { "de" => "de3", "en" => "en3" }] => 1,
2007
+ )
2008
+ end
2009
+ end
2010
+ end
2011
+
2012
+ context "when tallying an embedded field" do
2013
+ let(:tally) do
2014
+ context.tally("label.name")
2015
+ end
2016
+
2017
+ it "returns the correct hash" do
2018
+ expect(tally).to eq("Atlantic" => 2, "Columbia" => 1)
2019
+ end
2020
+ end
2021
+
2022
+ context "when tallying an element in an embeds_many field" do
2023
+
2024
+ let(:tally) do
2025
+ context2.tally("fanatics.age")
2026
+ end
2027
+
2028
+ it "returns the correct hash" do
2029
+ expect(tally).to eq(
2030
+ [1, 2] => 2,
2031
+ [1, 3] => 1
2032
+ )
2033
+ end
2034
+ end
2035
+
2036
+ context "when tallying an embeds_many field" do
2037
+
2038
+ let(:tally) do
2039
+ context2.tally("fanatics")
2040
+ end
2041
+
2042
+ it "returns the correct hash" do
2043
+ expect(tally).to eq(
2044
+ fans1 => 1,
2045
+ fans2 => 1,
2046
+ fans3 => 1,
2047
+ )
2048
+ end
2049
+ end
2050
+
2051
+ context "when tallying a field of type array" do
2052
+
2053
+ let(:tally) do
2054
+ context2.tally("genres")
2055
+ end
2056
+
2057
+ it "returns the correct hash" do
2058
+ expect(tally).to eq(
2059
+ [1, 2] => 2,
2060
+ [1, 3] => 1
2061
+ )
2062
+ end
2063
+ end
2064
+
2065
+ context "when tallying an element from an array of hashes" do
2066
+
2067
+ let(:tally) do
2068
+ context.tally("genres.x")
2069
+ end
2070
+
2071
+ it "returns the correct hash without the nil keys" do
2072
+ expect(tally).to eq(
2073
+ [1, 2] => 2,
2074
+ [1, 3] => 1
2075
+ )
2076
+ end
2077
+ end
2078
+
2079
+ context "when tallying an element from an array of hashes; with duplicate" do
2080
+
2081
+ let(:band4) { Band.new(origin: "tally", genres: [ { x: 1 }, {x: 1} ] ) }
2082
+
2083
+ let(:criteria) do
2084
+ Band.where(origin: "tally").all.tap do |crit|
2085
+ crit.documents = [ band1, band2, band3, band4 ]
2086
+ end
2087
+ end
2088
+
2089
+ let(:tally) do
2090
+ context.tally("genres.x")
2091
+ end
2092
+
2093
+ it "returns the correct hash without the nil keys" do
2094
+ expect(tally).to eq(
2095
+ [1, 2] => 2,
2096
+ [1, 3] => 1,
2097
+ [1, 1] => 1,
2098
+ )
2099
+ end
2100
+ end
2101
+
2102
+ context "when tallying an aliased field of type array" do
2103
+
2104
+ let(:person1) { Person.new(array: [ 1, 2 ]) }
2105
+ let(:person2) { Person.new(array: [ 1, 3 ]) }
2106
+
2107
+ let(:criteria) do
2108
+ Person.all.tap do |crit|
2109
+ crit.documents = [ person1, person2 ]
2110
+ end
2111
+ end
2112
+
2113
+ let(:tally) do
2114
+ context.tally("array")
2115
+ end
2116
+
2117
+ it "returns the correct hash" do
2118
+ expect(tally).to eq(
2119
+ [1, 2] => 1,
2120
+ [1, 3] => 1
2121
+ )
2122
+ end
2123
+ end
2124
+
2125
+ context "when going multiple levels deep in arrays" do
2126
+
2127
+ let(:tally) do
2128
+ context.tally("genres.y.z")
2129
+ end
2130
+
2131
+ it "returns the correct hash" do
2132
+ expect(tally).to eq(
2133
+ [1, 2] => 2,
2134
+ [1, 3] => 1
2135
+ )
2136
+ end
2137
+ end
2138
+
2139
+ context "when going multiple levels deep in an array" do
2140
+
2141
+ let(:tally) do
2142
+ context.tally("genres.y.z")
2143
+ end
2144
+
2145
+ it "returns the correct hash" do
2146
+ expect(tally).to eq(
2147
+ [1, 2] => 2,
2148
+ [1, 3] => 1
2149
+ )
2150
+ end
2151
+ end
2152
+
2153
+ context "when tallying deeply nested arrays/embedded associations" do
2154
+
2155
+ let(:person1) { Person.new(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ]) }
2156
+ let(:person2) { Person.new(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ]) }
2157
+ let(:person3) { Person.new(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))) ]) }
2158
+
2159
+ let(:criteria) do
2160
+ Person.all.tap do |crit|
2161
+ crit.documents = [ person1, person2, person3 ]
2162
+ end
2163
+ end
2164
+
2165
+ let(:tally) do
2166
+ context.tally("addresses.code.deepest.array.y.z")
2167
+ end
2168
+
2169
+ it "returns the correct hash" do
2170
+ expect(tally).to eq(
2171
+ [ [ 1, 2 ] ] => 2,
2172
+ [ [ 1, 3 ] ] => 1
2173
+ )
2174
+ end
2175
+ end
2176
+
2177
+ context "when tallying deeply nested arrays/embedded associations" do
2178
+
2179
+ let(:person1) do
2180
+ Person.new(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))),
2181
+ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
2182
+ end
2183
+
2184
+ let(:person2) do
2185
+ Person.new(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))),
2186
+ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
2187
+ end
2188
+
2189
+ let(:person3) do
2190
+ Person.new(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))),
2191
+ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))) ])
2192
+ end
2193
+
2194
+ let(:criteria) do
2195
+ Person.all.tap do |crit|
2196
+ crit.documents = [ person1, person2, person3 ]
2197
+ end
2198
+ end
2199
+
2200
+ let(:tally) do
2201
+ context.tally("addresses.code.deepest.array.y.z")
2202
+ end
2203
+
2204
+ it "returns the correct hash" do
2205
+ expect(tally).to eq(
2206
+ [ [ 1, 2 ], [ 1, 2 ] ] => 2,
2207
+ [ [ 1, 3 ], [ 1, 3 ] ] => 1
2208
+ )
2209
+ end
2210
+ end
2211
+
2212
+ context "when some keys are missing" do
2213
+
2214
+ let(:criteria) do
2215
+ Band.where(origin: "tally").all.tap do |crit|
2216
+ crit.documents = [ band1, band2, band3 ]
2217
+ 3.times{ crit.documents << Band.new(origin: "tally") }
2218
+ end
2219
+ end
2220
+
2221
+ let(:tally) do
2222
+ context.tally(:name)
2223
+ end
2224
+
2225
+ it "returns the correct hash" do
2226
+ expect(tally).to eq(
2227
+ "Depeche Mode" => 1,
2228
+ "New Order" => 1,
2229
+ "10,000 Maniacs" => 1,
2230
+ nil => 3
2231
+ )
2232
+ end
2233
+ end
2234
+
2235
+ context "when the first element is an embeds_one" do
2236
+ let(:person1) { Person.create!(name: Name.new(translations: [ Translation.new(language: 1), Translation.new(language: 2) ])) }
2237
+ let(:person2) { Person.create!(name: Name.new(translations: [ Translation.new(language: 1), Translation.new(language: 2) ])) }
2238
+ let(:person3) { Person.create!(name: Name.new(translations: [ Translation.new(language: 1), Translation.new(language: 3) ])) }
2239
+
2240
+ let(:criteria) do
2241
+ Person.all.tap do |crit|
2242
+ crit.documents = [ person1, person2, person3 ]
2243
+ end
2244
+ end
2245
+
2246
+ let(:tally) do
2247
+ context.tally("name.translations.language")
2248
+ end
2249
+
2250
+ it "returns the correct hash" do
2251
+ expect(tally).to eq(
2252
+ [1, 2] => 2,
2253
+ [1, 3] => 1
2254
+ )
988
2255
  end
989
2256
  end
990
2257
  end