mongoid 7.5.1 → 8.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (424) 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 +69 -45
  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 +29 -19
  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 +145 -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 +521 -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 +36 -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 +19 -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 +76 -15
  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 +360 -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 +276 -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 +193 -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 +1570 -413
  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 +599 -8
  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/lite_constraints.rb +8 -0
  390. data/spec/shared/shlib/server.sh +5 -5
  391. data/spec/spec_helper.rb +5 -0
  392. data/spec/support/constraints.rb +24 -0
  393. data/spec/support/immutable_ids.rb +118 -0
  394. data/spec/support/macros.rb +78 -0
  395. data/spec/support/models/artist.rb +0 -1
  396. data/spec/support/models/augmentation.rb +12 -0
  397. data/spec/support/models/band.rb +4 -0
  398. data/spec/support/models/book.rb +1 -0
  399. data/spec/support/models/building.rb +2 -0
  400. data/spec/support/models/catalog.rb +24 -0
  401. data/spec/support/models/circus.rb +3 -0
  402. data/spec/support/models/cover.rb +10 -0
  403. data/spec/support/models/fanatic.rb +8 -0
  404. data/spec/support/models/implant.rb +9 -0
  405. data/spec/support/models/label.rb +2 -0
  406. data/spec/support/models/passport.rb +9 -0
  407. data/spec/support/models/person.rb +2 -0
  408. data/spec/support/models/player.rb +2 -0
  409. data/spec/support/models/powerup.rb +12 -0
  410. data/spec/support/models/product.rb +1 -0
  411. data/spec/support/models/purse.rb +9 -0
  412. data/spec/support/models/registry.rb +1 -0
  413. data/spec/support/models/school.rb +14 -0
  414. data/spec/support/models/shield.rb +18 -0
  415. data/spec/support/models/student.rb +14 -0
  416. data/spec/support/models/weapon.rb +12 -0
  417. data.tar.gz.sig +0 -0
  418. metadata +720 -638
  419. metadata.gz.sig +0 -0
  420. data/lib/mongoid/errors/eager_load.rb +0 -23
  421. data/lib/mongoid/errors/invalid_value.rb +0 -17
  422. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -60
  423. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -60
  424. 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
 
@@ -217,16 +186,18 @@ describe Mongoid::Contextual::Mongo do
217
186
  end
218
187
  end
219
188
 
220
- context "when context is cached" do
189
+ context "when the query cache is enabled" do
190
+ query_cache_enabled
221
191
 
222
192
  let(:context) do
223
- described_class.new(criteria.cache)
193
+ described_class.new(criteria)
224
194
  end
225
195
 
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
196
+ it "the results are not cached" do
197
+ expect_query(2) do
198
+ 2.times do
199
+ context.estimated_count
200
+ end
230
201
  end
231
202
  end
232
203
  end
@@ -561,8 +532,22 @@ describe Mongoid::Contextual::Mongo do
561
532
  context "when legacy_pluck_distinct is set" do
562
533
  config_override :legacy_pluck_distinct, true
563
534
 
564
- it "returns the non-demongoized distinct field values" do
565
- expect(context.distinct(:sales).sort).to eq([ "1E2", "2E3" ])
535
+ context 'when storing BigDecimal as string' do
536
+ config_override :map_big_decimal_to_decimal128, false
537
+
538
+ it "returns the non-demongoized distinct field values" do
539
+ expect(context.distinct(:sales).sort).to eq([ "1E2", "2E3" ])
540
+ end
541
+ end
542
+
543
+ context 'when storing BigDecimal as decimal128' do
544
+ config_override :map_big_decimal_to_decimal128, true
545
+ min_bson_version '4.15.0'
546
+ max_bson_version '4.99.99'
547
+
548
+ it "returns the non-demongoized distinct field values" do
549
+ expect(context.distinct(:sales).sort).to eq([ BSON::Decimal128.new("1E2"), BSON::Decimal128.new("2E3") ])
550
+ end
566
551
  end
567
552
  end
568
553
 
@@ -576,6 +561,8 @@ describe Mongoid::Contextual::Mongo do
576
561
  end
577
562
 
578
563
  context "when getting a localized field" do
564
+ with_default_i18n_configs
565
+
579
566
  before do
580
567
  I18n.locale = :en
581
568
  d = Dictionary.create!(description: 'english-text')
@@ -584,10 +571,6 @@ describe Mongoid::Contextual::Mongo do
584
571
  d.save!
585
572
  end
586
573
 
587
- after do
588
- I18n.locale = :en
589
- end
590
-
591
574
  let(:criteria) do
592
575
  Dictionary.criteria
593
576
  end
@@ -680,16 +663,10 @@ describe Mongoid::Contextual::Mongo do
680
663
 
681
664
  context 'when fallbacks are enabled with a locale list' do
682
665
  require_fallbacks
666
+ with_default_i18n_configs
683
667
 
684
- around(:all) do |example|
685
- prev_fallbacks = I18n.fallbacks.dup
668
+ before do
686
669
  I18n.fallbacks[:he] = [ :en ]
687
- example.run
688
- I18n.fallbacks = prev_fallbacks
689
- end
690
-
691
- after do
692
- I18n.locale = :en
693
670
  end
694
671
 
695
672
  let(:distinct) do
@@ -709,7 +686,7 @@ describe Mongoid::Contextual::Mongo do
709
686
 
710
687
  it "correctly uses the fallback" do
711
688
  I18n.locale = :en
712
- d = Dictionary.create!(description: 'english-text')
689
+ Dictionary.create!(description: 'english-text')
713
690
  I18n.locale = :he
714
691
  distinct.should == "english-text"
715
692
  end
@@ -717,6 +694,8 @@ describe Mongoid::Contextual::Mongo do
717
694
  end
718
695
 
719
696
  context "when the localized field is embedded" do
697
+ with_default_i18n_configs
698
+
720
699
  before do
721
700
  p = Passport.new
722
701
  I18n.locale = :en
@@ -727,10 +706,6 @@ describe Mongoid::Contextual::Mongo do
727
706
  Person.create!(passport: p, employer_id: 12345)
728
707
  end
729
708
 
730
- after do
731
- I18n.locale = :en
732
- end
733
-
734
709
  let(:criteria) do
735
710
  Person.where(employer_id: 12345)
736
711
  end
@@ -794,9 +769,11 @@ describe Mongoid::Contextual::Mongo do
794
769
 
795
770
  context "when legacy_pluck_distinct is set" do
796
771
  config_override :legacy_pluck_distinct, true
772
+ config_override :map_big_decimal_to_decimal128, true
773
+ max_bson_version '4.99.99'
797
774
 
798
775
  it "returns the distinct matching fields" do
799
- expect(context.distinct("label.sales")).to eq([ "1E2" ])
776
+ expect(context.distinct("label.sales")).to eq([ BSON::Decimal128.new('1E+2') ])
800
777
  end
801
778
  end
802
779
 
@@ -809,6 +786,452 @@ describe Mongoid::Contextual::Mongo do
809
786
  end
810
787
  end
811
788
 
789
+ describe "#tally" do
790
+ let(:fans1) { [ Fanatic.new(age:1), Fanatic.new(age:2) ] }
791
+ let(:fans2) { [ Fanatic.new(age:1), Fanatic.new(age:2) ] }
792
+ let(:fans3) { [ Fanatic.new(age:1), Fanatic.new(age:3) ] }
793
+
794
+ let(:genres1) { [ { x: 1, y: { z: 1 } }, { x: 2, y: { z: 2 } }, { y: 3 } ]}
795
+ let(:genres2) { [ { x: 1, y: { z: 1 } }, { x: 2, y: { z: 2 } }, { y: 4 } ]}
796
+ let(:genres3) { [ { x: 1, y: { z: 1 } }, { x: 3, y: { z: 3 } }, { y: 5 } ]}
797
+
798
+ let(:label1) { Label.new(name: "Atlantic") }
799
+ let(:label2) { Label.new(name: "Atlantic") }
800
+ let(:label3) { Label.new(name: "Columbia") }
801
+
802
+ before do
803
+ Band.create!(origin: "tally", name: "Depeche Mode", years: 30, sales: "1E2", label: label1, genres: genres1)
804
+ Band.create!(origin: "tally", name: "New Order", years: 30, sales: "2E3", label: label2, genres: genres2)
805
+ Band.create!(origin: "tally", name: "10,000 Maniacs", years: 30, sales: "1E2", label: label3, genres: genres3)
806
+ Band.create!(origin: "tally2", fanatics: fans1, genres: [1, 2])
807
+ Band.create!(origin: "tally2", fanatics: fans2, genres: [1, 2])
808
+ Band.create!(origin: "tally2", fanatics: fans3, genres: [1, 3])
809
+ end
810
+
811
+ let(:criteria) { Band.where(origin: "tally") }
812
+
813
+ context "when tallying a string" do
814
+ let(:tally) do
815
+ criteria.tally(:name)
816
+ end
817
+
818
+ it "returns the correct hash" do
819
+ expect(tally).to eq("Depeche Mode" => 1, "New Order" => 1, "10,000 Maniacs" => 1)
820
+ end
821
+ end
822
+
823
+ context "using an aliased field" do
824
+ let(:tally) do
825
+ criteria.tally(:years)
826
+ end
827
+
828
+ it "returns the correct hash" do
829
+ expect(tally).to eq(30 => 3)
830
+ end
831
+ end
832
+
833
+ context "when tallying a demongoizable field" do
834
+ let(:tally) do
835
+ criteria.tally(:sales)
836
+ end
837
+
838
+ it "returns the correct hash" do
839
+ expect(tally).to eq(BigDecimal("1E2") => 2, BigDecimal("2E3") => 1)
840
+ end
841
+ end
842
+
843
+ context "when tallying a localized field" do
844
+ with_default_i18n_configs
845
+
846
+ before do
847
+ I18n.locale = :en
848
+ d1 = Dictionary.create!(description: 'en1')
849
+ d2 = Dictionary.create!(description: 'en1')
850
+ d3 = Dictionary.create!(description: 'en1')
851
+ d4 = Dictionary.create!(description: 'en2')
852
+ I18n.locale = :de
853
+ d1.description = 'de1'
854
+ d2.description = 'de1'
855
+ d3.description = 'de2'
856
+ d4.description = 'de3'
857
+ d1.save!
858
+ d2.save!
859
+ d3.save!
860
+ d4.save!
861
+ I18n.locale = :en
862
+ end
863
+
864
+ context "when getting the demongoized field" do
865
+ let(:tallied) do
866
+ Dictionary.tally(:description)
867
+ end
868
+
869
+ it "returns the translation for the current locale" do
870
+ expect(tallied).to eq("en1" => 3, "en2" => 1)
871
+ end
872
+ end
873
+
874
+ context "when getting a specific locale" do
875
+ let(:tallied) do
876
+ Dictionary.tally("description.de")
877
+ end
878
+
879
+ it "returns the translation for the the specific locale" do
880
+ expect(tallied).to eq("de1" => 2, "de2" => 1, "de3" => 1)
881
+ end
882
+ end
883
+
884
+ context "when getting the full hash" do
885
+ let(:tallied) do
886
+ Dictionary.tally("description_translations")
887
+ end
888
+
889
+ it "returns the correct hash" do
890
+ expect(tallied).to eq(
891
+ {"de" => "de1", "en" => "en1" } => 2,
892
+ {"de" => "de2", "en" => "en1" } => 1,
893
+ {"de" => "de3", "en" => "en2" } => 1
894
+ )
895
+ end
896
+ end
897
+ end
898
+
899
+ context "when tallying an embedded localized field" do
900
+ with_default_i18n_configs
901
+
902
+ before do
903
+ I18n.locale = :en
904
+ address1a = Address.new(name: "en1")
905
+ address1b = Address.new(name: "en2")
906
+ address2a = Address.new(name: "en1")
907
+ address2b = Address.new(name: "en3")
908
+ I18n.locale = :de
909
+ address1a.name = "de1"
910
+ address1b.name = "de2"
911
+ address2a.name = "de1"
912
+ address2b.name = "de3"
913
+ Person.create!(addresses: [ address1a, address1b ])
914
+ Person.create!(addresses: [ address2a, address2b ])
915
+ I18n.locale = :en
916
+ end
917
+
918
+ context "when getting the demongoized field" do
919
+ let(:tallied) do
920
+ Person.tally("addresses.name")
921
+ end
922
+
923
+ it "returns the translation for the current locale" do
924
+ expect(tallied).to eq(
925
+ [ "en1", "en2" ] => 1,
926
+ [ "en1", "en3" ] => 1,
927
+ )
928
+ end
929
+ end
930
+
931
+ context "when getting a specific locale" do
932
+ let(:tallied) do
933
+ Person.tally("addresses.name.de")
934
+ end
935
+
936
+ it "returns the translation for the the specific locale" do
937
+ expect(tallied).to eq(
938
+ [ "de1", "de2" ] => 1,
939
+ [ "de1", "de3" ] => 1,
940
+ )
941
+ end
942
+ end
943
+
944
+ context "when getting the full hash" do
945
+ let(:tallied) do
946
+ Person.tally("addresses.name_translations")
947
+ end
948
+
949
+ it "returns the correct hash" do
950
+ expect(tallied).to eq(
951
+ [{ "de" => "de1", "en" => "en1" }, { "de" => "de2", "en" => "en2" }] => 1,
952
+ [{ "de" => "de1", "en" => "en1" }, { "de" => "de3", "en" => "en3" }] => 1,
953
+ )
954
+ end
955
+ end
956
+
957
+ end
958
+
959
+ context "when tallying an embedded field" do
960
+ let(:tally) do
961
+ criteria.tally("label.name")
962
+ end
963
+
964
+ it "returns the correct hash" do
965
+ expect(tally).to eq("Atlantic" => 2, "Columbia" => 1)
966
+ end
967
+ end
968
+
969
+ context "when tallying an element in an embeds_many field" do
970
+ let(:criteria) { Band.where(origin: "tally2") }
971
+
972
+ let(:tally) do
973
+ criteria.tally("fanatics.age")
974
+ end
975
+
976
+ it "returns the correct hash" do
977
+ expect(tally).to eq(
978
+ [1, 2] => 2,
979
+ [1, 3] => 1
980
+ )
981
+ end
982
+ end
983
+
984
+ context "when tallying an embeds_many field" do
985
+ let(:criteria) { Band.where(origin: "tally2") }
986
+
987
+ let(:tally) do
988
+ criteria.tally("fanatics")
989
+ end
990
+
991
+ it "returns the correct hash" do
992
+ expect(tally).to eq(
993
+ fans1.map(&:attributes) => 1,
994
+ fans2.map(&:attributes) => 1,
995
+ fans3.map(&:attributes) => 1,
996
+ )
997
+ end
998
+ end
999
+
1000
+ context "when tallying a field of type array" do
1001
+ let(:criteria) { Band.where(origin: "tally2") }
1002
+
1003
+ let(:tally) do
1004
+ criteria.tally("genres")
1005
+ end
1006
+
1007
+ it "returns the correct hash" do
1008
+ expect(tally).to eq(
1009
+ [1, 2] => 2,
1010
+ [1, 3] => 1
1011
+ )
1012
+ end
1013
+ end
1014
+
1015
+ context "when tallying an element from an array of hashes" do
1016
+ let(:criteria) { Band.where(origin: "tally") }
1017
+
1018
+ let(:tally) do
1019
+ criteria.tally("genres.x")
1020
+ end
1021
+
1022
+ it "returns the correct hash without the nil keys" do
1023
+ expect(tally).to eq(
1024
+ [1, 2] => 2,
1025
+ [1, 3] => 1
1026
+ )
1027
+ end
1028
+ end
1029
+
1030
+ context "when tallying an element from an array of hashes; with duplicate" do
1031
+
1032
+ before do
1033
+ Band.create!(origin: "tally", genres: [ { x: 1 }, {x: 1} ] )
1034
+ end
1035
+
1036
+ let(:criteria) { Band.where(origin: "tally") }
1037
+
1038
+ let(:tally) do
1039
+ criteria.tally("genres.x")
1040
+ end
1041
+
1042
+ it "returns the correct hash without the nil keys" do
1043
+ expect(tally).to eq(
1044
+ [1, 2] => 2,
1045
+ [1, 3] => 1,
1046
+ [1, 1] => 1,
1047
+ )
1048
+ end
1049
+ end
1050
+
1051
+ context "when tallying an aliased field of type array" do
1052
+
1053
+ before do
1054
+ Person.create!(array: [ 1, 2 ])
1055
+ Person.create!(array: [ 1, 3 ])
1056
+ end
1057
+
1058
+ let(:tally) do
1059
+ Person.tally("array")
1060
+ end
1061
+
1062
+ it "returns the correct hash" do
1063
+ expect(tally).to eq(
1064
+ [1, 2] => 1,
1065
+ [1, 3] => 1
1066
+ )
1067
+ end
1068
+ end
1069
+
1070
+ context "when going multiple levels deep in arrays" do
1071
+ let(:criteria) { Band.where(origin: "tally") }
1072
+
1073
+ let(:tally) do
1074
+ criteria.tally("genres.y.z")
1075
+ end
1076
+
1077
+ it "returns the correct hash" do
1078
+ expect(tally).to eq(
1079
+ [1, 2] => 2,
1080
+ [1, 3] => 1
1081
+ )
1082
+ end
1083
+ end
1084
+
1085
+ context "when going multiple levels deep in an array" do
1086
+ let(:criteria) { Band.where(origin: "tally") }
1087
+
1088
+ let(:tally) do
1089
+ criteria.tally("genres.y.z")
1090
+ end
1091
+
1092
+ it "returns the correct hash" do
1093
+ expect(tally).to eq(
1094
+ [1, 2] => 2,
1095
+ [1, 3] => 1
1096
+ )
1097
+ end
1098
+ end
1099
+
1100
+ context "when tallying deeply nested arrays/embedded associations" do
1101
+
1102
+ before do
1103
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
1104
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
1105
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))) ])
1106
+ end
1107
+
1108
+ let(:tally) do
1109
+ Person.tally("addresses.code.deepest.array.y.z")
1110
+ end
1111
+
1112
+ it "returns the correct hash" do
1113
+ expect(tally).to eq(
1114
+ [ [ 1, 2 ] ] => 2,
1115
+ [ [ 1, 3 ] ] => 1
1116
+ )
1117
+ end
1118
+ end
1119
+
1120
+ context "when tallying deeply nested arrays/embedded associations" do
1121
+
1122
+ before do
1123
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))),
1124
+ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
1125
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))),
1126
+ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
1127
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))),
1128
+ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))) ])
1129
+ end
1130
+
1131
+ let(:tally) do
1132
+ Person.tally("addresses.code.deepest.array.y.z")
1133
+ end
1134
+
1135
+ it "returns the correct hash" do
1136
+ expect(tally).to eq(
1137
+ [ [ 1, 2 ], [ 1, 2 ] ] => 2,
1138
+ [ [ 1, 3 ], [ 1, 3 ] ] => 1
1139
+ )
1140
+ end
1141
+ end
1142
+
1143
+ context "when some keys are missing" do
1144
+ before do
1145
+ 3.times { Band.create!(origin: "tally") }
1146
+ end
1147
+
1148
+ let(:tally) do
1149
+ criteria.tally(:name)
1150
+ end
1151
+
1152
+ it "returns the correct hash" do
1153
+ expect(tally).to eq(
1154
+ "Depeche Mode" => 1,
1155
+ "New Order" => 1,
1156
+ "10,000 Maniacs" => 1,
1157
+ nil => 3
1158
+ )
1159
+ end
1160
+ end
1161
+
1162
+ context "when the first element is an embeds_one" do
1163
+ before do
1164
+ Person.create!(name: Name.new(translations: [ Translation.new(language: 1), Translation.new(language: 2) ]))
1165
+ Person.create!(name: Name.new(translations: [ Translation.new(language: 1), Translation.new(language: 2) ]))
1166
+ Person.create!(name: Name.new(translations: [ Translation.new(language: 1), Translation.new(language: 3) ]))
1167
+ end
1168
+
1169
+ let(:tally) do
1170
+ Person.tally("name.translations.language")
1171
+ end
1172
+
1173
+ it "returns the correct hash" do
1174
+ expect(tally).to eq(
1175
+ [1, 2] => 2,
1176
+ [1, 3] => 1
1177
+ )
1178
+ end
1179
+ end
1180
+
1181
+ context "when tallying demongoizable values from typeless fields" do
1182
+
1183
+ let!(:person1) { Person.create!(ssn: /hello/) }
1184
+ let!(:person2) { Person.create!(ssn: BSON::Decimal128.new("1")) }
1185
+ let(:tally) { Person.tally("ssn") }
1186
+
1187
+ let(:tallied_classes) do
1188
+ tally.keys.map(&:class).sort do |a, b|
1189
+ a.to_s.casecmp(b.to_s)
1190
+ end
1191
+ end
1192
+
1193
+ context "< BSON 5" do
1194
+ max_bson_version '4.99.99'
1195
+
1196
+ it "stores the correct types in the database" do
1197
+ expect(Person.find(person1.id).attributes["ssn"]).to be_a BSON::Regexp::Raw
1198
+ expect(Person.find(person2.id).attributes["ssn"]).to be_a BSON::Decimal128
1199
+ end
1200
+
1201
+ it "tallies the correct type" do
1202
+ expect(tallied_classes).to be == [ BSON::Decimal128, BSON::Regexp::Raw ]
1203
+ end
1204
+ end
1205
+
1206
+ context '>= BSON 5' do
1207
+ min_bson_version "5.0"
1208
+
1209
+ it "stores the correct types in the database" do
1210
+ expect(Person.find(person1.id).ssn).to be_a BSON::Regexp::Raw
1211
+ expect(Person.find(person2.id).ssn).to be_a BigDecimal
1212
+ end
1213
+
1214
+ it "tallies the correct type" do
1215
+ expect(tallied_classes).to be == [ BigDecimal, BSON::Regexp::Raw ]
1216
+ end
1217
+ end
1218
+
1219
+ context '>= BSON 5 with decimal128 allowed' do
1220
+ min_bson_version "5.0"
1221
+ config_override :allow_bson5_decimal128, true
1222
+
1223
+ it "stores the correct types in the database" do
1224
+ expect(Person.find(person1.id).ssn).to be_a BSON::Regexp::Raw
1225
+ expect(Person.find(person2.id).ssn).to be_a BSON::Decimal128
1226
+ end
1227
+
1228
+ it "tallies the correct type" do
1229
+ expect(tallied_classes).to be == [ BSON::Decimal128, BSON::Regexp::Raw ]
1230
+ end
1231
+ end
1232
+ end
1233
+ end
1234
+
812
1235
  describe "#each" do
813
1236
 
814
1237
  before do
@@ -973,98 +1396,175 @@ describe Mongoid::Contextual::Mongo do
973
1396
 
974
1397
  describe "#exists?" do
975
1398
 
976
- before do
977
- Band.create!(name: "Depeche Mode")
1399
+ let!(:band) do
1400
+ Band.create!(name: "Depeche Mode", active: true)
978
1401
  end
979
1402
 
980
- context "when the count is zero" do
1403
+ context "when not passing options" do
981
1404
 
982
- let(:criteria) do
983
- Band.where(name: "New Order")
984
- end
1405
+ context "when the count is zero" do
985
1406
 
986
- let(:context) do
987
- described_class.new(criteria)
988
- end
1407
+ let(:criteria) do
1408
+ Band.where(name: "New Order")
1409
+ end
989
1410
 
990
- it "returns false" do
991
- expect(context).to_not be_exists
1411
+ let(:context) do
1412
+ described_class.new(criteria)
1413
+ end
1414
+
1415
+ it "returns false" do
1416
+ expect(context).to_not be_exists
1417
+ end
992
1418
  end
993
- end
994
1419
 
995
- context "when the count is greater than zero" do
1420
+ context "when the count is greater than zero" do
996
1421
 
997
- let(:criteria) do
998
- Band.where(name: "Depeche Mode")
999
- end
1422
+ let(:criteria) do
1423
+ Band.where(name: "Depeche Mode")
1424
+ end
1000
1425
 
1001
- let(:context) do
1002
- described_class.new(criteria)
1426
+ let(:context) do
1427
+ described_class.new(criteria)
1428
+ end
1429
+
1430
+ it "returns true" do
1431
+ expect(context).to be_exists
1432
+ end
1003
1433
  end
1004
1434
 
1005
- it "returns true" do
1006
- expect(context).to be_exists
1435
+ context "when caching is not enabled" do
1436
+
1437
+ let(:criteria) do
1438
+ Band.where(name: "Depeche Mode")
1439
+ end
1440
+
1441
+ let(:context) do
1442
+ described_class.new(criteria)
1443
+ end
1444
+
1445
+ context "when exists? already called and query cache is enabled" do
1446
+ query_cache_enabled
1447
+
1448
+ before do
1449
+ context.exists?
1450
+ end
1451
+
1452
+ it "does not hit the database again" do
1453
+ expect_no_queries do
1454
+ expect(context).to be_exists
1455
+ end
1456
+ end
1457
+ end
1007
1458
  end
1008
1459
  end
1009
1460
 
1010
- context "when caching is not enabled" do
1461
+ context "when passing an _id" do
1011
1462
 
1012
- let(:criteria) do
1013
- Band.where(name: "Depeche Mode")
1014
- end
1463
+ context "when its of type BSON::ObjectId" do
1015
1464
 
1016
- let(:context) do
1017
- described_class.new(criteria)
1465
+ context "when calling it on the class" do
1466
+
1467
+ it "returns true" do
1468
+ expect(Band.exists?(band._id)).to be true
1469
+ end
1470
+ end
1471
+
1472
+ context "when calling it on a criteria that includes the object" do
1473
+
1474
+ it "returns true" do
1475
+ expect(Band.where(name: band.name).exists?(band._id)).to be true
1476
+ end
1477
+ end
1478
+
1479
+ context "when calling it on a criteria that does not include the object" do
1480
+
1481
+ it "returns false" do
1482
+ expect(Band.where(name: "bogus").exists?(band._id)).to be false
1483
+ end
1484
+ end
1485
+
1486
+ context "when the id does not exist" do
1487
+
1488
+ it "returns false" do
1489
+ expect(Band.exists?(BSON::ObjectId.new)).to be false
1490
+ end
1491
+ end
1018
1492
  end
1019
1493
 
1020
- context "when exists? already called" do
1494
+ context "when its of type String" do
1021
1495
 
1022
- before do
1023
- context.exists?
1496
+ context "when the id exists" do
1497
+
1498
+ it "returns true" do
1499
+ expect(Band.exists?(band._id.to_s)).to be true
1500
+ end
1024
1501
  end
1025
1502
 
1026
- it "hits the database again" do
1027
- expect(context).to receive(:view).once.and_call_original
1028
- expect(context).to be_exists
1503
+ context "when the id does not exist" do
1504
+
1505
+ it "returns false" do
1506
+ expect(Band.exists?(BSON::ObjectId.new.to_s)).to be false
1507
+ end
1029
1508
  end
1030
1509
  end
1031
1510
  end
1032
1511
 
1033
- context "when caching is enabled" do
1512
+ context "when passing a hash" do
1034
1513
 
1035
- let(:criteria) do
1036
- Band.where(name: "Depeche Mode").cache
1514
+ context "when calling it on the class" do
1515
+
1516
+ it "returns true" do
1517
+ expect(Band.exists?(name: band.name)).to be true
1518
+ end
1037
1519
  end
1038
1520
 
1039
- let(:context) do
1040
- described_class.new(criteria)
1521
+ context "when calling it on a criteria that includes the object" do
1522
+
1523
+ it "returns true" do
1524
+ expect(Band.where(active: true).exists?(name: band.name)).to be true
1525
+ end
1041
1526
  end
1042
1527
 
1043
- context "when the cache is loaded" do
1528
+ context "when calling it on a criteria that does not include the object" do
1044
1529
 
1045
- before do
1046
- context.to_a
1530
+ it "returns false" do
1531
+ expect(Band.where(active: false).exists?(name: band.name)).to be false
1047
1532
  end
1533
+ end
1048
1534
 
1049
- it "does not hit the database" do
1050
- expect(context).to receive(:view).never
1051
- expect(context).to be_exists
1535
+ context "when the conditions don't match" do
1536
+
1537
+ it "returns false" do
1538
+ expect(Band.exists?(name: "bogus")).to be false
1052
1539
  end
1053
1540
  end
1541
+ end
1054
1542
 
1055
- context "when the cache is not loaded" do
1543
+ context "when passing false" do
1056
1544
 
1057
- context "when a count has been executed" do
1545
+ it "returns false" do
1546
+ expect(Band.exists?(false)).to be false
1547
+ end
1548
+ end
1058
1549
 
1059
- before do
1060
- context.count
1061
- end
1550
+ context "when passing nil" do
1062
1551
 
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
1552
+ it "returns false" do
1553
+ expect(Band.exists?(nil)).to be false
1554
+ end
1555
+ end
1556
+
1557
+ context "when the limit is 0" do
1558
+
1559
+ it "returns false" do
1560
+ expect(Band.limit(0).exists?).to be false
1561
+ end
1562
+ end
1563
+
1564
+ context "when the criteria limit is 0" do
1565
+
1566
+ it "returns false" do
1567
+ expect(Band.criteria.limit(0).exists?).to be false
1068
1568
  end
1069
1569
  end
1070
1570
  end
@@ -1080,7 +1580,16 @@ describe Mongoid::Contextual::Mongo do
1080
1580
  end
1081
1581
 
1082
1582
  it "returns the criteria explain path" do
1083
- expect(context.explain).to_not be_empty
1583
+ explain = context.explain
1584
+ expect(explain).to_not be_empty
1585
+ expect(explain.keys).to include("queryPlanner", "executionStats", "serverInfo")
1586
+ end
1587
+
1588
+ it "respects options passed to explain" do
1589
+ explain = context.explain(verbosity: :query_planner)
1590
+ expect(explain).to_not be_empty
1591
+ expect(explain.keys).to include("queryPlanner", "serverInfo")
1592
+ expect(explain.keys).not_to include("executionStats")
1084
1593
  end
1085
1594
  end
1086
1595
 
@@ -1420,7 +1929,7 @@ describe Mongoid::Contextual::Mongo do
1420
1929
  it "deletes the document from the database" do
1421
1930
  expect {
1422
1931
  depeche.reload
1423
- }.to raise_error(Mongoid::Errors::DocumentNotFound)
1932
+ }.to raise_error(Mongoid::Errors::DocumentNotFound, /Document\(s\) not found for class Band with id\(s\)/)
1424
1933
  end
1425
1934
 
1426
1935
  context 'when a collation is specified on the criteria' do
@@ -1445,7 +1954,7 @@ describe Mongoid::Contextual::Mongo do
1445
1954
  it "deletes the document from the database" do
1446
1955
  expect {
1447
1956
  depeche.reload
1448
- }.to raise_error(Mongoid::Errors::DocumentNotFound)
1957
+ }.to raise_error(Mongoid::Errors::DocumentNotFound, /Document\(s\) not found for class Band with id\(s\)/)
1449
1958
  end
1450
1959
  end
1451
1960
  end
@@ -1561,24 +2070,6 @@ describe Mongoid::Contextual::Mongo do
1561
2070
  expect(context.last).to eq(rolling_stones)
1562
2071
  end
1563
2072
  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
2073
  end
1583
2074
 
1584
2075
  context 'when the criteria has a sort' do
@@ -1602,25 +2093,6 @@ describe Mongoid::Contextual::Mongo do
1602
2093
  expect(context.last).to eq(depeche_mode)
1603
2094
  end
1604
2095
  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
2096
  end
1625
2097
 
1626
2098
  context "when using .sort" do
@@ -1649,28 +2121,17 @@ describe Mongoid::Contextual::Mongo do
1649
2121
  end
1650
2122
  end
1651
2123
 
1652
- context "when the context is cached" do
2124
+ context "when the query cache is enabled" do
2125
+ query_cache_enabled
1653
2126
 
1654
2127
  let(:criteria) do
1655
- Band.where(name: "Depeche Mode").cache
2128
+ Band.where(name: "Depeche Mode")
1656
2129
  end
1657
2130
 
1658
2131
  let(:context) do
1659
2132
  described_class.new(criteria)
1660
2133
  end
1661
2134
 
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
2135
  context "when first method was called before" do
1675
2136
 
1676
2137
  before do
@@ -1678,8 +2139,9 @@ describe Mongoid::Contextual::Mongo do
1678
2139
  end
1679
2140
 
1680
2141
  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)
2142
+ expect_no_queries do
2143
+ expect(context.send(method)).to eq(depeche_mode)
2144
+ end
1683
2145
  end
1684
2146
  end
1685
2147
  end
@@ -1733,77 +2195,21 @@ describe Mongoid::Contextual::Mongo do
1733
2195
  end
1734
2196
  end
1735
2197
 
1736
- context "when the context is cached" do
2198
+ context "when the query cache is enabled" do
1737
2199
 
1738
2200
  let(:context) do
1739
2201
  described_class.new(criteria)
1740
2202
  end
1741
2203
 
1742
- context "when the whole context is loaded" do
1743
-
1744
- before do
1745
- context.to_a
1746
- end
1747
-
1748
- context "when all of the documents are cached" do
1749
-
1750
- let(:criteria) do
1751
- Band.all.cache
1752
- end
1753
-
1754
- context "when requesting all of the documents" do
1755
-
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
2204
+ context "when calling first beforehand" do
2205
+ query_cache_enabled
1800
2206
 
1801
2207
  let(:context) do
1802
2208
  described_class.new(criteria)
1803
2209
  end
1804
2210
 
1805
2211
  let(:criteria) do
1806
- Band.all.cache
2212
+ Band.all
1807
2213
  end
1808
2214
 
1809
2215
  before do
@@ -1821,8 +2227,9 @@ describe Mongoid::Contextual::Mongo do
1821
2227
  let(:limit) { 3 }
1822
2228
 
1823
2229
  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 ])
2230
+ expect_no_queries do
2231
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2232
+ end
1826
2233
  end
1827
2234
  end
1828
2235
 
@@ -1830,8 +2237,9 @@ describe Mongoid::Contextual::Mongo do
1830
2237
  let(:limit) { 2 }
1831
2238
 
1832
2239
  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 ])
2240
+ expect_no_queries do
2241
+ expect(docs).to eq([ depeche_mode, new_order ])
2242
+ end
1835
2243
  end
1836
2244
  end
1837
2245
  end
@@ -1843,8 +2251,9 @@ describe Mongoid::Contextual::Mongo do
1843
2251
  let(:limit) { 2 }
1844
2252
 
1845
2253
  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 ])
2254
+ expect_no_queries do
2255
+ expect(docs).to eq([ depeche_mode, new_order ])
2256
+ end
1848
2257
  end
1849
2258
  end
1850
2259
 
@@ -1852,8 +2261,9 @@ describe Mongoid::Contextual::Mongo do
1852
2261
  let(:limit) { 3 }
1853
2262
 
1854
2263
  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 ])
2264
+ expect_query(1) do
2265
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2266
+ end
1857
2267
  end
1858
2268
  end
1859
2269
  end
@@ -1865,8 +2275,9 @@ describe Mongoid::Contextual::Mongo do
1865
2275
  let(:limit) { 1 }
1866
2276
 
1867
2277
  it "returns the correct documents without touching the database" do
1868
- expect(context).to receive(:view).never
1869
- expect(docs).to eq([ depeche_mode ])
2278
+ expect_no_queries do
2279
+ expect(docs).to eq([ depeche_mode ])
2280
+ end
1870
2281
  end
1871
2282
  end
1872
2283
 
@@ -1874,8 +2285,9 @@ describe Mongoid::Contextual::Mongo do
1874
2285
  let(:limit) { 3 }
1875
2286
 
1876
2287
  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 ])
2288
+ expect_query(1) do
2289
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2290
+ end
1879
2291
  end
1880
2292
  end
1881
2293
  end
@@ -1883,14 +2295,15 @@ describe Mongoid::Contextual::Mongo do
1883
2295
  end
1884
2296
  end
1885
2297
 
1886
- context "when calling #first then #last" do
2298
+ context "when calling #first then #last and the query cache is enabled" do
2299
+ query_cache_enabled
1887
2300
 
1888
2301
  let(:context) do
1889
2302
  described_class.new(criteria)
1890
2303
  end
1891
2304
 
1892
2305
  let(:criteria) do
1893
- Band.all.cache
2306
+ Band.all
1894
2307
  end
1895
2308
 
1896
2309
  before do
@@ -1905,8 +2318,10 @@ describe Mongoid::Contextual::Mongo do
1905
2318
  let(:before_limit) { 2 }
1906
2319
  let(:limit) { 1 }
1907
2320
 
1908
- it "gets the correct document" do
1909
- expect(docs).to eq([rolling_stones])
2321
+ it "gets the correct document and hits the database" do
2322
+ expect_query(1) do
2323
+ expect(docs).to eq([rolling_stones])
2324
+ end
1910
2325
  end
1911
2326
  end
1912
2327
  end
@@ -1996,30 +2411,10 @@ describe Mongoid::Contextual::Mongo do
1996
2411
  context 'when calling #first' do
1997
2412
 
1998
2413
  it 'returns the first document, sorted by _id' do
1999
- pending "MONGOID-5416"
2000
2414
  expect(context.last).to eq(rolling_stones)
2001
2415
  expect(context.first).to eq(depeche_mode)
2002
2416
  end
2003
2417
  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
2418
  end
2024
2419
 
2025
2420
  context 'when the criteria has a sort' do
@@ -2044,25 +2439,6 @@ describe Mongoid::Contextual::Mongo do
2044
2439
  expect(context.first).to eq(rolling_stones)
2045
2440
  end
2046
2441
  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
2442
  end
2067
2443
 
2068
2444
  context "when using .sort" do
@@ -2091,28 +2467,17 @@ describe Mongoid::Contextual::Mongo do
2091
2467
  end
2092
2468
  end
2093
2469
 
2094
- context "when the context is cached" do
2470
+ context "when the query cache is enabled" do
2471
+ query_cache_enabled
2095
2472
 
2096
2473
  let(:criteria) do
2097
- Band.where(name: "Depeche Mode").cache
2474
+ Band.where(name: "Depeche Mode")
2098
2475
  end
2099
2476
 
2100
2477
  let(:context) do
2101
2478
  described_class.new(criteria)
2102
2479
  end
2103
2480
 
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
2481
  context "when last method was called before" do
2117
2482
 
2118
2483
  before do
@@ -2120,8 +2485,9 @@ describe Mongoid::Contextual::Mongo do
2120
2485
  end
2121
2486
 
2122
2487
  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)
2488
+ expect_no_queries do
2489
+ expect(context.last).to eq(depeche_mode)
2490
+ end
2125
2491
  end
2126
2492
  end
2127
2493
  end
@@ -2181,71 +2547,15 @@ describe Mongoid::Contextual::Mongo do
2181
2547
  described_class.new(criteria)
2182
2548
  end
2183
2549
 
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
2550
+ context "when query cache is enabled" do
2551
+ query_cache_enabled
2242
2552
 
2243
2553
  let(:context) do
2244
2554
  described_class.new(criteria)
2245
2555
  end
2246
2556
 
2247
2557
  let(:criteria) do
2248
- Band.all.cache
2558
+ Band.all
2249
2559
  end
2250
2560
 
2251
2561
  before do
@@ -2262,18 +2572,20 @@ describe Mongoid::Contextual::Mongo do
2262
2572
  context "when getting all of the documents" do
2263
2573
  let(:limit) { 3 }
2264
2574
 
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 ])
2575
+ it "returns all documents without touching the db" do
2576
+ expect_no_queries do
2577
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2578
+ end
2268
2579
  end
2269
2580
  end
2270
2581
 
2271
2582
  context "when getting fewer documents" do
2272
2583
  let(:limit) { 2 }
2273
2584
 
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 ])
2585
+ it "returns the correct documents without touching the db" do
2586
+ expect_no_queries do
2587
+ expect(docs).to eq([ new_order, rolling_stones ])
2588
+ end
2277
2589
  end
2278
2590
  end
2279
2591
  end
@@ -2284,9 +2596,10 @@ describe Mongoid::Contextual::Mongo do
2284
2596
  context "when getting the same number of documents" do
2285
2597
  let(:limit) { 2 }
2286
2598
 
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 ])
2599
+ it "returns the correct documents without touching the db" do
2600
+ expect_no_queries do
2601
+ expect(docs).to eq([ new_order, rolling_stones ])
2602
+ end
2290
2603
  end
2291
2604
  end
2292
2605
 
@@ -2294,8 +2607,9 @@ describe Mongoid::Contextual::Mongo do
2294
2607
  let(:limit) { 3 }
2295
2608
 
2296
2609
  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 ])
2610
+ expect_query(1) do
2611
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2612
+ end
2299
2613
  end
2300
2614
  end
2301
2615
  end
@@ -2307,8 +2621,9 @@ describe Mongoid::Contextual::Mongo do
2307
2621
  let(:limit) { 1 }
2308
2622
 
2309
2623
  it "returns the correct documents without touching the database" do
2310
- expect(context).to receive(:view).never
2311
- expect(docs).to eq([ rolling_stones ])
2624
+ expect_no_queries do
2625
+ expect(docs).to eq([ rolling_stones ])
2626
+ end
2312
2627
  end
2313
2628
  end
2314
2629
 
@@ -2316,8 +2631,9 @@ describe Mongoid::Contextual::Mongo do
2316
2631
  let(:limit) { 3 }
2317
2632
 
2318
2633
  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 ])
2634
+ expect_query(1) do
2635
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2636
+ end
2321
2637
  end
2322
2638
  end
2323
2639
  end
@@ -2325,14 +2641,15 @@ describe Mongoid::Contextual::Mongo do
2325
2641
  end
2326
2642
  end
2327
2643
 
2328
- context "when calling #last then #first" do
2644
+ context "when calling #last then #first and the query cache is enabled" do
2645
+ query_cache_enabled
2329
2646
 
2330
2647
  let(:context) do
2331
2648
  described_class.new(criteria)
2332
2649
  end
2333
2650
 
2334
2651
  let(:criteria) do
2335
- Band.all.cache
2652
+ Band.all
2336
2653
  end
2337
2654
 
2338
2655
  before do
@@ -2348,12 +2665,12 @@ describe Mongoid::Contextual::Mongo do
2348
2665
  let(:limit) { 1 }
2349
2666
 
2350
2667
  it "hits the database" do
2351
- expect(context).to receive(:view).exactly(3).times.and_call_original
2352
- docs
2668
+ expect_query(1) do
2669
+ docs
2670
+ end
2353
2671
  end
2354
2672
 
2355
2673
  it "gets the correct document" do
2356
- pending "MONGOID-5416"
2357
2674
  expect(docs).to eq([ depeche_mode ])
2358
2675
  end
2359
2676
  end
@@ -2406,37 +2723,28 @@ describe Mongoid::Contextual::Mongo do
2406
2723
  described_class.new(criteria)
2407
2724
  end
2408
2725
 
2409
- it "returns the number of documents that match" do
2410
- expect(context.send(method)).to eq(2)
2411
- end
2726
+ context "when broken_view_options is false" do
2727
+ driver_config_override :broken_view_options, false
2412
2728
 
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) }
2729
+ it "returns the number of documents that match" do
2730
+ expect(context.send(method)).to eq(1)
2417
2731
  end
2418
2732
  end
2419
2733
 
2420
- context "when the results have been iterated over" do
2421
-
2422
- before do
2423
- context.entries
2424
- end
2734
+ context "when broken_view_options is true" do
2735
+ driver_config_override :broken_view_options, true
2425
2736
 
2426
- it "returns the cached value for all calls" do
2427
- expect(context.view).to receive(:count_documents).once.and_return(2)
2737
+ it "returns the number of documents that match" do
2428
2738
  expect(context.send(method)).to eq(2)
2429
2739
  end
2740
+ end
2430
2741
 
2431
- context "when the results have been iterated over multiple times" do
2432
-
2433
- before do
2434
- context.entries
2435
- end
2742
+ context "when calling more than once with different limits" do
2743
+ driver_config_override :broken_view_options, false
2436
2744
 
2437
- it "resets the length on each full iteration" do
2438
- expect(context.size).to eq(2)
2439
- end
2745
+ it "does not cache the value" do
2746
+ expect(context.limit(1).send(method)).to eq(1)
2747
+ expect(context.limit(2).send(method)).to eq(2)
2440
2748
  end
2441
2749
  end
2442
2750
  end
@@ -2455,10 +2763,12 @@ describe Mongoid::Contextual::Mongo do
2455
2763
  expect(context.send(method)).to eq(1)
2456
2764
  end
2457
2765
 
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) }
2766
+ context "when calling more than once with different skips" do
2767
+ driver_config_override :broken_view_options, false
2768
+
2769
+ it "does not cache the value" do
2770
+ expect(context.skip(0).send(method)).to eq(1)
2771
+ expect(context.skip(1).send(method)).to eq(0)
2462
2772
  end
2463
2773
  end
2464
2774
 
@@ -2580,7 +2890,7 @@ describe Mongoid::Contextual::Mongo do
2580
2890
  it "raises an error" do
2581
2891
  expect do
2582
2892
  Person.take!
2583
- end.to raise_error(Mongoid::Errors::DocumentNotFound)
2893
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Person./)
2584
2894
  end
2585
2895
  end
2586
2896
  end
@@ -2602,9 +2912,10 @@ describe Mongoid::Contextual::Mongo do
2602
2912
 
2603
2913
  context "when passed the symbol field name" do
2604
2914
 
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"]
2915
+ it "raises an error" do
2916
+ expect do
2917
+ context.map(:name)
2918
+ end.to raise_error(ArgumentError)
2608
2919
  end
2609
2920
  end
2610
2921
 
@@ -3392,6 +3703,20 @@ describe Mongoid::Contextual::Mongo do
3392
3703
  end
3393
3704
  end
3394
3705
 
3706
+ context 'when using aliased field names' do
3707
+ before do
3708
+ context.update_all('$set' => { years: 100 })
3709
+ end
3710
+
3711
+ it "updates the first matching document" do
3712
+ expect(depeche_mode.reload.years).to eq(100)
3713
+ end
3714
+
3715
+ it "updates the last matching document" do
3716
+ expect(new_order.reload.years).to eq(100)
3717
+ end
3718
+ end
3719
+
3395
3720
  context "when the attributes must be mongoized" do
3396
3721
 
3397
3722
  before do
@@ -3506,4 +3831,836 @@ describe Mongoid::Contextual::Mongo do
3506
3831
  end
3507
3832
  end
3508
3833
  end
3834
+
3835
+ describe "#first!" do
3836
+
3837
+ let!(:depeche_mode) do
3838
+ Band.create!(name: "Depeche Mode")
3839
+ end
3840
+
3841
+ let!(:new_order) do
3842
+ Band.create!(name: "New Order")
3843
+ end
3844
+
3845
+ let!(:rolling_stones) do
3846
+ Band.create!(name: "The Rolling Stones")
3847
+ end
3848
+
3849
+ let!(:death_cab) do
3850
+ Band.create!(name: "Death Cab For Cutie")
3851
+ end
3852
+
3853
+ let(:context) do
3854
+ described_class.new(criteria)
3855
+ end
3856
+
3857
+ context "when there's no sort" do
3858
+ let(:criteria) do
3859
+ Band.all
3860
+ end
3861
+
3862
+ it "gets the first document" do
3863
+ expect(context.first!).to eq(depeche_mode)
3864
+ end
3865
+ end
3866
+
3867
+ context "when there's a custom sort" do
3868
+ let(:criteria) do
3869
+ Band.all
3870
+ end
3871
+
3872
+ it "gets the first document" do
3873
+ expect(context.sort(name: 1).first!).to eq(death_cab)
3874
+ end
3875
+ end
3876
+
3877
+ context "when there are no documents" do
3878
+ let(:criteria) do
3879
+ Band.where(name: "bogus")
3880
+ end
3881
+
3882
+ it "raises an error" do
3883
+ expect do
3884
+ context.first!
3885
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
3886
+ end
3887
+ end
3888
+ end
3889
+
3890
+ describe "#last!" do
3891
+
3892
+ let!(:depeche_mode) do
3893
+ Band.create!(name: "Depeche Mode")
3894
+ end
3895
+
3896
+ let!(:new_order) do
3897
+ Band.create!(name: "New Order")
3898
+ end
3899
+
3900
+ let!(:rolling_stones) do
3901
+ Band.create!(name: "The Rolling Stones")
3902
+ end
3903
+
3904
+ let!(:death_cab) do
3905
+ Band.create!(name: "Death Cab For Cutie")
3906
+ end
3907
+
3908
+ let(:context) do
3909
+ described_class.new(criteria)
3910
+ end
3911
+
3912
+ context "when there's no sort" do
3913
+ let(:criteria) do
3914
+ Band.all
3915
+ end
3916
+
3917
+ it "gets the last document" do
3918
+ expect(context.last!).to eq(death_cab)
3919
+ end
3920
+ end
3921
+
3922
+ context "when there's a custom sort" do
3923
+ let(:criteria) do
3924
+ Band.all
3925
+ end
3926
+
3927
+ it "gets the last document" do
3928
+ expect(context.sort(name: 1).last!).to eq(rolling_stones)
3929
+ end
3930
+ end
3931
+
3932
+ context "when there are no documents" do
3933
+ let(:criteria) do
3934
+ Band.where(name: "bogus")
3935
+ end
3936
+
3937
+ it "raises an error" do
3938
+ expect do
3939
+ context.last!
3940
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
3941
+ end
3942
+ end
3943
+ end
3944
+
3945
+ describe "#second" do
3946
+
3947
+ let!(:depeche_mode) do
3948
+ Band.create!(name: "Depeche Mode")
3949
+ end
3950
+
3951
+ let!(:new_order) do
3952
+ Band.create!(name: "New Order")
3953
+ end
3954
+
3955
+ let!(:rolling_stones) do
3956
+ Band.create!(name: "The Rolling Stones")
3957
+ end
3958
+
3959
+ let!(:death_cab) do
3960
+ Band.create!(name: "Death Cab For Cutie")
3961
+ end
3962
+
3963
+ let(:context) do
3964
+ described_class.new(criteria)
3965
+ end
3966
+
3967
+ context "when there's no sort" do
3968
+ let(:criteria) do
3969
+ Band.all
3970
+ end
3971
+
3972
+ it "gets the second document" do
3973
+ expect(context.second).to eq(new_order)
3974
+ end
3975
+ end
3976
+
3977
+ context "when there's a custom sort" do
3978
+ let(:criteria) do
3979
+ Band.all
3980
+ end
3981
+
3982
+ it "gets the second document" do
3983
+ expect(context.sort(name: 1).second).to eq(depeche_mode)
3984
+ end
3985
+ end
3986
+
3987
+ context "when there are no documents" do
3988
+ let(:criteria) do
3989
+ Band.where(name: "bogus")
3990
+ end
3991
+
3992
+ it "returns nil" do
3993
+ expect(context.second).to be_nil
3994
+ end
3995
+ end
3996
+ end
3997
+
3998
+ describe "#second!" do
3999
+
4000
+ let!(:depeche_mode) do
4001
+ Band.create!(name: "Depeche Mode")
4002
+ end
4003
+
4004
+ let!(:new_order) do
4005
+ Band.create!(name: "New Order")
4006
+ end
4007
+
4008
+ let!(:rolling_stones) do
4009
+ Band.create!(name: "The Rolling Stones")
4010
+ end
4011
+
4012
+ let!(:death_cab) do
4013
+ Band.create!(name: "Death Cab For Cutie")
4014
+ end
4015
+
4016
+ let(:context) do
4017
+ described_class.new(criteria)
4018
+ end
4019
+
4020
+ context "when there's no sort" do
4021
+ let(:criteria) do
4022
+ Band.all
4023
+ end
4024
+
4025
+ it "gets the second document" do
4026
+ expect(context.second!).to eq(new_order)
4027
+ end
4028
+ end
4029
+
4030
+ context "when there's a custom sort" do
4031
+ let(:criteria) do
4032
+ Band.all
4033
+ end
4034
+
4035
+ it "gets the second document" do
4036
+ expect(context.sort(name: 1).second!).to eq(depeche_mode)
4037
+ end
4038
+ end
4039
+
4040
+ context "when there are no documents" do
4041
+ let(:criteria) do
4042
+ Band.where(name: "bogus")
4043
+ end
4044
+
4045
+ it "raises an error" do
4046
+ expect do
4047
+ context.second!
4048
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
4049
+ end
4050
+ end
4051
+ end
4052
+
4053
+ describe "#third" do
4054
+
4055
+ let!(:depeche_mode) do
4056
+ Band.create!(name: "Depeche Mode")
4057
+ end
4058
+
4059
+ let!(:new_order) do
4060
+ Band.create!(name: "New Order")
4061
+ end
4062
+
4063
+ let!(:rolling_stones) do
4064
+ Band.create!(name: "The Rolling Stones")
4065
+ end
4066
+
4067
+ let!(:death_cab) do
4068
+ Band.create!(name: "Death Cab For Cutie")
4069
+ end
4070
+
4071
+ let(:context) do
4072
+ described_class.new(criteria)
4073
+ end
4074
+
4075
+ context "when there's no sort" do
4076
+ let(:criteria) do
4077
+ Band.all
4078
+ end
4079
+
4080
+ it "gets the third document" do
4081
+ expect(context.third).to eq(rolling_stones)
4082
+ end
4083
+ end
4084
+
4085
+ context "when there's a custom sort" do
4086
+ let(:criteria) do
4087
+ Band.all
4088
+ end
4089
+
4090
+ it "gets the third document" do
4091
+ expect(context.sort(name: 1).third).to eq(new_order)
4092
+ end
4093
+ end
4094
+
4095
+ context "when there are no documents" do
4096
+ let(:criteria) do
4097
+ Band.where(name: "bogus")
4098
+ end
4099
+
4100
+ it "returns nil" do
4101
+ expect(context.third).to be_nil
4102
+ end
4103
+ end
4104
+ end
4105
+
4106
+ describe "#third!" do
4107
+
4108
+ let!(:depeche_mode) do
4109
+ Band.create!(name: "Depeche Mode")
4110
+ end
4111
+
4112
+ let!(:new_order) do
4113
+ Band.create!(name: "New Order")
4114
+ end
4115
+
4116
+ let!(:rolling_stones) do
4117
+ Band.create!(name: "The Rolling Stones")
4118
+ end
4119
+
4120
+ let!(:death_cab) do
4121
+ Band.create!(name: "Death Cab For Cutie")
4122
+ end
4123
+
4124
+ let(:context) do
4125
+ described_class.new(criteria)
4126
+ end
4127
+
4128
+ context "when there's no sort" do
4129
+ let(:criteria) do
4130
+ Band.all
4131
+ end
4132
+
4133
+ it "gets the third document" do
4134
+ expect(context.third!).to eq(rolling_stones)
4135
+ end
4136
+ end
4137
+
4138
+ context "when there's a custom sort" do
4139
+ let(:criteria) do
4140
+ Band.all
4141
+ end
4142
+
4143
+ it "gets the third document" do
4144
+ expect(context.sort(name: 1).third!).to eq(new_order)
4145
+ end
4146
+ end
4147
+
4148
+ context "when there are no documents" do
4149
+ let(:criteria) do
4150
+ Band.where(name: "bogus")
4151
+ end
4152
+
4153
+ it "raises an error" do
4154
+ expect do
4155
+ context.third!
4156
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
4157
+ end
4158
+ end
4159
+ end
4160
+
4161
+ describe "#fourth" do
4162
+
4163
+ let!(:depeche_mode) do
4164
+ Band.create!(name: "Depeche Mode")
4165
+ end
4166
+
4167
+ let!(:new_order) do
4168
+ Band.create!(name: "New Order")
4169
+ end
4170
+
4171
+ let!(:rolling_stones) do
4172
+ Band.create!(name: "The Rolling Stones")
4173
+ end
4174
+
4175
+ let!(:death_cab) do
4176
+ Band.create!(name: "Death Cab For Cutie")
4177
+ end
4178
+
4179
+ let(:context) do
4180
+ described_class.new(criteria)
4181
+ end
4182
+
4183
+ context "when there's no sort" do
4184
+ let(:criteria) do
4185
+ Band.all
4186
+ end
4187
+
4188
+ it "gets the fourth document" do
4189
+ expect(context.fourth).to eq(death_cab)
4190
+ end
4191
+ end
4192
+
4193
+ context "when there's a custom sort" do
4194
+ let(:criteria) do
4195
+ Band.all
4196
+ end
4197
+
4198
+ it "gets the fourth document" do
4199
+ expect(context.sort(name: 1).fourth).to eq(rolling_stones)
4200
+ end
4201
+ end
4202
+
4203
+ context "when there are no documents" do
4204
+ let(:criteria) do
4205
+ Band.where(name: "bogus")
4206
+ end
4207
+
4208
+ it "returns nil" do
4209
+ expect(context.fourth).to be_nil
4210
+ end
4211
+ end
4212
+ end
4213
+
4214
+ describe "#fourth!" do
4215
+
4216
+ let!(:depeche_mode) do
4217
+ Band.create!(name: "Depeche Mode")
4218
+ end
4219
+
4220
+ let!(:new_order) do
4221
+ Band.create!(name: "New Order")
4222
+ end
4223
+
4224
+ let!(:rolling_stones) do
4225
+ Band.create!(name: "The Rolling Stones")
4226
+ end
4227
+
4228
+ let!(:death_cab) do
4229
+ Band.create!(name: "Death Cab For Cutie")
4230
+ end
4231
+
4232
+ let(:context) do
4233
+ described_class.new(criteria)
4234
+ end
4235
+
4236
+ context "when there's no sort" do
4237
+ let(:criteria) do
4238
+ Band.all
4239
+ end
4240
+
4241
+ it "gets the fourth document" do
4242
+ expect(context.fourth!).to eq(death_cab)
4243
+ end
4244
+ end
4245
+
4246
+ context "when there's a custom sort" do
4247
+ let(:criteria) do
4248
+ Band.all
4249
+ end
4250
+
4251
+ it "gets the fourth document" do
4252
+ expect(context.sort(name: 1).fourth!).to eq(rolling_stones)
4253
+ end
4254
+ end
4255
+
4256
+ context "when there are no documents" do
4257
+ let(:criteria) do
4258
+ Band.where(name: "bogus")
4259
+ end
4260
+
4261
+ it "raises an error" do
4262
+ expect do
4263
+ context.fourth!
4264
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
4265
+ end
4266
+ end
4267
+ end
4268
+
4269
+ describe "#fifth" do
4270
+
4271
+ let!(:depeche_mode) do
4272
+ Band.create!(name: "Depeche Mode")
4273
+ end
4274
+
4275
+ let!(:new_order) do
4276
+ Band.create!(name: "New Order")
4277
+ end
4278
+
4279
+ let!(:rolling_stones) do
4280
+ Band.create!(name: "The Rolling Stones")
4281
+ end
4282
+
4283
+ let!(:death_cab) do
4284
+ Band.create!(name: "Death Cab For Cutie")
4285
+ end
4286
+
4287
+ let!(:guns_and_roses) do
4288
+ Band.create!(name: "Guns and Roses")
4289
+ end
4290
+
4291
+ let(:context) do
4292
+ described_class.new(criteria)
4293
+ end
4294
+
4295
+ context "when there's no sort" do
4296
+ let(:criteria) do
4297
+ Band.all
4298
+ end
4299
+
4300
+ it "gets the fifth document" do
4301
+ expect(context.fifth).to eq(guns_and_roses)
4302
+ end
4303
+ end
4304
+
4305
+ context "when there's a custom sort" do
4306
+ let(:criteria) do
4307
+ Band.all
4308
+ end
4309
+
4310
+ it "gets the fifth document" do
4311
+ expect(context.sort(name: 1).fifth).to eq(rolling_stones)
4312
+ end
4313
+ end
4314
+
4315
+ context "when there are no documents" do
4316
+ let(:criteria) do
4317
+ Band.where(name: "bogus")
4318
+ end
4319
+
4320
+ it "returns nil" do
4321
+ expect(context.fifth).to be_nil
4322
+ end
4323
+ end
4324
+ end
4325
+
4326
+ describe "#fifth!" do
4327
+
4328
+ let!(:depeche_mode) do
4329
+ Band.create!(name: "Depeche Mode")
4330
+ end
4331
+
4332
+ let!(:new_order) do
4333
+ Band.create!(name: "New Order")
4334
+ end
4335
+
4336
+ let!(:rolling_stones) do
4337
+ Band.create!(name: "The Rolling Stones")
4338
+ end
4339
+
4340
+ let!(:death_cab) do
4341
+ Band.create!(name: "Death Cab For Cutie")
4342
+ end
4343
+
4344
+ let!(:guns_and_roses) do
4345
+ Band.create!(name: "Guns and Roses")
4346
+ end
4347
+
4348
+ let(:context) do
4349
+ described_class.new(criteria)
4350
+ end
4351
+
4352
+ context "when there's no sort" do
4353
+ let(:criteria) do
4354
+ Band.all
4355
+ end
4356
+
4357
+ it "gets the fifth document" do
4358
+ expect(context.fifth!).to eq(guns_and_roses)
4359
+ end
4360
+ end
4361
+
4362
+ context "when there's a custom sort" do
4363
+ let(:criteria) do
4364
+ Band.all
4365
+ end
4366
+
4367
+ it "gets the fifth document" do
4368
+ expect(context.sort(name: 1).fifth!).to eq(rolling_stones)
4369
+ end
4370
+ end
4371
+
4372
+ context "when there are no documents" do
4373
+ let(:criteria) do
4374
+ Band.where(name: "bogus")
4375
+ end
4376
+
4377
+ it "raises an error" do
4378
+ expect do
4379
+ context.fifth!
4380
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
4381
+ end
4382
+ end
4383
+ end
4384
+
4385
+ describe "#second_to_last" do
4386
+
4387
+ let!(:depeche_mode) do
4388
+ Band.create!(name: "Depeche Mode")
4389
+ end
4390
+
4391
+ let!(:new_order) do
4392
+ Band.create!(name: "New Order")
4393
+ end
4394
+
4395
+ let!(:rolling_stones) do
4396
+ Band.create!(name: "The Rolling Stones")
4397
+ end
4398
+
4399
+ let!(:death_cab) do
4400
+ Band.create!(name: "Death Cab For Cutie")
4401
+ end
4402
+
4403
+ let(:context) do
4404
+ described_class.new(criteria)
4405
+ end
4406
+
4407
+ context "when there's no sort" do
4408
+ let(:criteria) do
4409
+ Band.all
4410
+ end
4411
+
4412
+ it "gets the second_to_last document" do
4413
+ expect(context.second_to_last).to eq(rolling_stones)
4414
+ end
4415
+ end
4416
+
4417
+ context "when there's a custom sort" do
4418
+ let(:criteria) do
4419
+ Band.all
4420
+ end
4421
+
4422
+ it "gets the second_to_last document" do
4423
+ expect(context.sort(name: 1).second_to_last).to eq(new_order)
4424
+ end
4425
+ end
4426
+
4427
+ context "when there are no documents" do
4428
+ let(:criteria) do
4429
+ Band.where(name: "bogus")
4430
+ end
4431
+
4432
+ it "returns nil" do
4433
+ expect(context.second_to_last).to be_nil
4434
+ end
4435
+ end
4436
+ end
4437
+
4438
+ describe "#second_to_last!" do
4439
+
4440
+ let!(:depeche_mode) do
4441
+ Band.create!(name: "Depeche Mode")
4442
+ end
4443
+
4444
+ let!(:new_order) do
4445
+ Band.create!(name: "New Order")
4446
+ end
4447
+
4448
+ let!(:rolling_stones) do
4449
+ Band.create!(name: "The Rolling Stones")
4450
+ end
4451
+
4452
+ let!(:death_cab) do
4453
+ Band.create!(name: "Death Cab For Cutie")
4454
+ end
4455
+
4456
+ let(:context) do
4457
+ described_class.new(criteria)
4458
+ end
4459
+
4460
+ context "when there's no sort" do
4461
+ let(:criteria) do
4462
+ Band.all
4463
+ end
4464
+
4465
+ it "gets the second_to_last document" do
4466
+ expect(context.second_to_last!).to eq(rolling_stones)
4467
+ end
4468
+ end
4469
+
4470
+ context "when there's a custom sort" do
4471
+ let(:criteria) do
4472
+ Band.all
4473
+ end
4474
+
4475
+ it "gets the second_to_last document" do
4476
+ expect(context.sort(name: 1).second_to_last!).to eq(new_order)
4477
+ end
4478
+ end
4479
+
4480
+ context "when there are no documents" do
4481
+ let(:criteria) do
4482
+ Band.where(name: "bogus")
4483
+ end
4484
+
4485
+ it "raises an error" do
4486
+ expect do
4487
+ context.second_to_last!
4488
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
4489
+ end
4490
+ end
4491
+ end
4492
+
4493
+ describe "#third_to_last" do
4494
+
4495
+ let!(:depeche_mode) do
4496
+ Band.create!(name: "Depeche Mode")
4497
+ end
4498
+
4499
+ let!(:new_order) do
4500
+ Band.create!(name: "New Order")
4501
+ end
4502
+
4503
+ let!(:rolling_stones) do
4504
+ Band.create!(name: "The Rolling Stones")
4505
+ end
4506
+
4507
+ let!(:death_cab) do
4508
+ Band.create!(name: "Death Cab For Cutie")
4509
+ end
4510
+
4511
+ let(:context) do
4512
+ described_class.new(criteria)
4513
+ end
4514
+
4515
+ context "when there's no sort" do
4516
+ let(:criteria) do
4517
+ Band.all
4518
+ end
4519
+
4520
+ it "gets the third_to_last document" do
4521
+ expect(context.third_to_last).to eq(new_order)
4522
+ end
4523
+ end
4524
+
4525
+ context "when there's a custom sort" do
4526
+ let(:criteria) do
4527
+ Band.all
4528
+ end
4529
+
4530
+ it "gets the third_to_last document" do
4531
+ expect(context.sort(name: 1).third_to_last).to eq(depeche_mode)
4532
+ end
4533
+ end
4534
+
4535
+ context "when there are no documents" do
4536
+ let(:criteria) do
4537
+ Band.where(name: "bogus")
4538
+ end
4539
+
4540
+ it "returns nil" do
4541
+ expect(context.third_to_last).to be_nil
4542
+ end
4543
+ end
4544
+ end
4545
+
4546
+ describe "#third_to_last!" do
4547
+
4548
+ let!(:depeche_mode) do
4549
+ Band.create!(name: "Depeche Mode")
4550
+ end
4551
+
4552
+ let!(:new_order) do
4553
+ Band.create!(name: "New Order")
4554
+ end
4555
+
4556
+ let!(:rolling_stones) do
4557
+ Band.create!(name: "The Rolling Stones")
4558
+ end
4559
+
4560
+ let!(:death_cab) do
4561
+ Band.create!(name: "Death Cab For Cutie")
4562
+ end
4563
+
4564
+ let(:context) do
4565
+ described_class.new(criteria)
4566
+ end
4567
+
4568
+ context "when there's no sort" do
4569
+ let(:criteria) do
4570
+ Band.all
4571
+ end
4572
+
4573
+ it "gets the third_to_last document" do
4574
+ expect(context.third_to_last!).to eq(new_order)
4575
+ end
4576
+ end
4577
+
4578
+ context "when there's a custom sort" do
4579
+ let(:criteria) do
4580
+ Band.all
4581
+ end
4582
+
4583
+ it "gets the third_to_last document" do
4584
+ expect(context.sort(name: 1).third_to_last!).to eq(depeche_mode)
4585
+ end
4586
+ end
4587
+
4588
+ context "when there are no documents" do
4589
+ let(:criteria) do
4590
+ Band.where(name: "bogus")
4591
+ end
4592
+
4593
+ it "raises an error" do
4594
+ expect do
4595
+ context.third_to_last!
4596
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
4597
+ end
4598
+ end
4599
+ end
4600
+
4601
+ describe '#load_async' do
4602
+ let!(:band) do
4603
+ Band.create!(name: "Depeche Mode")
4604
+ end
4605
+
4606
+ let(:criteria) do
4607
+ Band.where(name: "Depeche Mode")
4608
+ end
4609
+
4610
+ let(:context) do
4611
+ described_class.new(criteria)
4612
+ end
4613
+
4614
+ context 'with global thread pool async query executor' do
4615
+ config_override :async_query_executor, :global_thread_pool
4616
+
4617
+ it 'preloads the documents' do
4618
+ context.load_async
4619
+ context.documents_loader.wait
4620
+
4621
+ expect(context.view).not_to receive(:map)
4622
+ expect(context.to_a).to eq([band])
4623
+ end
4624
+
4625
+ it 're-raises exception during preload' do
4626
+ expect_any_instance_of(Mongoid::Contextual::Mongo::DocumentsLoader)
4627
+ .to receive(:execute)
4628
+ .at_least(:once)
4629
+ .and_raise(Mongo::Error::OperationFailure)
4630
+
4631
+ context.load_async
4632
+ context.documents_loader.wait
4633
+
4634
+ expect do
4635
+ context.to_a
4636
+ end.to raise_error(Mongo::Error::OperationFailure)
4637
+ end
4638
+ end
4639
+
4640
+ context 'with immediate thread pool async query executor' do
4641
+ config_override :async_query_executor, :immediate
4642
+
4643
+ it 'preloads the documents' do
4644
+ context.load_async
4645
+ context.documents_loader.wait
4646
+
4647
+ expect(context.view).not_to receive(:map)
4648
+ expect(context.to_a).to eq([band])
4649
+ end
4650
+
4651
+ it 're-raises exception during preload' do
4652
+ expect_any_instance_of(Mongoid::Contextual::Mongo::DocumentsLoader)
4653
+ .to receive(:execute)
4654
+ .at_least(:once)
4655
+ .and_raise(Mongo::Error::OperationFailure)
4656
+
4657
+ context.load_async
4658
+ context.documents_loader.wait
4659
+
4660
+ expect do
4661
+ context.to_a
4662
+ end.to raise_error(Mongo::Error::OperationFailure)
4663
+ end
4664
+ end
4665
+ end
3509
4666
  end