mongoid 7.5.3 → 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 (417) 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 +34 -11
  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.rb +7 -0
  55. data/lib/mongoid/attributes/dynamic.rb +4 -4
  56. data/lib/mongoid/attributes/nested.rb +6 -6
  57. data/lib/mongoid/attributes/processing.rb +37 -6
  58. data/lib/mongoid/attributes/projector.rb +2 -2
  59. data/lib/mongoid/attributes/readonly.rb +3 -3
  60. data/lib/mongoid/attributes.rb +51 -42
  61. data/lib/mongoid/changeable.rb +147 -14
  62. data/lib/mongoid/clients/options.rb +5 -1
  63. data/lib/mongoid/clients/sessions.rb +2 -14
  64. data/lib/mongoid/clients/storage_options.rb +2 -5
  65. data/lib/mongoid/clients/validators/storage.rb +3 -15
  66. data/lib/mongoid/collection_configurable.rb +58 -0
  67. data/lib/mongoid/composable.rb +2 -0
  68. data/lib/mongoid/config/defaults.rb +60 -0
  69. data/lib/mongoid/config/options.rb +3 -0
  70. data/lib/mongoid/config/validators/async_query_executor.rb +24 -0
  71. data/lib/mongoid/config/validators/client.rb +6 -6
  72. data/lib/mongoid/config/validators.rb +1 -0
  73. data/lib/mongoid/config.rb +140 -18
  74. data/lib/mongoid/contextual/aggregable/memory.rb +24 -16
  75. data/lib/mongoid/contextual/aggregable/mongo.rb +5 -5
  76. data/lib/mongoid/contextual/aggregable/none.rb +1 -1
  77. data/lib/mongoid/contextual/atomic.rb +1 -1
  78. data/lib/mongoid/contextual/geo_near.rb +7 -7
  79. data/lib/mongoid/contextual/map_reduce.rb +2 -2
  80. data/lib/mongoid/contextual/memory.rb +285 -58
  81. data/lib/mongoid/contextual/mongo/documents_loader.rb +177 -0
  82. data/lib/mongoid/contextual/mongo.rb +517 -346
  83. data/lib/mongoid/contextual/none.rb +193 -20
  84. data/lib/mongoid/contextual/queryable.rb +1 -1
  85. data/lib/mongoid/contextual.rb +14 -2
  86. data/lib/mongoid/copyable.rb +32 -8
  87. data/lib/mongoid/criteria/findable.rb +8 -5
  88. data/lib/mongoid/criteria/includable.rb +27 -22
  89. data/lib/mongoid/criteria/marshalable.rb +10 -2
  90. data/lib/mongoid/criteria/permission.rb +1 -1
  91. data/lib/mongoid/criteria/queryable/aggregable.rb +2 -2
  92. data/lib/mongoid/criteria/queryable/extensions/array.rb +3 -16
  93. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +25 -4
  94. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +2 -2
  95. data/lib/mongoid/criteria/queryable/extensions/date.rb +6 -1
  96. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +6 -1
  97. data/lib/mongoid/criteria/queryable/extensions/hash.rb +1 -17
  98. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -9
  99. data/lib/mongoid/criteria/queryable/extensions/object.rb +2 -1
  100. data/lib/mongoid/criteria/queryable/extensions/range.rb +13 -5
  101. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +3 -3
  102. data/lib/mongoid/criteria/queryable/extensions/set.rb +1 -1
  103. data/lib/mongoid/criteria/queryable/extensions/string.rb +4 -14
  104. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +4 -12
  105. data/lib/mongoid/criteria/queryable/extensions/time.rb +6 -1
  106. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +6 -1
  107. data/lib/mongoid/criteria/queryable/key.rb +4 -4
  108. data/lib/mongoid/criteria/queryable/mergeable.rb +1 -1
  109. data/lib/mongoid/criteria/queryable/optional.rb +11 -17
  110. data/lib/mongoid/criteria/queryable/options.rb +2 -2
  111. data/lib/mongoid/criteria/queryable/pipeline.rb +1 -1
  112. data/lib/mongoid/criteria/queryable/selectable.rb +47 -38
  113. data/lib/mongoid/criteria/queryable/selector.rb +93 -8
  114. data/lib/mongoid/criteria/queryable/smash.rb +40 -7
  115. data/lib/mongoid/criteria/queryable/storable.rb +1 -1
  116. data/lib/mongoid/criteria/queryable.rb +12 -7
  117. data/lib/mongoid/criteria/scopable.rb +2 -2
  118. data/lib/mongoid/criteria/translator.rb +45 -0
  119. data/lib/mongoid/criteria.rb +20 -40
  120. data/lib/mongoid/deprecable.rb +36 -0
  121. data/lib/mongoid/deprecation.rb +25 -0
  122. data/lib/mongoid/document.rb +127 -35
  123. data/lib/mongoid/equality.rb +8 -8
  124. data/lib/mongoid/errors/create_collection_failure.rb +33 -0
  125. data/lib/mongoid/errors/document_not_found.rb +10 -6
  126. data/lib/mongoid/errors/drop_collection_failure.rb +27 -0
  127. data/lib/mongoid/errors/immutable_attribute.rb +26 -0
  128. data/lib/mongoid/errors/invalid_async_query_executor.rb +25 -0
  129. data/lib/mongoid/errors/invalid_config_option.rb +1 -1
  130. data/lib/mongoid/errors/invalid_dependent_strategy.rb +1 -1
  131. data/lib/mongoid/errors/invalid_dot_dollar_assignment.rb +23 -0
  132. data/lib/mongoid/errors/invalid_field.rb +6 -2
  133. data/lib/mongoid/errors/invalid_field_type.rb +26 -0
  134. data/lib/mongoid/errors/invalid_global_executor_concurrency.rb +22 -0
  135. data/lib/mongoid/errors/invalid_relation.rb +1 -1
  136. data/lib/mongoid/errors/invalid_relation_option.rb +1 -1
  137. data/lib/mongoid/errors/invalid_session_use.rb +1 -1
  138. data/lib/mongoid/errors/invalid_storage_options.rb +1 -1
  139. data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
  140. data/lib/mongoid/errors/mongoid_error.rb +3 -3
  141. data/lib/mongoid/errors/nested_attributes_metadata_not_found.rb +1 -1
  142. data/lib/mongoid/errors/no_client_database.rb +1 -1
  143. data/lib/mongoid/errors/no_client_hosts.rb +1 -1
  144. data/lib/mongoid/errors/readonly_attribute.rb +1 -1
  145. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +1 -1
  146. data/lib/mongoid/errors/unknown_attribute.rb +1 -1
  147. data/lib/mongoid/errors.rb +6 -3
  148. data/lib/mongoid/extensions/array.rb +9 -7
  149. data/lib/mongoid/extensions/big_decimal.rb +33 -10
  150. data/lib/mongoid/extensions/binary.rb +42 -0
  151. data/lib/mongoid/extensions/boolean.rb +8 -2
  152. data/lib/mongoid/extensions/date.rb +26 -20
  153. data/lib/mongoid/extensions/date_time.rb +1 -1
  154. data/lib/mongoid/extensions/false_class.rb +1 -1
  155. data/lib/mongoid/extensions/float.rb +7 -4
  156. data/lib/mongoid/extensions/hash.rb +19 -8
  157. data/lib/mongoid/extensions/integer.rb +7 -4
  158. data/lib/mongoid/extensions/module.rb +1 -1
  159. data/lib/mongoid/extensions/object.rb +10 -8
  160. data/lib/mongoid/extensions/range.rb +41 -10
  161. data/lib/mongoid/extensions/regexp.rb +11 -4
  162. data/lib/mongoid/extensions/set.rb +11 -4
  163. data/lib/mongoid/extensions/string.rb +11 -22
  164. data/lib/mongoid/extensions/symbol.rb +4 -15
  165. data/lib/mongoid/extensions/time.rb +29 -16
  166. data/lib/mongoid/extensions/time_with_zone.rb +1 -2
  167. data/lib/mongoid/extensions/true_class.rb +1 -1
  168. data/lib/mongoid/extensions.rb +1 -0
  169. data/lib/mongoid/factory.rb +55 -7
  170. data/lib/mongoid/fields/foreign_key.rb +11 -4
  171. data/lib/mongoid/fields/localized.rb +19 -4
  172. data/lib/mongoid/fields/standard.rb +17 -7
  173. data/lib/mongoid/fields/validators/macro.rb +3 -9
  174. data/lib/mongoid/fields.rb +129 -20
  175. data/lib/mongoid/findable.rb +54 -24
  176. data/lib/mongoid/indexable/specification.rb +2 -2
  177. data/lib/mongoid/indexable/validators/options.rb +6 -2
  178. data/lib/mongoid/interceptable.rb +76 -15
  179. data/lib/mongoid/matchable.rb +1 -1
  180. data/lib/mongoid/matcher/eq_impl.rb +1 -1
  181. data/lib/mongoid/matcher/type.rb +1 -1
  182. data/lib/mongoid/matcher.rb +33 -13
  183. data/lib/mongoid/persistable/creatable.rb +19 -9
  184. data/lib/mongoid/persistable/deletable.rb +2 -2
  185. data/lib/mongoid/persistable/destroyable.rb +1 -1
  186. data/lib/mongoid/persistable/savable.rb +14 -2
  187. data/lib/mongoid/persistable/unsettable.rb +2 -2
  188. data/lib/mongoid/persistable/updatable.rb +69 -12
  189. data/lib/mongoid/persistable/upsertable.rb +21 -2
  190. data/lib/mongoid/persistable.rb +6 -3
  191. data/lib/mongoid/persistence_context.rb +6 -4
  192. data/lib/mongoid/query_cache.rb +13 -261
  193. data/lib/mongoid/railties/controller_runtime.rb +1 -1
  194. data/lib/mongoid/railties/database.rake +7 -2
  195. data/lib/mongoid/reloadable.rb +10 -8
  196. data/lib/mongoid/scopable.rb +15 -13
  197. data/lib/mongoid/selectable.rb +1 -2
  198. data/lib/mongoid/serializable.rb +10 -6
  199. data/lib/mongoid/stateful.rb +57 -10
  200. data/lib/mongoid/tasks/database.rake +12 -0
  201. data/lib/mongoid/tasks/database.rb +20 -2
  202. data/lib/mongoid/threaded/lifecycle.rb +5 -5
  203. data/lib/mongoid/threaded.rb +42 -12
  204. data/lib/mongoid/timestamps/created.rb +1 -1
  205. data/lib/mongoid/timestamps/updated.rb +2 -2
  206. data/lib/mongoid/touchable.rb +2 -3
  207. data/lib/mongoid/traversable.rb +5 -4
  208. data/lib/mongoid/utils.rb +22 -0
  209. data/lib/mongoid/validatable/localizable.rb +1 -1
  210. data/lib/mongoid/validatable/macros.rb +5 -7
  211. data/lib/mongoid/validatable/presence.rb +2 -2
  212. data/lib/mongoid/validatable/uniqueness.rb +9 -8
  213. data/lib/mongoid/validatable.rb +9 -6
  214. data/lib/mongoid/version.rb +1 -1
  215. data/lib/mongoid/warnings.rb +19 -4
  216. data/lib/mongoid.rb +17 -3
  217. data/spec/config/mongoid.yml +16 -0
  218. data/spec/integration/app_spec.rb +10 -14
  219. data/spec/integration/associations/belongs_to_spec.rb +18 -0
  220. data/spec/integration/associations/embedded_spec.rb +15 -0
  221. data/spec/integration/associations/embeds_many_spec.rb +15 -2
  222. data/spec/integration/associations/embeds_one_spec.rb +18 -0
  223. data/spec/integration/associations/foreign_key_spec.rb +9 -0
  224. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +21 -0
  225. data/spec/integration/associations/has_one_spec.rb +97 -1
  226. data/spec/integration/associations/scope_option_spec.rb +1 -1
  227. data/spec/integration/callbacks_models.rb +132 -1
  228. data/spec/integration/callbacks_spec.rb +360 -4
  229. data/spec/integration/criteria/range_spec.rb +95 -1
  230. data/spec/integration/discriminator_key_spec.rb +118 -80
  231. data/spec/integration/dots_and_dollars_spec.rb +277 -0
  232. data/spec/integration/i18n_fallbacks_spec.rb +3 -32
  233. data/spec/integration/matcher_examples_spec.rb +20 -13
  234. data/spec/integration/matcher_operator_data/type_decimal.yml +3 -2
  235. data/spec/integration/matcher_operator_spec.rb +3 -5
  236. data/spec/integration/persistence/range_field_spec.rb +350 -0
  237. data/spec/mongoid/association/counter_cache_spec.rb +1 -1
  238. data/spec/mongoid/association/depending_spec.rb +9 -9
  239. data/spec/mongoid/association/eager_spec.rb +2 -1
  240. data/spec/mongoid/association/embedded/embedded_in/binding_spec.rb +2 -1
  241. data/spec/mongoid/association/embedded/embedded_in/buildable_spec.rb +54 -0
  242. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +96 -9
  243. data/spec/mongoid/association/embedded/embeds_many/buildable_spec.rb +112 -0
  244. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +255 -65
  245. data/spec/mongoid/association/embedded/embeds_many_models.rb +37 -0
  246. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +12 -0
  247. data/spec/mongoid/association/embedded/embeds_many_spec.rb +68 -0
  248. data/spec/mongoid/association/embedded/embeds_one/buildable_spec.rb +25 -0
  249. data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
  250. data/spec/mongoid/association/embedded/embeds_one_models.rb +19 -0
  251. data/spec/mongoid/association/embedded/embeds_one_spec.rb +28 -0
  252. data/spec/mongoid/association/referenced/belongs_to/binding_spec.rb +2 -1
  253. data/spec/mongoid/association/referenced/belongs_to/buildable_spec.rb +54 -0
  254. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +15 -0
  255. data/spec/mongoid/association/referenced/belongs_to_models.rb +11 -0
  256. data/spec/mongoid/association/referenced/belongs_to_spec.rb +4 -20
  257. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +186 -229
  258. data/spec/mongoid/association/referenced/has_and_belongs_to_many_models.rb +25 -0
  259. data/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb +35 -2
  260. data/spec/mongoid/association/referenced/has_many/buildable_spec.rb +109 -0
  261. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +2 -56
  262. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +173 -177
  263. data/spec/mongoid/association/referenced/has_many_models.rb +3 -1
  264. data/spec/mongoid/association/referenced/has_many_spec.rb +25 -0
  265. data/spec/mongoid/association/referenced/has_one/buildable_spec.rb +2 -2
  266. data/spec/mongoid/association/referenced/has_one/proxy_spec.rb +107 -1
  267. data/spec/mongoid/association/referenced/has_one_models.rb +16 -0
  268. data/spec/mongoid/association/syncable_spec.rb +15 -1
  269. data/spec/mongoid/atomic/paths_spec.rb +0 -14
  270. data/spec/mongoid/attributes/nested_spec.rb +80 -11
  271. data/spec/mongoid/attributes/nested_spec_models.rb +48 -0
  272. data/spec/mongoid/attributes/projector_spec.rb +1 -5
  273. data/spec/mongoid/attributes_spec.rb +510 -33
  274. data/spec/mongoid/changeable_spec.rb +429 -37
  275. data/spec/mongoid/clients/factory_spec.rb +23 -30
  276. data/spec/mongoid/clients/sessions_spec.rb +0 -38
  277. data/spec/mongoid/clients_spec.rb +149 -15
  278. data/spec/mongoid/collection_configurable_spec.rb +158 -0
  279. data/spec/mongoid/config/defaults_spec.rb +160 -0
  280. data/spec/mongoid/config_spec.rb +214 -31
  281. data/spec/mongoid/contextual/aggregable/memory_spec.rb +396 -158
  282. data/spec/mongoid/contextual/aggregable/memory_table.yml +88 -0
  283. data/spec/mongoid/contextual/aggregable/memory_table_spec.rb +62 -0
  284. data/spec/mongoid/contextual/map_reduce_spec.rb +2 -16
  285. data/spec/mongoid/contextual/memory_spec.rb +850 -88
  286. data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
  287. data/spec/mongoid/contextual/mongo_spec.rb +1572 -435
  288. data/spec/mongoid/contextual/none_spec.rb +60 -21
  289. data/spec/mongoid/copyable_spec.rb +453 -11
  290. data/spec/mongoid/criteria/findable_spec.rb +86 -210
  291. data/spec/mongoid/criteria/includable_spec.rb +1492 -0
  292. data/spec/mongoid/criteria/includable_spec_models.rb +54 -0
  293. data/spec/mongoid/criteria/marshalable_spec.rb +18 -1
  294. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +7 -19
  295. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +134 -26
  296. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +11 -0
  297. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +11 -0
  298. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +0 -15
  299. data/spec/mongoid/criteria/queryable/extensions/numeric_spec.rb +73 -7
  300. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -69
  301. data/spec/mongoid/criteria/queryable/extensions/symbol_spec.rb +0 -59
  302. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +11 -0
  303. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +11 -0
  304. data/spec/mongoid/criteria/queryable/optional_spec.rb +15 -484
  305. data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
  306. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +469 -0
  307. data/spec/mongoid/criteria/queryable/selectable_spec.rb +78 -86
  308. data/spec/mongoid/criteria/queryable/selector_spec.rb +90 -5
  309. data/spec/mongoid/criteria/queryable/storable_spec.rb +72 -0
  310. data/spec/mongoid/criteria/translator_spec.rb +132 -0
  311. data/spec/mongoid/criteria_projection_spec.rb +1 -5
  312. data/spec/mongoid/criteria_spec.rb +469 -1205
  313. data/spec/mongoid/document_fields_spec.rb +173 -24
  314. data/spec/mongoid/document_spec.rb +32 -41
  315. data/spec/mongoid/errors/document_not_found_spec.rb +29 -2
  316. data/spec/mongoid/errors/invalid_field_spec.rb +1 -1
  317. data/spec/mongoid/errors/invalid_field_type_spec.rb +55 -0
  318. data/spec/mongoid/errors/mongoid_error_spec.rb +3 -1
  319. data/spec/mongoid/errors/no_environment_spec.rb +3 -3
  320. data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
  321. data/spec/mongoid/errors/too_many_nested_attribute_records_spec.rb +1 -1
  322. data/spec/mongoid/extensions/array_spec.rb +16 -2
  323. data/spec/mongoid/extensions/big_decimal_spec.rb +712 -212
  324. data/spec/mongoid/extensions/binary_spec.rb +44 -9
  325. data/spec/mongoid/extensions/boolean_spec.rb +68 -82
  326. data/spec/mongoid/extensions/date_class_mongoize_spec.rb +7 -3
  327. data/spec/mongoid/extensions/date_spec.rb +71 -1
  328. data/spec/mongoid/extensions/date_time_spec.rb +15 -9
  329. data/spec/mongoid/extensions/float_spec.rb +53 -74
  330. data/spec/mongoid/extensions/hash_spec.rb +33 -3
  331. data/spec/mongoid/extensions/integer_spec.rb +50 -64
  332. data/spec/mongoid/extensions/range_spec.rb +255 -54
  333. data/spec/mongoid/extensions/regexp_spec.rb +58 -33
  334. data/spec/mongoid/extensions/set_spec.rb +106 -0
  335. data/spec/mongoid/extensions/string_spec.rb +53 -25
  336. data/spec/mongoid/extensions/symbol_spec.rb +18 -25
  337. data/spec/mongoid/extensions/time_spec.rb +639 -106
  338. data/spec/mongoid/extensions/time_with_zone_spec.rb +24 -83
  339. data/spec/mongoid/factory_spec.rb +61 -1
  340. data/spec/mongoid/fields/localized_spec.rb +80 -37
  341. data/spec/mongoid/fields_spec.rb +500 -84
  342. data/spec/mongoid/findable_spec.rb +450 -58
  343. data/spec/mongoid/indexable/specification_spec.rb +2 -2
  344. data/spec/mongoid/indexable_spec.rb +55 -30
  345. data/spec/mongoid/interceptable_spec.rb +599 -8
  346. data/spec/mongoid/interceptable_spec_models.rb +235 -4
  347. data/spec/mongoid/matcher/extract_attribute_spec.rb +1 -5
  348. data/spec/mongoid/mongoizable_spec.rb +285 -0
  349. data/spec/mongoid/persistable/creatable_spec.rb +2 -2
  350. data/spec/mongoid/persistable/deletable_spec.rb +28 -8
  351. data/spec/mongoid/persistable/destroyable_spec.rb +28 -8
  352. data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
  353. data/spec/mongoid/persistable/logical_spec.rb +37 -0
  354. data/spec/mongoid/persistable/poppable_spec.rb +36 -0
  355. data/spec/mongoid/persistable/pullable_spec.rb +72 -0
  356. data/spec/mongoid/persistable/pushable_spec.rb +72 -0
  357. data/spec/mongoid/persistable/renamable_spec.rb +36 -0
  358. data/spec/mongoid/persistable/savable_spec.rb +96 -0
  359. data/spec/mongoid/persistable/settable_spec.rb +37 -0
  360. data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
  361. data/spec/mongoid/persistable/updatable_spec.rb +20 -28
  362. data/spec/mongoid/persistable/upsertable_spec.rb +89 -1
  363. data/spec/mongoid/persistence_context_spec.rb +31 -57
  364. data/spec/mongoid/query_cache_middleware_spec.rb +0 -18
  365. data/spec/mongoid/query_cache_spec.rb +56 -215
  366. data/spec/mongoid/reloadable_spec.rb +83 -6
  367. data/spec/mongoid/scopable_spec.rb +91 -1
  368. data/spec/mongoid/serializable_spec.rb +9 -30
  369. data/spec/mongoid/shardable_spec.rb +4 -4
  370. data/spec/mongoid/stateful_spec.rb +150 -8
  371. data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
  372. data/spec/mongoid/tasks/database_spec.rb +127 -0
  373. data/spec/mongoid/timestamps_spec.rb +392 -4
  374. data/spec/mongoid/timestamps_spec_models.rb +67 -0
  375. data/spec/mongoid/touchable_spec.rb +390 -2
  376. data/spec/mongoid/touchable_spec_models.rb +14 -8
  377. data/spec/mongoid/traversable_spec.rb +13 -35
  378. data/spec/mongoid/validatable/presence_spec.rb +1 -1
  379. data/spec/mongoid/validatable/uniqueness_spec.rb +58 -31
  380. data/spec/mongoid/warnings_spec.rb +35 -0
  381. data/spec/mongoid_spec.rb +34 -16
  382. data/spec/rails/controller_extension/controller_runtime_spec.rb +2 -2
  383. data/spec/rails/mongoid_spec.rb +4 -16
  384. data/spec/spec_helper.rb +5 -0
  385. data/spec/support/constraints.rb +24 -0
  386. data/spec/support/immutable_ids.rb +118 -0
  387. data/spec/support/macros.rb +78 -0
  388. data/spec/support/models/artist.rb +0 -1
  389. data/spec/support/models/augmentation.rb +12 -0
  390. data/spec/support/models/band.rb +4 -0
  391. data/spec/support/models/book.rb +1 -0
  392. data/spec/support/models/building.rb +2 -0
  393. data/spec/support/models/catalog.rb +24 -0
  394. data/spec/support/models/circus.rb +3 -0
  395. data/spec/support/models/cover.rb +10 -0
  396. data/spec/support/models/fanatic.rb +8 -0
  397. data/spec/support/models/implant.rb +9 -0
  398. data/spec/support/models/label.rb +2 -0
  399. data/spec/support/models/passport.rb +9 -0
  400. data/spec/support/models/person.rb +2 -0
  401. data/spec/support/models/player.rb +2 -0
  402. data/spec/support/models/powerup.rb +12 -0
  403. data/spec/support/models/product.rb +1 -0
  404. data/spec/support/models/purse.rb +9 -0
  405. data/spec/support/models/registry.rb +1 -0
  406. data/spec/support/models/school.rb +14 -0
  407. data/spec/support/models/shield.rb +18 -0
  408. data/spec/support/models/student.rb +14 -0
  409. data/spec/support/models/weapon.rb +12 -0
  410. data.tar.gz.sig +0 -0
  411. metadata +718 -641
  412. metadata.gz.sig +0 -0
  413. data/lib/mongoid/errors/eager_load.rb +0 -23
  414. data/lib/mongoid/errors/invalid_value.rb +0 -17
  415. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -60
  416. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -60
  417. 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
@@ -1554,31 +2063,13 @@ describe Mongoid::Contextual::Mongo do
1554
2063
  expect(context.send(method)).to eq(depeche_mode)
1555
2064
  end
1556
2065
 
1557
- context "when calling ##{method}" do
2066
+ context 'when calling #last' do
1558
2067
 
1559
- it 'returns the requested document, sorted by _id' do
2068
+ it 'returns the last document, sorted by _id' do
1560
2069
  expect(context.send(method)).to eq(depeche_mode)
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 ##{method}" 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,24 +2295,15 @@ describe Mongoid::Contextual::Mongo do
1883
2295
  end
1884
2296
  end
1885
2297
 
1886
- context "when given an empty hash" do
1887
- let(:context) { described_class.new(criteria) }
1888
- let(:criteria) { Band.criteria }
1889
- let(:docs) { context.send(method, {}) }
1890
-
1891
- it "behaves as if limit is nil" do
1892
- expect(docs).to eq(depeche_mode)
1893
- end
1894
- end
1895
-
1896
- 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
1897
2300
 
1898
2301
  let(:context) do
1899
2302
  described_class.new(criteria)
1900
2303
  end
1901
2304
 
1902
2305
  let(:criteria) do
1903
- Band.all.cache
2306
+ Band.all
1904
2307
  end
1905
2308
 
1906
2309
  before do
@@ -1915,8 +2318,10 @@ describe Mongoid::Contextual::Mongo do
1915
2318
  let(:before_limit) { 2 }
1916
2319
  let(:limit) { 1 }
1917
2320
 
1918
- it "gets the correct document" do
1919
- 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
1920
2325
  end
1921
2326
  end
1922
2327
  end
@@ -2006,30 +2411,10 @@ describe Mongoid::Contextual::Mongo do
2006
2411
  context 'when calling #first' do
2007
2412
 
2008
2413
  it 'returns the first document, sorted by _id' do
2009
- pending "MONGOID-5416"
2010
2414
  expect(context.last).to eq(rolling_stones)
2011
2415
  expect(context.first).to eq(depeche_mode)
2012
2416
  end
2013
2417
  end
2014
-
2015
- context 'with option { id_sort: :none }' do
2016
- let(:opts) do
2017
- { id_sort: :none }
2018
- end
2019
-
2020
- it 'doesn\'t apply the sort on _id' do
2021
- expect(context.last(opts)).to eq(depeche_mode)
2022
- end
2023
-
2024
- context 'when calling #first' do
2025
-
2026
- it 'doesn\'t apply the sort on _id' do
2027
- pending "MONGOID-5416"
2028
- expect(context.last(opts)).to eq(rolling_stones)
2029
- expect(context.first(opts)).to eq(depeche_mode)
2030
- end
2031
- end
2032
- end
2033
2418
  end
2034
2419
 
2035
2420
  context 'when the criteria has a sort' do
@@ -2054,25 +2439,6 @@ describe Mongoid::Contextual::Mongo do
2054
2439
  expect(context.first).to eq(rolling_stones)
2055
2440
  end
2056
2441
  end
2057
-
2058
- context 'with option { id_sort: :none }' do
2059
-
2060
- let(:opts) do
2061
- { id_sort: :none }
2062
- end
2063
-
2064
- it 'uses the preexisting sort' do
2065
- expect(context.last(opts)).to eq(depeche_mode)
2066
- end
2067
-
2068
- context 'when calling #first' do
2069
-
2070
- it 'uses the preexisting sort' do
2071
- expect(context.last(opts)).to eq(depeche_mode)
2072
- expect(context.first(opts)).to eq(rolling_stones)
2073
- end
2074
- end
2075
- end
2076
2442
  end
2077
2443
 
2078
2444
  context "when using .sort" do
@@ -2101,28 +2467,17 @@ describe Mongoid::Contextual::Mongo do
2101
2467
  end
2102
2468
  end
2103
2469
 
2104
- context "when the context is cached" do
2470
+ context "when the query cache is enabled" do
2471
+ query_cache_enabled
2105
2472
 
2106
2473
  let(:criteria) do
2107
- Band.where(name: "Depeche Mode").cache
2474
+ Band.where(name: "Depeche Mode")
2108
2475
  end
2109
2476
 
2110
2477
  let(:context) do
2111
2478
  described_class.new(criteria)
2112
2479
  end
2113
2480
 
2114
- context "when the cache is loaded" do
2115
-
2116
- before do
2117
- context.to_a
2118
- end
2119
-
2120
- it "returns the last document without touching the database" do
2121
- expect(context).to receive(:view).never
2122
- expect(context.last).to eq(depeche_mode)
2123
- end
2124
- end
2125
-
2126
2481
  context "when last method was called before" do
2127
2482
 
2128
2483
  before do
@@ -2130,8 +2485,9 @@ describe Mongoid::Contextual::Mongo do
2130
2485
  end
2131
2486
 
2132
2487
  it "returns the last document without touching the database" do
2133
- expect(context).to receive(:view).never
2134
- expect(context.last).to eq(depeche_mode)
2488
+ expect_no_queries do
2489
+ expect(context.last).to eq(depeche_mode)
2490
+ end
2135
2491
  end
2136
2492
  end
2137
2493
  end
@@ -2191,71 +2547,15 @@ describe Mongoid::Contextual::Mongo do
2191
2547
  described_class.new(criteria)
2192
2548
  end
2193
2549
 
2194
- context "when the whole context is loaded" do
2195
-
2196
- before do
2197
- context.to_a
2198
- end
2199
-
2200
- context "when all of the documents are cached" do
2201
-
2202
- let(:criteria) do
2203
- Band.all.cache
2204
- end
2205
-
2206
- context "when requesting all of the documents" do
2207
-
2208
- let(:docs) do
2209
- context.last(3)
2210
- end
2211
-
2212
- it "returns all of the documents without touching the database" do
2213
- expect(context).to receive(:view).never
2214
- expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2215
- end
2216
- end
2217
-
2218
- context "when requesting fewer than all of the documents" do
2219
-
2220
- let(:docs) do
2221
- context.last(2)
2222
- end
2223
-
2224
- it "returns all of the documents without touching the database" do
2225
- expect(context).to receive(:view).never
2226
- expect(docs).to eq([ new_order, rolling_stones ])
2227
- end
2228
- end
2229
- end
2230
-
2231
- context "when only one document is cached" do
2232
-
2233
- let(:criteria) do
2234
- Band.where(name: "Depeche Mode").cache
2235
- end
2236
-
2237
- context "when requesting one document" do
2238
-
2239
- let(:docs) do
2240
- context.last(1)
2241
- end
2242
-
2243
- it "returns one document without touching the database" do
2244
- expect(context).to receive(:view).never
2245
- expect(docs).to eq([ depeche_mode ])
2246
- end
2247
- end
2248
- end
2249
- end
2250
-
2251
- context "when the last method was called before" do
2550
+ context "when query cache is enabled" do
2551
+ query_cache_enabled
2252
2552
 
2253
2553
  let(:context) do
2254
2554
  described_class.new(criteria)
2255
2555
  end
2256
2556
 
2257
2557
  let(:criteria) do
2258
- Band.all.cache
2558
+ Band.all
2259
2559
  end
2260
2560
 
2261
2561
  before do
@@ -2272,18 +2572,20 @@ describe Mongoid::Contextual::Mongo do
2272
2572
  context "when getting all of the documents" do
2273
2573
  let(:limit) { 3 }
2274
2574
 
2275
- it "returns all documents without touching the database" do
2276
- expect(context).to receive(:view).never
2277
- 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
2278
2579
  end
2279
2580
  end
2280
2581
 
2281
2582
  context "when getting fewer documents" do
2282
2583
  let(:limit) { 2 }
2283
2584
 
2284
- it "returns the correct documents without touching the database" do
2285
- expect(context).to receive(:view).never
2286
- 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
2287
2589
  end
2288
2590
  end
2289
2591
  end
@@ -2294,9 +2596,10 @@ describe Mongoid::Contextual::Mongo do
2294
2596
  context "when getting the same number of documents" do
2295
2597
  let(:limit) { 2 }
2296
2598
 
2297
- it "returns the correct documents without touching the database" do
2298
- expect(context).to receive(:view).never
2299
- 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
2300
2603
  end
2301
2604
  end
2302
2605
 
@@ -2304,8 +2607,9 @@ describe Mongoid::Contextual::Mongo do
2304
2607
  let(:limit) { 3 }
2305
2608
 
2306
2609
  it "returns the correct documents and touches the database" do
2307
- expect(context).to receive(:view).twice.and_call_original
2308
- 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
2309
2613
  end
2310
2614
  end
2311
2615
  end
@@ -2317,8 +2621,9 @@ describe Mongoid::Contextual::Mongo do
2317
2621
  let(:limit) { 1 }
2318
2622
 
2319
2623
  it "returns the correct documents without touching the database" do
2320
- expect(context).to receive(:view).never
2321
- expect(docs).to eq([ rolling_stones ])
2624
+ expect_no_queries do
2625
+ expect(docs).to eq([ rolling_stones ])
2626
+ end
2322
2627
  end
2323
2628
  end
2324
2629
 
@@ -2326,8 +2631,9 @@ describe Mongoid::Contextual::Mongo do
2326
2631
  let(:limit) { 3 }
2327
2632
 
2328
2633
  it "returns the correct documents and touches the database" do
2329
- expect(context).to receive(:view).twice.and_call_original
2330
- 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
2331
2637
  end
2332
2638
  end
2333
2639
  end
@@ -2335,14 +2641,15 @@ describe Mongoid::Contextual::Mongo do
2335
2641
  end
2336
2642
  end
2337
2643
 
2338
- 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
2339
2646
 
2340
2647
  let(:context) do
2341
2648
  described_class.new(criteria)
2342
2649
  end
2343
2650
 
2344
2651
  let(:criteria) do
2345
- Band.all.cache
2652
+ Band.all
2346
2653
  end
2347
2654
 
2348
2655
  before do
@@ -2358,26 +2665,16 @@ describe Mongoid::Contextual::Mongo do
2358
2665
  let(:limit) { 1 }
2359
2666
 
2360
2667
  it "hits the database" do
2361
- expect(context).to receive(:view).exactly(3).times.and_call_original
2362
- docs
2668
+ expect_query(1) do
2669
+ docs
2670
+ end
2363
2671
  end
2364
2672
 
2365
2673
  it "gets the correct document" do
2366
- pending "MONGOID-5416"
2367
2674
  expect(docs).to eq([ depeche_mode ])
2368
2675
  end
2369
2676
  end
2370
2677
  end
2371
-
2372
- context "when given an empty hash" do
2373
- let(:context) { described_class.new(criteria) }
2374
- let(:criteria) { Band.criteria }
2375
- let(:docs) { context.last({}) }
2376
-
2377
- it "behaves as if limit is nil" do
2378
- expect(docs).to eq(rolling_stones)
2379
- end
2380
- end
2381
2678
  end
2382
2679
 
2383
2680
  describe "#initialize" do
@@ -2426,37 +2723,28 @@ describe Mongoid::Contextual::Mongo do
2426
2723
  described_class.new(criteria)
2427
2724
  end
2428
2725
 
2429
- it "returns the number of documents that match" do
2430
- expect(context.send(method)).to eq(2)
2431
- end
2726
+ context "when broken_view_options is false" do
2727
+ driver_config_override :broken_view_options, false
2432
2728
 
2433
- context "when calling more than once" do
2434
- it "returns the cached value for subsequent calls" do
2435
- expect(context.view).to receive(:count_documents).once.and_return(2)
2436
- 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)
2437
2731
  end
2438
2732
  end
2439
2733
 
2440
- context "when the results have been iterated over" do
2441
-
2442
- before do
2443
- context.entries
2444
- end
2734
+ context "when broken_view_options is true" do
2735
+ driver_config_override :broken_view_options, true
2445
2736
 
2446
- it "returns the cached value for all calls" do
2447
- expect(context.view).to receive(:count_documents).once.and_return(2)
2737
+ it "returns the number of documents that match" do
2448
2738
  expect(context.send(method)).to eq(2)
2449
2739
  end
2740
+ end
2450
2741
 
2451
- context "when the results have been iterated over multiple times" do
2452
-
2453
- before do
2454
- context.entries
2455
- end
2742
+ context "when calling more than once with different limits" do
2743
+ driver_config_override :broken_view_options, false
2456
2744
 
2457
- it "resets the length on each full iteration" do
2458
- expect(context.size).to eq(2)
2459
- 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)
2460
2748
  end
2461
2749
  end
2462
2750
  end
@@ -2475,10 +2763,12 @@ describe Mongoid::Contextual::Mongo do
2475
2763
  expect(context.send(method)).to eq(1)
2476
2764
  end
2477
2765
 
2478
- context "when calling more than once" do
2479
- it "returns the cached value for subsequent calls" do
2480
- expect(context.view).to receive(:count_documents).once.and_return(1)
2481
- 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)
2482
2772
  end
2483
2773
  end
2484
2774
 
@@ -2600,7 +2890,7 @@ describe Mongoid::Contextual::Mongo do
2600
2890
  it "raises an error" do
2601
2891
  expect do
2602
2892
  Person.take!
2603
- end.to raise_error(Mongoid::Errors::DocumentNotFound)
2893
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Person./)
2604
2894
  end
2605
2895
  end
2606
2896
  end
@@ -2622,9 +2912,10 @@ describe Mongoid::Contextual::Mongo do
2622
2912
 
2623
2913
  context "when passed the symbol field name" do
2624
2914
 
2625
- it "performs mapping and warns" do
2626
- expect(Mongoid::Warnings).to receive(:warn_map_field_deprecated)
2627
- 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)
2628
2919
  end
2629
2920
  end
2630
2921
 
@@ -3412,6 +3703,20 @@ describe Mongoid::Contextual::Mongo do
3412
3703
  end
3413
3704
  end
3414
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
+
3415
3720
  context "when the attributes must be mongoized" do
3416
3721
 
3417
3722
  before do
@@ -3526,4 +3831,836 @@ describe Mongoid::Contextual::Mongo do
3526
3831
  end
3527
3832
  end
3528
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
3529
4666
  end