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
@@ -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