mongoid 7.5.1 → 8.1.4

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 (429) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +3 -3
  4. data/README.md +6 -6
  5. data/Rakefile +25 -0
  6. data/lib/config/locales/en.yml +92 -43
  7. data/lib/mongoid/association/accessors.rb +40 -11
  8. data/lib/mongoid/association/bindable.rb +50 -2
  9. data/lib/mongoid/association/builders.rb +5 -3
  10. data/lib/mongoid/association/constrainable.rb +0 -1
  11. data/lib/mongoid/association/eager_loadable.rb +29 -7
  12. data/lib/mongoid/association/embedded/batchable.rb +54 -14
  13. data/lib/mongoid/association/embedded/cyclic.rb +1 -1
  14. data/lib/mongoid/association/embedded/embedded_in/binding.rb +24 -2
  15. data/lib/mongoid/association/embedded/embedded_in/buildable.rb +2 -2
  16. data/lib/mongoid/association/embedded/embedded_in/proxy.rb +4 -3
  17. data/lib/mongoid/association/embedded/embedded_in.rb +3 -2
  18. data/lib/mongoid/association/embedded/embeds_many/binding.rb +1 -0
  19. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +4 -3
  20. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +85 -46
  21. data/lib/mongoid/association/embedded/embeds_many.rb +2 -2
  22. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +19 -5
  23. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +24 -5
  24. data/lib/mongoid/association/embedded/embeds_one.rb +3 -3
  25. data/lib/mongoid/association/macros.rb +8 -1
  26. data/lib/mongoid/association/many.rb +11 -7
  27. data/lib/mongoid/association/nested/many.rb +5 -4
  28. data/lib/mongoid/association/nested/nested_buildable.rb +4 -4
  29. data/lib/mongoid/association/nested/one.rb +45 -7
  30. data/lib/mongoid/association/one.rb +2 -2
  31. data/lib/mongoid/association/options.rb +9 -9
  32. data/lib/mongoid/association/proxy.rb +15 -4
  33. data/lib/mongoid/association/referenced/auto_save.rb +4 -3
  34. data/lib/mongoid/association/referenced/belongs_to/binding.rb +1 -0
  35. data/lib/mongoid/association/referenced/belongs_to/buildable.rb +1 -1
  36. data/lib/mongoid/association/referenced/belongs_to/proxy.rb +5 -6
  37. data/lib/mongoid/association/referenced/belongs_to.rb +2 -2
  38. data/lib/mongoid/association/referenced/counter_cache.rb +10 -10
  39. data/lib/mongoid/association/referenced/eager.rb +2 -2
  40. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +70 -13
  41. data/lib/mongoid/association/referenced/has_and_belongs_to_many.rb +6 -3
  42. data/lib/mongoid/association/referenced/has_many/enumerable.rb +22 -30
  43. data/lib/mongoid/association/referenced/has_many/proxy.rb +40 -21
  44. data/lib/mongoid/association/referenced/has_many.rb +3 -3
  45. data/lib/mongoid/association/referenced/has_one/buildable.rb +1 -1
  46. data/lib/mongoid/association/referenced/has_one/nested_builder.rb +5 -5
  47. data/lib/mongoid/association/referenced/has_one/proxy.rb +9 -12
  48. data/lib/mongoid/association/referenced/has_one.rb +3 -3
  49. data/lib/mongoid/association/referenced/syncable.rb +4 -4
  50. data/lib/mongoid/association/reflections.rb +4 -4
  51. data/lib/mongoid/association/relatable.rb +44 -10
  52. data/lib/mongoid/association.rb +5 -5
  53. data/lib/mongoid/atomic/modifiers.rb +2 -2
  54. data/lib/mongoid/atomic/paths/embedded/many.rb +19 -0
  55. data/lib/mongoid/atomic.rb +7 -0
  56. data/lib/mongoid/attributes/dynamic.rb +4 -4
  57. data/lib/mongoid/attributes/nested.rb +6 -6
  58. data/lib/mongoid/attributes/processing.rb +37 -6
  59. data/lib/mongoid/attributes/projector.rb +2 -2
  60. data/lib/mongoid/attributes/readonly.rb +3 -3
  61. data/lib/mongoid/attributes.rb +51 -42
  62. data/lib/mongoid/cacheable.rb +2 -2
  63. data/lib/mongoid/changeable.rb +147 -14
  64. data/lib/mongoid/clients/options.rb +5 -1
  65. data/lib/mongoid/clients/sessions.rb +2 -14
  66. data/lib/mongoid/clients/storage_options.rb +2 -5
  67. data/lib/mongoid/clients/validators/storage.rb +3 -15
  68. data/lib/mongoid/collection_configurable.rb +58 -0
  69. data/lib/mongoid/composable.rb +2 -0
  70. data/lib/mongoid/config/defaults.rb +60 -0
  71. data/lib/mongoid/config/options.rb +3 -0
  72. data/lib/mongoid/config/validators/async_query_executor.rb +24 -0
  73. data/lib/mongoid/config/validators/client.rb +6 -6
  74. data/lib/mongoid/config/validators.rb +1 -0
  75. data/lib/mongoid/config.rb +158 -17
  76. data/lib/mongoid/contextual/aggregable/memory.rb +24 -16
  77. data/lib/mongoid/contextual/aggregable/mongo.rb +5 -5
  78. data/lib/mongoid/contextual/aggregable/none.rb +1 -1
  79. data/lib/mongoid/contextual/atomic.rb +1 -1
  80. data/lib/mongoid/contextual/geo_near.rb +7 -7
  81. data/lib/mongoid/contextual/map_reduce.rb +2 -2
  82. data/lib/mongoid/contextual/memory.rb +285 -58
  83. data/lib/mongoid/contextual/mongo/documents_loader.rb +177 -0
  84. data/lib/mongoid/contextual/mongo.rb +544 -333
  85. data/lib/mongoid/contextual/none.rb +193 -20
  86. data/lib/mongoid/contextual/queryable.rb +1 -1
  87. data/lib/mongoid/contextual.rb +14 -2
  88. data/lib/mongoid/copyable.rb +32 -8
  89. data/lib/mongoid/criteria/findable.rb +8 -5
  90. data/lib/mongoid/criteria/includable.rb +27 -22
  91. data/lib/mongoid/criteria/marshalable.rb +10 -2
  92. data/lib/mongoid/criteria/permission.rb +1 -1
  93. data/lib/mongoid/criteria/queryable/aggregable.rb +2 -2
  94. data/lib/mongoid/criteria/queryable/extensions/array.rb +3 -16
  95. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +25 -4
  96. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +2 -2
  97. data/lib/mongoid/criteria/queryable/extensions/date.rb +6 -1
  98. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +6 -1
  99. data/lib/mongoid/criteria/queryable/extensions/hash.rb +1 -17
  100. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -9
  101. data/lib/mongoid/criteria/queryable/extensions/object.rb +2 -1
  102. data/lib/mongoid/criteria/queryable/extensions/range.rb +13 -5
  103. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +3 -3
  104. data/lib/mongoid/criteria/queryable/extensions/set.rb +1 -1
  105. data/lib/mongoid/criteria/queryable/extensions/string.rb +4 -14
  106. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +4 -12
  107. data/lib/mongoid/criteria/queryable/extensions/time.rb +6 -1
  108. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +6 -1
  109. data/lib/mongoid/criteria/queryable/key.rb +4 -4
  110. data/lib/mongoid/criteria/queryable/mergeable.rb +1 -1
  111. data/lib/mongoid/criteria/queryable/optional.rb +11 -17
  112. data/lib/mongoid/criteria/queryable/options.rb +2 -2
  113. data/lib/mongoid/criteria/queryable/pipeline.rb +1 -1
  114. data/lib/mongoid/criteria/queryable/selectable.rb +47 -38
  115. data/lib/mongoid/criteria/queryable/selector.rb +93 -8
  116. data/lib/mongoid/criteria/queryable/smash.rb +40 -7
  117. data/lib/mongoid/criteria/queryable/storable.rb +1 -1
  118. data/lib/mongoid/criteria/queryable.rb +12 -7
  119. data/lib/mongoid/criteria/scopable.rb +2 -2
  120. data/lib/mongoid/criteria/translator.rb +45 -0
  121. data/lib/mongoid/criteria.rb +20 -40
  122. data/lib/mongoid/deprecable.rb +37 -0
  123. data/lib/mongoid/deprecation.rb +25 -0
  124. data/lib/mongoid/document.rb +135 -36
  125. data/lib/mongoid/equality.rb +8 -8
  126. data/lib/mongoid/errors/create_collection_failure.rb +33 -0
  127. data/lib/mongoid/errors/document_not_found.rb +10 -6
  128. data/lib/mongoid/errors/drop_collection_failure.rb +27 -0
  129. data/lib/mongoid/errors/immutable_attribute.rb +26 -0
  130. data/lib/mongoid/errors/invalid_async_query_executor.rb +25 -0
  131. data/lib/mongoid/errors/invalid_config_option.rb +1 -1
  132. data/lib/mongoid/errors/invalid_dependent_strategy.rb +1 -1
  133. data/lib/mongoid/errors/invalid_dot_dollar_assignment.rb +23 -0
  134. data/lib/mongoid/errors/invalid_field.rb +6 -2
  135. data/lib/mongoid/errors/invalid_field_type.rb +26 -0
  136. data/lib/mongoid/errors/invalid_global_executor_concurrency.rb +22 -0
  137. data/lib/mongoid/errors/invalid_relation.rb +1 -1
  138. data/lib/mongoid/errors/invalid_relation_option.rb +1 -1
  139. data/lib/mongoid/errors/invalid_session_use.rb +1 -1
  140. data/lib/mongoid/errors/invalid_storage_options.rb +1 -1
  141. data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
  142. data/lib/mongoid/errors/mongoid_error.rb +3 -3
  143. data/lib/mongoid/errors/nested_attributes_metadata_not_found.rb +1 -1
  144. data/lib/mongoid/errors/no_client_database.rb +1 -1
  145. data/lib/mongoid/errors/no_client_hosts.rb +1 -1
  146. data/lib/mongoid/errors/readonly_attribute.rb +1 -1
  147. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +1 -1
  148. data/lib/mongoid/errors/unknown_attribute.rb +1 -1
  149. data/lib/mongoid/errors.rb +6 -3
  150. data/lib/mongoid/extensions/array.rb +9 -7
  151. data/lib/mongoid/extensions/big_decimal.rb +33 -10
  152. data/lib/mongoid/extensions/binary.rb +42 -0
  153. data/lib/mongoid/extensions/boolean.rb +8 -2
  154. data/lib/mongoid/extensions/date.rb +26 -20
  155. data/lib/mongoid/extensions/date_time.rb +1 -1
  156. data/lib/mongoid/extensions/false_class.rb +1 -1
  157. data/lib/mongoid/extensions/float.rb +7 -4
  158. data/lib/mongoid/extensions/hash.rb +37 -8
  159. data/lib/mongoid/extensions/integer.rb +7 -4
  160. data/lib/mongoid/extensions/module.rb +1 -1
  161. data/lib/mongoid/extensions/object.rb +10 -8
  162. data/lib/mongoid/extensions/range.rb +41 -10
  163. data/lib/mongoid/extensions/regexp.rb +11 -4
  164. data/lib/mongoid/extensions/set.rb +11 -4
  165. data/lib/mongoid/extensions/string.rb +11 -22
  166. data/lib/mongoid/extensions/symbol.rb +4 -15
  167. data/lib/mongoid/extensions/time.rb +29 -16
  168. data/lib/mongoid/extensions/time_with_zone.rb +1 -2
  169. data/lib/mongoid/extensions/true_class.rb +1 -1
  170. data/lib/mongoid/extensions.rb +1 -0
  171. data/lib/mongoid/factory.rb +55 -7
  172. data/lib/mongoid/fields/foreign_key.rb +11 -4
  173. data/lib/mongoid/fields/localized.rb +19 -4
  174. data/lib/mongoid/fields/standard.rb +17 -7
  175. data/lib/mongoid/fields/validators/macro.rb +3 -9
  176. data/lib/mongoid/fields.rb +129 -20
  177. data/lib/mongoid/findable.rb +54 -24
  178. data/lib/mongoid/indexable/specification.rb +2 -2
  179. data/lib/mongoid/indexable/validators/options.rb +6 -2
  180. data/lib/mongoid/interceptable.rb +186 -16
  181. data/lib/mongoid/matchable.rb +1 -1
  182. data/lib/mongoid/matcher/eq_impl.rb +1 -1
  183. data/lib/mongoid/matcher/type.rb +1 -1
  184. data/lib/mongoid/matcher.rb +33 -13
  185. data/lib/mongoid/persistable/creatable.rb +19 -9
  186. data/lib/mongoid/persistable/deletable.rb +2 -2
  187. data/lib/mongoid/persistable/destroyable.rb +1 -1
  188. data/lib/mongoid/persistable/savable.rb +14 -2
  189. data/lib/mongoid/persistable/unsettable.rb +2 -2
  190. data/lib/mongoid/persistable/updatable.rb +69 -12
  191. data/lib/mongoid/persistable/upsertable.rb +21 -2
  192. data/lib/mongoid/persistable.rb +6 -3
  193. data/lib/mongoid/persistence_context.rb +63 -10
  194. data/lib/mongoid/query_cache.rb +13 -261
  195. data/lib/mongoid/railties/controller_runtime.rb +1 -1
  196. data/lib/mongoid/railties/database.rake +7 -2
  197. data/lib/mongoid/reloadable.rb +10 -8
  198. data/lib/mongoid/scopable.rb +15 -13
  199. data/lib/mongoid/selectable.rb +1 -2
  200. data/lib/mongoid/serializable.rb +10 -6
  201. data/lib/mongoid/shardable.rb +35 -11
  202. data/lib/mongoid/stateful.rb +57 -10
  203. data/lib/mongoid/tasks/database.rake +12 -0
  204. data/lib/mongoid/tasks/database.rb +20 -2
  205. data/lib/mongoid/threaded/lifecycle.rb +5 -5
  206. data/lib/mongoid/threaded.rb +42 -12
  207. data/lib/mongoid/timestamps/created.rb +1 -1
  208. data/lib/mongoid/timestamps/updated.rb +2 -2
  209. data/lib/mongoid/touchable.rb +2 -3
  210. data/lib/mongoid/traversable.rb +5 -4
  211. data/lib/mongoid/utils.rb +22 -0
  212. data/lib/mongoid/validatable/localizable.rb +1 -1
  213. data/lib/mongoid/validatable/macros.rb +5 -7
  214. data/lib/mongoid/validatable/presence.rb +2 -2
  215. data/lib/mongoid/validatable/uniqueness.rb +9 -8
  216. data/lib/mongoid/validatable.rb +9 -6
  217. data/lib/mongoid/version.rb +1 -1
  218. data/lib/mongoid/warnings.rb +19 -4
  219. data/lib/mongoid.rb +17 -3
  220. data/spec/config/mongoid.yml +16 -0
  221. data/spec/integration/app_spec.rb +10 -14
  222. data/spec/integration/associations/belongs_to_spec.rb +18 -0
  223. data/spec/integration/associations/embedded_spec.rb +15 -0
  224. data/spec/integration/associations/embeds_many_spec.rb +15 -2
  225. data/spec/integration/associations/embeds_one_spec.rb +18 -0
  226. data/spec/integration/associations/foreign_key_spec.rb +9 -0
  227. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +21 -0
  228. data/spec/integration/associations/has_one_spec.rb +97 -1
  229. data/spec/integration/associations/scope_option_spec.rb +1 -1
  230. data/spec/integration/callbacks_models.rb +132 -1
  231. data/spec/integration/callbacks_spec.rb +381 -4
  232. data/spec/integration/criteria/range_spec.rb +95 -1
  233. data/spec/integration/discriminator_key_spec.rb +118 -80
  234. data/spec/integration/dots_and_dollars_spec.rb +277 -0
  235. data/spec/integration/i18n_fallbacks_spec.rb +3 -32
  236. data/spec/integration/matcher_examples_spec.rb +20 -13
  237. data/spec/integration/matcher_operator_data/type_decimal.yml +3 -2
  238. data/spec/integration/matcher_operator_spec.rb +3 -5
  239. data/spec/integration/persistence/range_field_spec.rb +350 -0
  240. data/spec/mongoid/association/counter_cache_spec.rb +1 -1
  241. data/spec/mongoid/association/depending_spec.rb +9 -9
  242. data/spec/mongoid/association/eager_spec.rb +2 -1
  243. data/spec/mongoid/association/embedded/embedded_in/binding_spec.rb +2 -1
  244. data/spec/mongoid/association/embedded/embedded_in/buildable_spec.rb +54 -0
  245. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +96 -9
  246. data/spec/mongoid/association/embedded/embeds_many/buildable_spec.rb +112 -0
  247. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +311 -65
  248. data/spec/mongoid/association/embedded/embeds_many_models.rb +158 -0
  249. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +12 -0
  250. data/spec/mongoid/association/embedded/embeds_many_spec.rb +68 -0
  251. data/spec/mongoid/association/embedded/embeds_one/buildable_spec.rb +25 -0
  252. data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
  253. data/spec/mongoid/association/embedded/embeds_one_models.rb +19 -0
  254. data/spec/mongoid/association/embedded/embeds_one_spec.rb +28 -0
  255. data/spec/mongoid/association/referenced/belongs_to/binding_spec.rb +2 -1
  256. data/spec/mongoid/association/referenced/belongs_to/buildable_spec.rb +54 -0
  257. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +15 -0
  258. data/spec/mongoid/association/referenced/belongs_to_models.rb +11 -0
  259. data/spec/mongoid/association/referenced/belongs_to_spec.rb +4 -20
  260. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +215 -228
  261. data/spec/mongoid/association/referenced/has_and_belongs_to_many_models.rb +25 -0
  262. data/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb +35 -2
  263. data/spec/mongoid/association/referenced/has_many/buildable_spec.rb +109 -0
  264. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +2 -56
  265. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +235 -177
  266. data/spec/mongoid/association/referenced/has_many_models.rb +3 -1
  267. data/spec/mongoid/association/referenced/has_many_spec.rb +25 -0
  268. data/spec/mongoid/association/referenced/has_one/buildable_spec.rb +2 -2
  269. data/spec/mongoid/association/referenced/has_one/proxy_spec.rb +107 -1
  270. data/spec/mongoid/association/referenced/has_one_models.rb +16 -0
  271. data/spec/mongoid/association/syncable_spec.rb +15 -1
  272. data/spec/mongoid/atomic/paths_spec.rb +0 -14
  273. data/spec/mongoid/attributes/nested_spec.rb +80 -11
  274. data/spec/mongoid/attributes/nested_spec_models.rb +48 -0
  275. data/spec/mongoid/attributes/projector_spec.rb +1 -5
  276. data/spec/mongoid/attributes_spec.rb +554 -33
  277. data/spec/mongoid/cacheable_spec.rb +3 -3
  278. data/spec/mongoid/changeable_spec.rb +429 -37
  279. data/spec/mongoid/clients/factory_spec.rb +23 -30
  280. data/spec/mongoid/clients/sessions_spec.rb +0 -38
  281. data/spec/mongoid/clients_spec.rb +179 -15
  282. data/spec/mongoid/collection_configurable_spec.rb +158 -0
  283. data/spec/mongoid/config/defaults_spec.rb +160 -0
  284. data/spec/mongoid/config_spec.rb +220 -30
  285. data/spec/mongoid/contextual/aggregable/memory_spec.rb +396 -158
  286. data/spec/mongoid/contextual/aggregable/memory_table.yml +88 -0
  287. data/spec/mongoid/contextual/aggregable/memory_table_spec.rb +62 -0
  288. data/spec/mongoid/contextual/map_reduce_spec.rb +2 -16
  289. data/spec/mongoid/contextual/memory_spec.rb +850 -88
  290. data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
  291. data/spec/mongoid/contextual/mongo_spec.rb +2307 -1105
  292. data/spec/mongoid/contextual/none_spec.rb +60 -21
  293. data/spec/mongoid/copyable_spec.rb +453 -11
  294. data/spec/mongoid/criteria/findable_spec.rb +86 -210
  295. data/spec/mongoid/criteria/includable_spec.rb +1492 -0
  296. data/spec/mongoid/criteria/includable_spec_models.rb +54 -0
  297. data/spec/mongoid/criteria/marshalable_spec.rb +18 -1
  298. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +7 -19
  299. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +134 -26
  300. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +11 -0
  301. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +11 -0
  302. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +0 -15
  303. data/spec/mongoid/criteria/queryable/extensions/numeric_spec.rb +73 -7
  304. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -69
  305. data/spec/mongoid/criteria/queryable/extensions/symbol_spec.rb +0 -59
  306. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +11 -0
  307. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +11 -0
  308. data/spec/mongoid/criteria/queryable/optional_spec.rb +15 -484
  309. data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
  310. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +469 -0
  311. data/spec/mongoid/criteria/queryable/selectable_spec.rb +78 -86
  312. data/spec/mongoid/criteria/queryable/selector_spec.rb +90 -5
  313. data/spec/mongoid/criteria/queryable/storable_spec.rb +72 -0
  314. data/spec/mongoid/criteria/translator_spec.rb +132 -0
  315. data/spec/mongoid/criteria_projection_spec.rb +1 -5
  316. data/spec/mongoid/criteria_spec.rb +469 -1205
  317. data/spec/mongoid/document_fields_spec.rb +173 -24
  318. data/spec/mongoid/document_spec.rb +32 -41
  319. data/spec/mongoid/errors/document_not_found_spec.rb +29 -2
  320. data/spec/mongoid/errors/invalid_field_spec.rb +1 -1
  321. data/spec/mongoid/errors/invalid_field_type_spec.rb +55 -0
  322. data/spec/mongoid/errors/mongoid_error_spec.rb +3 -1
  323. data/spec/mongoid/errors/no_environment_spec.rb +3 -3
  324. data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
  325. data/spec/mongoid/errors/too_many_nested_attribute_records_spec.rb +1 -1
  326. data/spec/mongoid/extensions/array_spec.rb +16 -2
  327. data/spec/mongoid/extensions/big_decimal_spec.rb +712 -212
  328. data/spec/mongoid/extensions/binary_spec.rb +44 -9
  329. data/spec/mongoid/extensions/boolean_spec.rb +68 -82
  330. data/spec/mongoid/extensions/date_class_mongoize_spec.rb +7 -3
  331. data/spec/mongoid/extensions/date_spec.rb +71 -1
  332. data/spec/mongoid/extensions/date_time_spec.rb +15 -9
  333. data/spec/mongoid/extensions/float_spec.rb +53 -74
  334. data/spec/mongoid/extensions/hash_spec.rb +33 -3
  335. data/spec/mongoid/extensions/integer_spec.rb +50 -64
  336. data/spec/mongoid/extensions/range_spec.rb +255 -54
  337. data/spec/mongoid/extensions/regexp_spec.rb +58 -33
  338. data/spec/mongoid/extensions/set_spec.rb +106 -0
  339. data/spec/mongoid/extensions/string_spec.rb +53 -25
  340. data/spec/mongoid/extensions/symbol_spec.rb +18 -25
  341. data/spec/mongoid/extensions/time_spec.rb +639 -106
  342. data/spec/mongoid/extensions/time_with_zone_spec.rb +24 -83
  343. data/spec/mongoid/factory_spec.rb +61 -1
  344. data/spec/mongoid/fields/localized_spec.rb +80 -37
  345. data/spec/mongoid/fields_spec.rb +500 -84
  346. data/spec/mongoid/findable_spec.rb +450 -58
  347. data/spec/mongoid/indexable/specification_spec.rb +2 -2
  348. data/spec/mongoid/indexable_spec.rb +55 -30
  349. data/spec/mongoid/interceptable_spec.rb +824 -22
  350. data/spec/mongoid/interceptable_spec_models.rb +235 -4
  351. data/spec/mongoid/matcher/extract_attribute_spec.rb +1 -5
  352. data/spec/mongoid/mongoizable_spec.rb +285 -0
  353. data/spec/mongoid/persistable/creatable_spec.rb +2 -2
  354. data/spec/mongoid/persistable/deletable_spec.rb +28 -8
  355. data/spec/mongoid/persistable/destroyable_spec.rb +28 -8
  356. data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
  357. data/spec/mongoid/persistable/logical_spec.rb +37 -0
  358. data/spec/mongoid/persistable/poppable_spec.rb +36 -0
  359. data/spec/mongoid/persistable/pullable_spec.rb +72 -0
  360. data/spec/mongoid/persistable/pushable_spec.rb +72 -0
  361. data/spec/mongoid/persistable/renamable_spec.rb +36 -0
  362. data/spec/mongoid/persistable/savable_spec.rb +96 -0
  363. data/spec/mongoid/persistable/settable_spec.rb +37 -0
  364. data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
  365. data/spec/mongoid/persistable/updatable_spec.rb +20 -28
  366. data/spec/mongoid/persistable/upsertable_spec.rb +89 -1
  367. data/spec/mongoid/persistence_context_spec.rb +57 -58
  368. data/spec/mongoid/query_cache_middleware_spec.rb +0 -18
  369. data/spec/mongoid/query_cache_spec.rb +56 -215
  370. data/spec/mongoid/reloadable_spec.rb +83 -6
  371. data/spec/mongoid/scopable_spec.rb +91 -1
  372. data/spec/mongoid/serializable_spec.rb +9 -30
  373. data/spec/mongoid/shardable_models.rb +14 -0
  374. data/spec/mongoid/shardable_spec.rb +157 -51
  375. data/spec/mongoid/stateful_spec.rb +150 -8
  376. data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
  377. data/spec/mongoid/tasks/database_spec.rb +127 -0
  378. data/spec/mongoid/timestamps_spec.rb +392 -4
  379. data/spec/mongoid/timestamps_spec_models.rb +67 -0
  380. data/spec/mongoid/touchable_spec.rb +390 -2
  381. data/spec/mongoid/touchable_spec_models.rb +14 -8
  382. data/spec/mongoid/traversable_spec.rb +13 -35
  383. data/spec/mongoid/validatable/presence_spec.rb +1 -1
  384. data/spec/mongoid/validatable/uniqueness_spec.rb +58 -31
  385. data/spec/mongoid/warnings_spec.rb +35 -0
  386. data/spec/mongoid_spec.rb +34 -10
  387. data/spec/rails/controller_extension/controller_runtime_spec.rb +2 -2
  388. data/spec/rails/mongoid_spec.rb +4 -16
  389. data/spec/shared/lib/mrss/docker_runner.rb +8 -0
  390. data/spec/shared/lib/mrss/lite_constraints.rb +10 -2
  391. data/spec/shared/lib/mrss/server_version_registry.rb +16 -23
  392. data/spec/shared/lib/mrss/utils.rb +28 -6
  393. data/spec/shared/share/Dockerfile.erb +36 -40
  394. data/spec/shared/shlib/server.sh +32 -8
  395. data/spec/shared/shlib/set_env.sh +4 -4
  396. data/spec/spec_helper.rb +5 -0
  397. data/spec/support/constraints.rb +24 -0
  398. data/spec/support/immutable_ids.rb +118 -0
  399. data/spec/support/macros.rb +78 -0
  400. data/spec/support/models/artist.rb +0 -1
  401. data/spec/support/models/augmentation.rb +12 -0
  402. data/spec/support/models/band.rb +4 -0
  403. data/spec/support/models/book.rb +1 -0
  404. data/spec/support/models/building.rb +2 -0
  405. data/spec/support/models/catalog.rb +24 -0
  406. data/spec/support/models/circus.rb +3 -0
  407. data/spec/support/models/cover.rb +10 -0
  408. data/spec/support/models/fanatic.rb +8 -0
  409. data/spec/support/models/implant.rb +9 -0
  410. data/spec/support/models/label.rb +2 -0
  411. data/spec/support/models/passport.rb +9 -0
  412. data/spec/support/models/person.rb +2 -0
  413. data/spec/support/models/player.rb +2 -0
  414. data/spec/support/models/powerup.rb +12 -0
  415. data/spec/support/models/product.rb +1 -0
  416. data/spec/support/models/purse.rb +9 -0
  417. data/spec/support/models/registry.rb +1 -0
  418. data/spec/support/models/school.rb +14 -0
  419. data/spec/support/models/shield.rb +18 -0
  420. data/spec/support/models/student.rb +14 -0
  421. data/spec/support/models/weapon.rb +12 -0
  422. data.tar.gz.sig +0 -0
  423. metadata +722 -640
  424. metadata.gz.sig +0 -0
  425. data/lib/mongoid/errors/eager_load.rb +0 -23
  426. data/lib/mongoid/errors/invalid_value.rb +0 -17
  427. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -60
  428. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -60
  429. data/spec/mongoid/errors/eager_load_spec.rb +0 -31
@@ -44,39 +44,6 @@ describe Mongoid::Contextual::Mongo do
44
44
  end
45
45
  end
46
46
 
47
- describe "#cached?" do
48
-
49
- context "when the criteria is cached" do
50
-
51
- let(:criteria) do
52
- Band.all.cache
53
- end
54
-
55
- let(:context) do
56
- described_class.new(criteria)
57
- end
58
-
59
- it "returns true" do
60
- expect(context).to be_cached
61
- end
62
- end
63
-
64
- context "when the criteria is not cached" do
65
-
66
- let(:criteria) do
67
- Band.all
68
- end
69
-
70
- let(:context) do
71
- described_class.new(criteria)
72
- end
73
-
74
- it "returns false" do
75
- expect(context).to_not be_cached
76
- end
77
- end
78
- end
79
-
80
47
  describe "#count" do
81
48
 
82
49
  let!(:depeche) do
@@ -102,15 +69,17 @@ describe Mongoid::Contextual::Mongo do
102
69
  end
103
70
  end
104
71
 
105
- context "when context is cached" do
72
+ context "when the query cache is enabled" do
73
+ query_cache_enabled
106
74
 
107
75
  let(:context) do
108
- described_class.new(criteria.cache)
76
+ described_class.new(criteria)
109
77
  end
110
78
 
111
- it "returns the count cached value after first call" do
112
- expect(context.view).to receive(:count_documents).once.and_return(1)
113
- 2.times { expect(context.count).to eq(1) }
79
+ it "only executes the count query once" do
80
+ expect_query(1) do
81
+ 2.times { expect(context.count).to eq(1) }
82
+ end
114
83
  end
115
84
  end
116
85
 
@@ -189,6 +158,16 @@ describe Mongoid::Contextual::Mongo do
189
158
  end
190
159
  end
191
160
  end
161
+
162
+ context 'when for_js is present' do
163
+ let(:context) do
164
+ Band.for_js('this.name == "Depeche Mode"')
165
+ end
166
+
167
+ it 'counts the expected records' do
168
+ expect(context.count).to eq(1)
169
+ end
170
+ end
192
171
  end
193
172
 
194
173
  describe "#estimated_count" do
@@ -217,16 +196,18 @@ describe Mongoid::Contextual::Mongo do
217
196
  end
218
197
  end
219
198
 
220
- context "when context is cached" do
199
+ context "when the query cache is enabled" do
200
+ query_cache_enabled
221
201
 
222
202
  let(:context) do
223
- described_class.new(criteria.cache)
203
+ described_class.new(criteria)
224
204
  end
225
205
 
226
- it "returns the count cached value after first call" do
227
- expect(context.view).to receive(:estimated_document_count).once.and_return(1)
228
- 2.times do
229
- context.estimated_count
206
+ it "the results are not cached" do
207
+ expect_query(2) do
208
+ 2.times do
209
+ context.estimated_count
210
+ end
230
211
  end
231
212
  end
232
213
  end
@@ -561,8 +542,22 @@ describe Mongoid::Contextual::Mongo do
561
542
  context "when legacy_pluck_distinct is set" do
562
543
  config_override :legacy_pluck_distinct, true
563
544
 
564
- it "returns the non-demongoized distinct field values" do
565
- expect(context.distinct(:sales).sort).to eq([ "1E2", "2E3" ])
545
+ context 'when storing BigDecimal as string' do
546
+ config_override :map_big_decimal_to_decimal128, false
547
+
548
+ it "returns the non-demongoized distinct field values" do
549
+ expect(context.distinct(:sales).sort).to eq([ "1E2", "2E3" ])
550
+ end
551
+ end
552
+
553
+ context 'when storing BigDecimal as decimal128' do
554
+ config_override :map_big_decimal_to_decimal128, true
555
+ min_bson_version '4.15.0'
556
+ max_bson_version '4.99.99'
557
+
558
+ it "returns the non-demongoized distinct field values" do
559
+ expect(context.distinct(:sales).sort).to eq([ BSON::Decimal128.new("1E2"), BSON::Decimal128.new("2E3") ])
560
+ end
566
561
  end
567
562
  end
568
563
 
@@ -576,6 +571,8 @@ describe Mongoid::Contextual::Mongo do
576
571
  end
577
572
 
578
573
  context "when getting a localized field" do
574
+ with_default_i18n_configs
575
+
579
576
  before do
580
577
  I18n.locale = :en
581
578
  d = Dictionary.create!(description: 'english-text')
@@ -584,10 +581,6 @@ describe Mongoid::Contextual::Mongo do
584
581
  d.save!
585
582
  end
586
583
 
587
- after do
588
- I18n.locale = :en
589
- end
590
-
591
584
  let(:criteria) do
592
585
  Dictionary.criteria
593
586
  end
@@ -680,16 +673,10 @@ describe Mongoid::Contextual::Mongo do
680
673
 
681
674
  context 'when fallbacks are enabled with a locale list' do
682
675
  require_fallbacks
676
+ with_default_i18n_configs
683
677
 
684
- around(:all) do |example|
685
- prev_fallbacks = I18n.fallbacks.dup
678
+ before do
686
679
  I18n.fallbacks[:he] = [ :en ]
687
- example.run
688
- I18n.fallbacks = prev_fallbacks
689
- end
690
-
691
- after do
692
- I18n.locale = :en
693
680
  end
694
681
 
695
682
  let(:distinct) do
@@ -709,7 +696,7 @@ describe Mongoid::Contextual::Mongo do
709
696
 
710
697
  it "correctly uses the fallback" do
711
698
  I18n.locale = :en
712
- d = Dictionary.create!(description: 'english-text')
699
+ Dictionary.create!(description: 'english-text')
713
700
  I18n.locale = :he
714
701
  distinct.should == "english-text"
715
702
  end
@@ -717,6 +704,8 @@ describe Mongoid::Contextual::Mongo do
717
704
  end
718
705
 
719
706
  context "when the localized field is embedded" do
707
+ with_default_i18n_configs
708
+
720
709
  before do
721
710
  p = Passport.new
722
711
  I18n.locale = :en
@@ -727,10 +716,6 @@ describe Mongoid::Contextual::Mongo do
727
716
  Person.create!(passport: p, employer_id: 12345)
728
717
  end
729
718
 
730
- after do
731
- I18n.locale = :en
732
- end
733
-
734
719
  let(:criteria) do
735
720
  Person.where(employer_id: 12345)
736
721
  end
@@ -794,9 +779,11 @@ describe Mongoid::Contextual::Mongo do
794
779
 
795
780
  context "when legacy_pluck_distinct is set" do
796
781
  config_override :legacy_pluck_distinct, true
782
+ config_override :map_big_decimal_to_decimal128, true
783
+ max_bson_version '4.99.99'
797
784
 
798
785
  it "returns the distinct matching fields" do
799
- expect(context.distinct("label.sales")).to eq([ "1E2" ])
786
+ expect(context.distinct("label.sales")).to eq([ BSON::Decimal128.new('1E+2') ])
800
787
  end
801
788
  end
802
789
 
@@ -809,484 +796,860 @@ describe Mongoid::Contextual::Mongo do
809
796
  end
810
797
  end
811
798
 
812
- describe "#each" do
799
+ describe "#tally" do
800
+ let(:fans1) { [ Fanatic.new(age:1), Fanatic.new(age:2) ] }
801
+ let(:fans2) { [ Fanatic.new(age:1), Fanatic.new(age:2) ] }
802
+ let(:fans3) { [ Fanatic.new(age:1), Fanatic.new(age:3) ] }
813
803
 
814
- before do
815
- Band.create!(name: "Depeche Mode")
816
- end
804
+ let(:genres1) { [ { x: 1, y: { z: 1 } }, { x: 2, y: { z: 2 } }, { y: 3 } ]}
805
+ let(:genres2) { [ { x: 1, y: { z: 1 } }, { x: 2, y: { z: 2 } }, { y: 4 } ]}
806
+ let(:genres3) { [ { x: 1, y: { z: 1 } }, { x: 3, y: { z: 3 } }, { y: 5 } ]}
817
807
 
818
- let(:criteria) do
819
- Band.where(name: "Depeche Mode")
820
- end
808
+ let(:label1) { Label.new(name: "Atlantic") }
809
+ let(:label2) { Label.new(name: "Atlantic") }
810
+ let(:label3) { Label.new(name: "Columbia") }
821
811
 
822
- let(:context) do
823
- described_class.new(criteria)
812
+ before do
813
+ Band.create!(origin: "tally", name: "Depeche Mode", years: 30, sales: "1E2", label: label1, genres: genres1)
814
+ Band.create!(origin: "tally", name: "New Order", years: 30, sales: "2E3", label: label2, genres: genres2)
815
+ Band.create!(origin: "tally", name: "10,000 Maniacs", years: 30, sales: "1E2", label: label3, genres: genres3)
816
+ Band.create!(origin: "tally2", fanatics: fans1, genres: [1, 2])
817
+ Band.create!(origin: "tally2", fanatics: fans2, genres: [1, 2])
818
+ Band.create!(origin: "tally2", fanatics: fans3, genres: [1, 3])
824
819
  end
825
820
 
826
- context 'when the criteria has a collation' do
827
- min_server_version '3.4'
821
+ let(:criteria) { Band.where(origin: "tally") }
828
822
 
829
- let(:criteria) do
830
- Band.where(name: "DEPECHE MODE").collation(locale: 'en_US', strength: 2)
823
+ context "when tallying a string" do
824
+ let(:tally) do
825
+ criteria.tally(:name)
831
826
  end
832
827
 
833
- it "yields mongoid documents to the block" do
834
- context.each do |doc|
835
- expect(doc).to be_a(Mongoid::Document)
836
- end
828
+ it "returns the correct hash" do
829
+ expect(tally).to eq("Depeche Mode" => 1, "New Order" => 1, "10,000 Maniacs" => 1)
837
830
  end
831
+ end
838
832
 
839
- it "iterates over the matching documents" do
840
- context.each do |doc|
841
- expect(doc.name).to eq("Depeche Mode")
842
- end
833
+ context "using an aliased field" do
834
+ let(:tally) do
835
+ criteria.tally(:years)
843
836
  end
844
837
 
845
- it "returns self" do
846
- expect(context.each{}).to be(context)
838
+ it "returns the correct hash" do
839
+ expect(tally).to eq(30 => 3)
847
840
  end
848
841
  end
849
842
 
850
- context "when providing a block" do
851
-
852
- it "yields mongoid documents to the block" do
853
- context.each do |doc|
854
- expect(doc).to be_a(Mongoid::Document)
855
- end
856
- end
857
-
858
- it "iterates over the matching documents" do
859
- context.each do |doc|
860
- expect(doc.name).to eq("Depeche Mode")
861
- end
843
+ context "when tallying a demongoizable field" do
844
+ let(:tally) do
845
+ criteria.tally(:sales)
862
846
  end
863
847
 
864
- it "returns self" do
865
- expect(context.each{}).to be(context)
848
+ it "returns the correct hash" do
849
+ expect(tally).to eq(BigDecimal("1E2") => 2, BigDecimal("2E3") => 1)
866
850
  end
867
851
  end
868
852
 
869
- context "when no block is provided" do
853
+ context "when tallying a localized field" do
854
+ with_default_i18n_configs
870
855
 
871
- let(:enum) do
872
- context.each
856
+ before do
857
+ I18n.locale = :en
858
+ d1 = Dictionary.create!(description: 'en1')
859
+ d2 = Dictionary.create!(description: 'en1')
860
+ d3 = Dictionary.create!(description: 'en1')
861
+ d4 = Dictionary.create!(description: 'en2')
862
+ I18n.locale = :de
863
+ d1.description = 'de1'
864
+ d2.description = 'de1'
865
+ d3.description = 'de2'
866
+ d4.description = 'de3'
867
+ d1.save!
868
+ d2.save!
869
+ d3.save!
870
+ d4.save!
871
+ I18n.locale = :en
873
872
  end
874
873
 
875
- it "returns an enumerator" do
876
- expect(enum).to be_a(Enumerator)
874
+ context "when getting the demongoized field" do
875
+ let(:tallied) do
876
+ Dictionary.tally(:description)
877
+ end
878
+
879
+ it "returns the translation for the current locale" do
880
+ expect(tallied).to eq("en1" => 3, "en2" => 1)
881
+ end
877
882
  end
878
883
 
879
- context "when iterating over the enumerator" do
884
+ context "when getting a specific locale" do
885
+ let(:tallied) do
886
+ Dictionary.tally("description.de")
887
+ end
880
888
 
881
- context "when iterating with each" do
889
+ it "returns the translation for the the specific locale" do
890
+ expect(tallied).to eq("de1" => 2, "de2" => 1, "de3" => 1)
891
+ end
892
+ end
882
893
 
883
- it "yields mongoid documents to the block" do
884
- enum.each do |doc|
885
- expect(doc).to be_a(Mongoid::Document)
886
- end
887
- end
894
+ context "when getting the full hash" do
895
+ let(:tallied) do
896
+ Dictionary.tally("description_translations")
888
897
  end
889
898
 
890
- context "when iterating with next" do
899
+ it "returns the correct hash" do
900
+ expect(tallied).to eq(
901
+ {"de" => "de1", "en" => "en1" } => 2,
902
+ {"de" => "de2", "en" => "en1" } => 1,
903
+ {"de" => "de3", "en" => "en2" } => 1
904
+ )
905
+ end
906
+ end
907
+ end
891
908
 
892
- before do
893
- 10.times { |i| Band.create!(name: "Test #{i}") }
894
- end
909
+ context "when tallying an embedded localized field" do
910
+ with_default_i18n_configs
895
911
 
896
- let(:criteria) do
897
- Band.batch_size(5)
898
- end
912
+ before do
913
+ I18n.locale = :en
914
+ address1a = Address.new(name: "en1")
915
+ address1b = Address.new(name: "en2")
916
+ address2a = Address.new(name: "en1")
917
+ address2b = Address.new(name: "en3")
918
+ I18n.locale = :de
919
+ address1a.name = "de1"
920
+ address1b.name = "de2"
921
+ address2a.name = "de1"
922
+ address2b.name = "de3"
923
+ Person.create!(addresses: [ address1a, address1b ])
924
+ Person.create!(addresses: [ address2a, address2b ])
925
+ I18n.locale = :en
926
+ end
899
927
 
900
- it "yields mongoid documents" do
901
- expect(enum.next).to be_a(Mongoid::Document)
902
- end
928
+ context "when getting the demongoized field" do
929
+ let(:tallied) do
930
+ Person.tally("addresses.name")
931
+ end
903
932
 
904
- it "does not load all documents" do
905
- subscriber = Mrss::EventSubscriber.new
906
- context.view.client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
933
+ it "returns the translation for the current locale" do
934
+ expect(tallied).to eq(
935
+ [ "en1", "en2" ] => 1,
936
+ [ "en1", "en3" ] => 1,
937
+ )
938
+ end
939
+ end
907
940
 
908
- enum.next
941
+ context "when getting a specific locale" do
942
+ let(:tallied) do
943
+ Person.tally("addresses.name.de")
944
+ end
909
945
 
910
- find_events = subscriber.all_events.select do |evt|
911
- evt.command_name == 'find'
912
- end
913
- expect(find_events.length).to be(2)
914
- get_more_events = subscriber.all_events.select do |evt|
915
- evt.command_name == 'getMore'
916
- end
917
- expect(get_more_events.length).to be(0)
918
- ensure
919
- context.view.client.unsubscribe(Mongo::Monitoring::COMMAND, subscriber)
920
- end
946
+ it "returns the translation for the the specific locale" do
947
+ expect(tallied).to eq(
948
+ [ "de1", "de2" ] => 1,
949
+ [ "de1", "de3" ] => 1,
950
+ )
921
951
  end
922
952
  end
923
- end
924
953
 
925
- context 'when the criteria has a parent document' do
954
+ context "when getting the full hash" do
955
+ let(:tallied) do
956
+ Person.tally("addresses.name_translations")
957
+ end
926
958
 
927
- before do
928
- Post.create!(person: person)
929
- Post.create!(person: person)
930
- Post.create!(person: person)
959
+ it "returns the correct hash" do
960
+ expect(tallied).to eq(
961
+ [{ "de" => "de1", "en" => "en1" }, { "de" => "de2", "en" => "en2" }] => 1,
962
+ [{ "de" => "de1", "en" => "en1" }, { "de" => "de3", "en" => "en3" }] => 1,
963
+ )
964
+ end
931
965
  end
932
966
 
933
- let(:person) do
934
- Person.new
967
+ end
968
+
969
+ context "when tallying an embedded field" do
970
+ let(:tally) do
971
+ criteria.tally("label.name")
935
972
  end
936
973
 
937
- let(:criteria) do
938
- person.posts.all
974
+ it "returns the correct hash" do
975
+ expect(tally).to eq("Atlantic" => 2, "Columbia" => 1)
939
976
  end
977
+ end
940
978
 
941
- let(:persons) do
942
- criteria.collect(&:person)
979
+ context "when tallying an element in an embeds_many field" do
980
+ let(:criteria) { Band.where(origin: "tally2") }
981
+
982
+ let(:tally) do
983
+ criteria.tally("fanatics.age")
943
984
  end
944
985
 
945
- it 'sets the same parent object on each related object' do
946
- expect(persons.uniq.size).to eq(1)
986
+ it "returns the correct hash" do
987
+ expect(tally).to eq(
988
+ [1, 2] => 2,
989
+ [1, 3] => 1
990
+ )
947
991
  end
948
992
  end
949
- end
950
993
 
951
- describe "#eager_load" do
994
+ context "when tallying an embeds_many field" do
995
+ let(:criteria) { Band.where(origin: "tally2") }
952
996
 
953
- let(:criteria) do
954
- Person.includes(:game)
955
- end
997
+ let(:tally) do
998
+ criteria.tally("fanatics")
999
+ end
956
1000
 
957
- let(:context) do
958
- described_class.new(criteria)
1001
+ it "returns the correct hash" do
1002
+ expect(tally).to eq(
1003
+ fans1.map(&:attributes) => 1,
1004
+ fans2.map(&:attributes) => 1,
1005
+ fans3.map(&:attributes) => 1,
1006
+ )
1007
+ end
959
1008
  end
960
1009
 
961
- context "when no documents are returned" do
1010
+ context "when tallying a field of type array" do
1011
+ let(:criteria) { Band.where(origin: "tally2") }
962
1012
 
963
- let(:game_association) do
964
- Person.reflect_on_association(:game)
1013
+ let(:tally) do
1014
+ criteria.tally("genres")
965
1015
  end
966
1016
 
967
- it "does not make any additional database queries" do
968
- expect(game_association).to receive(:eager_load).never
969
- context.send(:eager_load, [])
1017
+ it "returns the correct hash" do
1018
+ expect(tally).to eq(
1019
+ [1, 2] => 2,
1020
+ [1, 3] => 1
1021
+ )
970
1022
  end
971
1023
  end
972
- end
973
1024
 
974
- describe "#exists?" do
1025
+ context "when tallying an element from an array of hashes" do
1026
+ let(:criteria) { Band.where(origin: "tally") }
975
1027
 
976
- before do
977
- Band.create!(name: "Depeche Mode")
1028
+ let(:tally) do
1029
+ criteria.tally("genres.x")
1030
+ end
1031
+
1032
+ it "returns the correct hash without the nil keys" do
1033
+ expect(tally).to eq(
1034
+ [1, 2] => 2,
1035
+ [1, 3] => 1
1036
+ )
1037
+ end
978
1038
  end
979
1039
 
980
- context "when the count is zero" do
1040
+ context "when tallying an element from an array of hashes; with duplicate" do
981
1041
 
982
- let(:criteria) do
983
- Band.where(name: "New Order")
1042
+ before do
1043
+ Band.create!(origin: "tally", genres: [ { x: 1 }, {x: 1} ] )
984
1044
  end
985
1045
 
986
- let(:context) do
987
- described_class.new(criteria)
1046
+ let(:criteria) { Band.where(origin: "tally") }
1047
+
1048
+ let(:tally) do
1049
+ criteria.tally("genres.x")
988
1050
  end
989
1051
 
990
- it "returns false" do
991
- expect(context).to_not be_exists
1052
+ it "returns the correct hash without the nil keys" do
1053
+ expect(tally).to eq(
1054
+ [1, 2] => 2,
1055
+ [1, 3] => 1,
1056
+ [1, 1] => 1,
1057
+ )
992
1058
  end
993
1059
  end
994
1060
 
995
- context "when the count is greater than zero" do
1061
+ context "when tallying an aliased field of type array" do
996
1062
 
997
- let(:criteria) do
998
- Band.where(name: "Depeche Mode")
1063
+ before do
1064
+ Person.create!(array: [ 1, 2 ])
1065
+ Person.create!(array: [ 1, 3 ])
999
1066
  end
1000
1067
 
1001
- let(:context) do
1002
- described_class.new(criteria)
1068
+ let(:tally) do
1069
+ Person.tally("array")
1003
1070
  end
1004
1071
 
1005
- it "returns true" do
1006
- expect(context).to be_exists
1072
+ it "returns the correct hash" do
1073
+ expect(tally).to eq(
1074
+ [1, 2] => 1,
1075
+ [1, 3] => 1
1076
+ )
1007
1077
  end
1008
1078
  end
1009
1079
 
1010
- context "when caching is not enabled" do
1080
+ context "when going multiple levels deep in arrays" do
1081
+ let(:criteria) { Band.where(origin: "tally") }
1011
1082
 
1012
- let(:criteria) do
1013
- Band.where(name: "Depeche Mode")
1083
+ let(:tally) do
1084
+ criteria.tally("genres.y.z")
1014
1085
  end
1015
1086
 
1016
- let(:context) do
1017
- described_class.new(criteria)
1087
+ it "returns the correct hash" do
1088
+ expect(tally).to eq(
1089
+ [1, 2] => 2,
1090
+ [1, 3] => 1
1091
+ )
1018
1092
  end
1093
+ end
1019
1094
 
1020
- context "when exists? already called" do
1095
+ context "when going multiple levels deep in an array" do
1096
+ let(:criteria) { Band.where(origin: "tally") }
1021
1097
 
1022
- before do
1023
- context.exists?
1024
- end
1098
+ let(:tally) do
1099
+ criteria.tally("genres.y.z")
1100
+ end
1025
1101
 
1026
- it "hits the database again" do
1027
- expect(context).to receive(:view).once.and_call_original
1028
- expect(context).to be_exists
1029
- end
1102
+ it "returns the correct hash" do
1103
+ expect(tally).to eq(
1104
+ [1, 2] => 2,
1105
+ [1, 3] => 1
1106
+ )
1030
1107
  end
1031
1108
  end
1032
1109
 
1033
- context "when caching is enabled" do
1110
+ context "when tallying deeply nested arrays/embedded associations" do
1034
1111
 
1035
- let(:criteria) do
1036
- Band.where(name: "Depeche Mode").cache
1112
+ before do
1113
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
1114
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
1115
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))) ])
1037
1116
  end
1038
1117
 
1039
- let(:context) do
1040
- described_class.new(criteria)
1118
+ let(:tally) do
1119
+ Person.tally("addresses.code.deepest.array.y.z")
1041
1120
  end
1042
1121
 
1043
- context "when the cache is loaded" do
1044
-
1045
- before do
1046
- context.to_a
1047
- end
1048
-
1049
- it "does not hit the database" do
1050
- expect(context).to receive(:view).never
1051
- expect(context).to be_exists
1052
- end
1122
+ it "returns the correct hash" do
1123
+ expect(tally).to eq(
1124
+ [ [ 1, 2 ] ] => 2,
1125
+ [ [ 1, 3 ] ] => 1
1126
+ )
1053
1127
  end
1128
+ end
1054
1129
 
1055
- context "when the cache is not loaded" do
1130
+ context "when tallying deeply nested arrays/embedded associations" do
1056
1131
 
1057
- context "when a count has been executed" do
1132
+ before do
1133
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))),
1134
+ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
1135
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))),
1136
+ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
1137
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))),
1138
+ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))) ])
1139
+ end
1058
1140
 
1059
- before do
1060
- context.count
1061
- end
1141
+ let(:tally) do
1142
+ Person.tally("addresses.code.deepest.array.y.z")
1143
+ end
1062
1144
 
1063
- it "does not hit the database" do
1064
- expect(context).to receive(:view).never
1065
- expect(context).to be_exists
1066
- end
1067
- end
1145
+ it "returns the correct hash" do
1146
+ expect(tally).to eq(
1147
+ [ [ 1, 2 ], [ 1, 2 ] ] => 2,
1148
+ [ [ 1, 3 ], [ 1, 3 ] ] => 1
1149
+ )
1068
1150
  end
1069
1151
  end
1070
- end
1071
1152
 
1072
- describe "#explain" do
1153
+ context "when some keys are missing" do
1154
+ before do
1155
+ 3.times { Band.create!(origin: "tally") }
1156
+ end
1073
1157
 
1074
- let(:criteria) do
1075
- Band.where(name: "Depeche Mode")
1076
- end
1158
+ let(:tally) do
1159
+ criteria.tally(:name)
1160
+ end
1077
1161
 
1078
- let(:context) do
1079
- described_class.new(criteria)
1080
- end
1081
-
1082
- it "returns the criteria explain path" do
1083
- expect(context.explain).to_not be_empty
1162
+ it "returns the correct hash" do
1163
+ expect(tally).to eq(
1164
+ "Depeche Mode" => 1,
1165
+ "New Order" => 1,
1166
+ "10,000 Maniacs" => 1,
1167
+ nil => 3
1168
+ )
1169
+ end
1084
1170
  end
1085
- end
1086
1171
 
1087
- describe "#find_one_and_replace" do
1172
+ context "when the first element is an embeds_one" do
1173
+ before do
1174
+ Person.create!(name: Name.new(translations: [ Translation.new(language: 1), Translation.new(language: 2) ]))
1175
+ Person.create!(name: Name.new(translations: [ Translation.new(language: 1), Translation.new(language: 2) ]))
1176
+ Person.create!(name: Name.new(translations: [ Translation.new(language: 1), Translation.new(language: 3) ]))
1177
+ end
1088
1178
 
1089
- let!(:depeche) do
1090
- Band.create!(name: "Depeche Mode")
1091
- end
1179
+ let(:tally) do
1180
+ Person.tally("name.translations.language")
1181
+ end
1092
1182
 
1093
- let!(:tool) do
1094
- Band.create!(name: "Tool")
1183
+ it "returns the correct hash" do
1184
+ expect(tally).to eq(
1185
+ [1, 2] => 2,
1186
+ [1, 3] => 1
1187
+ )
1188
+ end
1095
1189
  end
1096
1190
 
1097
- context "when the selector matches" do
1098
-
1099
- context "when not providing options" do
1191
+ context "when tallying demongoizable values from typeless fields" do
1100
1192
 
1101
- let(:criteria) do
1102
- Band.where(name: "Depeche Mode")
1103
- end
1193
+ let!(:person1) { Person.create!(ssn: /hello/) }
1194
+ let!(:person2) { Person.create!(ssn: BSON::Decimal128.new("1")) }
1195
+ let(:tally) { Person.tally("ssn") }
1104
1196
 
1105
- let(:context) do
1106
- described_class.new(criteria)
1197
+ let(:tallied_classes) do
1198
+ tally.keys.map(&:class).sort do |a, b|
1199
+ a.to_s.casecmp(b.to_s)
1107
1200
  end
1201
+ end
1108
1202
 
1109
- let!(:result) do
1110
- context.find_one_and_replace(name: 'FKA Twigs')
1111
- end
1203
+ context "< BSON 5" do
1204
+ max_bson_version '4.99.99'
1112
1205
 
1113
- it "returns the first matching document" do
1114
- expect(result).to eq(depeche)
1206
+ it "stores the correct types in the database" do
1207
+ expect(Person.find(person1.id).attributes["ssn"]).to be_a BSON::Regexp::Raw
1208
+ expect(Person.find(person2.id).attributes["ssn"]).to be_a BSON::Decimal128
1115
1209
  end
1116
1210
 
1117
- it "updates the document in the database" do
1118
- expect(depeche.reload.name).to eq('FKA Twigs')
1211
+ it "tallies the correct type" do
1212
+ expect(tallied_classes).to be == [ BSON::Decimal128, BSON::Regexp::Raw ]
1119
1213
  end
1120
1214
  end
1121
1215
 
1122
- context "when sorting" do
1216
+ context '>= BSON 5' do
1217
+ min_bson_version "5.0"
1123
1218
 
1124
- let(:criteria) do
1125
- Band.desc(:name)
1219
+ it "stores the correct types in the database" do
1220
+ expect(Person.find(person1.id).ssn).to be_a BSON::Regexp::Raw
1221
+ expect(Person.find(person2.id).ssn).to be_a BigDecimal
1126
1222
  end
1127
1223
 
1128
- let(:context) do
1129
- described_class.new(criteria)
1224
+ it "tallies the correct type" do
1225
+ expect(tallied_classes).to be == [ BigDecimal, BSON::Regexp::Raw ]
1130
1226
  end
1227
+ end
1131
1228
 
1132
- let!(:result) do
1133
- context.find_one_and_replace(likes: 1)
1134
- end
1229
+ context '>= BSON 5 with decimal128 allowed' do
1230
+ min_bson_version "5.0"
1231
+ config_override :allow_bson5_decimal128, true
1135
1232
 
1136
- it "returns the first matching document" do
1137
- expect(result).to eq(tool)
1233
+ it "stores the correct types in the database" do
1234
+ expect(Person.find(person1.id).ssn).to be_a BSON::Regexp::Raw
1235
+ expect(Person.find(person2.id).ssn).to be_a BSON::Decimal128
1138
1236
  end
1139
1237
 
1140
- it "updates the document in the database" do
1141
- expect(tool.reload.likes).to eq(1)
1142
- expect(tool.reload.name).to be_nil
1238
+ it "tallies the correct type" do
1239
+ expect(tallied_classes).to be == [ BSON::Decimal128, BSON::Regexp::Raw ]
1143
1240
  end
1144
1241
  end
1242
+ end
1243
+ end
1145
1244
 
1146
- context "when limiting fields" do
1245
+ describe "#each" do
1147
1246
 
1148
- let(:criteria) do
1149
- Band.only(:_id)
1150
- end
1247
+ before do
1248
+ Band.create!(name: "Depeche Mode")
1249
+ end
1151
1250
 
1152
- let(:context) do
1153
- described_class.new(criteria)
1154
- end
1251
+ let(:criteria) do
1252
+ Band.where(name: "Depeche Mode")
1253
+ end
1155
1254
 
1156
- let!(:result) do
1157
- context.find_one_and_replace(name: 'FKA Twigs', likes: 1)
1158
- end
1255
+ let(:context) do
1256
+ described_class.new(criteria)
1257
+ end
1159
1258
 
1160
- it "returns the first matching document" do
1161
- expect(result).to eq(depeche)
1162
- end
1259
+ context 'when the criteria has a collation' do
1260
+ min_server_version '3.4'
1163
1261
 
1164
- it "limits the returned fields" do
1165
- expect(result.name).to be_nil
1262
+ let(:criteria) do
1263
+ Band.where(name: "DEPECHE MODE").collation(locale: 'en_US', strength: 2)
1264
+ end
1265
+
1266
+ it "yields mongoid documents to the block" do
1267
+ context.each do |doc|
1268
+ expect(doc).to be_a(Mongoid::Document)
1166
1269
  end
1270
+ end
1167
1271
 
1168
- it "updates the document in the database" do
1169
- expect(depeche.reload.likes).to eq(1)
1272
+ it "iterates over the matching documents" do
1273
+ context.each do |doc|
1274
+ expect(doc.name).to eq("Depeche Mode")
1170
1275
  end
1171
1276
  end
1172
1277
 
1173
- context "when returning new" do
1278
+ it "returns self" do
1279
+ expect(context.each{}).to be(context)
1280
+ end
1281
+ end
1174
1282
 
1175
- let(:criteria) do
1176
- Band.where(name: "Depeche Mode")
1177
- end
1283
+ context "when providing a block" do
1178
1284
 
1179
- let(:context) do
1180
- described_class.new(criteria)
1285
+ it "yields mongoid documents to the block" do
1286
+ context.each do |doc|
1287
+ expect(doc).to be_a(Mongoid::Document)
1181
1288
  end
1289
+ end
1182
1290
 
1183
- let!(:result) do
1184
- context.find_one_and_replace({ likes: 1 }, return_document: :after)
1291
+ it "iterates over the matching documents" do
1292
+ context.each do |doc|
1293
+ expect(doc.name).to eq("Depeche Mode")
1185
1294
  end
1295
+ end
1186
1296
 
1187
- it "returns the first matching document" do
1188
- expect(result).to eq(depeche)
1189
- end
1297
+ it "returns self" do
1298
+ expect(context.each{}).to be(context)
1299
+ end
1300
+ end
1190
1301
 
1191
- it "returns the updated document" do
1192
- expect(result.name).to be_nil
1193
- expect(result.likes).to eq(1)
1194
- end
1302
+ context "when no block is provided" do
1303
+
1304
+ let(:enum) do
1305
+ context.each
1195
1306
  end
1196
1307
 
1197
- context 'when a collation is specified on the criteria' do
1198
- min_server_version '3.4'
1308
+ it "returns an enumerator" do
1309
+ expect(enum).to be_a(Enumerator)
1310
+ end
1199
1311
 
1200
- let(:criteria) do
1201
- Band.where(name: "DEPECHE MODE").collation(locale: 'en_US', strength: 2)
1202
- end
1312
+ context "when iterating over the enumerator" do
1203
1313
 
1204
- let(:context) do
1205
- described_class.new(criteria)
1206
- end
1314
+ context "when iterating with each" do
1207
1315
 
1208
- let!(:result) do
1209
- context.find_one_and_replace({ likes: 1 }, return_document: :after)
1316
+ it "yields mongoid documents to the block" do
1317
+ enum.each do |doc|
1318
+ expect(doc).to be_a(Mongoid::Document)
1319
+ end
1320
+ end
1210
1321
  end
1211
1322
 
1212
- it "returns the first matching document" do
1213
- expect(result).to eq(depeche)
1214
- end
1323
+ context "when iterating with next" do
1215
1324
 
1216
- it "returns the updated document" do
1217
- expect(result.likes).to eq(1)
1218
- expect(result.name).to be_nil
1325
+ before do
1326
+ 10.times { |i| Band.create!(name: "Test #{i}") }
1327
+ end
1328
+
1329
+ let(:criteria) do
1330
+ Band.batch_size(5)
1331
+ end
1332
+
1333
+ it "yields mongoid documents" do
1334
+ expect(enum.next).to be_a(Mongoid::Document)
1335
+ end
1336
+
1337
+ it "does not load all documents" do
1338
+ subscriber = Mrss::EventSubscriber.new
1339
+ context.view.client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
1340
+
1341
+ enum.next
1342
+
1343
+ find_events = subscriber.all_events.select do |evt|
1344
+ evt.command_name == 'find'
1345
+ end
1346
+ expect(find_events.length).to be(2)
1347
+ get_more_events = subscriber.all_events.select do |evt|
1348
+ evt.command_name == 'getMore'
1349
+ end
1350
+ expect(get_more_events.length).to be(0)
1351
+ ensure
1352
+ context.view.client.unsubscribe(Mongo::Monitoring::COMMAND, subscriber)
1353
+ end
1219
1354
  end
1220
1355
  end
1221
1356
  end
1222
1357
 
1223
- context "when the selector does not match" do
1358
+ context 'when the criteria has a parent document' do
1224
1359
 
1225
- let(:criteria) do
1226
- Band.where(name: "DEPECHE MODE")
1360
+ before do
1361
+ Post.create!(person: person)
1362
+ Post.create!(person: person)
1363
+ Post.create!(person: person)
1227
1364
  end
1228
1365
 
1229
- let(:context) do
1230
- described_class.new(criteria)
1366
+ let(:person) do
1367
+ Person.new
1231
1368
  end
1232
1369
 
1233
- let(:result) do
1234
- context.find_one_and_replace(name: 'FKA Twigs')
1370
+ let(:criteria) do
1371
+ person.posts.all
1235
1372
  end
1236
1373
 
1237
- it "returns nil" do
1238
- expect(result).to be_nil
1374
+ let(:persons) do
1375
+ criteria.collect(&:person)
1376
+ end
1377
+
1378
+ it 'sets the same parent object on each related object' do
1379
+ expect(persons.uniq.size).to eq(1)
1239
1380
  end
1240
1381
  end
1241
1382
  end
1242
1383
 
1243
- describe "#find_one_and_update" do
1384
+ describe "#eager_load" do
1244
1385
 
1245
- let!(:depeche) do
1246
- Band.create!(name: "Depeche Mode")
1386
+ let(:criteria) do
1387
+ Person.includes(:game)
1247
1388
  end
1248
1389
 
1249
- let!(:tool) do
1250
- Band.create!(name: "Tool")
1390
+ let(:context) do
1391
+ described_class.new(criteria)
1251
1392
  end
1252
1393
 
1253
- context "when the selector matches" do
1394
+ context "when no documents are returned" do
1254
1395
 
1255
- context "when not providing options" do
1396
+ let(:game_association) do
1397
+ Person.reflect_on_association(:game)
1398
+ end
1399
+
1400
+ it "does not make any additional database queries" do
1401
+ expect(game_association).to receive(:eager_load).never
1402
+ context.send(:eager_load, [])
1403
+ end
1404
+ end
1405
+ end
1406
+
1407
+ describe "#exists?" do
1408
+
1409
+ let!(:band) do
1410
+ Band.create!(name: "Depeche Mode", active: true)
1411
+ end
1412
+
1413
+ context "when not passing options" do
1414
+
1415
+ context "when the count is zero" do
1256
1416
 
1257
1417
  let(:criteria) do
1258
- Band.where(name: "Depeche Mode")
1418
+ Band.where(name: "New Order")
1259
1419
  end
1260
1420
 
1261
1421
  let(:context) do
1262
1422
  described_class.new(criteria)
1263
1423
  end
1264
1424
 
1265
- let!(:result) do
1266
- context.find_one_and_update("$inc" => { likes: 1 })
1267
- end
1268
-
1269
- it "returns the first matching document" do
1270
- expect(result).to eq(depeche)
1271
- end
1272
-
1273
- it "updates the document in the database" do
1274
- expect(depeche.reload.likes).to eq(1)
1425
+ it "returns false" do
1426
+ expect(context).to_not be_exists
1275
1427
  end
1276
1428
  end
1277
1429
 
1278
- context "when sorting" do
1430
+ context "when the count is greater than zero" do
1279
1431
 
1280
1432
  let(:criteria) do
1281
- Band.desc(:name)
1433
+ Band.where(name: "Depeche Mode")
1282
1434
  end
1283
1435
 
1284
1436
  let(:context) do
1285
1437
  described_class.new(criteria)
1286
1438
  end
1287
1439
 
1288
- let!(:result) do
1289
- context.find_one_and_update("$inc" => { likes: 1 })
1440
+ it "returns true" do
1441
+ expect(context).to be_exists
1442
+ end
1443
+ end
1444
+
1445
+ context "when caching is not enabled" do
1446
+
1447
+ let(:criteria) do
1448
+ Band.where(name: "Depeche Mode")
1449
+ end
1450
+
1451
+ let(:context) do
1452
+ described_class.new(criteria)
1453
+ end
1454
+
1455
+ context "when exists? already called and query cache is enabled" do
1456
+ query_cache_enabled
1457
+
1458
+ before do
1459
+ context.exists?
1460
+ end
1461
+
1462
+ it "does not hit the database again" do
1463
+ expect_no_queries do
1464
+ expect(context).to be_exists
1465
+ end
1466
+ end
1467
+ end
1468
+ end
1469
+ end
1470
+
1471
+ context "when passing an _id" do
1472
+
1473
+ context "when its of type BSON::ObjectId" do
1474
+
1475
+ context "when calling it on the class" do
1476
+
1477
+ it "returns true" do
1478
+ expect(Band.exists?(band._id)).to be true
1479
+ end
1480
+ end
1481
+
1482
+ context "when calling it on a criteria that includes the object" do
1483
+
1484
+ it "returns true" do
1485
+ expect(Band.where(name: band.name).exists?(band._id)).to be true
1486
+ end
1487
+ end
1488
+
1489
+ context "when calling it on a criteria that does not include the object" do
1490
+
1491
+ it "returns false" do
1492
+ expect(Band.where(name: "bogus").exists?(band._id)).to be false
1493
+ end
1494
+ end
1495
+
1496
+ context "when the id does not exist" do
1497
+
1498
+ it "returns false" do
1499
+ expect(Band.exists?(BSON::ObjectId.new)).to be false
1500
+ end
1501
+ end
1502
+ end
1503
+
1504
+ context "when its of type String" do
1505
+
1506
+ context "when the id exists" do
1507
+
1508
+ it "returns true" do
1509
+ expect(Band.exists?(band._id.to_s)).to be true
1510
+ end
1511
+ end
1512
+
1513
+ context "when the id does not exist" do
1514
+
1515
+ it "returns false" do
1516
+ expect(Band.exists?(BSON::ObjectId.new.to_s)).to be false
1517
+ end
1518
+ end
1519
+ end
1520
+ end
1521
+
1522
+ context "when passing a hash" do
1523
+
1524
+ context "when calling it on the class" do
1525
+
1526
+ it "returns true" do
1527
+ expect(Band.exists?(name: band.name)).to be true
1528
+ end
1529
+ end
1530
+
1531
+ context "when calling it on a criteria that includes the object" do
1532
+
1533
+ it "returns true" do
1534
+ expect(Band.where(active: true).exists?(name: band.name)).to be true
1535
+ end
1536
+ end
1537
+
1538
+ context "when calling it on a criteria that does not include the object" do
1539
+
1540
+ it "returns false" do
1541
+ expect(Band.where(active: false).exists?(name: band.name)).to be false
1542
+ end
1543
+ end
1544
+
1545
+ context "when the conditions don't match" do
1546
+
1547
+ it "returns false" do
1548
+ expect(Band.exists?(name: "bogus")).to be false
1549
+ end
1550
+ end
1551
+ end
1552
+
1553
+ context "when passing false" do
1554
+
1555
+ it "returns false" do
1556
+ expect(Band.exists?(false)).to be false
1557
+ end
1558
+ end
1559
+
1560
+ context "when passing nil" do
1561
+
1562
+ it "returns false" do
1563
+ expect(Band.exists?(nil)).to be false
1564
+ end
1565
+ end
1566
+
1567
+ context "when the limit is 0" do
1568
+
1569
+ it "returns false" do
1570
+ expect(Band.limit(0).exists?).to be false
1571
+ end
1572
+ end
1573
+
1574
+ context "when the criteria limit is 0" do
1575
+
1576
+ it "returns false" do
1577
+ expect(Band.criteria.limit(0).exists?).to be false
1578
+ end
1579
+ end
1580
+ end
1581
+
1582
+ describe "#explain" do
1583
+
1584
+ let(:criteria) do
1585
+ Band.where(name: "Depeche Mode")
1586
+ end
1587
+
1588
+ let(:context) do
1589
+ described_class.new(criteria)
1590
+ end
1591
+
1592
+ it "returns the criteria explain path" do
1593
+ explain = context.explain
1594
+ expect(explain).to_not be_empty
1595
+ expect(explain.keys).to include("queryPlanner", "executionStats", "serverInfo")
1596
+ end
1597
+
1598
+ it "respects options passed to explain" do
1599
+ explain = context.explain(verbosity: :query_planner)
1600
+ expect(explain).to_not be_empty
1601
+ expect(explain.keys).to include("queryPlanner", "serverInfo")
1602
+ expect(explain.keys).not_to include("executionStats")
1603
+ end
1604
+ end
1605
+
1606
+ describe "#find_one_and_replace" do
1607
+
1608
+ let!(:depeche) do
1609
+ Band.create!(name: "Depeche Mode")
1610
+ end
1611
+
1612
+ let!(:tool) do
1613
+ Band.create!(name: "Tool")
1614
+ end
1615
+
1616
+ context "when the selector matches" do
1617
+
1618
+ context "when not providing options" do
1619
+
1620
+ let(:criteria) do
1621
+ Band.where(name: "Depeche Mode")
1622
+ end
1623
+
1624
+ let(:context) do
1625
+ described_class.new(criteria)
1626
+ end
1627
+
1628
+ let!(:result) do
1629
+ context.find_one_and_replace(name: 'FKA Twigs')
1630
+ end
1631
+
1632
+ it "returns the first matching document" do
1633
+ expect(result).to eq(depeche)
1634
+ end
1635
+
1636
+ it "updates the document in the database" do
1637
+ expect(depeche.reload.name).to eq('FKA Twigs')
1638
+ end
1639
+ end
1640
+
1641
+ context "when sorting" do
1642
+
1643
+ let(:criteria) do
1644
+ Band.desc(:name)
1645
+ end
1646
+
1647
+ let(:context) do
1648
+ described_class.new(criteria)
1649
+ end
1650
+
1651
+ let!(:result) do
1652
+ context.find_one_and_replace(likes: 1)
1290
1653
  end
1291
1654
 
1292
1655
  it "returns the first matching document" do
@@ -1295,6 +1658,7 @@ describe Mongoid::Contextual::Mongo do
1295
1658
 
1296
1659
  it "updates the document in the database" do
1297
1660
  expect(tool.reload.likes).to eq(1)
1661
+ expect(tool.reload.name).to be_nil
1298
1662
  end
1299
1663
  end
1300
1664
 
@@ -1309,7 +1673,7 @@ describe Mongoid::Contextual::Mongo do
1309
1673
  end
1310
1674
 
1311
1675
  let!(:result) do
1312
- context.find_one_and_update("$inc" => { likes: 1 })
1676
+ context.find_one_and_replace(name: 'FKA Twigs', likes: 1)
1313
1677
  end
1314
1678
 
1315
1679
  it "returns the first matching document" do
@@ -1336,7 +1700,7 @@ describe Mongoid::Contextual::Mongo do
1336
1700
  end
1337
1701
 
1338
1702
  let!(:result) do
1339
- context.find_one_and_update({ "$inc" => { likes: 1 }}, return_document: :after)
1703
+ context.find_one_and_replace({ likes: 1 }, return_document: :after)
1340
1704
  end
1341
1705
 
1342
1706
  it "returns the first matching document" do
@@ -1344,6 +1708,7 @@ describe Mongoid::Contextual::Mongo do
1344
1708
  end
1345
1709
 
1346
1710
  it "returns the updated document" do
1711
+ expect(result.name).to be_nil
1347
1712
  expect(result.likes).to eq(1)
1348
1713
  end
1349
1714
  end
@@ -1360,7 +1725,7 @@ describe Mongoid::Contextual::Mongo do
1360
1725
  end
1361
1726
 
1362
1727
  let!(:result) do
1363
- context.find_one_and_update({ "$inc" => { likes: 1 }}, return_document: :after)
1728
+ context.find_one_and_replace({ likes: 1 }, return_document: :after)
1364
1729
  end
1365
1730
 
1366
1731
  it "returns the first matching document" do
@@ -1369,6 +1734,7 @@ describe Mongoid::Contextual::Mongo do
1369
1734
 
1370
1735
  it "returns the updated document" do
1371
1736
  expect(result.likes).to eq(1)
1737
+ expect(result.name).to be_nil
1372
1738
  end
1373
1739
  end
1374
1740
  end
@@ -1376,7 +1742,7 @@ describe Mongoid::Contextual::Mongo do
1376
1742
  context "when the selector does not match" do
1377
1743
 
1378
1744
  let(:criteria) do
1379
- Band.where(name: "Placebo")
1745
+ Band.where(name: "DEPECHE MODE")
1380
1746
  end
1381
1747
 
1382
1748
  let(:context) do
@@ -1384,7 +1750,7 @@ describe Mongoid::Contextual::Mongo do
1384
1750
  end
1385
1751
 
1386
1752
  let(:result) do
1387
- context.find_one_and_update("$inc" => { likes: 1 })
1753
+ context.find_one_and_replace(name: 'FKA Twigs')
1388
1754
  end
1389
1755
 
1390
1756
  it "returns nil" do
@@ -1393,41 +1759,22 @@ describe Mongoid::Contextual::Mongo do
1393
1759
  end
1394
1760
  end
1395
1761
 
1396
- describe "#find_one_and_delete" do
1762
+ describe "#find_one_and_update" do
1397
1763
 
1398
1764
  let!(:depeche) do
1399
1765
  Band.create!(name: "Depeche Mode")
1400
1766
  end
1401
1767
 
1402
- let(:criteria) do
1403
- Band.where(name: "Depeche Mode")
1404
- end
1405
-
1406
- let(:context) do
1407
- described_class.new(criteria)
1408
- end
1409
-
1410
- let!(:result) do
1411
- context.find_one_and_delete
1768
+ let!(:tool) do
1769
+ Band.create!(name: "Tool")
1412
1770
  end
1413
1771
 
1414
- context 'when the selector matches a document' do
1415
-
1416
- it "returns the first matching document" do
1417
- expect(result).to eq(depeche)
1418
- end
1419
-
1420
- it "deletes the document from the database" do
1421
- expect {
1422
- depeche.reload
1423
- }.to raise_error(Mongoid::Errors::DocumentNotFound)
1424
- end
1772
+ context "when the selector matches" do
1425
1773
 
1426
- context 'when a collation is specified on the criteria' do
1427
- min_server_version '3.4'
1774
+ context "when not providing options" do
1428
1775
 
1429
1776
  let(:criteria) do
1430
- Band.where(name: "DEPECHE MODE").collation(locale: 'en_US', strength: 2)
1777
+ Band.where(name: "Depeche Mode")
1431
1778
  end
1432
1779
 
1433
1780
  let(:context) do
@@ -1435,72 +1782,244 @@ describe Mongoid::Contextual::Mongo do
1435
1782
  end
1436
1783
 
1437
1784
  let!(:result) do
1438
- context.find_one_and_delete
1785
+ context.find_one_and_update("$inc" => { likes: 1 })
1439
1786
  end
1440
1787
 
1441
1788
  it "returns the first matching document" do
1442
1789
  expect(result).to eq(depeche)
1443
1790
  end
1444
1791
 
1445
- it "deletes the document from the database" do
1446
- expect {
1447
- depeche.reload
1448
- }.to raise_error(Mongoid::Errors::DocumentNotFound)
1792
+ it "updates the document in the database" do
1793
+ expect(depeche.reload.likes).to eq(1)
1449
1794
  end
1450
1795
  end
1451
- end
1452
-
1453
- context 'when the selector does not match a document' do
1454
-
1455
- let(:criteria) do
1456
- Band.where(name: "Placebo")
1457
- end
1458
-
1459
- let(:context) do
1460
- described_class.new(criteria)
1461
- end
1462
-
1463
- let(:result) do
1464
- context.find_one_and_delete
1465
- end
1466
-
1467
- it "returns nil" do
1468
- expect(result).to be_nil
1469
- end
1470
- end
1471
- end
1472
-
1473
- [ :first, :one ].each do |method|
1474
-
1475
- describe "##{method}" do
1476
-
1477
- let!(:depeche_mode) do
1478
- Band.create!(name: "Depeche Mode")
1479
- end
1480
-
1481
- let!(:new_order) do
1482
- Band.create!(name: "New Order")
1483
- end
1484
-
1485
- let!(:rolling_stones) do
1486
- Band.create!(name: "The Rolling Stones")
1487
- end
1488
1796
 
1489
- context "when the context is not cached" do
1797
+ context "when sorting" do
1490
1798
 
1491
1799
  let(:criteria) do
1492
- Band.where(name: "Depeche Mode")
1800
+ Band.desc(:name)
1493
1801
  end
1494
1802
 
1495
1803
  let(:context) do
1496
1804
  described_class.new(criteria)
1497
1805
  end
1498
1806
 
1499
- it "returns the first matching document" do
1500
- expect(context.send(method)).to eq(depeche_mode)
1807
+ let!(:result) do
1808
+ context.find_one_and_update("$inc" => { likes: 1 })
1501
1809
  end
1502
1810
 
1503
- context 'when the criteria has a collation' do
1811
+ it "returns the first matching document" do
1812
+ expect(result).to eq(tool)
1813
+ end
1814
+
1815
+ it "updates the document in the database" do
1816
+ expect(tool.reload.likes).to eq(1)
1817
+ end
1818
+ end
1819
+
1820
+ context "when limiting fields" do
1821
+
1822
+ let(:criteria) do
1823
+ Band.only(:_id)
1824
+ end
1825
+
1826
+ let(:context) do
1827
+ described_class.new(criteria)
1828
+ end
1829
+
1830
+ let!(:result) do
1831
+ context.find_one_and_update("$inc" => { likes: 1 })
1832
+ end
1833
+
1834
+ it "returns the first matching document" do
1835
+ expect(result).to eq(depeche)
1836
+ end
1837
+
1838
+ it "limits the returned fields" do
1839
+ expect(result.name).to be_nil
1840
+ end
1841
+
1842
+ it "updates the document in the database" do
1843
+ expect(depeche.reload.likes).to eq(1)
1844
+ end
1845
+ end
1846
+
1847
+ context "when returning new" do
1848
+
1849
+ let(:criteria) do
1850
+ Band.where(name: "Depeche Mode")
1851
+ end
1852
+
1853
+ let(:context) do
1854
+ described_class.new(criteria)
1855
+ end
1856
+
1857
+ let!(:result) do
1858
+ context.find_one_and_update({ "$inc" => { likes: 1 }}, return_document: :after)
1859
+ end
1860
+
1861
+ it "returns the first matching document" do
1862
+ expect(result).to eq(depeche)
1863
+ end
1864
+
1865
+ it "returns the updated document" do
1866
+ expect(result.likes).to eq(1)
1867
+ end
1868
+ end
1869
+
1870
+ context 'when a collation is specified on the criteria' do
1871
+ min_server_version '3.4'
1872
+
1873
+ let(:criteria) do
1874
+ Band.where(name: "DEPECHE MODE").collation(locale: 'en_US', strength: 2)
1875
+ end
1876
+
1877
+ let(:context) do
1878
+ described_class.new(criteria)
1879
+ end
1880
+
1881
+ let!(:result) do
1882
+ context.find_one_and_update({ "$inc" => { likes: 1 }}, return_document: :after)
1883
+ end
1884
+
1885
+ it "returns the first matching document" do
1886
+ expect(result).to eq(depeche)
1887
+ end
1888
+
1889
+ it "returns the updated document" do
1890
+ expect(result.likes).to eq(1)
1891
+ end
1892
+ end
1893
+ end
1894
+
1895
+ context "when the selector does not match" do
1896
+
1897
+ let(:criteria) do
1898
+ Band.where(name: "Placebo")
1899
+ end
1900
+
1901
+ let(:context) do
1902
+ described_class.new(criteria)
1903
+ end
1904
+
1905
+ let(:result) do
1906
+ context.find_one_and_update("$inc" => { likes: 1 })
1907
+ end
1908
+
1909
+ it "returns nil" do
1910
+ expect(result).to be_nil
1911
+ end
1912
+ end
1913
+ end
1914
+
1915
+ describe "#find_one_and_delete" do
1916
+
1917
+ let!(:depeche) do
1918
+ Band.create!(name: "Depeche Mode")
1919
+ end
1920
+
1921
+ let(:criteria) do
1922
+ Band.where(name: "Depeche Mode")
1923
+ end
1924
+
1925
+ let(:context) do
1926
+ described_class.new(criteria)
1927
+ end
1928
+
1929
+ let!(:result) do
1930
+ context.find_one_and_delete
1931
+ end
1932
+
1933
+ context 'when the selector matches a document' do
1934
+
1935
+ it "returns the first matching document" do
1936
+ expect(result).to eq(depeche)
1937
+ end
1938
+
1939
+ it "deletes the document from the database" do
1940
+ expect {
1941
+ depeche.reload
1942
+ }.to raise_error(Mongoid::Errors::DocumentNotFound, /Document\(s\) not found for class Band with id\(s\)/)
1943
+ end
1944
+
1945
+ context 'when a collation is specified on the criteria' do
1946
+ min_server_version '3.4'
1947
+
1948
+ let(:criteria) do
1949
+ Band.where(name: "DEPECHE MODE").collation(locale: 'en_US', strength: 2)
1950
+ end
1951
+
1952
+ let(:context) do
1953
+ described_class.new(criteria)
1954
+ end
1955
+
1956
+ let!(:result) do
1957
+ context.find_one_and_delete
1958
+ end
1959
+
1960
+ it "returns the first matching document" do
1961
+ expect(result).to eq(depeche)
1962
+ end
1963
+
1964
+ it "deletes the document from the database" do
1965
+ expect {
1966
+ depeche.reload
1967
+ }.to raise_error(Mongoid::Errors::DocumentNotFound, /Document\(s\) not found for class Band with id\(s\)/)
1968
+ end
1969
+ end
1970
+ end
1971
+
1972
+ context 'when the selector does not match a document' do
1973
+
1974
+ let(:criteria) do
1975
+ Band.where(name: "Placebo")
1976
+ end
1977
+
1978
+ let(:context) do
1979
+ described_class.new(criteria)
1980
+ end
1981
+
1982
+ let(:result) do
1983
+ context.find_one_and_delete
1984
+ end
1985
+
1986
+ it "returns nil" do
1987
+ expect(result).to be_nil
1988
+ end
1989
+ end
1990
+ end
1991
+
1992
+ [ :first, :one ].each do |method|
1993
+
1994
+ describe "##{method}" do
1995
+
1996
+ let!(:depeche_mode) do
1997
+ Band.create!(name: "Depeche Mode")
1998
+ end
1999
+
2000
+ let!(:new_order) do
2001
+ Band.create!(name: "New Order")
2002
+ end
2003
+
2004
+ let!(:rolling_stones) do
2005
+ Band.create!(name: "The Rolling Stones")
2006
+ end
2007
+
2008
+ context "when the context is not cached" do
2009
+
2010
+ let(:criteria) do
2011
+ Band.where(name: "Depeche Mode")
2012
+ end
2013
+
2014
+ let(:context) do
2015
+ described_class.new(criteria)
2016
+ end
2017
+
2018
+ it "returns the first matching document" do
2019
+ expect(context.send(method)).to eq(depeche_mode)
2020
+ end
2021
+
2022
+ context 'when the criteria has a collation' do
1504
2023
  min_server_version '3.4'
1505
2024
 
1506
2025
  let(:criteria) do
@@ -1561,24 +2080,6 @@ describe Mongoid::Contextual::Mongo do
1561
2080
  expect(context.last).to eq(rolling_stones)
1562
2081
  end
1563
2082
  end
1564
-
1565
- context 'with option { id_sort: :none }' do
1566
- let(:opts) do
1567
- { id_sort: :none }
1568
- end
1569
-
1570
- it 'applies the sort on _id' do
1571
- expect(context.send(method, opts)).to eq(depeche_mode)
1572
- end
1573
-
1574
- context 'when calling #last' do
1575
-
1576
- it 'doesn\'t apply a sort on _id' do
1577
- expect(context.send(method, opts)).to eq(depeche_mode)
1578
- expect(context.last(opts)).to eq(depeche_mode)
1579
- end
1580
- end
1581
- end
1582
2083
  end
1583
2084
 
1584
2085
  context 'when the criteria has a sort' do
@@ -1602,25 +2103,6 @@ describe Mongoid::Contextual::Mongo do
1602
2103
  expect(context.last).to eq(depeche_mode)
1603
2104
  end
1604
2105
  end
1605
-
1606
- context 'with option { id_sort: :none }' do
1607
-
1608
- let(:opts) do
1609
- { id_sort: :none }
1610
- end
1611
-
1612
- it 'uses the preexisting sort' do
1613
- expect(context.send(method, opts)).to eq(rolling_stones)
1614
- end
1615
-
1616
- context 'when calling #last' do
1617
-
1618
- it 'uses the preexisting sort' do
1619
- expect(context.send(method, opts)).to eq(rolling_stones)
1620
- expect(context.last(opts)).to eq(depeche_mode)
1621
- end
1622
- end
1623
- end
1624
2106
  end
1625
2107
 
1626
2108
  context "when using .sort" do
@@ -1649,28 +2131,17 @@ describe Mongoid::Contextual::Mongo do
1649
2131
  end
1650
2132
  end
1651
2133
 
1652
- context "when the context is cached" do
2134
+ context "when the query cache is enabled" do
2135
+ query_cache_enabled
1653
2136
 
1654
2137
  let(:criteria) do
1655
- Band.where(name: "Depeche Mode").cache
2138
+ Band.where(name: "Depeche Mode")
1656
2139
  end
1657
2140
 
1658
2141
  let(:context) do
1659
2142
  described_class.new(criteria)
1660
2143
  end
1661
2144
 
1662
- context "when the cache is loaded" do
1663
-
1664
- before do
1665
- context.to_a
1666
- end
1667
-
1668
- it "returns the first document without touching the database" do
1669
- expect(context).to receive(:view).never
1670
- expect(context.send(method)).to eq(depeche_mode)
1671
- end
1672
- end
1673
-
1674
2145
  context "when first method was called before" do
1675
2146
 
1676
2147
  before do
@@ -1678,8 +2149,9 @@ describe Mongoid::Contextual::Mongo do
1678
2149
  end
1679
2150
 
1680
2151
  it "returns the first document without touching the database" do
1681
- expect(context).to receive(:view).never
1682
- expect(context.send(method)).to eq(depeche_mode)
2152
+ expect_no_queries do
2153
+ expect(context.send(method)).to eq(depeche_mode)
2154
+ end
1683
2155
  end
1684
2156
  end
1685
2157
  end
@@ -1733,86 +2205,30 @@ describe Mongoid::Contextual::Mongo do
1733
2205
  end
1734
2206
  end
1735
2207
 
1736
- context "when the context is cached" do
2208
+ context "when the query cache is enabled" do
1737
2209
 
1738
2210
  let(:context) do
1739
2211
  described_class.new(criteria)
1740
2212
  end
1741
2213
 
1742
- context "when the whole context is loaded" do
2214
+ context "when calling first beforehand" do
2215
+ query_cache_enabled
1743
2216
 
1744
- before do
1745
- context.to_a
2217
+ let(:context) do
2218
+ described_class.new(criteria)
1746
2219
  end
1747
2220
 
1748
- context "when all of the documents are cached" do
1749
-
1750
- let(:criteria) do
1751
- Band.all.cache
1752
- end
2221
+ let(:criteria) do
2222
+ Band.all
2223
+ end
1753
2224
 
1754
- context "when requesting all of the documents" do
2225
+ before do
2226
+ context.first(before_limit)
2227
+ end
1755
2228
 
1756
- let(:docs) do
1757
- context.send(method, 3)
1758
- end
1759
-
1760
- it "returns all of the documents without touching the database" do
1761
- expect(context).to receive(:view).never
1762
- expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
1763
- end
1764
- end
1765
-
1766
- context "when requesting fewer than all of the documents" do
1767
-
1768
- let(:docs) do
1769
- context.send(method, 2)
1770
- end
1771
-
1772
- it "returns all of the documents without touching the database" do
1773
- expect(context).to receive(:view).never
1774
- expect(docs).to eq([ depeche_mode, new_order ])
1775
- end
1776
- end
1777
- end
1778
-
1779
- context "when only one document is cached" do
1780
-
1781
- let(:criteria) do
1782
- Band.where(name: "Depeche Mode").cache
1783
- end
1784
-
1785
- context "when requesting one document" do
1786
-
1787
- let(:docs) do
1788
- context.send(method, 1)
1789
- end
1790
-
1791
- it "returns one document without touching the database" do
1792
- expect(context).to receive(:view).never
1793
- expect(docs).to eq([ depeche_mode ])
1794
- end
1795
- end
1796
- end
1797
- end
1798
-
1799
- context "when the first method was called before" do
1800
-
1801
- let(:context) do
1802
- described_class.new(criteria)
1803
- end
1804
-
1805
- let(:criteria) do
1806
- Band.all.cache
1807
- end
1808
-
1809
- before do
1810
- context.first(before_limit)
1811
- end
1812
-
1813
- let(:docs) do
1814
- context.send(method, limit)
1815
- end
2229
+ let(:docs) do
2230
+ context.send(method, limit)
2231
+ end
1816
2232
 
1817
2233
  context "when getting all of the documents before" do
1818
2234
  let(:before_limit) { 3 }
@@ -1821,8 +2237,9 @@ describe Mongoid::Contextual::Mongo do
1821
2237
  let(:limit) { 3 }
1822
2238
 
1823
2239
  it "returns all documents without touching the database" do
1824
- expect(context).to receive(:view).never
1825
- expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2240
+ expect_no_queries do
2241
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2242
+ end
1826
2243
  end
1827
2244
  end
1828
2245
 
@@ -1830,8 +2247,9 @@ describe Mongoid::Contextual::Mongo do
1830
2247
  let(:limit) { 2 }
1831
2248
 
1832
2249
  it "returns the correct documents without touching the database" do
1833
- expect(context).to receive(:view).never
1834
- expect(docs).to eq([ depeche_mode, new_order ])
2250
+ expect_no_queries do
2251
+ expect(docs).to eq([ depeche_mode, new_order ])
2252
+ end
1835
2253
  end
1836
2254
  end
1837
2255
  end
@@ -1843,8 +2261,9 @@ describe Mongoid::Contextual::Mongo do
1843
2261
  let(:limit) { 2 }
1844
2262
 
1845
2263
  it "returns the correct documents without touching the database" do
1846
- expect(context).to receive(:view).never
1847
- expect(docs).to eq([ depeche_mode, new_order ])
2264
+ expect_no_queries do
2265
+ expect(docs).to eq([ depeche_mode, new_order ])
2266
+ end
1848
2267
  end
1849
2268
  end
1850
2269
 
@@ -1852,8 +2271,9 @@ describe Mongoid::Contextual::Mongo do
1852
2271
  let(:limit) { 3 }
1853
2272
 
1854
2273
  it "returns the correct documents and touches the database" do
1855
- expect(context).to receive(:view).exactly(3).times.and_call_original
1856
- expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2274
+ expect_query(1) do
2275
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2276
+ end
1857
2277
  end
1858
2278
  end
1859
2279
  end
@@ -1865,8 +2285,9 @@ describe Mongoid::Contextual::Mongo do
1865
2285
  let(:limit) { 1 }
1866
2286
 
1867
2287
  it "returns the correct documents without touching the database" do
1868
- expect(context).to receive(:view).never
1869
- expect(docs).to eq([ depeche_mode ])
2288
+ expect_no_queries do
2289
+ expect(docs).to eq([ depeche_mode ])
2290
+ end
1870
2291
  end
1871
2292
  end
1872
2293
 
@@ -1874,8 +2295,9 @@ describe Mongoid::Contextual::Mongo do
1874
2295
  let(:limit) { 3 }
1875
2296
 
1876
2297
  it "returns the correct documents and touches the database" do
1877
- expect(context).to receive(:view).exactly(3).times.and_call_original
1878
- expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2298
+ expect_query(1) do
2299
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2300
+ end
1879
2301
  end
1880
2302
  end
1881
2303
  end
@@ -1883,14 +2305,15 @@ describe Mongoid::Contextual::Mongo do
1883
2305
  end
1884
2306
  end
1885
2307
 
1886
- context "when calling #first then #last" do
2308
+ context "when calling #first then #last and the query cache is enabled" do
2309
+ query_cache_enabled
1887
2310
 
1888
2311
  let(:context) do
1889
2312
  described_class.new(criteria)
1890
2313
  end
1891
2314
 
1892
2315
  let(:criteria) do
1893
- Band.all.cache
2316
+ Band.all
1894
2317
  end
1895
2318
 
1896
2319
  before do
@@ -1905,8 +2328,10 @@ describe Mongoid::Contextual::Mongo do
1905
2328
  let(:before_limit) { 2 }
1906
2329
  let(:limit) { 1 }
1907
2330
 
1908
- it "gets the correct document" do
1909
- expect(docs).to eq([rolling_stones])
2331
+ it "gets the correct document and hits the database" do
2332
+ expect_query(1) do
2333
+ expect(docs).to eq([rolling_stones])
2334
+ end
1910
2335
  end
1911
2336
  end
1912
2337
  end
@@ -1996,30 +2421,10 @@ describe Mongoid::Contextual::Mongo do
1996
2421
  context 'when calling #first' do
1997
2422
 
1998
2423
  it 'returns the first document, sorted by _id' do
1999
- pending "MONGOID-5416"
2000
2424
  expect(context.last).to eq(rolling_stones)
2001
2425
  expect(context.first).to eq(depeche_mode)
2002
2426
  end
2003
2427
  end
2004
-
2005
- context 'with option { id_sort: :none }' do
2006
- let(:opts) do
2007
- { id_sort: :none }
2008
- end
2009
-
2010
- it 'doesn\'t apply the sort on _id' do
2011
- expect(context.last(opts)).to eq(depeche_mode)
2012
- end
2013
-
2014
- context 'when calling #first' do
2015
-
2016
- it 'doesn\'t apply the sort on _id' do
2017
- pending "MONGOID-5416"
2018
- expect(context.last(opts)).to eq(rolling_stones)
2019
- expect(context.first(opts)).to eq(depeche_mode)
2020
- end
2021
- end
2022
- end
2023
2428
  end
2024
2429
 
2025
2430
  context 'when the criteria has a sort' do
@@ -2044,25 +2449,6 @@ describe Mongoid::Contextual::Mongo do
2044
2449
  expect(context.first).to eq(rolling_stones)
2045
2450
  end
2046
2451
  end
2047
-
2048
- context 'with option { id_sort: :none }' do
2049
-
2050
- let(:opts) do
2051
- { id_sort: :none }
2052
- end
2053
-
2054
- it 'uses the preexisting sort' do
2055
- expect(context.last(opts)).to eq(depeche_mode)
2056
- end
2057
-
2058
- context 'when calling #first' do
2059
-
2060
- it 'uses the preexisting sort' do
2061
- expect(context.last(opts)).to eq(depeche_mode)
2062
- expect(context.first(opts)).to eq(rolling_stones)
2063
- end
2064
- end
2065
- end
2066
2452
  end
2067
2453
 
2068
2454
  context "when using .sort" do
@@ -2091,28 +2477,17 @@ describe Mongoid::Contextual::Mongo do
2091
2477
  end
2092
2478
  end
2093
2479
 
2094
- context "when the context is cached" do
2480
+ context "when the query cache is enabled" do
2481
+ query_cache_enabled
2095
2482
 
2096
2483
  let(:criteria) do
2097
- Band.where(name: "Depeche Mode").cache
2484
+ Band.where(name: "Depeche Mode")
2098
2485
  end
2099
2486
 
2100
2487
  let(:context) do
2101
2488
  described_class.new(criteria)
2102
2489
  end
2103
2490
 
2104
- context "when the cache is loaded" do
2105
-
2106
- before do
2107
- context.to_a
2108
- end
2109
-
2110
- it "returns the last document without touching the database" do
2111
- expect(context).to receive(:view).never
2112
- expect(context.last).to eq(depeche_mode)
2113
- end
2114
- end
2115
-
2116
2491
  context "when last method was called before" do
2117
2492
 
2118
2493
  before do
@@ -2120,8 +2495,9 @@ describe Mongoid::Contextual::Mongo do
2120
2495
  end
2121
2496
 
2122
2497
  it "returns the last document without touching the database" do
2123
- expect(context).to receive(:view).never
2124
- expect(context.last).to eq(depeche_mode)
2498
+ expect_no_queries do
2499
+ expect(context.last).to eq(depeche_mode)
2500
+ end
2125
2501
  end
2126
2502
  end
2127
2503
  end
@@ -2181,71 +2557,15 @@ describe Mongoid::Contextual::Mongo do
2181
2557
  described_class.new(criteria)
2182
2558
  end
2183
2559
 
2184
- context "when the whole context is loaded" do
2185
-
2186
- before do
2187
- context.to_a
2188
- end
2189
-
2190
- context "when all of the documents are cached" do
2191
-
2192
- let(:criteria) do
2193
- Band.all.cache
2194
- end
2195
-
2196
- context "when requesting all of the documents" do
2197
-
2198
- let(:docs) do
2199
- context.last(3)
2200
- end
2201
-
2202
- it "returns all of the documents without touching the database" do
2203
- expect(context).to receive(:view).never
2204
- expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2205
- end
2206
- end
2207
-
2208
- context "when requesting fewer than all of the documents" do
2209
-
2210
- let(:docs) do
2211
- context.last(2)
2212
- end
2213
-
2214
- it "returns all of the documents without touching the database" do
2215
- expect(context).to receive(:view).never
2216
- expect(docs).to eq([ new_order, rolling_stones ])
2217
- end
2218
- end
2219
- end
2220
-
2221
- context "when only one document is cached" do
2222
-
2223
- let(:criteria) do
2224
- Band.where(name: "Depeche Mode").cache
2225
- end
2226
-
2227
- context "when requesting one document" do
2228
-
2229
- let(:docs) do
2230
- context.last(1)
2231
- end
2232
-
2233
- it "returns one document without touching the database" do
2234
- expect(context).to receive(:view).never
2235
- expect(docs).to eq([ depeche_mode ])
2236
- end
2237
- end
2238
- end
2239
- end
2240
-
2241
- context "when the last method was called before" do
2560
+ context "when query cache is enabled" do
2561
+ query_cache_enabled
2242
2562
 
2243
2563
  let(:context) do
2244
2564
  described_class.new(criteria)
2245
2565
  end
2246
2566
 
2247
2567
  let(:criteria) do
2248
- Band.all.cache
2568
+ Band.all
2249
2569
  end
2250
2570
 
2251
2571
  before do
@@ -2262,18 +2582,20 @@ describe Mongoid::Contextual::Mongo do
2262
2582
  context "when getting all of the documents" do
2263
2583
  let(:limit) { 3 }
2264
2584
 
2265
- it "returns all documents without touching the database" do
2266
- expect(context).to receive(:view).never
2267
- expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2585
+ it "returns all documents without touching the db" do
2586
+ expect_no_queries do
2587
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2588
+ end
2268
2589
  end
2269
2590
  end
2270
2591
 
2271
2592
  context "when getting fewer documents" do
2272
2593
  let(:limit) { 2 }
2273
2594
 
2274
- it "returns the correct documents without touching the database" do
2275
- expect(context).to receive(:view).never
2276
- expect(docs).to eq([ new_order, rolling_stones ])
2595
+ it "returns the correct documents without touching the db" do
2596
+ expect_no_queries do
2597
+ expect(docs).to eq([ new_order, rolling_stones ])
2598
+ end
2277
2599
  end
2278
2600
  end
2279
2601
  end
@@ -2284,9 +2606,10 @@ describe Mongoid::Contextual::Mongo do
2284
2606
  context "when getting the same number of documents" do
2285
2607
  let(:limit) { 2 }
2286
2608
 
2287
- it "returns the correct documents without touching the database" do
2288
- expect(context).to receive(:view).never
2289
- expect(docs).to eq([ new_order, rolling_stones ])
2609
+ it "returns the correct documents without touching the db" do
2610
+ expect_no_queries do
2611
+ expect(docs).to eq([ new_order, rolling_stones ])
2612
+ end
2290
2613
  end
2291
2614
  end
2292
2615
 
@@ -2294,8 +2617,9 @@ describe Mongoid::Contextual::Mongo do
2294
2617
  let(:limit) { 3 }
2295
2618
 
2296
2619
  it "returns the correct documents and touches the database" do
2297
- expect(context).to receive(:view).twice.and_call_original
2298
- expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2620
+ expect_query(1) do
2621
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2622
+ end
2299
2623
  end
2300
2624
  end
2301
2625
  end
@@ -2307,8 +2631,9 @@ describe Mongoid::Contextual::Mongo do
2307
2631
  let(:limit) { 1 }
2308
2632
 
2309
2633
  it "returns the correct documents without touching the database" do
2310
- expect(context).to receive(:view).never
2311
- expect(docs).to eq([ rolling_stones ])
2634
+ expect_no_queries do
2635
+ expect(docs).to eq([ rolling_stones ])
2636
+ end
2312
2637
  end
2313
2638
  end
2314
2639
 
@@ -2316,8 +2641,9 @@ describe Mongoid::Contextual::Mongo do
2316
2641
  let(:limit) { 3 }
2317
2642
 
2318
2643
  it "returns the correct documents and touches the database" do
2319
- expect(context).to receive(:view).twice.and_call_original
2320
- expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2644
+ expect_query(1) do
2645
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2646
+ end
2321
2647
  end
2322
2648
  end
2323
2649
  end
@@ -2325,14 +2651,15 @@ describe Mongoid::Contextual::Mongo do
2325
2651
  end
2326
2652
  end
2327
2653
 
2328
- context "when calling #last then #first" do
2654
+ context "when calling #last then #first and the query cache is enabled" do
2655
+ query_cache_enabled
2329
2656
 
2330
2657
  let(:context) do
2331
2658
  described_class.new(criteria)
2332
2659
  end
2333
2660
 
2334
2661
  let(:criteria) do
2335
- Band.all.cache
2662
+ Band.all
2336
2663
  end
2337
2664
 
2338
2665
  before do
@@ -2348,12 +2675,12 @@ describe Mongoid::Contextual::Mongo do
2348
2675
  let(:limit) { 1 }
2349
2676
 
2350
2677
  it "hits the database" do
2351
- expect(context).to receive(:view).exactly(3).times.and_call_original
2352
- docs
2678
+ expect_query(1) do
2679
+ docs
2680
+ end
2353
2681
  end
2354
2682
 
2355
2683
  it "gets the correct document" do
2356
- pending "MONGOID-5416"
2357
2684
  expect(docs).to eq([ depeche_mode ])
2358
2685
  end
2359
2686
  end
@@ -2406,37 +2733,28 @@ describe Mongoid::Contextual::Mongo do
2406
2733
  described_class.new(criteria)
2407
2734
  end
2408
2735
 
2409
- it "returns the number of documents that match" do
2410
- expect(context.send(method)).to eq(2)
2411
- end
2736
+ context "when broken_view_options is false" do
2737
+ driver_config_override :broken_view_options, false
2412
2738
 
2413
- context "when calling more than once" do
2414
- it "returns the cached value for subsequent calls" do
2415
- expect(context.view).to receive(:count_documents).once.and_return(2)
2416
- 2.times { expect(context.send(method)).to eq(2) }
2739
+ it "returns the number of documents that match" do
2740
+ expect(context.send(method)).to eq(1)
2417
2741
  end
2418
2742
  end
2419
2743
 
2420
- context "when the results have been iterated over" do
2421
-
2422
- before do
2423
- context.entries
2424
- end
2744
+ context "when broken_view_options is true" do
2745
+ driver_config_override :broken_view_options, true
2425
2746
 
2426
- it "returns the cached value for all calls" do
2427
- expect(context.view).to receive(:count_documents).once.and_return(2)
2747
+ it "returns the number of documents that match" do
2428
2748
  expect(context.send(method)).to eq(2)
2429
2749
  end
2750
+ end
2430
2751
 
2431
- context "when the results have been iterated over multiple times" do
2432
-
2433
- before do
2434
- context.entries
2435
- end
2752
+ context "when calling more than once with different limits" do
2753
+ driver_config_override :broken_view_options, false
2436
2754
 
2437
- it "resets the length on each full iteration" do
2438
- expect(context.size).to eq(2)
2439
- end
2755
+ it "does not cache the value" do
2756
+ expect(context.limit(1).send(method)).to eq(1)
2757
+ expect(context.limit(2).send(method)).to eq(2)
2440
2758
  end
2441
2759
  end
2442
2760
  end
@@ -2455,10 +2773,12 @@ describe Mongoid::Contextual::Mongo do
2455
2773
  expect(context.send(method)).to eq(1)
2456
2774
  end
2457
2775
 
2458
- context "when calling more than once" do
2459
- it "returns the cached value for subsequent calls" do
2460
- expect(context.view).to receive(:count_documents).once.and_return(1)
2461
- 2.times { expect(context.send(method)).to eq(1) }
2776
+ context "when calling more than once with different skips" do
2777
+ driver_config_override :broken_view_options, false
2778
+
2779
+ it "does not cache the value" do
2780
+ expect(context.skip(0).send(method)).to eq(1)
2781
+ expect(context.skip(1).send(method)).to eq(0)
2462
2782
  end
2463
2783
  end
2464
2784
 
@@ -2580,7 +2900,7 @@ describe Mongoid::Contextual::Mongo do
2580
2900
  it "raises an error" do
2581
2901
  expect do
2582
2902
  Person.take!
2583
- end.to raise_error(Mongoid::Errors::DocumentNotFound)
2903
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Person./)
2584
2904
  end
2585
2905
  end
2586
2906
  end
@@ -2602,9 +2922,10 @@ describe Mongoid::Contextual::Mongo do
2602
2922
 
2603
2923
  context "when passed the symbol field name" do
2604
2924
 
2605
- it "performs mapping and warns" do
2606
- expect(Mongoid::Warnings).to receive(:warn_map_field_deprecated)
2607
- expect(context.map(:name)).to eq ["Depeche Mode", "New Order"]
2925
+ it "raises an error" do
2926
+ expect do
2927
+ context.map(:name)
2928
+ end.to raise_error(ArgumentError)
2608
2929
  end
2609
2930
  end
2610
2931
 
@@ -2833,219 +3154,948 @@ describe Mongoid::Contextual::Mongo do
2833
3154
  described_class.new(criteria)
2834
3155
  end
2835
3156
 
2836
- let(:results) do
2837
- context.map_reduce(map, reduce).out(inline: 1)
3157
+ let(:results) do
3158
+ context.map_reduce(map, reduce).out(inline: 1)
3159
+ end
3160
+
3161
+ it "returns the first aggregate result" do
3162
+ expect(results).to include(
3163
+ { "_id" => "Depeche Mode", "value" => { "likes" => 200 }}
3164
+ )
3165
+ end
3166
+
3167
+ it "returns the correct number of documents" do
3168
+ expect(results.count).to eq(1)
3169
+ end
3170
+
3171
+ it "contains the entire raw results" do
3172
+ expect(results["results"]).to eq([
3173
+ { "_id" => "Depeche Mode", "value" => { "likes" => 200 }}
3174
+ ])
3175
+ end
3176
+ end
3177
+
3178
+ context "when the output is replace" do
3179
+
3180
+ let(:criteria) do
3181
+ Band.limit(1)
3182
+ end
3183
+
3184
+ let(:context) do
3185
+ described_class.new(criteria)
3186
+ end
3187
+
3188
+ let(:results) do
3189
+ context.map_reduce(map, reduce).out(replace: "mr-output")
3190
+ end
3191
+
3192
+ it "returns the correct number of documents" do
3193
+ expect(results.count).to eq(1)
3194
+ end
3195
+
3196
+ it "contains the entire results" do
3197
+ expect(results).to eq([
3198
+ { "_id" => "Depeche Mode", "value" => { "likes" => 200 }}
3199
+ ])
3200
+ end
3201
+ end
3202
+
3203
+ context "when the output is reduce" do
3204
+
3205
+ let(:criteria) do
3206
+ Band.limit(1)
3207
+ end
3208
+
3209
+ let(:context) do
3210
+ described_class.new(criteria)
3211
+ end
3212
+
3213
+ let(:results) do
3214
+ context.map_reduce(map, reduce).out(reduce: :mr_output)
3215
+ end
3216
+
3217
+ it "returns the correct number of documents" do
3218
+ expect(results.count).to eq(1)
3219
+ end
3220
+
3221
+ it "contains the entire results" do
3222
+ expect(results).to eq([
3223
+ { "_id" => "Depeche Mode", "value" => { "likes" => 200 }}
3224
+ ])
3225
+ end
3226
+ end
3227
+
3228
+ context "when the output is merge" do
3229
+
3230
+ let(:criteria) do
3231
+ Band.limit(1)
3232
+ end
3233
+
3234
+ let(:context) do
3235
+ described_class.new(criteria)
3236
+ end
3237
+
3238
+ let(:results) do
3239
+ context.map_reduce(map, reduce).out(merge: :mr_output)
3240
+ end
3241
+
3242
+ it "returns the correct number of documents" do
3243
+ expect(results.count).to eq(1)
3244
+ end
3245
+
3246
+ it "contains the entire results" do
3247
+ expect(results).to eq([
3248
+ { "_id" => "Depeche Mode", "value" => { "likes" => 200 }}
3249
+ ])
3250
+ end
3251
+ end
3252
+
3253
+ context "when the output specifies a different db" do
3254
+ # Limit is not supported in sharded clusters
3255
+ require_topology :single, :replica_set
3256
+
3257
+ let(:criteria) do
3258
+ Band.limit(1)
3259
+ end
3260
+
3261
+ let(:context) do
3262
+ described_class.new(criteria)
3263
+ end
3264
+
3265
+ after do
3266
+ Band.with(database: 'another-db') do |b|
3267
+ b.all.delete
3268
+ end
3269
+ end
3270
+
3271
+ context 'when db is a string' do
3272
+
3273
+ let(:results) do
3274
+ context.map_reduce(map, reduce).out(merge: :mr_output, db: 'another-db')
3275
+ end
3276
+
3277
+ it "returns the correct number of documents" do
3278
+ expect(results.count).to eq(1)
3279
+ end
3280
+
3281
+ it "contains the entire results" do
3282
+ expect(results).to eq([
3283
+ { "_id" => "Depeche Mode", "value" => { "likes" => 200 }}
3284
+ ])
3285
+ end
3286
+
3287
+ it 'writes to the specified db' do
3288
+ expect(Band.mongo_client.with(database: 'another-db')[:mr_output].find.count).to eq(1)
3289
+ end
3290
+ end
3291
+
3292
+ context 'when db is a symbol' do
3293
+
3294
+ let(:results) do
3295
+ context.map_reduce(map, reduce).out(merge: :mr_output, 'db' => 'another-db')
3296
+ end
3297
+
3298
+ it "returns the correct number of documents" do
3299
+ expect(results.count).to eq(1)
3300
+ end
3301
+
3302
+ it "contains the entire results" do
3303
+ expect(results).to eq([
3304
+ { "_id" => "Depeche Mode", "value" => { "likes" => 200 }}
3305
+ ])
3306
+ end
3307
+
3308
+ it 'writes to the specified db' do
3309
+ expect(Band.mongo_client.with(database: 'another-db')[:mr_output].find.count).to eq(1)
3310
+ end
3311
+ end
3312
+ end
3313
+
3314
+ context "when providing no output" do
3315
+
3316
+ let(:criteria) do
3317
+ Band.limit(1)
3318
+ end
3319
+
3320
+ let(:context) do
3321
+ described_class.new(criteria)
3322
+ end
3323
+
3324
+ let(:results) do
3325
+ context.map_reduce(map, reduce)
3326
+ end
3327
+
3328
+ it "raises an error" do
3329
+ expect {
3330
+ results.entries
3331
+ }.to raise_error(Mongoid::Errors::NoMapReduceOutput)
3332
+ end
3333
+ end
3334
+
3335
+ context "when providing a finalize" do
3336
+
3337
+ let(:criteria) do
3338
+ Band.limit(1)
3339
+ end
3340
+
3341
+ let(:context) do
3342
+ described_class.new(criteria)
3343
+ end
3344
+
3345
+ let(:finalize) do
3346
+ %Q{
3347
+ function(key, value) {
3348
+ value.extra = true;
3349
+ return value;
3350
+ }}
3351
+ end
3352
+
3353
+ let(:results) do
3354
+ context.map_reduce(map, reduce).out(inline: 1).finalize(finalize)
3355
+ end
3356
+
3357
+ it "returns the correct number of documents" do
3358
+ expect(results.count).to eq(1)
3359
+ end
3360
+
3361
+ it "contains the entire results" do
3362
+ expect(results).to eq([
3363
+ { "_id" => "Depeche Mode", "value" => { "likes" => 200, "extra" => true }}
3364
+ ])
3365
+ end
3366
+ end
3367
+ end
3368
+
3369
+ describe "#skip" do
3370
+
3371
+ let!(:depeche_mode) do
3372
+ Band.create!(name: "Depeche Mode")
3373
+ end
3374
+
3375
+ let!(:new_order) do
3376
+ Band.create!(name: "New Order")
3377
+ end
3378
+
3379
+ let(:criteria) do
3380
+ Band.all
3381
+ end
3382
+
3383
+ let(:context) do
3384
+ described_class.new(criteria)
3385
+ end
3386
+
3387
+ it "limits the results" do
3388
+ expect(context.skip(1).entries).to eq([ new_order ])
3389
+ end
3390
+ end
3391
+
3392
+ describe "#sort" do
3393
+
3394
+ let!(:depeche_mode) do
3395
+ Band.create!(name: "Depeche Mode")
3396
+ end
3397
+
3398
+ let!(:new_order) do
3399
+ Band.create!(name: "New Order")
3400
+ end
3401
+
3402
+ let(:criteria) do
3403
+ Band.all
3404
+ end
3405
+
3406
+ let(:context) do
3407
+ described_class.new(criteria)
3408
+ end
3409
+
3410
+ context "when providing a spec" do
3411
+
3412
+ it "sorts the results" do
3413
+ expect(context.sort(name: -1).entries).to eq([ new_order, depeche_mode ])
3414
+ end
3415
+
3416
+ it "returns the context" do
3417
+ expect(context.sort(name: 1)).to eq(context)
3418
+ end
3419
+ end
3420
+
3421
+ context "when providing a block" do
3422
+
3423
+ let(:sorted) do
3424
+ context.sort do |a, b|
3425
+ b.name <=> a.name
3426
+ end
3427
+ end
3428
+
3429
+ it "sorts the results in memory" do
3430
+ expect(sorted).to eq([ new_order, depeche_mode ])
3431
+ end
3432
+ end
3433
+ end
3434
+
3435
+ describe "#update" do
3436
+
3437
+ let!(:depeche_mode) do
3438
+ Band.create!(name: "Depeche Mode")
3439
+ end
3440
+
3441
+ let!(:new_order) do
3442
+ Band.create!(name: "New Order")
3443
+ end
3444
+
3445
+ let(:criteria) do
3446
+ Band.all
3447
+ end
3448
+
3449
+ let(:context) do
3450
+ described_class.new(criteria)
3451
+ end
3452
+
3453
+ context "when adding an element to a HABTM set" do
3454
+
3455
+ let(:person) do
3456
+ Person.create!
3457
+ end
3458
+
3459
+ let(:preference) do
3460
+ Preference.create!
3461
+ end
3462
+
3463
+ before do
3464
+ Person.where(id: person.id).
3465
+ update("$addToSet" => { preference_ids: preference.id })
3466
+ end
3467
+
3468
+ it "adds a single element to the array" do
3469
+ expect(person.reload.preference_ids).to eq([ preference.id ])
3470
+ end
3471
+ end
3472
+
3473
+ context "when providing attributes" do
3474
+
3475
+ context "when the attributes are of the correct type" do
3476
+
3477
+ before do
3478
+ context.update(name: "Smiths")
3479
+ end
3480
+
3481
+ it "updates only the first matching document" do
3482
+ expect(depeche_mode.reload.name).to eq("Smiths")
3483
+ end
3484
+
3485
+ it "does not update the last matching document" do
3486
+ expect(new_order.reload.name).to eq("New Order")
3487
+ end
3488
+ end
3489
+
3490
+ context "when the attributes must be mongoized" do
3491
+
3492
+ context "when coercing a string to integer" do
3493
+
3494
+ before do
3495
+ context.update(member_count: "1")
3496
+ end
3497
+
3498
+ it "updates the first matching document" do
3499
+ expect(depeche_mode.reload.member_count).to eq(1)
3500
+ end
3501
+
3502
+ it "does not update the last matching document" do
3503
+ expect(new_order.reload.member_count).to be_nil
3504
+ end
3505
+ end
3506
+
3507
+ context "when coercing a string to date" do
3508
+
3509
+ before do
3510
+ context.update(founded: "1979/1/1")
3511
+ end
3512
+
3513
+ it "updates the first matching document" do
3514
+ expect(depeche_mode.reload.founded).to eq(Date.new(1979, 1, 1))
3515
+ end
3516
+
3517
+ it "does not update the last matching document" do
3518
+ expect(new_order.reload.founded).to be_nil
3519
+ end
3520
+ end
3521
+ end
3522
+ end
3523
+
3524
+ context "when providing atomic operations" do
3525
+
3526
+ context "when only atomic operations are provided" do
3527
+
3528
+ context "when the attributes are in the correct type" do
3529
+
3530
+ before do
3531
+ context.update("$set" => { name: "Smiths" })
3532
+ end
3533
+
3534
+ it "updates the first matching document" do
3535
+ expect(depeche_mode.reload.name).to eq("Smiths")
3536
+ end
3537
+
3538
+ it "does not update the last matching document" do
3539
+ expect(new_order.reload.name).to eq("New Order")
3540
+ end
3541
+ end
3542
+
3543
+ context "when the attributes must be mongoized" do
3544
+
3545
+ before do
3546
+ context.update("$set" => { member_count: "1" })
3547
+ end
3548
+
3549
+ it "updates the first matching document" do
3550
+ expect(depeche_mode.reload.member_count).to eq(1)
3551
+ end
3552
+
3553
+ it "does not update the last matching document" do
3554
+ expect(new_order.reload.member_count).to be_nil
3555
+ end
3556
+ end
3557
+ end
3558
+
3559
+ context "when a mix are provided" do
3560
+
3561
+ before do
3562
+ context.update("$set" => { name: "Smiths" }, likes: 100)
3563
+ end
3564
+
3565
+ it "updates the first matching document's set" do
3566
+ expect(depeche_mode.reload.name).to eq("Smiths")
3567
+ end
3568
+
3569
+ it "updates the first matching document's updates" do
3570
+ expect(depeche_mode.reload.likes).to eq(100)
3571
+ end
3572
+
3573
+ it "does not update the last matching document's set" do
3574
+ expect(new_order.reload.name).to eq("New Order")
3575
+ end
3576
+
3577
+ it "does not update the last matching document's updates" do
3578
+ expect(new_order.reload.likes).to be_nil
3579
+ end
3580
+ end
3581
+ end
3582
+
3583
+ context "when providing no attributes" do
3584
+
3585
+ it "returns false" do
3586
+ expect(context.update).to be false
3587
+ end
3588
+ end
3589
+
3590
+ context 'when provided array filters' do
3591
+ min_server_version '3.6'
3592
+
3593
+ before do
3594
+ Band.delete_all
3595
+ b = Band.new(name: 'Depeche Mode')
3596
+ b.labels << Label.new(name: 'Warner')
3597
+ b.labels << Label.new(name: 'Sony')
3598
+ b.labels << Label.new(name: 'Cbs')
3599
+ b.save!
3600
+
3601
+ b = Band.new(name: 'FKA Twigs')
3602
+ b.labels << Label.new(name: 'Warner')
3603
+ b.labels << Label.new(name: 'Cbs')
3604
+ b.save!
3605
+ end
3606
+
3607
+
3608
+ let(:criteria) do
3609
+ Band.where(name: 'Depeche Mode')
3610
+ end
3611
+
3612
+ let!(:update) do
3613
+ context.update({ '$set' => { 'labels.$[i].name' => 'Sony' } },
3614
+ array_filters: [{ 'i.name' => 'Cbs' }])
3615
+ end
3616
+
3617
+ it 'applies the array filters' do
3618
+ expect(Band.where(name: 'Depeche Mode').first.labels.collect(&:name)).to match_array(['Warner', 'Sony', 'Sony'])
3619
+ end
3620
+
3621
+ it 'does not affect other documents' do
3622
+ expect(Band.where(name: 'FKA Twigs').first.labels.collect(&:name)).to match_array(['Warner', 'Cbs'])
3623
+ end
3624
+ end
3625
+ end
3626
+
3627
+ describe "#update_all" do
3628
+
3629
+ let!(:depeche_mode) do
3630
+ Band.create!(name: "Depeche Mode", origin: "Essex")
3631
+ end
3632
+
3633
+ let!(:new_order) do
3634
+ Band.create!(name: "New Order")
3635
+ end
3636
+
3637
+ let(:criteria) do
3638
+ Band.all
3639
+ end
3640
+
3641
+ let(:context) do
3642
+ described_class.new(criteria)
3643
+ end
3644
+
3645
+ context "when providing attributes" do
3646
+
3647
+ context "when the attributes are of the correct type" do
3648
+
3649
+ before do
3650
+ context.update_all(name: "Smiths")
3651
+ end
3652
+
3653
+ it "updates the first matching document" do
3654
+ expect(depeche_mode.reload.name).to eq("Smiths")
3655
+ end
3656
+
3657
+ it "does not clear out other attributes" do
3658
+ expect(depeche_mode.reload.origin).to eq("Essex")
3659
+ end
3660
+
3661
+ it "updates the last matching document" do
3662
+ expect(new_order.reload.name).to eq("Smiths")
3663
+ end
3664
+ end
3665
+
3666
+ context "when the attributes must be mongoized" do
3667
+
3668
+ before do
3669
+ context.update_all(member_count: "1")
3670
+ end
3671
+
3672
+ it "updates the first matching document" do
3673
+ expect(depeche_mode.reload.member_count).to eq(1)
3674
+ end
3675
+
3676
+ it "updates the last matching document" do
3677
+ expect(new_order.reload.member_count).to eq(1)
3678
+ end
3679
+ end
3680
+
3681
+ context "when using aliased field names" do
3682
+
3683
+ before do
3684
+ context.update_all(years: 100)
3685
+ end
3686
+
3687
+ it "updates the first matching document" do
3688
+ expect(depeche_mode.reload.years).to eq(100)
3689
+ end
3690
+
3691
+ it "updates the last matching document" do
3692
+ expect(new_order.reload.years).to eq(100)
3693
+ end
3694
+ end
3695
+ end
3696
+
3697
+ context "when providing atomic operations" do
3698
+
3699
+ context "when only atomic operations are provided" do
3700
+
3701
+ context "when the attributes are in the correct type" do
3702
+
3703
+ context "when operation is $set" do
3704
+
3705
+ before do
3706
+ context.update_all("$set" => { name: "Smiths" })
3707
+ end
3708
+
3709
+ it "updates the first matching document" do
3710
+ expect(depeche_mode.reload.name).to eq("Smiths")
3711
+ end
3712
+
3713
+ it "updates the last matching document" do
3714
+ expect(new_order.reload.name).to eq("Smiths")
3715
+ end
3716
+ end
3717
+
3718
+ context "when operation is $push" do
3719
+
3720
+ before do
3721
+ depeche_mode.update_attribute(:genres, ["electronic"])
3722
+ new_order.update_attribute(:genres, ["electronic"])
3723
+ context.update_all("$push" => { genres: "pop" })
3724
+ end
3725
+
3726
+ it "updates the first matching document" do
3727
+ expect(depeche_mode.reload.genres).to eq(["electronic", "pop"])
3728
+ end
3729
+
3730
+ it "updates the last matching document" do
3731
+ expect(new_order.reload.genres).to eq(["electronic", "pop"])
3732
+ end
3733
+ end
3734
+
3735
+ context "when operation is $addToSet" do
3736
+
3737
+ before do
3738
+ context.update_all("$addToSet" => { genres: "electronic" })
3739
+ end
3740
+
3741
+ it "updates the first matching document" do
3742
+ expect(depeche_mode.reload.genres).to eq(["electronic"])
3743
+ end
3744
+
3745
+ it "updates the last matching document" do
3746
+ expect(new_order.reload.genres).to eq(["electronic"])
3747
+ end
3748
+ end
3749
+ end
3750
+
3751
+ context 'when using aliased field names' do
3752
+ before do
3753
+ context.update_all('$set' => { years: 100 })
3754
+ end
3755
+
3756
+ it "updates the first matching document" do
3757
+ expect(depeche_mode.reload.years).to eq(100)
3758
+ end
3759
+
3760
+ it "updates the last matching document" do
3761
+ expect(new_order.reload.years).to eq(100)
3762
+ end
3763
+ end
3764
+
3765
+ context "when the attributes must be mongoized" do
3766
+
3767
+ before do
3768
+ context.update_all("$set" => { member_count: "1" })
3769
+ end
3770
+
3771
+ it "updates the first matching document" do
3772
+ expect(depeche_mode.reload.member_count).to eq(1)
3773
+ end
3774
+
3775
+ it "updates the last matching document" do
3776
+ expect(new_order.reload.member_count).to eq(1)
3777
+ end
3778
+ end
3779
+ end
3780
+
3781
+ context "when a mix are provided" do
3782
+
3783
+ before do
3784
+ context.update_all("$set" => { name: "Smiths" }, likes: 100)
3785
+ end
3786
+
3787
+ it "updates the first matching document's set" do
3788
+ expect(depeche_mode.reload.name).to eq("Smiths")
3789
+ end
3790
+
3791
+ it "updates the first matching document's updates" do
3792
+ expect(depeche_mode.reload.likes).to eq(100)
3793
+ end
3794
+
3795
+ it "updates the last matching document's set" do
3796
+ expect(new_order.reload.name).to eq("Smiths")
3797
+ end
3798
+
3799
+ it "updates the last matching document's updates" do
3800
+ expect(new_order.reload.likes).to eq(100)
3801
+ end
3802
+ end
3803
+ end
3804
+
3805
+ context "when providing no attributes" do
3806
+
3807
+ it "returns false" do
3808
+ expect(context.update_all).to be false
3809
+ end
3810
+ end
3811
+
3812
+ context 'when provided array filters' do
3813
+ min_server_version '3.6'
3814
+
3815
+ before do
3816
+ Band.delete_all
3817
+ b = Band.new(name: 'Depeche Mode')
3818
+ b.labels << Label.new(name: 'Warner')
3819
+ b.labels << Label.new(name: 'Sony')
3820
+ b.labels << Label.new(name: 'Cbs')
3821
+ b.save!
3822
+
3823
+ b = Band.new(name: 'FKA Twigs')
3824
+ b.labels << Label.new(name: 'Warner')
3825
+ b.labels << Label.new(name: 'Cbs')
3826
+ b.save!
3827
+ end
3828
+
3829
+
3830
+ let(:criteria) do
3831
+ Band.all
2838
3832
  end
2839
3833
 
2840
- it "returns the first aggregate result" do
2841
- expect(results).to include(
2842
- { "_id" => "Depeche Mode", "value" => { "likes" => 200 }}
2843
- )
3834
+ let!(:update) do
3835
+ context.update_all({ '$set' => { 'labels.$[i].name' => 'Sony' } },
3836
+ array_filters: [{ 'i.name' => 'Cbs' }])
2844
3837
  end
2845
3838
 
2846
- it "returns the correct number of documents" do
2847
- expect(results.count).to eq(1)
3839
+ it 'applies the array filters' do
3840
+ expect(Band.where(name: 'Depeche Mode').first.labels.collect(&:name)).to match_array(['Warner', 'Sony', 'Sony'])
2848
3841
  end
2849
3842
 
2850
- it "contains the entire raw results" do
2851
- expect(results["results"]).to eq([
2852
- { "_id" => "Depeche Mode", "value" => { "likes" => 200 }}
2853
- ])
3843
+ it 'updates all documents' do
3844
+ expect(Band.where(name: 'FKA Twigs').first.labels.collect(&:name)).to match_array(['Warner', 'Sony'])
2854
3845
  end
2855
3846
  end
3847
+ end
2856
3848
 
2857
- context "when the output is replace" do
3849
+ describe '#pipeline' do
3850
+
3851
+ context 'when the criteria has a selector' do
3852
+
3853
+ before do
3854
+ Artist.index(name: "text")
3855
+ Artist.create_indexes
3856
+ end
2858
3857
 
2859
3858
  let(:criteria) do
2860
- Band.limit(1)
3859
+ Artist.text_search("New Order")
2861
3860
  end
2862
3861
 
2863
3862
  let(:context) do
2864
3863
  described_class.new(criteria)
2865
3864
  end
2866
3865
 
2867
- let(:results) do
2868
- context.map_reduce(map, reduce).out(replace: "mr-output")
3866
+ let(:pipeline_match) do
3867
+ context.send(:pipeline, :some_field).first['$match']
2869
3868
  end
2870
3869
 
2871
- it "returns the correct number of documents" do
2872
- expect(results.count).to eq(1)
3870
+ it 'creates a pipeline with the selector as one of the $match criteria' do
3871
+ expect(pipeline_match).to include({ '$text' => { '$search' => "New Order" } })
2873
3872
  end
2874
3873
 
2875
- it "contains the entire results" do
2876
- expect(results).to eq([
2877
- { "_id" => "Depeche Mode", "value" => { "likes" => 200 }}
2878
- ])
3874
+ it 'creates a pipeline with the $exists operator as one of the $match criteria' do
3875
+ expect(pipeline_match).to include({ 'some_field' => { '$exists' => true } })
2879
3876
  end
2880
3877
  end
3878
+ end
2881
3879
 
2882
- context "when the output is reduce" do
3880
+ describe "#first!" do
2883
3881
 
2884
- let(:criteria) do
2885
- Band.limit(1)
2886
- end
3882
+ let!(:depeche_mode) do
3883
+ Band.create!(name: "Depeche Mode")
3884
+ end
2887
3885
 
2888
- let(:context) do
2889
- described_class.new(criteria)
2890
- end
3886
+ let!(:new_order) do
3887
+ Band.create!(name: "New Order")
3888
+ end
2891
3889
 
2892
- let(:results) do
2893
- context.map_reduce(map, reduce).out(reduce: :mr_output)
2894
- end
3890
+ let!(:rolling_stones) do
3891
+ Band.create!(name: "The Rolling Stones")
3892
+ end
2895
3893
 
2896
- it "returns the correct number of documents" do
2897
- expect(results.count).to eq(1)
3894
+ let!(:death_cab) do
3895
+ Band.create!(name: "Death Cab For Cutie")
3896
+ end
3897
+
3898
+ let(:context) do
3899
+ described_class.new(criteria)
3900
+ end
3901
+
3902
+ context "when there's no sort" do
3903
+ let(:criteria) do
3904
+ Band.all
2898
3905
  end
2899
3906
 
2900
- it "contains the entire results" do
2901
- expect(results).to eq([
2902
- { "_id" => "Depeche Mode", "value" => { "likes" => 200 }}
2903
- ])
3907
+ it "gets the first document" do
3908
+ expect(context.first!).to eq(depeche_mode)
2904
3909
  end
2905
3910
  end
2906
3911
 
2907
- context "when the output is merge" do
2908
-
3912
+ context "when there's a custom sort" do
2909
3913
  let(:criteria) do
2910
- Band.limit(1)
3914
+ Band.all
2911
3915
  end
2912
3916
 
2913
- let(:context) do
2914
- described_class.new(criteria)
3917
+ it "gets the first document" do
3918
+ expect(context.sort(name: 1).first!).to eq(death_cab)
2915
3919
  end
3920
+ end
2916
3921
 
2917
- let(:results) do
2918
- context.map_reduce(map, reduce).out(merge: :mr_output)
3922
+ context "when there are no documents" do
3923
+ let(:criteria) do
3924
+ Band.where(name: "bogus")
2919
3925
  end
2920
3926
 
2921
- it "returns the correct number of documents" do
2922
- expect(results.count).to eq(1)
3927
+ it "raises an error" do
3928
+ expect do
3929
+ context.first!
3930
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
2923
3931
  end
3932
+ end
3933
+ end
2924
3934
 
2925
- it "contains the entire results" do
2926
- expect(results).to eq([
2927
- { "_id" => "Depeche Mode", "value" => { "likes" => 200 }}
2928
- ])
2929
- end
3935
+ describe "#last!" do
3936
+
3937
+ let!(:depeche_mode) do
3938
+ Band.create!(name: "Depeche Mode")
2930
3939
  end
2931
3940
 
2932
- context "when the output specifies a different db" do
2933
- # Limit is not supported in sharded clusters
2934
- require_topology :single, :replica_set
3941
+ let!(:new_order) do
3942
+ Band.create!(name: "New Order")
3943
+ end
3944
+
3945
+ let!(:rolling_stones) do
3946
+ Band.create!(name: "The Rolling Stones")
3947
+ end
3948
+
3949
+ let!(:death_cab) do
3950
+ Band.create!(name: "Death Cab For Cutie")
3951
+ end
2935
3952
 
3953
+ let(:context) do
3954
+ described_class.new(criteria)
3955
+ end
3956
+
3957
+ context "when there's no sort" do
2936
3958
  let(:criteria) do
2937
- Band.limit(1)
3959
+ Band.all
2938
3960
  end
2939
3961
 
2940
- let(:context) do
2941
- described_class.new(criteria)
3962
+ it "gets the last document" do
3963
+ expect(context.last!).to eq(death_cab)
2942
3964
  end
3965
+ end
2943
3966
 
2944
- after do
2945
- Band.with(database: 'another-db') do |b|
2946
- b.all.delete
2947
- end
3967
+ context "when there's a custom sort" do
3968
+ let(:criteria) do
3969
+ Band.all
2948
3970
  end
2949
3971
 
2950
- context 'when db is a string' do
3972
+ it "gets the last document" do
3973
+ expect(context.sort(name: 1).last!).to eq(rolling_stones)
3974
+ end
3975
+ end
2951
3976
 
2952
- let(:results) do
2953
- context.map_reduce(map, reduce).out(merge: :mr_output, db: 'another-db')
2954
- end
3977
+ context "when there are no documents" do
3978
+ let(:criteria) do
3979
+ Band.where(name: "bogus")
3980
+ end
2955
3981
 
2956
- it "returns the correct number of documents" do
2957
- expect(results.count).to eq(1)
2958
- end
3982
+ it "raises an error" do
3983
+ expect do
3984
+ context.last!
3985
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
3986
+ end
3987
+ end
3988
+ end
2959
3989
 
2960
- it "contains the entire results" do
2961
- expect(results).to eq([
2962
- { "_id" => "Depeche Mode", "value" => { "likes" => 200 }}
2963
- ])
2964
- end
3990
+ describe "#second" do
2965
3991
 
2966
- it 'writes to the specified db' do
2967
- expect(Band.mongo_client.with(database: 'another-db')[:mr_output].find.count).to eq(1)
2968
- end
2969
- end
3992
+ let!(:depeche_mode) do
3993
+ Band.create!(name: "Depeche Mode")
3994
+ end
2970
3995
 
2971
- context 'when db is a symbol' do
3996
+ let!(:new_order) do
3997
+ Band.create!(name: "New Order")
3998
+ end
2972
3999
 
2973
- let(:results) do
2974
- context.map_reduce(map, reduce).out(merge: :mr_output, 'db' => 'another-db')
2975
- end
4000
+ let!(:rolling_stones) do
4001
+ Band.create!(name: "The Rolling Stones")
4002
+ end
2976
4003
 
2977
- it "returns the correct number of documents" do
2978
- expect(results.count).to eq(1)
2979
- end
4004
+ let!(:death_cab) do
4005
+ Band.create!(name: "Death Cab For Cutie")
4006
+ end
2980
4007
 
2981
- it "contains the entire results" do
2982
- expect(results).to eq([
2983
- { "_id" => "Depeche Mode", "value" => { "likes" => 200 }}
2984
- ])
2985
- end
4008
+ let(:context) do
4009
+ described_class.new(criteria)
4010
+ end
2986
4011
 
2987
- it 'writes to the specified db' do
2988
- expect(Band.mongo_client.with(database: 'another-db')[:mr_output].find.count).to eq(1)
2989
- end
4012
+ context "when there's no sort" do
4013
+ let(:criteria) do
4014
+ Band.all
2990
4015
  end
2991
- end
2992
4016
 
2993
- context "when providing no output" do
4017
+ it "gets the second document" do
4018
+ expect(context.second).to eq(new_order)
4019
+ end
4020
+ end
2994
4021
 
4022
+ context "when there's a custom sort" do
2995
4023
  let(:criteria) do
2996
- Band.limit(1)
4024
+ Band.all
2997
4025
  end
2998
4026
 
2999
- let(:context) do
3000
- described_class.new(criteria)
4027
+ it "gets the second document" do
4028
+ expect(context.sort(name: 1).second).to eq(depeche_mode)
3001
4029
  end
4030
+ end
3002
4031
 
3003
- let(:results) do
3004
- context.map_reduce(map, reduce)
4032
+ context "when there are no documents" do
4033
+ let(:criteria) do
4034
+ Band.where(name: "bogus")
3005
4035
  end
3006
4036
 
3007
- it "raises an error" do
3008
- expect {
3009
- results.entries
3010
- }.to raise_error(Mongoid::Errors::NoMapReduceOutput)
4037
+ it "returns nil" do
4038
+ expect(context.second).to be_nil
3011
4039
  end
3012
4040
  end
4041
+ end
3013
4042
 
3014
- context "when providing a finalize" do
4043
+ describe "#second!" do
4044
+
4045
+ let!(:depeche_mode) do
4046
+ Band.create!(name: "Depeche Mode")
4047
+ end
4048
+
4049
+ let!(:new_order) do
4050
+ Band.create!(name: "New Order")
4051
+ end
4052
+
4053
+ let!(:rolling_stones) do
4054
+ Band.create!(name: "The Rolling Stones")
4055
+ end
4056
+
4057
+ let!(:death_cab) do
4058
+ Band.create!(name: "Death Cab For Cutie")
4059
+ end
4060
+
4061
+ let(:context) do
4062
+ described_class.new(criteria)
4063
+ end
3015
4064
 
4065
+ context "when there's no sort" do
3016
4066
  let(:criteria) do
3017
- Band.limit(1)
4067
+ Band.all
3018
4068
  end
3019
4069
 
3020
- let(:context) do
3021
- described_class.new(criteria)
4070
+ it "gets the second document" do
4071
+ expect(context.second!).to eq(new_order)
3022
4072
  end
4073
+ end
3023
4074
 
3024
- let(:finalize) do
3025
- %Q{
3026
- function(key, value) {
3027
- value.extra = true;
3028
- return value;
3029
- }}
4075
+ context "when there's a custom sort" do
4076
+ let(:criteria) do
4077
+ Band.all
3030
4078
  end
3031
4079
 
3032
- let(:results) do
3033
- context.map_reduce(map, reduce).out(inline: 1).finalize(finalize)
4080
+ it "gets the second document" do
4081
+ expect(context.sort(name: 1).second!).to eq(depeche_mode)
3034
4082
  end
4083
+ end
3035
4084
 
3036
- it "returns the correct number of documents" do
3037
- expect(results.count).to eq(1)
4085
+ context "when there are no documents" do
4086
+ let(:criteria) do
4087
+ Band.where(name: "bogus")
3038
4088
  end
3039
4089
 
3040
- it "contains the entire results" do
3041
- expect(results).to eq([
3042
- { "_id" => "Depeche Mode", "value" => { "likes" => 200, "extra" => true }}
3043
- ])
4090
+ it "raises an error" do
4091
+ expect do
4092
+ context.second!
4093
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
3044
4094
  end
3045
4095
  end
3046
4096
  end
3047
4097
 
3048
- describe "#skip" do
4098
+ describe "#third" do
3049
4099
 
3050
4100
  let!(:depeche_mode) do
3051
4101
  Band.create!(name: "Depeche Mode")
@@ -3055,20 +4105,50 @@ describe Mongoid::Contextual::Mongo do
3055
4105
  Band.create!(name: "New Order")
3056
4106
  end
3057
4107
 
3058
- let(:criteria) do
3059
- Band.all
4108
+ let!(:rolling_stones) do
4109
+ Band.create!(name: "The Rolling Stones")
4110
+ end
4111
+
4112
+ let!(:death_cab) do
4113
+ Band.create!(name: "Death Cab For Cutie")
3060
4114
  end
3061
4115
 
3062
4116
  let(:context) do
3063
4117
  described_class.new(criteria)
3064
4118
  end
3065
4119
 
3066
- it "limits the results" do
3067
- expect(context.skip(1).entries).to eq([ new_order ])
4120
+ context "when there's no sort" do
4121
+ let(:criteria) do
4122
+ Band.all
4123
+ end
4124
+
4125
+ it "gets the third document" do
4126
+ expect(context.third).to eq(rolling_stones)
4127
+ end
4128
+ end
4129
+
4130
+ context "when there's a custom sort" do
4131
+ let(:criteria) do
4132
+ Band.all
4133
+ end
4134
+
4135
+ it "gets the third document" do
4136
+ expect(context.sort(name: 1).third).to eq(new_order)
4137
+ end
4138
+ end
4139
+
4140
+ context "when there are no documents" do
4141
+ let(:criteria) do
4142
+ Band.where(name: "bogus")
4143
+ end
4144
+
4145
+ it "returns nil" do
4146
+ expect(context.third).to be_nil
4147
+ end
3068
4148
  end
3069
4149
  end
3070
4150
 
3071
- describe "#sort" do
4151
+ describe "#third!" do
3072
4152
 
3073
4153
  let!(:depeche_mode) do
3074
4154
  Band.create!(name: "Depeche Mode")
@@ -3078,40 +4158,52 @@ describe Mongoid::Contextual::Mongo do
3078
4158
  Band.create!(name: "New Order")
3079
4159
  end
3080
4160
 
3081
- let(:criteria) do
3082
- Band.all
4161
+ let!(:rolling_stones) do
4162
+ Band.create!(name: "The Rolling Stones")
4163
+ end
4164
+
4165
+ let!(:death_cab) do
4166
+ Band.create!(name: "Death Cab For Cutie")
3083
4167
  end
3084
4168
 
3085
4169
  let(:context) do
3086
4170
  described_class.new(criteria)
3087
4171
  end
3088
4172
 
3089
- context "when providing a spec" do
3090
-
3091
- it "sorts the results" do
3092
- expect(context.sort(name: -1).entries).to eq([ new_order, depeche_mode ])
4173
+ context "when there's no sort" do
4174
+ let(:criteria) do
4175
+ Band.all
3093
4176
  end
3094
4177
 
3095
- it "returns the context" do
3096
- expect(context.sort(name: 1)).to eq(context)
4178
+ it "gets the third document" do
4179
+ expect(context.third!).to eq(rolling_stones)
3097
4180
  end
3098
4181
  end
3099
4182
 
3100
- context "when providing a block" do
4183
+ context "when there's a custom sort" do
4184
+ let(:criteria) do
4185
+ Band.all
4186
+ end
3101
4187
 
3102
- let(:sorted) do
3103
- context.sort do |a, b|
3104
- b.name <=> a.name
3105
- end
4188
+ it "gets the third document" do
4189
+ expect(context.sort(name: 1).third!).to eq(new_order)
3106
4190
  end
4191
+ end
3107
4192
 
3108
- it "sorts the results in memory" do
3109
- expect(sorted).to eq([ new_order, depeche_mode ])
4193
+ context "when there are no documents" do
4194
+ let(:criteria) do
4195
+ Band.where(name: "bogus")
4196
+ end
4197
+
4198
+ it "raises an error" do
4199
+ expect do
4200
+ context.third!
4201
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
3110
4202
  end
3111
4203
  end
3112
4204
  end
3113
4205
 
3114
- describe "#update" do
4206
+ describe "#fourth" do
3115
4207
 
3116
4208
  let!(:depeche_mode) do
3117
4209
  Band.create!(name: "Depeche Mode")
@@ -3121,388 +4213,498 @@ describe Mongoid::Contextual::Mongo do
3121
4213
  Band.create!(name: "New Order")
3122
4214
  end
3123
4215
 
3124
- let(:criteria) do
3125
- Band.all
4216
+ let!(:rolling_stones) do
4217
+ Band.create!(name: "The Rolling Stones")
4218
+ end
4219
+
4220
+ let!(:death_cab) do
4221
+ Band.create!(name: "Death Cab For Cutie")
3126
4222
  end
3127
4223
 
3128
4224
  let(:context) do
3129
4225
  described_class.new(criteria)
3130
4226
  end
3131
4227
 
3132
- context "when adding an element to a HABTM set" do
3133
-
3134
- let(:person) do
3135
- Person.create!
4228
+ context "when there's no sort" do
4229
+ let(:criteria) do
4230
+ Band.all
3136
4231
  end
3137
4232
 
3138
- let(:preference) do
3139
- Preference.create!
4233
+ it "gets the fourth document" do
4234
+ expect(context.fourth).to eq(death_cab)
3140
4235
  end
4236
+ end
3141
4237
 
3142
- before do
3143
- Person.where(id: person.id).
3144
- update("$addToSet" => { preference_ids: preference.id })
4238
+ context "when there's a custom sort" do
4239
+ let(:criteria) do
4240
+ Band.all
3145
4241
  end
3146
4242
 
3147
- it "adds a single element to the array" do
3148
- expect(person.reload.preference_ids).to eq([ preference.id ])
4243
+ it "gets the fourth document" do
4244
+ expect(context.sort(name: 1).fourth).to eq(rolling_stones)
3149
4245
  end
3150
4246
  end
3151
4247
 
3152
- context "when providing attributes" do
4248
+ context "when there are no documents" do
4249
+ let(:criteria) do
4250
+ Band.where(name: "bogus")
4251
+ end
3153
4252
 
3154
- context "when the attributes are of the correct type" do
4253
+ it "returns nil" do
4254
+ expect(context.fourth).to be_nil
4255
+ end
4256
+ end
4257
+ end
3155
4258
 
3156
- before do
3157
- context.update(name: "Smiths")
3158
- end
4259
+ describe "#fourth!" do
3159
4260
 
3160
- it "updates only the first matching document" do
3161
- expect(depeche_mode.reload.name).to eq("Smiths")
3162
- end
4261
+ let!(:depeche_mode) do
4262
+ Band.create!(name: "Depeche Mode")
4263
+ end
3163
4264
 
3164
- it "does not update the last matching document" do
3165
- expect(new_order.reload.name).to eq("New Order")
3166
- end
3167
- end
4265
+ let!(:new_order) do
4266
+ Band.create!(name: "New Order")
4267
+ end
3168
4268
 
3169
- context "when the attributes must be mongoized" do
4269
+ let!(:rolling_stones) do
4270
+ Band.create!(name: "The Rolling Stones")
4271
+ end
3170
4272
 
3171
- context "when coercing a string to integer" do
4273
+ let!(:death_cab) do
4274
+ Band.create!(name: "Death Cab For Cutie")
4275
+ end
3172
4276
 
3173
- before do
3174
- context.update(member_count: "1")
3175
- end
4277
+ let(:context) do
4278
+ described_class.new(criteria)
4279
+ end
3176
4280
 
3177
- it "updates the first matching document" do
3178
- expect(depeche_mode.reload.member_count).to eq(1)
3179
- end
4281
+ context "when there's no sort" do
4282
+ let(:criteria) do
4283
+ Band.all
4284
+ end
3180
4285
 
3181
- it "does not update the last matching document" do
3182
- expect(new_order.reload.member_count).to be_nil
3183
- end
3184
- end
4286
+ it "gets the fourth document" do
4287
+ expect(context.fourth!).to eq(death_cab)
4288
+ end
4289
+ end
3185
4290
 
3186
- context "when coercing a string to date" do
4291
+ context "when there's a custom sort" do
4292
+ let(:criteria) do
4293
+ Band.all
4294
+ end
3187
4295
 
3188
- before do
3189
- context.update(founded: "1979/1/1")
3190
- end
4296
+ it "gets the fourth document" do
4297
+ expect(context.sort(name: 1).fourth!).to eq(rolling_stones)
4298
+ end
4299
+ end
3191
4300
 
3192
- it "updates the first matching document" do
3193
- expect(depeche_mode.reload.founded).to eq(Date.new(1979, 1, 1))
3194
- end
4301
+ context "when there are no documents" do
4302
+ let(:criteria) do
4303
+ Band.where(name: "bogus")
4304
+ end
3195
4305
 
3196
- it "does not update the last matching document" do
3197
- expect(new_order.reload.founded).to be_nil
3198
- end
3199
- end
4306
+ it "raises an error" do
4307
+ expect do
4308
+ context.fourth!
4309
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
3200
4310
  end
3201
4311
  end
4312
+ end
3202
4313
 
3203
- context "when providing atomic operations" do
4314
+ describe "#fifth" do
3204
4315
 
3205
- context "when only atomic operations are provided" do
4316
+ let!(:depeche_mode) do
4317
+ Band.create!(name: "Depeche Mode")
4318
+ end
3206
4319
 
3207
- context "when the attributes are in the correct type" do
4320
+ let!(:new_order) do
4321
+ Band.create!(name: "New Order")
4322
+ end
3208
4323
 
3209
- before do
3210
- context.update("$set" => { name: "Smiths" })
3211
- end
4324
+ let!(:rolling_stones) do
4325
+ Band.create!(name: "The Rolling Stones")
4326
+ end
3212
4327
 
3213
- it "updates the first matching document" do
3214
- expect(depeche_mode.reload.name).to eq("Smiths")
3215
- end
4328
+ let!(:death_cab) do
4329
+ Band.create!(name: "Death Cab For Cutie")
4330
+ end
3216
4331
 
3217
- it "does not update the last matching document" do
3218
- expect(new_order.reload.name).to eq("New Order")
3219
- end
3220
- end
4332
+ let!(:guns_and_roses) do
4333
+ Band.create!(name: "Guns and Roses")
4334
+ end
3221
4335
 
3222
- context "when the attributes must be mongoized" do
4336
+ let(:context) do
4337
+ described_class.new(criteria)
4338
+ end
3223
4339
 
3224
- before do
3225
- context.update("$set" => { member_count: "1" })
3226
- end
4340
+ context "when there's no sort" do
4341
+ let(:criteria) do
4342
+ Band.all
4343
+ end
3227
4344
 
3228
- it "updates the first matching document" do
3229
- expect(depeche_mode.reload.member_count).to eq(1)
3230
- end
4345
+ it "gets the fifth document" do
4346
+ expect(context.fifth).to eq(guns_and_roses)
4347
+ end
4348
+ end
3231
4349
 
3232
- it "does not update the last matching document" do
3233
- expect(new_order.reload.member_count).to be_nil
3234
- end
3235
- end
4350
+ context "when there's a custom sort" do
4351
+ let(:criteria) do
4352
+ Band.all
3236
4353
  end
3237
4354
 
3238
- context "when a mix are provided" do
4355
+ it "gets the fifth document" do
4356
+ expect(context.sort(name: 1).fifth).to eq(rolling_stones)
4357
+ end
4358
+ end
3239
4359
 
3240
- before do
3241
- context.update("$set" => { name: "Smiths" }, likes: 100)
3242
- end
4360
+ context "when there are no documents" do
4361
+ let(:criteria) do
4362
+ Band.where(name: "bogus")
4363
+ end
3243
4364
 
3244
- it "updates the first matching document's set" do
3245
- expect(depeche_mode.reload.name).to eq("Smiths")
3246
- end
4365
+ it "returns nil" do
4366
+ expect(context.fifth).to be_nil
4367
+ end
4368
+ end
4369
+ end
3247
4370
 
3248
- it "updates the first matching document's updates" do
3249
- expect(depeche_mode.reload.likes).to eq(100)
3250
- end
4371
+ describe "#fifth!" do
3251
4372
 
3252
- it "does not update the last matching document's set" do
3253
- expect(new_order.reload.name).to eq("New Order")
3254
- end
4373
+ let!(:depeche_mode) do
4374
+ Band.create!(name: "Depeche Mode")
4375
+ end
3255
4376
 
3256
- it "does not update the last matching document's updates" do
3257
- expect(new_order.reload.likes).to be_nil
3258
- end
3259
- end
4377
+ let!(:new_order) do
4378
+ Band.create!(name: "New Order")
3260
4379
  end
3261
4380
 
3262
- context "when providing no attributes" do
4381
+ let!(:rolling_stones) do
4382
+ Band.create!(name: "The Rolling Stones")
4383
+ end
3263
4384
 
3264
- it "returns false" do
3265
- expect(context.update).to be false
3266
- end
4385
+ let!(:death_cab) do
4386
+ Band.create!(name: "Death Cab For Cutie")
3267
4387
  end
3268
4388
 
3269
- context 'when provided array filters' do
3270
- min_server_version '3.6'
4389
+ let!(:guns_and_roses) do
4390
+ Band.create!(name: "Guns and Roses")
4391
+ end
3271
4392
 
3272
- before do
3273
- Band.delete_all
3274
- b = Band.new(name: 'Depeche Mode')
3275
- b.labels << Label.new(name: 'Warner')
3276
- b.labels << Label.new(name: 'Sony')
3277
- b.labels << Label.new(name: 'Cbs')
3278
- b.save!
4393
+ let(:context) do
4394
+ described_class.new(criteria)
4395
+ end
3279
4396
 
3280
- b = Band.new(name: 'FKA Twigs')
3281
- b.labels << Label.new(name: 'Warner')
3282
- b.labels << Label.new(name: 'Cbs')
3283
- b.save!
4397
+ context "when there's no sort" do
4398
+ let(:criteria) do
4399
+ Band.all
3284
4400
  end
3285
4401
 
4402
+ it "gets the fifth document" do
4403
+ expect(context.fifth!).to eq(guns_and_roses)
4404
+ end
4405
+ end
3286
4406
 
4407
+ context "when there's a custom sort" do
3287
4408
  let(:criteria) do
3288
- Band.where(name: 'Depeche Mode')
4409
+ Band.all
3289
4410
  end
3290
4411
 
3291
- let!(:update) do
3292
- context.update({ '$set' => { 'labels.$[i].name' => 'Sony' } },
3293
- array_filters: [{ 'i.name' => 'Cbs' }])
4412
+ it "gets the fifth document" do
4413
+ expect(context.sort(name: 1).fifth!).to eq(rolling_stones)
3294
4414
  end
4415
+ end
3295
4416
 
3296
- it 'applies the array filters' do
3297
- expect(Band.where(name: 'Depeche Mode').first.labels.collect(&:name)).to match_array(['Warner', 'Sony', 'Sony'])
4417
+ context "when there are no documents" do
4418
+ let(:criteria) do
4419
+ Band.where(name: "bogus")
3298
4420
  end
3299
4421
 
3300
- it 'does not affect other documents' do
3301
- expect(Band.where(name: 'FKA Twigs').first.labels.collect(&:name)).to match_array(['Warner', 'Cbs'])
4422
+ it "raises an error" do
4423
+ expect do
4424
+ context.fifth!
4425
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
3302
4426
  end
3303
4427
  end
3304
4428
  end
3305
4429
 
3306
- describe "#update_all" do
4430
+ describe "#second_to_last" do
3307
4431
 
3308
4432
  let!(:depeche_mode) do
3309
- Band.create!(name: "Depeche Mode", origin: "Essex")
4433
+ Band.create!(name: "Depeche Mode")
3310
4434
  end
3311
4435
 
3312
4436
  let!(:new_order) do
3313
4437
  Band.create!(name: "New Order")
3314
4438
  end
3315
4439
 
3316
- let(:criteria) do
3317
- Band.all
4440
+ let!(:rolling_stones) do
4441
+ Band.create!(name: "The Rolling Stones")
4442
+ end
4443
+
4444
+ let!(:death_cab) do
4445
+ Band.create!(name: "Death Cab For Cutie")
3318
4446
  end
3319
4447
 
3320
4448
  let(:context) do
3321
4449
  described_class.new(criteria)
3322
4450
  end
3323
4451
 
3324
- context "when providing attributes" do
4452
+ context "when there's no sort" do
4453
+ let(:criteria) do
4454
+ Band.all
4455
+ end
3325
4456
 
3326
- context "when the attributes are of the correct type" do
4457
+ it "gets the second_to_last document" do
4458
+ expect(context.second_to_last).to eq(rolling_stones)
4459
+ end
4460
+ end
3327
4461
 
3328
- before do
3329
- context.update_all(name: "Smiths")
3330
- end
4462
+ context "when there's a custom sort" do
4463
+ let(:criteria) do
4464
+ Band.all
4465
+ end
3331
4466
 
3332
- it "updates the first matching document" do
3333
- expect(depeche_mode.reload.name).to eq("Smiths")
3334
- end
4467
+ it "gets the second_to_last document" do
4468
+ expect(context.sort(name: 1).second_to_last).to eq(new_order)
4469
+ end
4470
+ end
3335
4471
 
3336
- it "does not clear out other attributes" do
3337
- expect(depeche_mode.reload.origin).to eq("Essex")
3338
- end
4472
+ context "when there are no documents" do
4473
+ let(:criteria) do
4474
+ Band.where(name: "bogus")
4475
+ end
3339
4476
 
3340
- it "updates the last matching document" do
3341
- expect(new_order.reload.name).to eq("Smiths")
3342
- end
4477
+ it "returns nil" do
4478
+ expect(context.second_to_last).to be_nil
3343
4479
  end
4480
+ end
4481
+ end
3344
4482
 
3345
- context "when the attributes must be mongoized" do
4483
+ describe "#second_to_last!" do
3346
4484
 
3347
- before do
3348
- context.update_all(member_count: "1")
3349
- end
4485
+ let!(:depeche_mode) do
4486
+ Band.create!(name: "Depeche Mode")
4487
+ end
3350
4488
 
3351
- it "updates the first matching document" do
3352
- expect(depeche_mode.reload.member_count).to eq(1)
3353
- end
4489
+ let!(:new_order) do
4490
+ Band.create!(name: "New Order")
4491
+ end
3354
4492
 
3355
- it "updates the last matching document" do
3356
- expect(new_order.reload.member_count).to eq(1)
3357
- end
3358
- end
4493
+ let!(:rolling_stones) do
4494
+ Band.create!(name: "The Rolling Stones")
4495
+ end
3359
4496
 
3360
- context "when using aliased field names" do
4497
+ let!(:death_cab) do
4498
+ Band.create!(name: "Death Cab For Cutie")
4499
+ end
3361
4500
 
3362
- before do
3363
- context.update_all(years: 100)
3364
- end
4501
+ let(:context) do
4502
+ described_class.new(criteria)
4503
+ end
3365
4504
 
3366
- it "updates the first matching document" do
3367
- expect(depeche_mode.reload.years).to eq(100)
3368
- end
4505
+ context "when there's no sort" do
4506
+ let(:criteria) do
4507
+ Band.all
4508
+ end
3369
4509
 
3370
- it "updates the last matching document" do
3371
- expect(new_order.reload.years).to eq(100)
3372
- end
4510
+ it "gets the second_to_last document" do
4511
+ expect(context.second_to_last!).to eq(rolling_stones)
3373
4512
  end
3374
4513
  end
3375
4514
 
3376
- context "when providing atomic operations" do
4515
+ context "when there's a custom sort" do
4516
+ let(:criteria) do
4517
+ Band.all
4518
+ end
3377
4519
 
3378
- context "when only atomic operations are provided" do
4520
+ it "gets the second_to_last document" do
4521
+ expect(context.sort(name: 1).second_to_last!).to eq(new_order)
4522
+ end
4523
+ end
3379
4524
 
3380
- context "when the attributes are in the correct type" do
4525
+ context "when there are no documents" do
4526
+ let(:criteria) do
4527
+ Band.where(name: "bogus")
4528
+ end
3381
4529
 
3382
- before do
3383
- context.update_all("$set" => { name: "Smiths" })
3384
- end
4530
+ it "raises an error" do
4531
+ expect do
4532
+ context.second_to_last!
4533
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
4534
+ end
4535
+ end
4536
+ end
3385
4537
 
3386
- it "updates the first matching document" do
3387
- expect(depeche_mode.reload.name).to eq("Smiths")
3388
- end
4538
+ describe "#third_to_last" do
3389
4539
 
3390
- it "updates the last matching document" do
3391
- expect(new_order.reload.name).to eq("Smiths")
3392
- end
3393
- end
4540
+ let!(:depeche_mode) do
4541
+ Band.create!(name: "Depeche Mode")
4542
+ end
3394
4543
 
3395
- context "when the attributes must be mongoized" do
4544
+ let!(:new_order) do
4545
+ Band.create!(name: "New Order")
4546
+ end
3396
4547
 
3397
- before do
3398
- context.update_all("$set" => { member_count: "1" })
3399
- end
4548
+ let!(:rolling_stones) do
4549
+ Band.create!(name: "The Rolling Stones")
4550
+ end
3400
4551
 
3401
- it "updates the first matching document" do
3402
- expect(depeche_mode.reload.member_count).to eq(1)
3403
- end
4552
+ let!(:death_cab) do
4553
+ Band.create!(name: "Death Cab For Cutie")
4554
+ end
3404
4555
 
3405
- it "updates the last matching document" do
3406
- expect(new_order.reload.member_count).to eq(1)
3407
- end
3408
- end
3409
- end
4556
+ let(:context) do
4557
+ described_class.new(criteria)
4558
+ end
3410
4559
 
3411
- context "when a mix are provided" do
4560
+ context "when there's no sort" do
4561
+ let(:criteria) do
4562
+ Band.all
4563
+ end
3412
4564
 
3413
- before do
3414
- context.update_all("$set" => { name: "Smiths" }, likes: 100)
3415
- end
4565
+ it "gets the third_to_last document" do
4566
+ expect(context.third_to_last).to eq(new_order)
4567
+ end
4568
+ end
3416
4569
 
3417
- it "updates the first matching document's set" do
3418
- expect(depeche_mode.reload.name).to eq("Smiths")
3419
- end
4570
+ context "when there's a custom sort" do
4571
+ let(:criteria) do
4572
+ Band.all
4573
+ end
3420
4574
 
3421
- it "updates the first matching document's updates" do
3422
- expect(depeche_mode.reload.likes).to eq(100)
3423
- end
4575
+ it "gets the third_to_last document" do
4576
+ expect(context.sort(name: 1).third_to_last).to eq(depeche_mode)
4577
+ end
4578
+ end
3424
4579
 
3425
- it "updates the last matching document's set" do
3426
- expect(new_order.reload.name).to eq("Smiths")
3427
- end
4580
+ context "when there are no documents" do
4581
+ let(:criteria) do
4582
+ Band.where(name: "bogus")
4583
+ end
3428
4584
 
3429
- it "updates the last matching document's updates" do
3430
- expect(new_order.reload.likes).to eq(100)
3431
- end
4585
+ it "returns nil" do
4586
+ expect(context.third_to_last).to be_nil
3432
4587
  end
3433
4588
  end
4589
+ end
3434
4590
 
3435
- context "when providing no attributes" do
4591
+ describe "#third_to_last!" do
3436
4592
 
3437
- it "returns false" do
3438
- expect(context.update_all).to be false
3439
- end
4593
+ let!(:depeche_mode) do
4594
+ Band.create!(name: "Depeche Mode")
3440
4595
  end
3441
4596
 
3442
- context 'when provided array filters' do
3443
- min_server_version '3.6'
4597
+ let!(:new_order) do
4598
+ Band.create!(name: "New Order")
4599
+ end
3444
4600
 
3445
- before do
3446
- Band.delete_all
3447
- b = Band.new(name: 'Depeche Mode')
3448
- b.labels << Label.new(name: 'Warner')
3449
- b.labels << Label.new(name: 'Sony')
3450
- b.labels << Label.new(name: 'Cbs')
3451
- b.save!
4601
+ let!(:rolling_stones) do
4602
+ Band.create!(name: "The Rolling Stones")
4603
+ end
3452
4604
 
3453
- b = Band.new(name: 'FKA Twigs')
3454
- b.labels << Label.new(name: 'Warner')
3455
- b.labels << Label.new(name: 'Cbs')
3456
- b.save!
4605
+ let!(:death_cab) do
4606
+ Band.create!(name: "Death Cab For Cutie")
4607
+ end
4608
+
4609
+ let(:context) do
4610
+ described_class.new(criteria)
4611
+ end
4612
+
4613
+ context "when there's no sort" do
4614
+ let(:criteria) do
4615
+ Band.all
3457
4616
  end
3458
4617
 
4618
+ it "gets the third_to_last document" do
4619
+ expect(context.third_to_last!).to eq(new_order)
4620
+ end
4621
+ end
3459
4622
 
4623
+ context "when there's a custom sort" do
3460
4624
  let(:criteria) do
3461
4625
  Band.all
3462
4626
  end
3463
4627
 
3464
- let!(:update) do
3465
- context.update_all({ '$set' => { 'labels.$[i].name' => 'Sony' } },
3466
- array_filters: [{ 'i.name' => 'Cbs' }])
4628
+ it "gets the third_to_last document" do
4629
+ expect(context.sort(name: 1).third_to_last!).to eq(depeche_mode)
3467
4630
  end
4631
+ end
3468
4632
 
3469
- it 'applies the array filters' do
3470
- expect(Band.where(name: 'Depeche Mode').first.labels.collect(&:name)).to match_array(['Warner', 'Sony', 'Sony'])
4633
+ context "when there are no documents" do
4634
+ let(:criteria) do
4635
+ Band.where(name: "bogus")
3471
4636
  end
3472
4637
 
3473
- it 'updates all documents' do
3474
- expect(Band.where(name: 'FKA Twigs').first.labels.collect(&:name)).to match_array(['Warner', 'Sony'])
4638
+ it "raises an error" do
4639
+ expect do
4640
+ context.third_to_last!
4641
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
3475
4642
  end
3476
4643
  end
3477
4644
  end
3478
4645
 
3479
- describe '#pipeline' do
4646
+ describe '#load_async' do
4647
+ let!(:band) do
4648
+ Band.create!(name: "Depeche Mode")
4649
+ end
3480
4650
 
3481
- context 'when the criteria has a selector' do
4651
+ let(:criteria) do
4652
+ Band.where(name: "Depeche Mode")
4653
+ end
3482
4654
 
3483
- before do
3484
- Artist.index(name: "text")
3485
- Artist.create_indexes
3486
- end
4655
+ let(:context) do
4656
+ described_class.new(criteria)
4657
+ end
3487
4658
 
3488
- let(:criteria) do
3489
- Artist.text_search("New Order")
3490
- end
4659
+ context 'with global thread pool async query executor' do
4660
+ config_override :async_query_executor, :global_thread_pool
3491
4661
 
3492
- let(:context) do
3493
- described_class.new(criteria)
4662
+ it 'preloads the documents' do
4663
+ context.load_async
4664
+ context.documents_loader.wait
4665
+
4666
+ expect(context.view).not_to receive(:map)
4667
+ expect(context.to_a).to eq([band])
3494
4668
  end
3495
4669
 
3496
- let(:pipeline_match) do
3497
- context.send(:pipeline, :some_field).first['$match']
4670
+ it 're-raises exception during preload' do
4671
+ expect_any_instance_of(Mongoid::Contextual::Mongo::DocumentsLoader)
4672
+ .to receive(:execute)
4673
+ .at_least(:once)
4674
+ .and_raise(Mongo::Error::OperationFailure)
4675
+
4676
+ context.load_async
4677
+ context.documents_loader.wait
4678
+
4679
+ expect do
4680
+ context.to_a
4681
+ end.to raise_error(Mongo::Error::OperationFailure)
3498
4682
  end
4683
+ end
3499
4684
 
3500
- it 'creates a pipeline with the selector as one of the $match criteria' do
3501
- expect(pipeline_match).to include({ '$text' => { '$search' => "New Order" } })
4685
+ context 'with immediate thread pool async query executor' do
4686
+ config_override :async_query_executor, :immediate
4687
+
4688
+ it 'preloads the documents' do
4689
+ context.load_async
4690
+ context.documents_loader.wait
4691
+
4692
+ expect(context.view).not_to receive(:map)
4693
+ expect(context.to_a).to eq([band])
3502
4694
  end
3503
4695
 
3504
- it 'creates a pipeline with the $exists operator as one of the $match criteria' do
3505
- expect(pipeline_match).to include({ 'some_field' => { '$exists' => true } })
4696
+ it 're-raises exception during preload' do
4697
+ expect_any_instance_of(Mongoid::Contextual::Mongo::DocumentsLoader)
4698
+ .to receive(:execute)
4699
+ .at_least(:once)
4700
+ .and_raise(Mongo::Error::OperationFailure)
4701
+
4702
+ context.load_async
4703
+ context.documents_loader.wait
4704
+
4705
+ expect do
4706
+ context.to_a
4707
+ end.to raise_error(Mongo::Error::OperationFailure)
3506
4708
  end
3507
4709
  end
3508
4710
  end