mongoid 7.5.1 → 8.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (424) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +3 -3
  4. data/README.md +6 -6
  5. data/Rakefile +25 -0
  6. data/lib/config/locales/en.yml +92 -43
  7. data/lib/mongoid/association/accessors.rb +40 -11
  8. data/lib/mongoid/association/bindable.rb +50 -2
  9. data/lib/mongoid/association/builders.rb +5 -3
  10. data/lib/mongoid/association/constrainable.rb +0 -1
  11. data/lib/mongoid/association/eager_loadable.rb +29 -7
  12. data/lib/mongoid/association/embedded/batchable.rb +54 -14
  13. data/lib/mongoid/association/embedded/cyclic.rb +1 -1
  14. data/lib/mongoid/association/embedded/embedded_in/binding.rb +24 -2
  15. data/lib/mongoid/association/embedded/embedded_in/buildable.rb +2 -2
  16. data/lib/mongoid/association/embedded/embedded_in/proxy.rb +4 -3
  17. data/lib/mongoid/association/embedded/embedded_in.rb +3 -2
  18. data/lib/mongoid/association/embedded/embeds_many/binding.rb +1 -0
  19. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +4 -3
  20. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +69 -45
  21. data/lib/mongoid/association/embedded/embeds_many.rb +2 -2
  22. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +19 -5
  23. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +24 -5
  24. data/lib/mongoid/association/embedded/embeds_one.rb +3 -3
  25. data/lib/mongoid/association/macros.rb +8 -1
  26. data/lib/mongoid/association/many.rb +11 -7
  27. data/lib/mongoid/association/nested/many.rb +5 -4
  28. data/lib/mongoid/association/nested/nested_buildable.rb +4 -4
  29. data/lib/mongoid/association/nested/one.rb +45 -7
  30. data/lib/mongoid/association/one.rb +2 -2
  31. data/lib/mongoid/association/options.rb +9 -9
  32. data/lib/mongoid/association/proxy.rb +15 -4
  33. data/lib/mongoid/association/referenced/auto_save.rb +4 -3
  34. data/lib/mongoid/association/referenced/belongs_to/binding.rb +1 -0
  35. data/lib/mongoid/association/referenced/belongs_to/buildable.rb +1 -1
  36. data/lib/mongoid/association/referenced/belongs_to/proxy.rb +5 -6
  37. data/lib/mongoid/association/referenced/belongs_to.rb +2 -2
  38. data/lib/mongoid/association/referenced/counter_cache.rb +10 -10
  39. data/lib/mongoid/association/referenced/eager.rb +2 -2
  40. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +70 -13
  41. data/lib/mongoid/association/referenced/has_and_belongs_to_many.rb +6 -3
  42. data/lib/mongoid/association/referenced/has_many/enumerable.rb +22 -30
  43. data/lib/mongoid/association/referenced/has_many/proxy.rb +29 -19
  44. data/lib/mongoid/association/referenced/has_many.rb +3 -3
  45. data/lib/mongoid/association/referenced/has_one/buildable.rb +1 -1
  46. data/lib/mongoid/association/referenced/has_one/nested_builder.rb +5 -5
  47. data/lib/mongoid/association/referenced/has_one/proxy.rb +9 -12
  48. data/lib/mongoid/association/referenced/has_one.rb +3 -3
  49. data/lib/mongoid/association/referenced/syncable.rb +4 -4
  50. data/lib/mongoid/association/reflections.rb +4 -4
  51. data/lib/mongoid/association/relatable.rb +44 -10
  52. data/lib/mongoid/association.rb +5 -5
  53. data/lib/mongoid/atomic/modifiers.rb +2 -2
  54. data/lib/mongoid/atomic/paths/embedded/many.rb +19 -0
  55. data/lib/mongoid/atomic.rb +7 -0
  56. data/lib/mongoid/attributes/dynamic.rb +4 -4
  57. data/lib/mongoid/attributes/nested.rb +6 -6
  58. data/lib/mongoid/attributes/processing.rb +37 -6
  59. data/lib/mongoid/attributes/projector.rb +2 -2
  60. data/lib/mongoid/attributes/readonly.rb +3 -3
  61. data/lib/mongoid/attributes.rb +51 -42
  62. data/lib/mongoid/cacheable.rb +2 -2
  63. data/lib/mongoid/changeable.rb +147 -14
  64. data/lib/mongoid/clients/options.rb +5 -1
  65. data/lib/mongoid/clients/sessions.rb +2 -14
  66. data/lib/mongoid/clients/storage_options.rb +2 -5
  67. data/lib/mongoid/clients/validators/storage.rb +3 -15
  68. data/lib/mongoid/collection_configurable.rb +58 -0
  69. data/lib/mongoid/composable.rb +2 -0
  70. data/lib/mongoid/config/defaults.rb +60 -0
  71. data/lib/mongoid/config/options.rb +3 -0
  72. data/lib/mongoid/config/validators/async_query_executor.rb +24 -0
  73. data/lib/mongoid/config/validators/client.rb +6 -6
  74. data/lib/mongoid/config/validators.rb +1 -0
  75. data/lib/mongoid/config.rb +145 -17
  76. data/lib/mongoid/contextual/aggregable/memory.rb +24 -16
  77. data/lib/mongoid/contextual/aggregable/mongo.rb +5 -5
  78. data/lib/mongoid/contextual/aggregable/none.rb +1 -1
  79. data/lib/mongoid/contextual/atomic.rb +1 -1
  80. data/lib/mongoid/contextual/geo_near.rb +7 -7
  81. data/lib/mongoid/contextual/map_reduce.rb +2 -2
  82. data/lib/mongoid/contextual/memory.rb +285 -58
  83. data/lib/mongoid/contextual/mongo/documents_loader.rb +177 -0
  84. data/lib/mongoid/contextual/mongo.rb +521 -333
  85. data/lib/mongoid/contextual/none.rb +193 -20
  86. data/lib/mongoid/contextual/queryable.rb +1 -1
  87. data/lib/mongoid/contextual.rb +14 -2
  88. data/lib/mongoid/copyable.rb +32 -8
  89. data/lib/mongoid/criteria/findable.rb +8 -5
  90. data/lib/mongoid/criteria/includable.rb +27 -22
  91. data/lib/mongoid/criteria/marshalable.rb +10 -2
  92. data/lib/mongoid/criteria/permission.rb +1 -1
  93. data/lib/mongoid/criteria/queryable/aggregable.rb +2 -2
  94. data/lib/mongoid/criteria/queryable/extensions/array.rb +3 -16
  95. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +25 -4
  96. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +2 -2
  97. data/lib/mongoid/criteria/queryable/extensions/date.rb +6 -1
  98. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +6 -1
  99. data/lib/mongoid/criteria/queryable/extensions/hash.rb +1 -17
  100. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -9
  101. data/lib/mongoid/criteria/queryable/extensions/object.rb +2 -1
  102. data/lib/mongoid/criteria/queryable/extensions/range.rb +13 -5
  103. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +3 -3
  104. data/lib/mongoid/criteria/queryable/extensions/set.rb +1 -1
  105. data/lib/mongoid/criteria/queryable/extensions/string.rb +4 -14
  106. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +4 -12
  107. data/lib/mongoid/criteria/queryable/extensions/time.rb +6 -1
  108. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +6 -1
  109. data/lib/mongoid/criteria/queryable/key.rb +4 -4
  110. data/lib/mongoid/criteria/queryable/mergeable.rb +1 -1
  111. data/lib/mongoid/criteria/queryable/optional.rb +11 -17
  112. data/lib/mongoid/criteria/queryable/options.rb +2 -2
  113. data/lib/mongoid/criteria/queryable/pipeline.rb +1 -1
  114. data/lib/mongoid/criteria/queryable/selectable.rb +47 -38
  115. data/lib/mongoid/criteria/queryable/selector.rb +93 -8
  116. data/lib/mongoid/criteria/queryable/smash.rb +40 -7
  117. data/lib/mongoid/criteria/queryable/storable.rb +1 -1
  118. data/lib/mongoid/criteria/queryable.rb +12 -7
  119. data/lib/mongoid/criteria/scopable.rb +2 -2
  120. data/lib/mongoid/criteria/translator.rb +45 -0
  121. data/lib/mongoid/criteria.rb +20 -40
  122. data/lib/mongoid/deprecable.rb +36 -0
  123. data/lib/mongoid/deprecation.rb +25 -0
  124. data/lib/mongoid/document.rb +135 -36
  125. data/lib/mongoid/equality.rb +8 -8
  126. data/lib/mongoid/errors/create_collection_failure.rb +33 -0
  127. data/lib/mongoid/errors/document_not_found.rb +10 -6
  128. data/lib/mongoid/errors/drop_collection_failure.rb +27 -0
  129. data/lib/mongoid/errors/immutable_attribute.rb +26 -0
  130. data/lib/mongoid/errors/invalid_async_query_executor.rb +25 -0
  131. data/lib/mongoid/errors/invalid_config_option.rb +1 -1
  132. data/lib/mongoid/errors/invalid_dependent_strategy.rb +1 -1
  133. data/lib/mongoid/errors/invalid_dot_dollar_assignment.rb +23 -0
  134. data/lib/mongoid/errors/invalid_field.rb +6 -2
  135. data/lib/mongoid/errors/invalid_field_type.rb +26 -0
  136. data/lib/mongoid/errors/invalid_global_executor_concurrency.rb +22 -0
  137. data/lib/mongoid/errors/invalid_relation.rb +1 -1
  138. data/lib/mongoid/errors/invalid_relation_option.rb +1 -1
  139. data/lib/mongoid/errors/invalid_session_use.rb +1 -1
  140. data/lib/mongoid/errors/invalid_storage_options.rb +1 -1
  141. data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
  142. data/lib/mongoid/errors/mongoid_error.rb +3 -3
  143. data/lib/mongoid/errors/nested_attributes_metadata_not_found.rb +1 -1
  144. data/lib/mongoid/errors/no_client_database.rb +1 -1
  145. data/lib/mongoid/errors/no_client_hosts.rb +1 -1
  146. data/lib/mongoid/errors/readonly_attribute.rb +1 -1
  147. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +1 -1
  148. data/lib/mongoid/errors/unknown_attribute.rb +1 -1
  149. data/lib/mongoid/errors.rb +6 -3
  150. data/lib/mongoid/extensions/array.rb +9 -7
  151. data/lib/mongoid/extensions/big_decimal.rb +33 -10
  152. data/lib/mongoid/extensions/binary.rb +42 -0
  153. data/lib/mongoid/extensions/boolean.rb +8 -2
  154. data/lib/mongoid/extensions/date.rb +26 -20
  155. data/lib/mongoid/extensions/date_time.rb +1 -1
  156. data/lib/mongoid/extensions/false_class.rb +1 -1
  157. data/lib/mongoid/extensions/float.rb +7 -4
  158. data/lib/mongoid/extensions/hash.rb +19 -8
  159. data/lib/mongoid/extensions/integer.rb +7 -4
  160. data/lib/mongoid/extensions/module.rb +1 -1
  161. data/lib/mongoid/extensions/object.rb +10 -8
  162. data/lib/mongoid/extensions/range.rb +41 -10
  163. data/lib/mongoid/extensions/regexp.rb +11 -4
  164. data/lib/mongoid/extensions/set.rb +11 -4
  165. data/lib/mongoid/extensions/string.rb +11 -22
  166. data/lib/mongoid/extensions/symbol.rb +4 -15
  167. data/lib/mongoid/extensions/time.rb +29 -16
  168. data/lib/mongoid/extensions/time_with_zone.rb +1 -2
  169. data/lib/mongoid/extensions/true_class.rb +1 -1
  170. data/lib/mongoid/extensions.rb +1 -0
  171. data/lib/mongoid/factory.rb +55 -7
  172. data/lib/mongoid/fields/foreign_key.rb +11 -4
  173. data/lib/mongoid/fields/localized.rb +19 -4
  174. data/lib/mongoid/fields/standard.rb +17 -7
  175. data/lib/mongoid/fields/validators/macro.rb +3 -9
  176. data/lib/mongoid/fields.rb +129 -20
  177. data/lib/mongoid/findable.rb +54 -24
  178. data/lib/mongoid/indexable/specification.rb +2 -2
  179. data/lib/mongoid/indexable/validators/options.rb +6 -2
  180. data/lib/mongoid/interceptable.rb +76 -15
  181. data/lib/mongoid/matchable.rb +1 -1
  182. data/lib/mongoid/matcher/eq_impl.rb +1 -1
  183. data/lib/mongoid/matcher/type.rb +1 -1
  184. data/lib/mongoid/matcher.rb +33 -13
  185. data/lib/mongoid/persistable/creatable.rb +19 -9
  186. data/lib/mongoid/persistable/deletable.rb +2 -2
  187. data/lib/mongoid/persistable/destroyable.rb +1 -1
  188. data/lib/mongoid/persistable/savable.rb +14 -2
  189. data/lib/mongoid/persistable/unsettable.rb +2 -2
  190. data/lib/mongoid/persistable/updatable.rb +69 -12
  191. data/lib/mongoid/persistable/upsertable.rb +21 -2
  192. data/lib/mongoid/persistable.rb +6 -3
  193. data/lib/mongoid/persistence_context.rb +63 -10
  194. data/lib/mongoid/query_cache.rb +13 -261
  195. data/lib/mongoid/railties/controller_runtime.rb +1 -1
  196. data/lib/mongoid/railties/database.rake +7 -2
  197. data/lib/mongoid/reloadable.rb +10 -8
  198. data/lib/mongoid/scopable.rb +15 -13
  199. data/lib/mongoid/selectable.rb +1 -2
  200. data/lib/mongoid/serializable.rb +10 -6
  201. data/lib/mongoid/shardable.rb +35 -11
  202. data/lib/mongoid/stateful.rb +57 -10
  203. data/lib/mongoid/tasks/database.rake +12 -0
  204. data/lib/mongoid/tasks/database.rb +20 -2
  205. data/lib/mongoid/threaded/lifecycle.rb +5 -5
  206. data/lib/mongoid/threaded.rb +42 -12
  207. data/lib/mongoid/timestamps/created.rb +1 -1
  208. data/lib/mongoid/timestamps/updated.rb +2 -2
  209. data/lib/mongoid/touchable.rb +2 -3
  210. data/lib/mongoid/traversable.rb +5 -4
  211. data/lib/mongoid/utils.rb +22 -0
  212. data/lib/mongoid/validatable/localizable.rb +1 -1
  213. data/lib/mongoid/validatable/macros.rb +5 -7
  214. data/lib/mongoid/validatable/presence.rb +2 -2
  215. data/lib/mongoid/validatable/uniqueness.rb +9 -8
  216. data/lib/mongoid/validatable.rb +9 -6
  217. data/lib/mongoid/version.rb +1 -1
  218. data/lib/mongoid/warnings.rb +19 -4
  219. data/lib/mongoid.rb +17 -3
  220. data/spec/config/mongoid.yml +16 -0
  221. data/spec/integration/app_spec.rb +10 -14
  222. data/spec/integration/associations/belongs_to_spec.rb +18 -0
  223. data/spec/integration/associations/embedded_spec.rb +15 -0
  224. data/spec/integration/associations/embeds_many_spec.rb +15 -2
  225. data/spec/integration/associations/embeds_one_spec.rb +18 -0
  226. data/spec/integration/associations/foreign_key_spec.rb +9 -0
  227. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +21 -0
  228. data/spec/integration/associations/has_one_spec.rb +97 -1
  229. data/spec/integration/associations/scope_option_spec.rb +1 -1
  230. data/spec/integration/callbacks_models.rb +132 -1
  231. data/spec/integration/callbacks_spec.rb +360 -4
  232. data/spec/integration/criteria/range_spec.rb +95 -1
  233. data/spec/integration/discriminator_key_spec.rb +118 -80
  234. data/spec/integration/dots_and_dollars_spec.rb +277 -0
  235. data/spec/integration/i18n_fallbacks_spec.rb +3 -32
  236. data/spec/integration/matcher_examples_spec.rb +20 -13
  237. data/spec/integration/matcher_operator_data/type_decimal.yml +3 -2
  238. data/spec/integration/matcher_operator_spec.rb +3 -5
  239. data/spec/integration/persistence/range_field_spec.rb +350 -0
  240. data/spec/mongoid/association/counter_cache_spec.rb +1 -1
  241. data/spec/mongoid/association/depending_spec.rb +9 -9
  242. data/spec/mongoid/association/eager_spec.rb +2 -1
  243. data/spec/mongoid/association/embedded/embedded_in/binding_spec.rb +2 -1
  244. data/spec/mongoid/association/embedded/embedded_in/buildable_spec.rb +54 -0
  245. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +96 -9
  246. data/spec/mongoid/association/embedded/embeds_many/buildable_spec.rb +112 -0
  247. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +276 -65
  248. data/spec/mongoid/association/embedded/embeds_many_models.rb +158 -0
  249. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +12 -0
  250. data/spec/mongoid/association/embedded/embeds_many_spec.rb +68 -0
  251. data/spec/mongoid/association/embedded/embeds_one/buildable_spec.rb +25 -0
  252. data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
  253. data/spec/mongoid/association/embedded/embeds_one_models.rb +19 -0
  254. data/spec/mongoid/association/embedded/embeds_one_spec.rb +28 -0
  255. data/spec/mongoid/association/referenced/belongs_to/binding_spec.rb +2 -1
  256. data/spec/mongoid/association/referenced/belongs_to/buildable_spec.rb +54 -0
  257. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +15 -0
  258. data/spec/mongoid/association/referenced/belongs_to_models.rb +11 -0
  259. data/spec/mongoid/association/referenced/belongs_to_spec.rb +4 -20
  260. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +215 -228
  261. data/spec/mongoid/association/referenced/has_and_belongs_to_many_models.rb +25 -0
  262. data/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb +35 -2
  263. data/spec/mongoid/association/referenced/has_many/buildable_spec.rb +109 -0
  264. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +2 -56
  265. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +193 -177
  266. data/spec/mongoid/association/referenced/has_many_models.rb +3 -1
  267. data/spec/mongoid/association/referenced/has_many_spec.rb +25 -0
  268. data/spec/mongoid/association/referenced/has_one/buildable_spec.rb +2 -2
  269. data/spec/mongoid/association/referenced/has_one/proxy_spec.rb +107 -1
  270. data/spec/mongoid/association/referenced/has_one_models.rb +16 -0
  271. data/spec/mongoid/association/syncable_spec.rb +15 -1
  272. data/spec/mongoid/atomic/paths_spec.rb +0 -14
  273. data/spec/mongoid/attributes/nested_spec.rb +80 -11
  274. data/spec/mongoid/attributes/nested_spec_models.rb +48 -0
  275. data/spec/mongoid/attributes/projector_spec.rb +1 -5
  276. data/spec/mongoid/attributes_spec.rb +554 -33
  277. data/spec/mongoid/cacheable_spec.rb +3 -3
  278. data/spec/mongoid/changeable_spec.rb +429 -37
  279. data/spec/mongoid/clients/factory_spec.rb +23 -30
  280. data/spec/mongoid/clients/sessions_spec.rb +0 -38
  281. data/spec/mongoid/clients_spec.rb +179 -15
  282. data/spec/mongoid/collection_configurable_spec.rb +158 -0
  283. data/spec/mongoid/config/defaults_spec.rb +160 -0
  284. data/spec/mongoid/config_spec.rb +220 -30
  285. data/spec/mongoid/contextual/aggregable/memory_spec.rb +396 -158
  286. data/spec/mongoid/contextual/aggregable/memory_table.yml +88 -0
  287. data/spec/mongoid/contextual/aggregable/memory_table_spec.rb +62 -0
  288. data/spec/mongoid/contextual/map_reduce_spec.rb +2 -16
  289. data/spec/mongoid/contextual/memory_spec.rb +850 -88
  290. data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
  291. data/spec/mongoid/contextual/mongo_spec.rb +1570 -413
  292. data/spec/mongoid/contextual/none_spec.rb +60 -21
  293. data/spec/mongoid/copyable_spec.rb +453 -11
  294. data/spec/mongoid/criteria/findable_spec.rb +86 -210
  295. data/spec/mongoid/criteria/includable_spec.rb +1492 -0
  296. data/spec/mongoid/criteria/includable_spec_models.rb +54 -0
  297. data/spec/mongoid/criteria/marshalable_spec.rb +18 -1
  298. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +7 -19
  299. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +134 -26
  300. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +11 -0
  301. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +11 -0
  302. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +0 -15
  303. data/spec/mongoid/criteria/queryable/extensions/numeric_spec.rb +73 -7
  304. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -69
  305. data/spec/mongoid/criteria/queryable/extensions/symbol_spec.rb +0 -59
  306. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +11 -0
  307. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +11 -0
  308. data/spec/mongoid/criteria/queryable/optional_spec.rb +15 -484
  309. data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
  310. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +469 -0
  311. data/spec/mongoid/criteria/queryable/selectable_spec.rb +78 -86
  312. data/spec/mongoid/criteria/queryable/selector_spec.rb +90 -5
  313. data/spec/mongoid/criteria/queryable/storable_spec.rb +72 -0
  314. data/spec/mongoid/criteria/translator_spec.rb +132 -0
  315. data/spec/mongoid/criteria_projection_spec.rb +1 -5
  316. data/spec/mongoid/criteria_spec.rb +469 -1205
  317. data/spec/mongoid/document_fields_spec.rb +173 -24
  318. data/spec/mongoid/document_spec.rb +32 -41
  319. data/spec/mongoid/errors/document_not_found_spec.rb +29 -2
  320. data/spec/mongoid/errors/invalid_field_spec.rb +1 -1
  321. data/spec/mongoid/errors/invalid_field_type_spec.rb +55 -0
  322. data/spec/mongoid/errors/mongoid_error_spec.rb +3 -1
  323. data/spec/mongoid/errors/no_environment_spec.rb +3 -3
  324. data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
  325. data/spec/mongoid/errors/too_many_nested_attribute_records_spec.rb +1 -1
  326. data/spec/mongoid/extensions/array_spec.rb +16 -2
  327. data/spec/mongoid/extensions/big_decimal_spec.rb +712 -212
  328. data/spec/mongoid/extensions/binary_spec.rb +44 -9
  329. data/spec/mongoid/extensions/boolean_spec.rb +68 -82
  330. data/spec/mongoid/extensions/date_class_mongoize_spec.rb +7 -3
  331. data/spec/mongoid/extensions/date_spec.rb +71 -1
  332. data/spec/mongoid/extensions/date_time_spec.rb +15 -9
  333. data/spec/mongoid/extensions/float_spec.rb +53 -74
  334. data/spec/mongoid/extensions/hash_spec.rb +33 -3
  335. data/spec/mongoid/extensions/integer_spec.rb +50 -64
  336. data/spec/mongoid/extensions/range_spec.rb +255 -54
  337. data/spec/mongoid/extensions/regexp_spec.rb +58 -33
  338. data/spec/mongoid/extensions/set_spec.rb +106 -0
  339. data/spec/mongoid/extensions/string_spec.rb +53 -25
  340. data/spec/mongoid/extensions/symbol_spec.rb +18 -25
  341. data/spec/mongoid/extensions/time_spec.rb +639 -106
  342. data/spec/mongoid/extensions/time_with_zone_spec.rb +24 -83
  343. data/spec/mongoid/factory_spec.rb +61 -1
  344. data/spec/mongoid/fields/localized_spec.rb +80 -37
  345. data/spec/mongoid/fields_spec.rb +500 -84
  346. data/spec/mongoid/findable_spec.rb +450 -58
  347. data/spec/mongoid/indexable/specification_spec.rb +2 -2
  348. data/spec/mongoid/indexable_spec.rb +55 -30
  349. data/spec/mongoid/interceptable_spec.rb +599 -8
  350. data/spec/mongoid/interceptable_spec_models.rb +235 -4
  351. data/spec/mongoid/matcher/extract_attribute_spec.rb +1 -5
  352. data/spec/mongoid/mongoizable_spec.rb +285 -0
  353. data/spec/mongoid/persistable/creatable_spec.rb +2 -2
  354. data/spec/mongoid/persistable/deletable_spec.rb +28 -8
  355. data/spec/mongoid/persistable/destroyable_spec.rb +28 -8
  356. data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
  357. data/spec/mongoid/persistable/logical_spec.rb +37 -0
  358. data/spec/mongoid/persistable/poppable_spec.rb +36 -0
  359. data/spec/mongoid/persistable/pullable_spec.rb +72 -0
  360. data/spec/mongoid/persistable/pushable_spec.rb +72 -0
  361. data/spec/mongoid/persistable/renamable_spec.rb +36 -0
  362. data/spec/mongoid/persistable/savable_spec.rb +96 -0
  363. data/spec/mongoid/persistable/settable_spec.rb +37 -0
  364. data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
  365. data/spec/mongoid/persistable/updatable_spec.rb +20 -28
  366. data/spec/mongoid/persistable/upsertable_spec.rb +89 -1
  367. data/spec/mongoid/persistence_context_spec.rb +57 -58
  368. data/spec/mongoid/query_cache_middleware_spec.rb +0 -18
  369. data/spec/mongoid/query_cache_spec.rb +56 -215
  370. data/spec/mongoid/reloadable_spec.rb +83 -6
  371. data/spec/mongoid/scopable_spec.rb +91 -1
  372. data/spec/mongoid/serializable_spec.rb +9 -30
  373. data/spec/mongoid/shardable_models.rb +14 -0
  374. data/spec/mongoid/shardable_spec.rb +157 -51
  375. data/spec/mongoid/stateful_spec.rb +150 -8
  376. data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
  377. data/spec/mongoid/tasks/database_spec.rb +127 -0
  378. data/spec/mongoid/timestamps_spec.rb +392 -4
  379. data/spec/mongoid/timestamps_spec_models.rb +67 -0
  380. data/spec/mongoid/touchable_spec.rb +390 -2
  381. data/spec/mongoid/touchable_spec_models.rb +14 -8
  382. data/spec/mongoid/traversable_spec.rb +13 -35
  383. data/spec/mongoid/validatable/presence_spec.rb +1 -1
  384. data/spec/mongoid/validatable/uniqueness_spec.rb +58 -31
  385. data/spec/mongoid/warnings_spec.rb +35 -0
  386. data/spec/mongoid_spec.rb +34 -10
  387. data/spec/rails/controller_extension/controller_runtime_spec.rb +2 -2
  388. data/spec/rails/mongoid_spec.rb +4 -16
  389. data/spec/shared/lib/mrss/lite_constraints.rb +8 -0
  390. data/spec/shared/shlib/server.sh +5 -5
  391. data/spec/spec_helper.rb +5 -0
  392. data/spec/support/constraints.rb +24 -0
  393. data/spec/support/immutable_ids.rb +118 -0
  394. data/spec/support/macros.rb +78 -0
  395. data/spec/support/models/artist.rb +0 -1
  396. data/spec/support/models/augmentation.rb +12 -0
  397. data/spec/support/models/band.rb +4 -0
  398. data/spec/support/models/book.rb +1 -0
  399. data/spec/support/models/building.rb +2 -0
  400. data/spec/support/models/catalog.rb +24 -0
  401. data/spec/support/models/circus.rb +3 -0
  402. data/spec/support/models/cover.rb +10 -0
  403. data/spec/support/models/fanatic.rb +8 -0
  404. data/spec/support/models/implant.rb +9 -0
  405. data/spec/support/models/label.rb +2 -0
  406. data/spec/support/models/passport.rb +9 -0
  407. data/spec/support/models/person.rb +2 -0
  408. data/spec/support/models/player.rb +2 -0
  409. data/spec/support/models/powerup.rb +12 -0
  410. data/spec/support/models/product.rb +1 -0
  411. data/spec/support/models/purse.rb +9 -0
  412. data/spec/support/models/registry.rb +1 -0
  413. data/spec/support/models/school.rb +14 -0
  414. data/spec/support/models/shield.rb +18 -0
  415. data/spec/support/models/student.rb +14 -0
  416. data/spec/support/models/weapon.rb +12 -0
  417. data.tar.gz.sig +0 -0
  418. metadata +720 -638
  419. metadata.gz.sig +0 -0
  420. data/lib/mongoid/errors/eager_load.rb +0 -23
  421. data/lib/mongoid/errors/invalid_value.rb +0 -17
  422. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -60
  423. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -60
  424. data/spec/mongoid/errors/eager_load_spec.rb +0 -31
@@ -44,39 +44,6 @@ describe Mongoid::Contextual::Mongo do
44
44
  end
45
45
  end
46
46
 
47
- describe "#cached?" do
48
-
49
- context "when the criteria is cached" do
50
-
51
- let(:criteria) do
52
- Band.all.cache
53
- end
54
-
55
- let(:context) do
56
- described_class.new(criteria)
57
- end
58
-
59
- it "returns true" do
60
- expect(context).to be_cached
61
- end
62
- end
63
-
64
- context "when the criteria is not cached" do
65
-
66
- let(:criteria) do
67
- Band.all
68
- end
69
-
70
- let(:context) do
71
- described_class.new(criteria)
72
- end
73
-
74
- it "returns false" do
75
- expect(context).to_not be_cached
76
- end
77
- end
78
- end
79
-
80
47
  describe "#count" do
81
48
 
82
49
  let!(:depeche) do
@@ -102,15 +69,17 @@ describe Mongoid::Contextual::Mongo do
102
69
  end
103
70
  end
104
71
 
105
- context "when context is cached" do
72
+ context "when the query cache is enabled" do
73
+ query_cache_enabled
106
74
 
107
75
  let(:context) do
108
- described_class.new(criteria.cache)
76
+ described_class.new(criteria)
109
77
  end
110
78
 
111
- it "returns the count cached value after first call" do
112
- expect(context.view).to receive(:count_documents).once.and_return(1)
113
- 2.times { expect(context.count).to eq(1) }
79
+ it "only executes the count query once" do
80
+ expect_query(1) do
81
+ 2.times { expect(context.count).to eq(1) }
82
+ end
114
83
  end
115
84
  end
116
85
 
@@ -217,16 +186,18 @@ describe Mongoid::Contextual::Mongo do
217
186
  end
218
187
  end
219
188
 
220
- context "when context is cached" do
189
+ context "when the query cache is enabled" do
190
+ query_cache_enabled
221
191
 
222
192
  let(:context) do
223
- described_class.new(criteria.cache)
193
+ described_class.new(criteria)
224
194
  end
225
195
 
226
- it "returns the count cached value after first call" do
227
- expect(context.view).to receive(:estimated_document_count).once.and_return(1)
228
- 2.times do
229
- context.estimated_count
196
+ it "the results are not cached" do
197
+ expect_query(2) do
198
+ 2.times do
199
+ context.estimated_count
200
+ end
230
201
  end
231
202
  end
232
203
  end
@@ -561,8 +532,22 @@ describe Mongoid::Contextual::Mongo do
561
532
  context "when legacy_pluck_distinct is set" do
562
533
  config_override :legacy_pluck_distinct, true
563
534
 
564
- it "returns the non-demongoized distinct field values" do
565
- expect(context.distinct(:sales).sort).to eq([ "1E2", "2E3" ])
535
+ context 'when storing BigDecimal as string' do
536
+ config_override :map_big_decimal_to_decimal128, false
537
+
538
+ it "returns the non-demongoized distinct field values" do
539
+ expect(context.distinct(:sales).sort).to eq([ "1E2", "2E3" ])
540
+ end
541
+ end
542
+
543
+ context 'when storing BigDecimal as decimal128' do
544
+ config_override :map_big_decimal_to_decimal128, true
545
+ min_bson_version '4.15.0'
546
+ max_bson_version '4.99.99'
547
+
548
+ it "returns the non-demongoized distinct field values" do
549
+ expect(context.distinct(:sales).sort).to eq([ BSON::Decimal128.new("1E2"), BSON::Decimal128.new("2E3") ])
550
+ end
566
551
  end
567
552
  end
568
553
 
@@ -576,6 +561,8 @@ describe Mongoid::Contextual::Mongo do
576
561
  end
577
562
 
578
563
  context "when getting a localized field" do
564
+ with_default_i18n_configs
565
+
579
566
  before do
580
567
  I18n.locale = :en
581
568
  d = Dictionary.create!(description: 'english-text')
@@ -584,10 +571,6 @@ describe Mongoid::Contextual::Mongo do
584
571
  d.save!
585
572
  end
586
573
 
587
- after do
588
- I18n.locale = :en
589
- end
590
-
591
574
  let(:criteria) do
592
575
  Dictionary.criteria
593
576
  end
@@ -680,16 +663,10 @@ describe Mongoid::Contextual::Mongo do
680
663
 
681
664
  context 'when fallbacks are enabled with a locale list' do
682
665
  require_fallbacks
666
+ with_default_i18n_configs
683
667
 
684
- around(:all) do |example|
685
- prev_fallbacks = I18n.fallbacks.dup
668
+ before do
686
669
  I18n.fallbacks[:he] = [ :en ]
687
- example.run
688
- I18n.fallbacks = prev_fallbacks
689
- end
690
-
691
- after do
692
- I18n.locale = :en
693
670
  end
694
671
 
695
672
  let(:distinct) do
@@ -709,7 +686,7 @@ describe Mongoid::Contextual::Mongo do
709
686
 
710
687
  it "correctly uses the fallback" do
711
688
  I18n.locale = :en
712
- d = Dictionary.create!(description: 'english-text')
689
+ Dictionary.create!(description: 'english-text')
713
690
  I18n.locale = :he
714
691
  distinct.should == "english-text"
715
692
  end
@@ -717,6 +694,8 @@ describe Mongoid::Contextual::Mongo do
717
694
  end
718
695
 
719
696
  context "when the localized field is embedded" do
697
+ with_default_i18n_configs
698
+
720
699
  before do
721
700
  p = Passport.new
722
701
  I18n.locale = :en
@@ -727,10 +706,6 @@ describe Mongoid::Contextual::Mongo do
727
706
  Person.create!(passport: p, employer_id: 12345)
728
707
  end
729
708
 
730
- after do
731
- I18n.locale = :en
732
- end
733
-
734
709
  let(:criteria) do
735
710
  Person.where(employer_id: 12345)
736
711
  end
@@ -794,9 +769,11 @@ describe Mongoid::Contextual::Mongo do
794
769
 
795
770
  context "when legacy_pluck_distinct is set" do
796
771
  config_override :legacy_pluck_distinct, true
772
+ config_override :map_big_decimal_to_decimal128, true
773
+ max_bson_version '4.99.99'
797
774
 
798
775
  it "returns the distinct matching fields" do
799
- expect(context.distinct("label.sales")).to eq([ "1E2" ])
776
+ expect(context.distinct("label.sales")).to eq([ BSON::Decimal128.new('1E+2') ])
800
777
  end
801
778
  end
802
779
 
@@ -809,6 +786,452 @@ describe Mongoid::Contextual::Mongo do
809
786
  end
810
787
  end
811
788
 
789
+ describe "#tally" do
790
+ let(:fans1) { [ Fanatic.new(age:1), Fanatic.new(age:2) ] }
791
+ let(:fans2) { [ Fanatic.new(age:1), Fanatic.new(age:2) ] }
792
+ let(:fans3) { [ Fanatic.new(age:1), Fanatic.new(age:3) ] }
793
+
794
+ let(:genres1) { [ { x: 1, y: { z: 1 } }, { x: 2, y: { z: 2 } }, { y: 3 } ]}
795
+ let(:genres2) { [ { x: 1, y: { z: 1 } }, { x: 2, y: { z: 2 } }, { y: 4 } ]}
796
+ let(:genres3) { [ { x: 1, y: { z: 1 } }, { x: 3, y: { z: 3 } }, { y: 5 } ]}
797
+
798
+ let(:label1) { Label.new(name: "Atlantic") }
799
+ let(:label2) { Label.new(name: "Atlantic") }
800
+ let(:label3) { Label.new(name: "Columbia") }
801
+
802
+ before do
803
+ Band.create!(origin: "tally", name: "Depeche Mode", years: 30, sales: "1E2", label: label1, genres: genres1)
804
+ Band.create!(origin: "tally", name: "New Order", years: 30, sales: "2E3", label: label2, genres: genres2)
805
+ Band.create!(origin: "tally", name: "10,000 Maniacs", years: 30, sales: "1E2", label: label3, genres: genres3)
806
+ Band.create!(origin: "tally2", fanatics: fans1, genres: [1, 2])
807
+ Band.create!(origin: "tally2", fanatics: fans2, genres: [1, 2])
808
+ Band.create!(origin: "tally2", fanatics: fans3, genres: [1, 3])
809
+ end
810
+
811
+ let(:criteria) { Band.where(origin: "tally") }
812
+
813
+ context "when tallying a string" do
814
+ let(:tally) do
815
+ criteria.tally(:name)
816
+ end
817
+
818
+ it "returns the correct hash" do
819
+ expect(tally).to eq("Depeche Mode" => 1, "New Order" => 1, "10,000 Maniacs" => 1)
820
+ end
821
+ end
822
+
823
+ context "using an aliased field" do
824
+ let(:tally) do
825
+ criteria.tally(:years)
826
+ end
827
+
828
+ it "returns the correct hash" do
829
+ expect(tally).to eq(30 => 3)
830
+ end
831
+ end
832
+
833
+ context "when tallying a demongoizable field" do
834
+ let(:tally) do
835
+ criteria.tally(:sales)
836
+ end
837
+
838
+ it "returns the correct hash" do
839
+ expect(tally).to eq(BigDecimal("1E2") => 2, BigDecimal("2E3") => 1)
840
+ end
841
+ end
842
+
843
+ context "when tallying a localized field" do
844
+ with_default_i18n_configs
845
+
846
+ before do
847
+ I18n.locale = :en
848
+ d1 = Dictionary.create!(description: 'en1')
849
+ d2 = Dictionary.create!(description: 'en1')
850
+ d3 = Dictionary.create!(description: 'en1')
851
+ d4 = Dictionary.create!(description: 'en2')
852
+ I18n.locale = :de
853
+ d1.description = 'de1'
854
+ d2.description = 'de1'
855
+ d3.description = 'de2'
856
+ d4.description = 'de3'
857
+ d1.save!
858
+ d2.save!
859
+ d3.save!
860
+ d4.save!
861
+ I18n.locale = :en
862
+ end
863
+
864
+ context "when getting the demongoized field" do
865
+ let(:tallied) do
866
+ Dictionary.tally(:description)
867
+ end
868
+
869
+ it "returns the translation for the current locale" do
870
+ expect(tallied).to eq("en1" => 3, "en2" => 1)
871
+ end
872
+ end
873
+
874
+ context "when getting a specific locale" do
875
+ let(:tallied) do
876
+ Dictionary.tally("description.de")
877
+ end
878
+
879
+ it "returns the translation for the the specific locale" do
880
+ expect(tallied).to eq("de1" => 2, "de2" => 1, "de3" => 1)
881
+ end
882
+ end
883
+
884
+ context "when getting the full hash" do
885
+ let(:tallied) do
886
+ Dictionary.tally("description_translations")
887
+ end
888
+
889
+ it "returns the correct hash" do
890
+ expect(tallied).to eq(
891
+ {"de" => "de1", "en" => "en1" } => 2,
892
+ {"de" => "de2", "en" => "en1" } => 1,
893
+ {"de" => "de3", "en" => "en2" } => 1
894
+ )
895
+ end
896
+ end
897
+ end
898
+
899
+ context "when tallying an embedded localized field" do
900
+ with_default_i18n_configs
901
+
902
+ before do
903
+ I18n.locale = :en
904
+ address1a = Address.new(name: "en1")
905
+ address1b = Address.new(name: "en2")
906
+ address2a = Address.new(name: "en1")
907
+ address2b = Address.new(name: "en3")
908
+ I18n.locale = :de
909
+ address1a.name = "de1"
910
+ address1b.name = "de2"
911
+ address2a.name = "de1"
912
+ address2b.name = "de3"
913
+ Person.create!(addresses: [ address1a, address1b ])
914
+ Person.create!(addresses: [ address2a, address2b ])
915
+ I18n.locale = :en
916
+ end
917
+
918
+ context "when getting the demongoized field" do
919
+ let(:tallied) do
920
+ Person.tally("addresses.name")
921
+ end
922
+
923
+ it "returns the translation for the current locale" do
924
+ expect(tallied).to eq(
925
+ [ "en1", "en2" ] => 1,
926
+ [ "en1", "en3" ] => 1,
927
+ )
928
+ end
929
+ end
930
+
931
+ context "when getting a specific locale" do
932
+ let(:tallied) do
933
+ Person.tally("addresses.name.de")
934
+ end
935
+
936
+ it "returns the translation for the the specific locale" do
937
+ expect(tallied).to eq(
938
+ [ "de1", "de2" ] => 1,
939
+ [ "de1", "de3" ] => 1,
940
+ )
941
+ end
942
+ end
943
+
944
+ context "when getting the full hash" do
945
+ let(:tallied) do
946
+ Person.tally("addresses.name_translations")
947
+ end
948
+
949
+ it "returns the correct hash" do
950
+ expect(tallied).to eq(
951
+ [{ "de" => "de1", "en" => "en1" }, { "de" => "de2", "en" => "en2" }] => 1,
952
+ [{ "de" => "de1", "en" => "en1" }, { "de" => "de3", "en" => "en3" }] => 1,
953
+ )
954
+ end
955
+ end
956
+
957
+ end
958
+
959
+ context "when tallying an embedded field" do
960
+ let(:tally) do
961
+ criteria.tally("label.name")
962
+ end
963
+
964
+ it "returns the correct hash" do
965
+ expect(tally).to eq("Atlantic" => 2, "Columbia" => 1)
966
+ end
967
+ end
968
+
969
+ context "when tallying an element in an embeds_many field" do
970
+ let(:criteria) { Band.where(origin: "tally2") }
971
+
972
+ let(:tally) do
973
+ criteria.tally("fanatics.age")
974
+ end
975
+
976
+ it "returns the correct hash" do
977
+ expect(tally).to eq(
978
+ [1, 2] => 2,
979
+ [1, 3] => 1
980
+ )
981
+ end
982
+ end
983
+
984
+ context "when tallying an embeds_many field" do
985
+ let(:criteria) { Band.where(origin: "tally2") }
986
+
987
+ let(:tally) do
988
+ criteria.tally("fanatics")
989
+ end
990
+
991
+ it "returns the correct hash" do
992
+ expect(tally).to eq(
993
+ fans1.map(&:attributes) => 1,
994
+ fans2.map(&:attributes) => 1,
995
+ fans3.map(&:attributes) => 1,
996
+ )
997
+ end
998
+ end
999
+
1000
+ context "when tallying a field of type array" do
1001
+ let(:criteria) { Band.where(origin: "tally2") }
1002
+
1003
+ let(:tally) do
1004
+ criteria.tally("genres")
1005
+ end
1006
+
1007
+ it "returns the correct hash" do
1008
+ expect(tally).to eq(
1009
+ [1, 2] => 2,
1010
+ [1, 3] => 1
1011
+ )
1012
+ end
1013
+ end
1014
+
1015
+ context "when tallying an element from an array of hashes" do
1016
+ let(:criteria) { Band.where(origin: "tally") }
1017
+
1018
+ let(:tally) do
1019
+ criteria.tally("genres.x")
1020
+ end
1021
+
1022
+ it "returns the correct hash without the nil keys" do
1023
+ expect(tally).to eq(
1024
+ [1, 2] => 2,
1025
+ [1, 3] => 1
1026
+ )
1027
+ end
1028
+ end
1029
+
1030
+ context "when tallying an element from an array of hashes; with duplicate" do
1031
+
1032
+ before do
1033
+ Band.create!(origin: "tally", genres: [ { x: 1 }, {x: 1} ] )
1034
+ end
1035
+
1036
+ let(:criteria) { Band.where(origin: "tally") }
1037
+
1038
+ let(:tally) do
1039
+ criteria.tally("genres.x")
1040
+ end
1041
+
1042
+ it "returns the correct hash without the nil keys" do
1043
+ expect(tally).to eq(
1044
+ [1, 2] => 2,
1045
+ [1, 3] => 1,
1046
+ [1, 1] => 1,
1047
+ )
1048
+ end
1049
+ end
1050
+
1051
+ context "when tallying an aliased field of type array" do
1052
+
1053
+ before do
1054
+ Person.create!(array: [ 1, 2 ])
1055
+ Person.create!(array: [ 1, 3 ])
1056
+ end
1057
+
1058
+ let(:tally) do
1059
+ Person.tally("array")
1060
+ end
1061
+
1062
+ it "returns the correct hash" do
1063
+ expect(tally).to eq(
1064
+ [1, 2] => 1,
1065
+ [1, 3] => 1
1066
+ )
1067
+ end
1068
+ end
1069
+
1070
+ context "when going multiple levels deep in arrays" do
1071
+ let(:criteria) { Band.where(origin: "tally") }
1072
+
1073
+ let(:tally) do
1074
+ criteria.tally("genres.y.z")
1075
+ end
1076
+
1077
+ it "returns the correct hash" do
1078
+ expect(tally).to eq(
1079
+ [1, 2] => 2,
1080
+ [1, 3] => 1
1081
+ )
1082
+ end
1083
+ end
1084
+
1085
+ context "when going multiple levels deep in an array" do
1086
+ let(:criteria) { Band.where(origin: "tally") }
1087
+
1088
+ let(:tally) do
1089
+ criteria.tally("genres.y.z")
1090
+ end
1091
+
1092
+ it "returns the correct hash" do
1093
+ expect(tally).to eq(
1094
+ [1, 2] => 2,
1095
+ [1, 3] => 1
1096
+ )
1097
+ end
1098
+ end
1099
+
1100
+ context "when tallying deeply nested arrays/embedded associations" do
1101
+
1102
+ before do
1103
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
1104
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
1105
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))) ])
1106
+ end
1107
+
1108
+ let(:tally) do
1109
+ Person.tally("addresses.code.deepest.array.y.z")
1110
+ end
1111
+
1112
+ it "returns the correct hash" do
1113
+ expect(tally).to eq(
1114
+ [ [ 1, 2 ] ] => 2,
1115
+ [ [ 1, 3 ] ] => 1
1116
+ )
1117
+ end
1118
+ end
1119
+
1120
+ context "when tallying deeply nested arrays/embedded associations" do
1121
+
1122
+ before do
1123
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))),
1124
+ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
1125
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))),
1126
+ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
1127
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))),
1128
+ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))) ])
1129
+ end
1130
+
1131
+ let(:tally) do
1132
+ Person.tally("addresses.code.deepest.array.y.z")
1133
+ end
1134
+
1135
+ it "returns the correct hash" do
1136
+ expect(tally).to eq(
1137
+ [ [ 1, 2 ], [ 1, 2 ] ] => 2,
1138
+ [ [ 1, 3 ], [ 1, 3 ] ] => 1
1139
+ )
1140
+ end
1141
+ end
1142
+
1143
+ context "when some keys are missing" do
1144
+ before do
1145
+ 3.times { Band.create!(origin: "tally") }
1146
+ end
1147
+
1148
+ let(:tally) do
1149
+ criteria.tally(:name)
1150
+ end
1151
+
1152
+ it "returns the correct hash" do
1153
+ expect(tally).to eq(
1154
+ "Depeche Mode" => 1,
1155
+ "New Order" => 1,
1156
+ "10,000 Maniacs" => 1,
1157
+ nil => 3
1158
+ )
1159
+ end
1160
+ end
1161
+
1162
+ context "when the first element is an embeds_one" do
1163
+ before do
1164
+ Person.create!(name: Name.new(translations: [ Translation.new(language: 1), Translation.new(language: 2) ]))
1165
+ Person.create!(name: Name.new(translations: [ Translation.new(language: 1), Translation.new(language: 2) ]))
1166
+ Person.create!(name: Name.new(translations: [ Translation.new(language: 1), Translation.new(language: 3) ]))
1167
+ end
1168
+
1169
+ let(:tally) do
1170
+ Person.tally("name.translations.language")
1171
+ end
1172
+
1173
+ it "returns the correct hash" do
1174
+ expect(tally).to eq(
1175
+ [1, 2] => 2,
1176
+ [1, 3] => 1
1177
+ )
1178
+ end
1179
+ end
1180
+
1181
+ context "when tallying demongoizable values from typeless fields" do
1182
+
1183
+ let!(:person1) { Person.create!(ssn: /hello/) }
1184
+ let!(:person2) { Person.create!(ssn: BSON::Decimal128.new("1")) }
1185
+ let(:tally) { Person.tally("ssn") }
1186
+
1187
+ let(:tallied_classes) do
1188
+ tally.keys.map(&:class).sort do |a, b|
1189
+ a.to_s.casecmp(b.to_s)
1190
+ end
1191
+ end
1192
+
1193
+ context "< BSON 5" do
1194
+ max_bson_version '4.99.99'
1195
+
1196
+ it "stores the correct types in the database" do
1197
+ expect(Person.find(person1.id).attributes["ssn"]).to be_a BSON::Regexp::Raw
1198
+ expect(Person.find(person2.id).attributes["ssn"]).to be_a BSON::Decimal128
1199
+ end
1200
+
1201
+ it "tallies the correct type" do
1202
+ expect(tallied_classes).to be == [ BSON::Decimal128, BSON::Regexp::Raw ]
1203
+ end
1204
+ end
1205
+
1206
+ context '>= BSON 5' do
1207
+ min_bson_version "5.0"
1208
+
1209
+ it "stores the correct types in the database" do
1210
+ expect(Person.find(person1.id).ssn).to be_a BSON::Regexp::Raw
1211
+ expect(Person.find(person2.id).ssn).to be_a BigDecimal
1212
+ end
1213
+
1214
+ it "tallies the correct type" do
1215
+ expect(tallied_classes).to be == [ BigDecimal, BSON::Regexp::Raw ]
1216
+ end
1217
+ end
1218
+
1219
+ context '>= BSON 5 with decimal128 allowed' do
1220
+ min_bson_version "5.0"
1221
+ config_override :allow_bson5_decimal128, true
1222
+
1223
+ it "stores the correct types in the database" do
1224
+ expect(Person.find(person1.id).ssn).to be_a BSON::Regexp::Raw
1225
+ expect(Person.find(person2.id).ssn).to be_a BSON::Decimal128
1226
+ end
1227
+
1228
+ it "tallies the correct type" do
1229
+ expect(tallied_classes).to be == [ BSON::Decimal128, BSON::Regexp::Raw ]
1230
+ end
1231
+ end
1232
+ end
1233
+ end
1234
+
812
1235
  describe "#each" do
813
1236
 
814
1237
  before do
@@ -973,98 +1396,175 @@ describe Mongoid::Contextual::Mongo do
973
1396
 
974
1397
  describe "#exists?" do
975
1398
 
976
- before do
977
- Band.create!(name: "Depeche Mode")
1399
+ let!(:band) do
1400
+ Band.create!(name: "Depeche Mode", active: true)
978
1401
  end
979
1402
 
980
- context "when the count is zero" do
1403
+ context "when not passing options" do
981
1404
 
982
- let(:criteria) do
983
- Band.where(name: "New Order")
984
- end
1405
+ context "when the count is zero" do
985
1406
 
986
- let(:context) do
987
- described_class.new(criteria)
988
- end
1407
+ let(:criteria) do
1408
+ Band.where(name: "New Order")
1409
+ end
989
1410
 
990
- it "returns false" do
991
- expect(context).to_not be_exists
1411
+ let(:context) do
1412
+ described_class.new(criteria)
1413
+ end
1414
+
1415
+ it "returns false" do
1416
+ expect(context).to_not be_exists
1417
+ end
992
1418
  end
993
- end
994
1419
 
995
- context "when the count is greater than zero" do
1420
+ context "when the count is greater than zero" do
996
1421
 
997
- let(:criteria) do
998
- Band.where(name: "Depeche Mode")
999
- end
1422
+ let(:criteria) do
1423
+ Band.where(name: "Depeche Mode")
1424
+ end
1000
1425
 
1001
- let(:context) do
1002
- described_class.new(criteria)
1426
+ let(:context) do
1427
+ described_class.new(criteria)
1428
+ end
1429
+
1430
+ it "returns true" do
1431
+ expect(context).to be_exists
1432
+ end
1003
1433
  end
1004
1434
 
1005
- it "returns true" do
1006
- expect(context).to be_exists
1435
+ context "when caching is not enabled" do
1436
+
1437
+ let(:criteria) do
1438
+ Band.where(name: "Depeche Mode")
1439
+ end
1440
+
1441
+ let(:context) do
1442
+ described_class.new(criteria)
1443
+ end
1444
+
1445
+ context "when exists? already called and query cache is enabled" do
1446
+ query_cache_enabled
1447
+
1448
+ before do
1449
+ context.exists?
1450
+ end
1451
+
1452
+ it "does not hit the database again" do
1453
+ expect_no_queries do
1454
+ expect(context).to be_exists
1455
+ end
1456
+ end
1457
+ end
1007
1458
  end
1008
1459
  end
1009
1460
 
1010
- context "when caching is not enabled" do
1461
+ context "when passing an _id" do
1011
1462
 
1012
- let(:criteria) do
1013
- Band.where(name: "Depeche Mode")
1014
- end
1463
+ context "when its of type BSON::ObjectId" do
1015
1464
 
1016
- let(:context) do
1017
- described_class.new(criteria)
1465
+ context "when calling it on the class" do
1466
+
1467
+ it "returns true" do
1468
+ expect(Band.exists?(band._id)).to be true
1469
+ end
1470
+ end
1471
+
1472
+ context "when calling it on a criteria that includes the object" do
1473
+
1474
+ it "returns true" do
1475
+ expect(Band.where(name: band.name).exists?(band._id)).to be true
1476
+ end
1477
+ end
1478
+
1479
+ context "when calling it on a criteria that does not include the object" do
1480
+
1481
+ it "returns false" do
1482
+ expect(Band.where(name: "bogus").exists?(band._id)).to be false
1483
+ end
1484
+ end
1485
+
1486
+ context "when the id does not exist" do
1487
+
1488
+ it "returns false" do
1489
+ expect(Band.exists?(BSON::ObjectId.new)).to be false
1490
+ end
1491
+ end
1018
1492
  end
1019
1493
 
1020
- context "when exists? already called" do
1494
+ context "when its of type String" do
1021
1495
 
1022
- before do
1023
- context.exists?
1496
+ context "when the id exists" do
1497
+
1498
+ it "returns true" do
1499
+ expect(Band.exists?(band._id.to_s)).to be true
1500
+ end
1024
1501
  end
1025
1502
 
1026
- it "hits the database again" do
1027
- expect(context).to receive(:view).once.and_call_original
1028
- expect(context).to be_exists
1503
+ context "when the id does not exist" do
1504
+
1505
+ it "returns false" do
1506
+ expect(Band.exists?(BSON::ObjectId.new.to_s)).to be false
1507
+ end
1029
1508
  end
1030
1509
  end
1031
1510
  end
1032
1511
 
1033
- context "when caching is enabled" do
1512
+ context "when passing a hash" do
1034
1513
 
1035
- let(:criteria) do
1036
- Band.where(name: "Depeche Mode").cache
1514
+ context "when calling it on the class" do
1515
+
1516
+ it "returns true" do
1517
+ expect(Band.exists?(name: band.name)).to be true
1518
+ end
1037
1519
  end
1038
1520
 
1039
- let(:context) do
1040
- described_class.new(criteria)
1521
+ context "when calling it on a criteria that includes the object" do
1522
+
1523
+ it "returns true" do
1524
+ expect(Band.where(active: true).exists?(name: band.name)).to be true
1525
+ end
1041
1526
  end
1042
1527
 
1043
- context "when the cache is loaded" do
1528
+ context "when calling it on a criteria that does not include the object" do
1044
1529
 
1045
- before do
1046
- context.to_a
1530
+ it "returns false" do
1531
+ expect(Band.where(active: false).exists?(name: band.name)).to be false
1047
1532
  end
1533
+ end
1048
1534
 
1049
- it "does not hit the database" do
1050
- expect(context).to receive(:view).never
1051
- expect(context).to be_exists
1535
+ context "when the conditions don't match" do
1536
+
1537
+ it "returns false" do
1538
+ expect(Band.exists?(name: "bogus")).to be false
1052
1539
  end
1053
1540
  end
1541
+ end
1054
1542
 
1055
- context "when the cache is not loaded" do
1543
+ context "when passing false" do
1056
1544
 
1057
- context "when a count has been executed" do
1545
+ it "returns false" do
1546
+ expect(Band.exists?(false)).to be false
1547
+ end
1548
+ end
1058
1549
 
1059
- before do
1060
- context.count
1061
- end
1550
+ context "when passing nil" do
1062
1551
 
1063
- it "does not hit the database" do
1064
- expect(context).to receive(:view).never
1065
- expect(context).to be_exists
1066
- end
1067
- end
1552
+ it "returns false" do
1553
+ expect(Band.exists?(nil)).to be false
1554
+ end
1555
+ end
1556
+
1557
+ context "when the limit is 0" do
1558
+
1559
+ it "returns false" do
1560
+ expect(Band.limit(0).exists?).to be false
1561
+ end
1562
+ end
1563
+
1564
+ context "when the criteria limit is 0" do
1565
+
1566
+ it "returns false" do
1567
+ expect(Band.criteria.limit(0).exists?).to be false
1068
1568
  end
1069
1569
  end
1070
1570
  end
@@ -1080,7 +1580,16 @@ describe Mongoid::Contextual::Mongo do
1080
1580
  end
1081
1581
 
1082
1582
  it "returns the criteria explain path" do
1083
- expect(context.explain).to_not be_empty
1583
+ explain = context.explain
1584
+ expect(explain).to_not be_empty
1585
+ expect(explain.keys).to include("queryPlanner", "executionStats", "serverInfo")
1586
+ end
1587
+
1588
+ it "respects options passed to explain" do
1589
+ explain = context.explain(verbosity: :query_planner)
1590
+ expect(explain).to_not be_empty
1591
+ expect(explain.keys).to include("queryPlanner", "serverInfo")
1592
+ expect(explain.keys).not_to include("executionStats")
1084
1593
  end
1085
1594
  end
1086
1595
 
@@ -1420,7 +1929,7 @@ describe Mongoid::Contextual::Mongo do
1420
1929
  it "deletes the document from the database" do
1421
1930
  expect {
1422
1931
  depeche.reload
1423
- }.to raise_error(Mongoid::Errors::DocumentNotFound)
1932
+ }.to raise_error(Mongoid::Errors::DocumentNotFound, /Document\(s\) not found for class Band with id\(s\)/)
1424
1933
  end
1425
1934
 
1426
1935
  context 'when a collation is specified on the criteria' do
@@ -1445,7 +1954,7 @@ describe Mongoid::Contextual::Mongo do
1445
1954
  it "deletes the document from the database" do
1446
1955
  expect {
1447
1956
  depeche.reload
1448
- }.to raise_error(Mongoid::Errors::DocumentNotFound)
1957
+ }.to raise_error(Mongoid::Errors::DocumentNotFound, /Document\(s\) not found for class Band with id\(s\)/)
1449
1958
  end
1450
1959
  end
1451
1960
  end
@@ -1561,24 +2070,6 @@ describe Mongoid::Contextual::Mongo do
1561
2070
  expect(context.last).to eq(rolling_stones)
1562
2071
  end
1563
2072
  end
1564
-
1565
- context 'with option { id_sort: :none }' do
1566
- let(:opts) do
1567
- { id_sort: :none }
1568
- end
1569
-
1570
- it 'applies the sort on _id' do
1571
- expect(context.send(method, opts)).to eq(depeche_mode)
1572
- end
1573
-
1574
- context 'when calling #last' do
1575
-
1576
- it 'doesn\'t apply a sort on _id' do
1577
- expect(context.send(method, opts)).to eq(depeche_mode)
1578
- expect(context.last(opts)).to eq(depeche_mode)
1579
- end
1580
- end
1581
- end
1582
2073
  end
1583
2074
 
1584
2075
  context 'when the criteria has a sort' do
@@ -1602,25 +2093,6 @@ describe Mongoid::Contextual::Mongo do
1602
2093
  expect(context.last).to eq(depeche_mode)
1603
2094
  end
1604
2095
  end
1605
-
1606
- context 'with option { id_sort: :none }' do
1607
-
1608
- let(:opts) do
1609
- { id_sort: :none }
1610
- end
1611
-
1612
- it 'uses the preexisting sort' do
1613
- expect(context.send(method, opts)).to eq(rolling_stones)
1614
- end
1615
-
1616
- context 'when calling #last' do
1617
-
1618
- it 'uses the preexisting sort' do
1619
- expect(context.send(method, opts)).to eq(rolling_stones)
1620
- expect(context.last(opts)).to eq(depeche_mode)
1621
- end
1622
- end
1623
- end
1624
2096
  end
1625
2097
 
1626
2098
  context "when using .sort" do
@@ -1649,28 +2121,17 @@ describe Mongoid::Contextual::Mongo do
1649
2121
  end
1650
2122
  end
1651
2123
 
1652
- context "when the context is cached" do
2124
+ context "when the query cache is enabled" do
2125
+ query_cache_enabled
1653
2126
 
1654
2127
  let(:criteria) do
1655
- Band.where(name: "Depeche Mode").cache
2128
+ Band.where(name: "Depeche Mode")
1656
2129
  end
1657
2130
 
1658
2131
  let(:context) do
1659
2132
  described_class.new(criteria)
1660
2133
  end
1661
2134
 
1662
- context "when the cache is loaded" do
1663
-
1664
- before do
1665
- context.to_a
1666
- end
1667
-
1668
- it "returns the first document without touching the database" do
1669
- expect(context).to receive(:view).never
1670
- expect(context.send(method)).to eq(depeche_mode)
1671
- end
1672
- end
1673
-
1674
2135
  context "when first method was called before" do
1675
2136
 
1676
2137
  before do
@@ -1678,8 +2139,9 @@ describe Mongoid::Contextual::Mongo do
1678
2139
  end
1679
2140
 
1680
2141
  it "returns the first document without touching the database" do
1681
- expect(context).to receive(:view).never
1682
- expect(context.send(method)).to eq(depeche_mode)
2142
+ expect_no_queries do
2143
+ expect(context.send(method)).to eq(depeche_mode)
2144
+ end
1683
2145
  end
1684
2146
  end
1685
2147
  end
@@ -1733,77 +2195,21 @@ describe Mongoid::Contextual::Mongo do
1733
2195
  end
1734
2196
  end
1735
2197
 
1736
- context "when the context is cached" do
2198
+ context "when the query cache is enabled" do
1737
2199
 
1738
2200
  let(:context) do
1739
2201
  described_class.new(criteria)
1740
2202
  end
1741
2203
 
1742
- context "when the whole context is loaded" do
1743
-
1744
- before do
1745
- context.to_a
1746
- end
1747
-
1748
- context "when all of the documents are cached" do
1749
-
1750
- let(:criteria) do
1751
- Band.all.cache
1752
- end
1753
-
1754
- context "when requesting all of the documents" do
1755
-
1756
- let(:docs) do
1757
- context.send(method, 3)
1758
- end
1759
-
1760
- it "returns all of the documents without touching the database" do
1761
- expect(context).to receive(:view).never
1762
- expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
1763
- end
1764
- end
1765
-
1766
- context "when requesting fewer than all of the documents" do
1767
-
1768
- let(:docs) do
1769
- context.send(method, 2)
1770
- end
1771
-
1772
- it "returns all of the documents without touching the database" do
1773
- expect(context).to receive(:view).never
1774
- expect(docs).to eq([ depeche_mode, new_order ])
1775
- end
1776
- end
1777
- end
1778
-
1779
- context "when only one document is cached" do
1780
-
1781
- let(:criteria) do
1782
- Band.where(name: "Depeche Mode").cache
1783
- end
1784
-
1785
- context "when requesting one document" do
1786
-
1787
- let(:docs) do
1788
- context.send(method, 1)
1789
- end
1790
-
1791
- it "returns one document without touching the database" do
1792
- expect(context).to receive(:view).never
1793
- expect(docs).to eq([ depeche_mode ])
1794
- end
1795
- end
1796
- end
1797
- end
1798
-
1799
- context "when the first method was called before" do
2204
+ context "when calling first beforehand" do
2205
+ query_cache_enabled
1800
2206
 
1801
2207
  let(:context) do
1802
2208
  described_class.new(criteria)
1803
2209
  end
1804
2210
 
1805
2211
  let(:criteria) do
1806
- Band.all.cache
2212
+ Band.all
1807
2213
  end
1808
2214
 
1809
2215
  before do
@@ -1821,8 +2227,9 @@ describe Mongoid::Contextual::Mongo do
1821
2227
  let(:limit) { 3 }
1822
2228
 
1823
2229
  it "returns all documents without touching the database" do
1824
- expect(context).to receive(:view).never
1825
- expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2230
+ expect_no_queries do
2231
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2232
+ end
1826
2233
  end
1827
2234
  end
1828
2235
 
@@ -1830,8 +2237,9 @@ describe Mongoid::Contextual::Mongo do
1830
2237
  let(:limit) { 2 }
1831
2238
 
1832
2239
  it "returns the correct documents without touching the database" do
1833
- expect(context).to receive(:view).never
1834
- expect(docs).to eq([ depeche_mode, new_order ])
2240
+ expect_no_queries do
2241
+ expect(docs).to eq([ depeche_mode, new_order ])
2242
+ end
1835
2243
  end
1836
2244
  end
1837
2245
  end
@@ -1843,8 +2251,9 @@ describe Mongoid::Contextual::Mongo do
1843
2251
  let(:limit) { 2 }
1844
2252
 
1845
2253
  it "returns the correct documents without touching the database" do
1846
- expect(context).to receive(:view).never
1847
- expect(docs).to eq([ depeche_mode, new_order ])
2254
+ expect_no_queries do
2255
+ expect(docs).to eq([ depeche_mode, new_order ])
2256
+ end
1848
2257
  end
1849
2258
  end
1850
2259
 
@@ -1852,8 +2261,9 @@ describe Mongoid::Contextual::Mongo do
1852
2261
  let(:limit) { 3 }
1853
2262
 
1854
2263
  it "returns the correct documents and touches the database" do
1855
- expect(context).to receive(:view).exactly(3).times.and_call_original
1856
- expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2264
+ expect_query(1) do
2265
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2266
+ end
1857
2267
  end
1858
2268
  end
1859
2269
  end
@@ -1865,8 +2275,9 @@ describe Mongoid::Contextual::Mongo do
1865
2275
  let(:limit) { 1 }
1866
2276
 
1867
2277
  it "returns the correct documents without touching the database" do
1868
- expect(context).to receive(:view).never
1869
- expect(docs).to eq([ depeche_mode ])
2278
+ expect_no_queries do
2279
+ expect(docs).to eq([ depeche_mode ])
2280
+ end
1870
2281
  end
1871
2282
  end
1872
2283
 
@@ -1874,8 +2285,9 @@ describe Mongoid::Contextual::Mongo do
1874
2285
  let(:limit) { 3 }
1875
2286
 
1876
2287
  it "returns the correct documents and touches the database" do
1877
- expect(context).to receive(:view).exactly(3).times.and_call_original
1878
- expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2288
+ expect_query(1) do
2289
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2290
+ end
1879
2291
  end
1880
2292
  end
1881
2293
  end
@@ -1883,14 +2295,15 @@ describe Mongoid::Contextual::Mongo do
1883
2295
  end
1884
2296
  end
1885
2297
 
1886
- context "when calling #first then #last" do
2298
+ context "when calling #first then #last and the query cache is enabled" do
2299
+ query_cache_enabled
1887
2300
 
1888
2301
  let(:context) do
1889
2302
  described_class.new(criteria)
1890
2303
  end
1891
2304
 
1892
2305
  let(:criteria) do
1893
- Band.all.cache
2306
+ Band.all
1894
2307
  end
1895
2308
 
1896
2309
  before do
@@ -1905,8 +2318,10 @@ describe Mongoid::Contextual::Mongo do
1905
2318
  let(:before_limit) { 2 }
1906
2319
  let(:limit) { 1 }
1907
2320
 
1908
- it "gets the correct document" do
1909
- expect(docs).to eq([rolling_stones])
2321
+ it "gets the correct document and hits the database" do
2322
+ expect_query(1) do
2323
+ expect(docs).to eq([rolling_stones])
2324
+ end
1910
2325
  end
1911
2326
  end
1912
2327
  end
@@ -1996,30 +2411,10 @@ describe Mongoid::Contextual::Mongo do
1996
2411
  context 'when calling #first' do
1997
2412
 
1998
2413
  it 'returns the first document, sorted by _id' do
1999
- pending "MONGOID-5416"
2000
2414
  expect(context.last).to eq(rolling_stones)
2001
2415
  expect(context.first).to eq(depeche_mode)
2002
2416
  end
2003
2417
  end
2004
-
2005
- context 'with option { id_sort: :none }' do
2006
- let(:opts) do
2007
- { id_sort: :none }
2008
- end
2009
-
2010
- it 'doesn\'t apply the sort on _id' do
2011
- expect(context.last(opts)).to eq(depeche_mode)
2012
- end
2013
-
2014
- context 'when calling #first' do
2015
-
2016
- it 'doesn\'t apply the sort on _id' do
2017
- pending "MONGOID-5416"
2018
- expect(context.last(opts)).to eq(rolling_stones)
2019
- expect(context.first(opts)).to eq(depeche_mode)
2020
- end
2021
- end
2022
- end
2023
2418
  end
2024
2419
 
2025
2420
  context 'when the criteria has a sort' do
@@ -2044,25 +2439,6 @@ describe Mongoid::Contextual::Mongo do
2044
2439
  expect(context.first).to eq(rolling_stones)
2045
2440
  end
2046
2441
  end
2047
-
2048
- context 'with option { id_sort: :none }' do
2049
-
2050
- let(:opts) do
2051
- { id_sort: :none }
2052
- end
2053
-
2054
- it 'uses the preexisting sort' do
2055
- expect(context.last(opts)).to eq(depeche_mode)
2056
- end
2057
-
2058
- context 'when calling #first' do
2059
-
2060
- it 'uses the preexisting sort' do
2061
- expect(context.last(opts)).to eq(depeche_mode)
2062
- expect(context.first(opts)).to eq(rolling_stones)
2063
- end
2064
- end
2065
- end
2066
2442
  end
2067
2443
 
2068
2444
  context "when using .sort" do
@@ -2091,28 +2467,17 @@ describe Mongoid::Contextual::Mongo do
2091
2467
  end
2092
2468
  end
2093
2469
 
2094
- context "when the context is cached" do
2470
+ context "when the query cache is enabled" do
2471
+ query_cache_enabled
2095
2472
 
2096
2473
  let(:criteria) do
2097
- Band.where(name: "Depeche Mode").cache
2474
+ Band.where(name: "Depeche Mode")
2098
2475
  end
2099
2476
 
2100
2477
  let(:context) do
2101
2478
  described_class.new(criteria)
2102
2479
  end
2103
2480
 
2104
- context "when the cache is loaded" do
2105
-
2106
- before do
2107
- context.to_a
2108
- end
2109
-
2110
- it "returns the last document without touching the database" do
2111
- expect(context).to receive(:view).never
2112
- expect(context.last).to eq(depeche_mode)
2113
- end
2114
- end
2115
-
2116
2481
  context "when last method was called before" do
2117
2482
 
2118
2483
  before do
@@ -2120,8 +2485,9 @@ describe Mongoid::Contextual::Mongo do
2120
2485
  end
2121
2486
 
2122
2487
  it "returns the last document without touching the database" do
2123
- expect(context).to receive(:view).never
2124
- expect(context.last).to eq(depeche_mode)
2488
+ expect_no_queries do
2489
+ expect(context.last).to eq(depeche_mode)
2490
+ end
2125
2491
  end
2126
2492
  end
2127
2493
  end
@@ -2181,71 +2547,15 @@ describe Mongoid::Contextual::Mongo do
2181
2547
  described_class.new(criteria)
2182
2548
  end
2183
2549
 
2184
- context "when the whole context is loaded" do
2185
-
2186
- before do
2187
- context.to_a
2188
- end
2189
-
2190
- context "when all of the documents are cached" do
2191
-
2192
- let(:criteria) do
2193
- Band.all.cache
2194
- end
2195
-
2196
- context "when requesting all of the documents" do
2197
-
2198
- let(:docs) do
2199
- context.last(3)
2200
- end
2201
-
2202
- it "returns all of the documents without touching the database" do
2203
- expect(context).to receive(:view).never
2204
- expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2205
- end
2206
- end
2207
-
2208
- context "when requesting fewer than all of the documents" do
2209
-
2210
- let(:docs) do
2211
- context.last(2)
2212
- end
2213
-
2214
- it "returns all of the documents without touching the database" do
2215
- expect(context).to receive(:view).never
2216
- expect(docs).to eq([ new_order, rolling_stones ])
2217
- end
2218
- end
2219
- end
2220
-
2221
- context "when only one document is cached" do
2222
-
2223
- let(:criteria) do
2224
- Band.where(name: "Depeche Mode").cache
2225
- end
2226
-
2227
- context "when requesting one document" do
2228
-
2229
- let(:docs) do
2230
- context.last(1)
2231
- end
2232
-
2233
- it "returns one document without touching the database" do
2234
- expect(context).to receive(:view).never
2235
- expect(docs).to eq([ depeche_mode ])
2236
- end
2237
- end
2238
- end
2239
- end
2240
-
2241
- context "when the last method was called before" do
2550
+ context "when query cache is enabled" do
2551
+ query_cache_enabled
2242
2552
 
2243
2553
  let(:context) do
2244
2554
  described_class.new(criteria)
2245
2555
  end
2246
2556
 
2247
2557
  let(:criteria) do
2248
- Band.all.cache
2558
+ Band.all
2249
2559
  end
2250
2560
 
2251
2561
  before do
@@ -2262,18 +2572,20 @@ describe Mongoid::Contextual::Mongo do
2262
2572
  context "when getting all of the documents" do
2263
2573
  let(:limit) { 3 }
2264
2574
 
2265
- it "returns all documents without touching the database" do
2266
- expect(context).to receive(:view).never
2267
- expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2575
+ it "returns all documents without touching the db" do
2576
+ expect_no_queries do
2577
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2578
+ end
2268
2579
  end
2269
2580
  end
2270
2581
 
2271
2582
  context "when getting fewer documents" do
2272
2583
  let(:limit) { 2 }
2273
2584
 
2274
- it "returns the correct documents without touching the database" do
2275
- expect(context).to receive(:view).never
2276
- expect(docs).to eq([ new_order, rolling_stones ])
2585
+ it "returns the correct documents without touching the db" do
2586
+ expect_no_queries do
2587
+ expect(docs).to eq([ new_order, rolling_stones ])
2588
+ end
2277
2589
  end
2278
2590
  end
2279
2591
  end
@@ -2284,9 +2596,10 @@ describe Mongoid::Contextual::Mongo do
2284
2596
  context "when getting the same number of documents" do
2285
2597
  let(:limit) { 2 }
2286
2598
 
2287
- it "returns the correct documents without touching the database" do
2288
- expect(context).to receive(:view).never
2289
- expect(docs).to eq([ new_order, rolling_stones ])
2599
+ it "returns the correct documents without touching the db" do
2600
+ expect_no_queries do
2601
+ expect(docs).to eq([ new_order, rolling_stones ])
2602
+ end
2290
2603
  end
2291
2604
  end
2292
2605
 
@@ -2294,8 +2607,9 @@ describe Mongoid::Contextual::Mongo do
2294
2607
  let(:limit) { 3 }
2295
2608
 
2296
2609
  it "returns the correct documents and touches the database" do
2297
- expect(context).to receive(:view).twice.and_call_original
2298
- expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2610
+ expect_query(1) do
2611
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2612
+ end
2299
2613
  end
2300
2614
  end
2301
2615
  end
@@ -2307,8 +2621,9 @@ describe Mongoid::Contextual::Mongo do
2307
2621
  let(:limit) { 1 }
2308
2622
 
2309
2623
  it "returns the correct documents without touching the database" do
2310
- expect(context).to receive(:view).never
2311
- expect(docs).to eq([ rolling_stones ])
2624
+ expect_no_queries do
2625
+ expect(docs).to eq([ rolling_stones ])
2626
+ end
2312
2627
  end
2313
2628
  end
2314
2629
 
@@ -2316,8 +2631,9 @@ describe Mongoid::Contextual::Mongo do
2316
2631
  let(:limit) { 3 }
2317
2632
 
2318
2633
  it "returns the correct documents and touches the database" do
2319
- expect(context).to receive(:view).twice.and_call_original
2320
- expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2634
+ expect_query(1) do
2635
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2636
+ end
2321
2637
  end
2322
2638
  end
2323
2639
  end
@@ -2325,14 +2641,15 @@ describe Mongoid::Contextual::Mongo do
2325
2641
  end
2326
2642
  end
2327
2643
 
2328
- context "when calling #last then #first" do
2644
+ context "when calling #last then #first and the query cache is enabled" do
2645
+ query_cache_enabled
2329
2646
 
2330
2647
  let(:context) do
2331
2648
  described_class.new(criteria)
2332
2649
  end
2333
2650
 
2334
2651
  let(:criteria) do
2335
- Band.all.cache
2652
+ Band.all
2336
2653
  end
2337
2654
 
2338
2655
  before do
@@ -2348,12 +2665,12 @@ describe Mongoid::Contextual::Mongo do
2348
2665
  let(:limit) { 1 }
2349
2666
 
2350
2667
  it "hits the database" do
2351
- expect(context).to receive(:view).exactly(3).times.and_call_original
2352
- docs
2668
+ expect_query(1) do
2669
+ docs
2670
+ end
2353
2671
  end
2354
2672
 
2355
2673
  it "gets the correct document" do
2356
- pending "MONGOID-5416"
2357
2674
  expect(docs).to eq([ depeche_mode ])
2358
2675
  end
2359
2676
  end
@@ -2406,37 +2723,28 @@ describe Mongoid::Contextual::Mongo do
2406
2723
  described_class.new(criteria)
2407
2724
  end
2408
2725
 
2409
- it "returns the number of documents that match" do
2410
- expect(context.send(method)).to eq(2)
2411
- end
2726
+ context "when broken_view_options is false" do
2727
+ driver_config_override :broken_view_options, false
2412
2728
 
2413
- context "when calling more than once" do
2414
- it "returns the cached value for subsequent calls" do
2415
- expect(context.view).to receive(:count_documents).once.and_return(2)
2416
- 2.times { expect(context.send(method)).to eq(2) }
2729
+ it "returns the number of documents that match" do
2730
+ expect(context.send(method)).to eq(1)
2417
2731
  end
2418
2732
  end
2419
2733
 
2420
- context "when the results have been iterated over" do
2421
-
2422
- before do
2423
- context.entries
2424
- end
2734
+ context "when broken_view_options is true" do
2735
+ driver_config_override :broken_view_options, true
2425
2736
 
2426
- it "returns the cached value for all calls" do
2427
- expect(context.view).to receive(:count_documents).once.and_return(2)
2737
+ it "returns the number of documents that match" do
2428
2738
  expect(context.send(method)).to eq(2)
2429
2739
  end
2740
+ end
2430
2741
 
2431
- context "when the results have been iterated over multiple times" do
2432
-
2433
- before do
2434
- context.entries
2435
- end
2742
+ context "when calling more than once with different limits" do
2743
+ driver_config_override :broken_view_options, false
2436
2744
 
2437
- it "resets the length on each full iteration" do
2438
- expect(context.size).to eq(2)
2439
- end
2745
+ it "does not cache the value" do
2746
+ expect(context.limit(1).send(method)).to eq(1)
2747
+ expect(context.limit(2).send(method)).to eq(2)
2440
2748
  end
2441
2749
  end
2442
2750
  end
@@ -2455,10 +2763,12 @@ describe Mongoid::Contextual::Mongo do
2455
2763
  expect(context.send(method)).to eq(1)
2456
2764
  end
2457
2765
 
2458
- context "when calling more than once" do
2459
- it "returns the cached value for subsequent calls" do
2460
- expect(context.view).to receive(:count_documents).once.and_return(1)
2461
- 2.times { expect(context.send(method)).to eq(1) }
2766
+ context "when calling more than once with different skips" do
2767
+ driver_config_override :broken_view_options, false
2768
+
2769
+ it "does not cache the value" do
2770
+ expect(context.skip(0).send(method)).to eq(1)
2771
+ expect(context.skip(1).send(method)).to eq(0)
2462
2772
  end
2463
2773
  end
2464
2774
 
@@ -2580,7 +2890,7 @@ describe Mongoid::Contextual::Mongo do
2580
2890
  it "raises an error" do
2581
2891
  expect do
2582
2892
  Person.take!
2583
- end.to raise_error(Mongoid::Errors::DocumentNotFound)
2893
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Person./)
2584
2894
  end
2585
2895
  end
2586
2896
  end
@@ -2602,9 +2912,10 @@ describe Mongoid::Contextual::Mongo do
2602
2912
 
2603
2913
  context "when passed the symbol field name" do
2604
2914
 
2605
- it "performs mapping and warns" do
2606
- expect(Mongoid::Warnings).to receive(:warn_map_field_deprecated)
2607
- expect(context.map(:name)).to eq ["Depeche Mode", "New Order"]
2915
+ it "raises an error" do
2916
+ expect do
2917
+ context.map(:name)
2918
+ end.to raise_error(ArgumentError)
2608
2919
  end
2609
2920
  end
2610
2921
 
@@ -3392,6 +3703,20 @@ describe Mongoid::Contextual::Mongo do
3392
3703
  end
3393
3704
  end
3394
3705
 
3706
+ context 'when using aliased field names' do
3707
+ before do
3708
+ context.update_all('$set' => { years: 100 })
3709
+ end
3710
+
3711
+ it "updates the first matching document" do
3712
+ expect(depeche_mode.reload.years).to eq(100)
3713
+ end
3714
+
3715
+ it "updates the last matching document" do
3716
+ expect(new_order.reload.years).to eq(100)
3717
+ end
3718
+ end
3719
+
3395
3720
  context "when the attributes must be mongoized" do
3396
3721
 
3397
3722
  before do
@@ -3506,4 +3831,836 @@ describe Mongoid::Contextual::Mongo do
3506
3831
  end
3507
3832
  end
3508
3833
  end
3834
+
3835
+ describe "#first!" do
3836
+
3837
+ let!(:depeche_mode) do
3838
+ Band.create!(name: "Depeche Mode")
3839
+ end
3840
+
3841
+ let!(:new_order) do
3842
+ Band.create!(name: "New Order")
3843
+ end
3844
+
3845
+ let!(:rolling_stones) do
3846
+ Band.create!(name: "The Rolling Stones")
3847
+ end
3848
+
3849
+ let!(:death_cab) do
3850
+ Band.create!(name: "Death Cab For Cutie")
3851
+ end
3852
+
3853
+ let(:context) do
3854
+ described_class.new(criteria)
3855
+ end
3856
+
3857
+ context "when there's no sort" do
3858
+ let(:criteria) do
3859
+ Band.all
3860
+ end
3861
+
3862
+ it "gets the first document" do
3863
+ expect(context.first!).to eq(depeche_mode)
3864
+ end
3865
+ end
3866
+
3867
+ context "when there's a custom sort" do
3868
+ let(:criteria) do
3869
+ Band.all
3870
+ end
3871
+
3872
+ it "gets the first document" do
3873
+ expect(context.sort(name: 1).first!).to eq(death_cab)
3874
+ end
3875
+ end
3876
+
3877
+ context "when there are no documents" do
3878
+ let(:criteria) do
3879
+ Band.where(name: "bogus")
3880
+ end
3881
+
3882
+ it "raises an error" do
3883
+ expect do
3884
+ context.first!
3885
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
3886
+ end
3887
+ end
3888
+ end
3889
+
3890
+ describe "#last!" do
3891
+
3892
+ let!(:depeche_mode) do
3893
+ Band.create!(name: "Depeche Mode")
3894
+ end
3895
+
3896
+ let!(:new_order) do
3897
+ Band.create!(name: "New Order")
3898
+ end
3899
+
3900
+ let!(:rolling_stones) do
3901
+ Band.create!(name: "The Rolling Stones")
3902
+ end
3903
+
3904
+ let!(:death_cab) do
3905
+ Band.create!(name: "Death Cab For Cutie")
3906
+ end
3907
+
3908
+ let(:context) do
3909
+ described_class.new(criteria)
3910
+ end
3911
+
3912
+ context "when there's no sort" do
3913
+ let(:criteria) do
3914
+ Band.all
3915
+ end
3916
+
3917
+ it "gets the last document" do
3918
+ expect(context.last!).to eq(death_cab)
3919
+ end
3920
+ end
3921
+
3922
+ context "when there's a custom sort" do
3923
+ let(:criteria) do
3924
+ Band.all
3925
+ end
3926
+
3927
+ it "gets the last document" do
3928
+ expect(context.sort(name: 1).last!).to eq(rolling_stones)
3929
+ end
3930
+ end
3931
+
3932
+ context "when there are no documents" do
3933
+ let(:criteria) do
3934
+ Band.where(name: "bogus")
3935
+ end
3936
+
3937
+ it "raises an error" do
3938
+ expect do
3939
+ context.last!
3940
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
3941
+ end
3942
+ end
3943
+ end
3944
+
3945
+ describe "#second" do
3946
+
3947
+ let!(:depeche_mode) do
3948
+ Band.create!(name: "Depeche Mode")
3949
+ end
3950
+
3951
+ let!(:new_order) do
3952
+ Band.create!(name: "New Order")
3953
+ end
3954
+
3955
+ let!(:rolling_stones) do
3956
+ Band.create!(name: "The Rolling Stones")
3957
+ end
3958
+
3959
+ let!(:death_cab) do
3960
+ Band.create!(name: "Death Cab For Cutie")
3961
+ end
3962
+
3963
+ let(:context) do
3964
+ described_class.new(criteria)
3965
+ end
3966
+
3967
+ context "when there's no sort" do
3968
+ let(:criteria) do
3969
+ Band.all
3970
+ end
3971
+
3972
+ it "gets the second document" do
3973
+ expect(context.second).to eq(new_order)
3974
+ end
3975
+ end
3976
+
3977
+ context "when there's a custom sort" do
3978
+ let(:criteria) do
3979
+ Band.all
3980
+ end
3981
+
3982
+ it "gets the second document" do
3983
+ expect(context.sort(name: 1).second).to eq(depeche_mode)
3984
+ end
3985
+ end
3986
+
3987
+ context "when there are no documents" do
3988
+ let(:criteria) do
3989
+ Band.where(name: "bogus")
3990
+ end
3991
+
3992
+ it "returns nil" do
3993
+ expect(context.second).to be_nil
3994
+ end
3995
+ end
3996
+ end
3997
+
3998
+ describe "#second!" do
3999
+
4000
+ let!(:depeche_mode) do
4001
+ Band.create!(name: "Depeche Mode")
4002
+ end
4003
+
4004
+ let!(:new_order) do
4005
+ Band.create!(name: "New Order")
4006
+ end
4007
+
4008
+ let!(:rolling_stones) do
4009
+ Band.create!(name: "The Rolling Stones")
4010
+ end
4011
+
4012
+ let!(:death_cab) do
4013
+ Band.create!(name: "Death Cab For Cutie")
4014
+ end
4015
+
4016
+ let(:context) do
4017
+ described_class.new(criteria)
4018
+ end
4019
+
4020
+ context "when there's no sort" do
4021
+ let(:criteria) do
4022
+ Band.all
4023
+ end
4024
+
4025
+ it "gets the second document" do
4026
+ expect(context.second!).to eq(new_order)
4027
+ end
4028
+ end
4029
+
4030
+ context "when there's a custom sort" do
4031
+ let(:criteria) do
4032
+ Band.all
4033
+ end
4034
+
4035
+ it "gets the second document" do
4036
+ expect(context.sort(name: 1).second!).to eq(depeche_mode)
4037
+ end
4038
+ end
4039
+
4040
+ context "when there are no documents" do
4041
+ let(:criteria) do
4042
+ Band.where(name: "bogus")
4043
+ end
4044
+
4045
+ it "raises an error" do
4046
+ expect do
4047
+ context.second!
4048
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
4049
+ end
4050
+ end
4051
+ end
4052
+
4053
+ describe "#third" do
4054
+
4055
+ let!(:depeche_mode) do
4056
+ Band.create!(name: "Depeche Mode")
4057
+ end
4058
+
4059
+ let!(:new_order) do
4060
+ Band.create!(name: "New Order")
4061
+ end
4062
+
4063
+ let!(:rolling_stones) do
4064
+ Band.create!(name: "The Rolling Stones")
4065
+ end
4066
+
4067
+ let!(:death_cab) do
4068
+ Band.create!(name: "Death Cab For Cutie")
4069
+ end
4070
+
4071
+ let(:context) do
4072
+ described_class.new(criteria)
4073
+ end
4074
+
4075
+ context "when there's no sort" do
4076
+ let(:criteria) do
4077
+ Band.all
4078
+ end
4079
+
4080
+ it "gets the third document" do
4081
+ expect(context.third).to eq(rolling_stones)
4082
+ end
4083
+ end
4084
+
4085
+ context "when there's a custom sort" do
4086
+ let(:criteria) do
4087
+ Band.all
4088
+ end
4089
+
4090
+ it "gets the third document" do
4091
+ expect(context.sort(name: 1).third).to eq(new_order)
4092
+ end
4093
+ end
4094
+
4095
+ context "when there are no documents" do
4096
+ let(:criteria) do
4097
+ Band.where(name: "bogus")
4098
+ end
4099
+
4100
+ it "returns nil" do
4101
+ expect(context.third).to be_nil
4102
+ end
4103
+ end
4104
+ end
4105
+
4106
+ describe "#third!" do
4107
+
4108
+ let!(:depeche_mode) do
4109
+ Band.create!(name: "Depeche Mode")
4110
+ end
4111
+
4112
+ let!(:new_order) do
4113
+ Band.create!(name: "New Order")
4114
+ end
4115
+
4116
+ let!(:rolling_stones) do
4117
+ Band.create!(name: "The Rolling Stones")
4118
+ end
4119
+
4120
+ let!(:death_cab) do
4121
+ Band.create!(name: "Death Cab For Cutie")
4122
+ end
4123
+
4124
+ let(:context) do
4125
+ described_class.new(criteria)
4126
+ end
4127
+
4128
+ context "when there's no sort" do
4129
+ let(:criteria) do
4130
+ Band.all
4131
+ end
4132
+
4133
+ it "gets the third document" do
4134
+ expect(context.third!).to eq(rolling_stones)
4135
+ end
4136
+ end
4137
+
4138
+ context "when there's a custom sort" do
4139
+ let(:criteria) do
4140
+ Band.all
4141
+ end
4142
+
4143
+ it "gets the third document" do
4144
+ expect(context.sort(name: 1).third!).to eq(new_order)
4145
+ end
4146
+ end
4147
+
4148
+ context "when there are no documents" do
4149
+ let(:criteria) do
4150
+ Band.where(name: "bogus")
4151
+ end
4152
+
4153
+ it "raises an error" do
4154
+ expect do
4155
+ context.third!
4156
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
4157
+ end
4158
+ end
4159
+ end
4160
+
4161
+ describe "#fourth" do
4162
+
4163
+ let!(:depeche_mode) do
4164
+ Band.create!(name: "Depeche Mode")
4165
+ end
4166
+
4167
+ let!(:new_order) do
4168
+ Band.create!(name: "New Order")
4169
+ end
4170
+
4171
+ let!(:rolling_stones) do
4172
+ Band.create!(name: "The Rolling Stones")
4173
+ end
4174
+
4175
+ let!(:death_cab) do
4176
+ Band.create!(name: "Death Cab For Cutie")
4177
+ end
4178
+
4179
+ let(:context) do
4180
+ described_class.new(criteria)
4181
+ end
4182
+
4183
+ context "when there's no sort" do
4184
+ let(:criteria) do
4185
+ Band.all
4186
+ end
4187
+
4188
+ it "gets the fourth document" do
4189
+ expect(context.fourth).to eq(death_cab)
4190
+ end
4191
+ end
4192
+
4193
+ context "when there's a custom sort" do
4194
+ let(:criteria) do
4195
+ Band.all
4196
+ end
4197
+
4198
+ it "gets the fourth document" do
4199
+ expect(context.sort(name: 1).fourth).to eq(rolling_stones)
4200
+ end
4201
+ end
4202
+
4203
+ context "when there are no documents" do
4204
+ let(:criteria) do
4205
+ Band.where(name: "bogus")
4206
+ end
4207
+
4208
+ it "returns nil" do
4209
+ expect(context.fourth).to be_nil
4210
+ end
4211
+ end
4212
+ end
4213
+
4214
+ describe "#fourth!" do
4215
+
4216
+ let!(:depeche_mode) do
4217
+ Band.create!(name: "Depeche Mode")
4218
+ end
4219
+
4220
+ let!(:new_order) do
4221
+ Band.create!(name: "New Order")
4222
+ end
4223
+
4224
+ let!(:rolling_stones) do
4225
+ Band.create!(name: "The Rolling Stones")
4226
+ end
4227
+
4228
+ let!(:death_cab) do
4229
+ Band.create!(name: "Death Cab For Cutie")
4230
+ end
4231
+
4232
+ let(:context) do
4233
+ described_class.new(criteria)
4234
+ end
4235
+
4236
+ context "when there's no sort" do
4237
+ let(:criteria) do
4238
+ Band.all
4239
+ end
4240
+
4241
+ it "gets the fourth document" do
4242
+ expect(context.fourth!).to eq(death_cab)
4243
+ end
4244
+ end
4245
+
4246
+ context "when there's a custom sort" do
4247
+ let(:criteria) do
4248
+ Band.all
4249
+ end
4250
+
4251
+ it "gets the fourth document" do
4252
+ expect(context.sort(name: 1).fourth!).to eq(rolling_stones)
4253
+ end
4254
+ end
4255
+
4256
+ context "when there are no documents" do
4257
+ let(:criteria) do
4258
+ Band.where(name: "bogus")
4259
+ end
4260
+
4261
+ it "raises an error" do
4262
+ expect do
4263
+ context.fourth!
4264
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
4265
+ end
4266
+ end
4267
+ end
4268
+
4269
+ describe "#fifth" do
4270
+
4271
+ let!(:depeche_mode) do
4272
+ Band.create!(name: "Depeche Mode")
4273
+ end
4274
+
4275
+ let!(:new_order) do
4276
+ Band.create!(name: "New Order")
4277
+ end
4278
+
4279
+ let!(:rolling_stones) do
4280
+ Band.create!(name: "The Rolling Stones")
4281
+ end
4282
+
4283
+ let!(:death_cab) do
4284
+ Band.create!(name: "Death Cab For Cutie")
4285
+ end
4286
+
4287
+ let!(:guns_and_roses) do
4288
+ Band.create!(name: "Guns and Roses")
4289
+ end
4290
+
4291
+ let(:context) do
4292
+ described_class.new(criteria)
4293
+ end
4294
+
4295
+ context "when there's no sort" do
4296
+ let(:criteria) do
4297
+ Band.all
4298
+ end
4299
+
4300
+ it "gets the fifth document" do
4301
+ expect(context.fifth).to eq(guns_and_roses)
4302
+ end
4303
+ end
4304
+
4305
+ context "when there's a custom sort" do
4306
+ let(:criteria) do
4307
+ Band.all
4308
+ end
4309
+
4310
+ it "gets the fifth document" do
4311
+ expect(context.sort(name: 1).fifth).to eq(rolling_stones)
4312
+ end
4313
+ end
4314
+
4315
+ context "when there are no documents" do
4316
+ let(:criteria) do
4317
+ Band.where(name: "bogus")
4318
+ end
4319
+
4320
+ it "returns nil" do
4321
+ expect(context.fifth).to be_nil
4322
+ end
4323
+ end
4324
+ end
4325
+
4326
+ describe "#fifth!" do
4327
+
4328
+ let!(:depeche_mode) do
4329
+ Band.create!(name: "Depeche Mode")
4330
+ end
4331
+
4332
+ let!(:new_order) do
4333
+ Band.create!(name: "New Order")
4334
+ end
4335
+
4336
+ let!(:rolling_stones) do
4337
+ Band.create!(name: "The Rolling Stones")
4338
+ end
4339
+
4340
+ let!(:death_cab) do
4341
+ Band.create!(name: "Death Cab For Cutie")
4342
+ end
4343
+
4344
+ let!(:guns_and_roses) do
4345
+ Band.create!(name: "Guns and Roses")
4346
+ end
4347
+
4348
+ let(:context) do
4349
+ described_class.new(criteria)
4350
+ end
4351
+
4352
+ context "when there's no sort" do
4353
+ let(:criteria) do
4354
+ Band.all
4355
+ end
4356
+
4357
+ it "gets the fifth document" do
4358
+ expect(context.fifth!).to eq(guns_and_roses)
4359
+ end
4360
+ end
4361
+
4362
+ context "when there's a custom sort" do
4363
+ let(:criteria) do
4364
+ Band.all
4365
+ end
4366
+
4367
+ it "gets the fifth document" do
4368
+ expect(context.sort(name: 1).fifth!).to eq(rolling_stones)
4369
+ end
4370
+ end
4371
+
4372
+ context "when there are no documents" do
4373
+ let(:criteria) do
4374
+ Band.where(name: "bogus")
4375
+ end
4376
+
4377
+ it "raises an error" do
4378
+ expect do
4379
+ context.fifth!
4380
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
4381
+ end
4382
+ end
4383
+ end
4384
+
4385
+ describe "#second_to_last" do
4386
+
4387
+ let!(:depeche_mode) do
4388
+ Band.create!(name: "Depeche Mode")
4389
+ end
4390
+
4391
+ let!(:new_order) do
4392
+ Band.create!(name: "New Order")
4393
+ end
4394
+
4395
+ let!(:rolling_stones) do
4396
+ Band.create!(name: "The Rolling Stones")
4397
+ end
4398
+
4399
+ let!(:death_cab) do
4400
+ Band.create!(name: "Death Cab For Cutie")
4401
+ end
4402
+
4403
+ let(:context) do
4404
+ described_class.new(criteria)
4405
+ end
4406
+
4407
+ context "when there's no sort" do
4408
+ let(:criteria) do
4409
+ Band.all
4410
+ end
4411
+
4412
+ it "gets the second_to_last document" do
4413
+ expect(context.second_to_last).to eq(rolling_stones)
4414
+ end
4415
+ end
4416
+
4417
+ context "when there's a custom sort" do
4418
+ let(:criteria) do
4419
+ Band.all
4420
+ end
4421
+
4422
+ it "gets the second_to_last document" do
4423
+ expect(context.sort(name: 1).second_to_last).to eq(new_order)
4424
+ end
4425
+ end
4426
+
4427
+ context "when there are no documents" do
4428
+ let(:criteria) do
4429
+ Band.where(name: "bogus")
4430
+ end
4431
+
4432
+ it "returns nil" do
4433
+ expect(context.second_to_last).to be_nil
4434
+ end
4435
+ end
4436
+ end
4437
+
4438
+ describe "#second_to_last!" do
4439
+
4440
+ let!(:depeche_mode) do
4441
+ Band.create!(name: "Depeche Mode")
4442
+ end
4443
+
4444
+ let!(:new_order) do
4445
+ Band.create!(name: "New Order")
4446
+ end
4447
+
4448
+ let!(:rolling_stones) do
4449
+ Band.create!(name: "The Rolling Stones")
4450
+ end
4451
+
4452
+ let!(:death_cab) do
4453
+ Band.create!(name: "Death Cab For Cutie")
4454
+ end
4455
+
4456
+ let(:context) do
4457
+ described_class.new(criteria)
4458
+ end
4459
+
4460
+ context "when there's no sort" do
4461
+ let(:criteria) do
4462
+ Band.all
4463
+ end
4464
+
4465
+ it "gets the second_to_last document" do
4466
+ expect(context.second_to_last!).to eq(rolling_stones)
4467
+ end
4468
+ end
4469
+
4470
+ context "when there's a custom sort" do
4471
+ let(:criteria) do
4472
+ Band.all
4473
+ end
4474
+
4475
+ it "gets the second_to_last document" do
4476
+ expect(context.sort(name: 1).second_to_last!).to eq(new_order)
4477
+ end
4478
+ end
4479
+
4480
+ context "when there are no documents" do
4481
+ let(:criteria) do
4482
+ Band.where(name: "bogus")
4483
+ end
4484
+
4485
+ it "raises an error" do
4486
+ expect do
4487
+ context.second_to_last!
4488
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
4489
+ end
4490
+ end
4491
+ end
4492
+
4493
+ describe "#third_to_last" do
4494
+
4495
+ let!(:depeche_mode) do
4496
+ Band.create!(name: "Depeche Mode")
4497
+ end
4498
+
4499
+ let!(:new_order) do
4500
+ Band.create!(name: "New Order")
4501
+ end
4502
+
4503
+ let!(:rolling_stones) do
4504
+ Band.create!(name: "The Rolling Stones")
4505
+ end
4506
+
4507
+ let!(:death_cab) do
4508
+ Band.create!(name: "Death Cab For Cutie")
4509
+ end
4510
+
4511
+ let(:context) do
4512
+ described_class.new(criteria)
4513
+ end
4514
+
4515
+ context "when there's no sort" do
4516
+ let(:criteria) do
4517
+ Band.all
4518
+ end
4519
+
4520
+ it "gets the third_to_last document" do
4521
+ expect(context.third_to_last).to eq(new_order)
4522
+ end
4523
+ end
4524
+
4525
+ context "when there's a custom sort" do
4526
+ let(:criteria) do
4527
+ Band.all
4528
+ end
4529
+
4530
+ it "gets the third_to_last document" do
4531
+ expect(context.sort(name: 1).third_to_last).to eq(depeche_mode)
4532
+ end
4533
+ end
4534
+
4535
+ context "when there are no documents" do
4536
+ let(:criteria) do
4537
+ Band.where(name: "bogus")
4538
+ end
4539
+
4540
+ it "returns nil" do
4541
+ expect(context.third_to_last).to be_nil
4542
+ end
4543
+ end
4544
+ end
4545
+
4546
+ describe "#third_to_last!" do
4547
+
4548
+ let!(:depeche_mode) do
4549
+ Band.create!(name: "Depeche Mode")
4550
+ end
4551
+
4552
+ let!(:new_order) do
4553
+ Band.create!(name: "New Order")
4554
+ end
4555
+
4556
+ let!(:rolling_stones) do
4557
+ Band.create!(name: "The Rolling Stones")
4558
+ end
4559
+
4560
+ let!(:death_cab) do
4561
+ Band.create!(name: "Death Cab For Cutie")
4562
+ end
4563
+
4564
+ let(:context) do
4565
+ described_class.new(criteria)
4566
+ end
4567
+
4568
+ context "when there's no sort" do
4569
+ let(:criteria) do
4570
+ Band.all
4571
+ end
4572
+
4573
+ it "gets the third_to_last document" do
4574
+ expect(context.third_to_last!).to eq(new_order)
4575
+ end
4576
+ end
4577
+
4578
+ context "when there's a custom sort" do
4579
+ let(:criteria) do
4580
+ Band.all
4581
+ end
4582
+
4583
+ it "gets the third_to_last document" do
4584
+ expect(context.sort(name: 1).third_to_last!).to eq(depeche_mode)
4585
+ end
4586
+ end
4587
+
4588
+ context "when there are no documents" do
4589
+ let(:criteria) do
4590
+ Band.where(name: "bogus")
4591
+ end
4592
+
4593
+ it "raises an error" do
4594
+ expect do
4595
+ context.third_to_last!
4596
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Band./)
4597
+ end
4598
+ end
4599
+ end
4600
+
4601
+ describe '#load_async' do
4602
+ let!(:band) do
4603
+ Band.create!(name: "Depeche Mode")
4604
+ end
4605
+
4606
+ let(:criteria) do
4607
+ Band.where(name: "Depeche Mode")
4608
+ end
4609
+
4610
+ let(:context) do
4611
+ described_class.new(criteria)
4612
+ end
4613
+
4614
+ context 'with global thread pool async query executor' do
4615
+ config_override :async_query_executor, :global_thread_pool
4616
+
4617
+ it 'preloads the documents' do
4618
+ context.load_async
4619
+ context.documents_loader.wait
4620
+
4621
+ expect(context.view).not_to receive(:map)
4622
+ expect(context.to_a).to eq([band])
4623
+ end
4624
+
4625
+ it 're-raises exception during preload' do
4626
+ expect_any_instance_of(Mongoid::Contextual::Mongo::DocumentsLoader)
4627
+ .to receive(:execute)
4628
+ .at_least(:once)
4629
+ .and_raise(Mongo::Error::OperationFailure)
4630
+
4631
+ context.load_async
4632
+ context.documents_loader.wait
4633
+
4634
+ expect do
4635
+ context.to_a
4636
+ end.to raise_error(Mongo::Error::OperationFailure)
4637
+ end
4638
+ end
4639
+
4640
+ context 'with immediate thread pool async query executor' do
4641
+ config_override :async_query_executor, :immediate
4642
+
4643
+ it 'preloads the documents' do
4644
+ context.load_async
4645
+ context.documents_loader.wait
4646
+
4647
+ expect(context.view).not_to receive(:map)
4648
+ expect(context.to_a).to eq([band])
4649
+ end
4650
+
4651
+ it 're-raises exception during preload' do
4652
+ expect_any_instance_of(Mongoid::Contextual::Mongo::DocumentsLoader)
4653
+ .to receive(:execute)
4654
+ .at_least(:once)
4655
+ .and_raise(Mongo::Error::OperationFailure)
4656
+
4657
+ context.load_async
4658
+ context.documents_loader.wait
4659
+
4660
+ expect do
4661
+ context.to_a
4662
+ end.to raise_error(Mongo::Error::OperationFailure)
4663
+ end
4664
+ end
4665
+ end
3509
4666
  end