mongoid 7.4.0 → 8.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (315) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +3 -3
  4. data/lib/config/locales/en.yml +51 -28
  5. data/lib/mongoid/association/accessors.rb +32 -3
  6. data/lib/mongoid/association/bindable.rb +48 -0
  7. data/lib/mongoid/association/builders.rb +4 -2
  8. data/lib/mongoid/association/eager_loadable.rb +29 -7
  9. data/lib/mongoid/association/embedded/batchable.rb +48 -8
  10. data/lib/mongoid/association/embedded/embedded_in/binding.rb +24 -2
  11. data/lib/mongoid/association/embedded/embedded_in.rb +2 -1
  12. data/lib/mongoid/association/embedded/embeds_many/binding.rb +1 -0
  13. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +1 -1
  14. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +40 -18
  15. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +18 -4
  16. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +21 -2
  17. data/lib/mongoid/association/macros.rb +22 -1
  18. data/lib/mongoid/association/many.rb +5 -0
  19. data/lib/mongoid/association/nested/many.rb +2 -1
  20. data/lib/mongoid/association/proxy.rb +12 -0
  21. data/lib/mongoid/association/referenced/auto_save.rb +3 -2
  22. data/lib/mongoid/association/referenced/belongs_to/binding.rb +1 -0
  23. data/lib/mongoid/association/referenced/belongs_to/buildable.rb +1 -1
  24. data/lib/mongoid/association/referenced/belongs_to.rb +1 -1
  25. data/lib/mongoid/association/referenced/counter_cache.rb +8 -8
  26. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +64 -11
  27. data/lib/mongoid/association/referenced/has_and_belongs_to_many.rb +4 -1
  28. data/lib/mongoid/association/referenced/has_many/enumerable.rb +10 -14
  29. data/lib/mongoid/association/referenced/has_many/proxy.rb +12 -9
  30. data/lib/mongoid/association/referenced/has_one/buildable.rb +1 -1
  31. data/lib/mongoid/association/referenced/has_one/proxy.rb +8 -11
  32. data/lib/mongoid/association/referenced/syncable.rb +2 -2
  33. data/lib/mongoid/association/relatable.rb +38 -4
  34. data/lib/mongoid/atomic/paths/embedded/many.rb +19 -0
  35. data/lib/mongoid/attributes/processing.rb +9 -2
  36. data/lib/mongoid/attributes.rb +30 -27
  37. data/lib/mongoid/changeable.rb +37 -2
  38. data/lib/mongoid/clients/options.rb +4 -0
  39. data/lib/mongoid/clients/sessions.rb +2 -14
  40. data/lib/mongoid/config/environment.rb +20 -4
  41. data/lib/mongoid/config.rb +25 -10
  42. data/lib/mongoid/contextual/aggregable/memory.rb +23 -15
  43. data/lib/mongoid/contextual/aggregable/mongo.rb +1 -1
  44. data/lib/mongoid/contextual/map_reduce.rb +2 -2
  45. data/lib/mongoid/contextual/memory.rb +176 -17
  46. data/lib/mongoid/contextual/mongo.rb +226 -206
  47. data/lib/mongoid/contextual/none.rb +66 -4
  48. data/lib/mongoid/copyable.rb +32 -8
  49. data/lib/mongoid/criteria/includable.rb +24 -20
  50. data/lib/mongoid/criteria/marshalable.rb +10 -2
  51. data/lib/mongoid/criteria/queryable/extensions/array.rb +2 -13
  52. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +25 -4
  53. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +1 -1
  54. data/lib/mongoid/criteria/queryable/extensions/date.rb +6 -1
  55. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +6 -1
  56. data/lib/mongoid/criteria/queryable/extensions/hash.rb +0 -14
  57. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
  58. data/lib/mongoid/criteria/queryable/extensions/object.rb +2 -1
  59. data/lib/mongoid/criteria/queryable/extensions/range.rb +13 -5
  60. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +1 -1
  61. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +3 -1
  62. data/lib/mongoid/criteria/queryable/extensions/time.rb +6 -1
  63. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +6 -1
  64. data/lib/mongoid/criteria/queryable/mergeable.rb +21 -0
  65. data/lib/mongoid/criteria/queryable/optional.rb +3 -9
  66. data/lib/mongoid/criteria/queryable/options.rb +1 -1
  67. data/lib/mongoid/criteria/queryable/selectable.rb +28 -34
  68. data/lib/mongoid/criteria/queryable/selector.rb +89 -4
  69. data/lib/mongoid/criteria/queryable/smash.rb +39 -6
  70. data/lib/mongoid/criteria/queryable.rb +11 -6
  71. data/lib/mongoid/criteria.rb +1 -26
  72. data/lib/mongoid/deprecable.rb +36 -0
  73. data/lib/mongoid/deprecation.rb +25 -0
  74. data/lib/mongoid/document.rb +96 -32
  75. data/lib/mongoid/errors/document_not_found.rb +29 -8
  76. data/lib/mongoid/errors/invalid_dot_dollar_assignment.rb +23 -0
  77. data/lib/mongoid/errors/invalid_field.rb +5 -1
  78. data/lib/mongoid/errors/invalid_field_type.rb +26 -0
  79. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +1 -1
  80. data/lib/mongoid/errors.rb +2 -2
  81. data/lib/mongoid/extensions/array.rb +8 -6
  82. data/lib/mongoid/extensions/big_decimal.rb +29 -10
  83. data/lib/mongoid/extensions/binary.rb +42 -0
  84. data/lib/mongoid/extensions/boolean.rb +8 -2
  85. data/lib/mongoid/extensions/date.rb +26 -20
  86. data/lib/mongoid/extensions/date_time.rb +1 -1
  87. data/lib/mongoid/extensions/float.rb +4 -5
  88. data/lib/mongoid/extensions/hash.rb +12 -5
  89. data/lib/mongoid/extensions/integer.rb +4 -5
  90. data/lib/mongoid/extensions/object.rb +2 -0
  91. data/lib/mongoid/extensions/range.rb +41 -10
  92. data/lib/mongoid/extensions/regexp.rb +11 -4
  93. data/lib/mongoid/extensions/set.rb +11 -4
  94. data/lib/mongoid/extensions/string.rb +2 -13
  95. data/lib/mongoid/extensions/symbol.rb +3 -14
  96. data/lib/mongoid/extensions/time.rb +27 -16
  97. data/lib/mongoid/extensions/time_with_zone.rb +1 -2
  98. data/lib/mongoid/extensions.rb +1 -0
  99. data/lib/mongoid/factory.rb +42 -7
  100. data/lib/mongoid/fields/foreign_key.rb +7 -0
  101. data/lib/mongoid/fields/validators/macro.rb +3 -9
  102. data/lib/mongoid/fields.rb +194 -28
  103. data/lib/mongoid/findable.rb +27 -7
  104. data/lib/mongoid/indexable/specification.rb +1 -1
  105. data/lib/mongoid/indexable/validators/options.rb +4 -1
  106. data/lib/mongoid/interceptable.rb +69 -9
  107. data/lib/mongoid/persistable/creatable.rb +14 -5
  108. data/lib/mongoid/persistable/updatable.rb +12 -5
  109. data/lib/mongoid/persistable/upsertable.rb +1 -1
  110. data/lib/mongoid/persistence_context.rb +19 -2
  111. data/lib/mongoid/query_cache.rb +6 -258
  112. data/lib/mongoid/railties/controller_runtime.rb +1 -1
  113. data/lib/mongoid/reloadable.rb +7 -3
  114. data/lib/mongoid/selectable.rb +1 -2
  115. data/lib/mongoid/stateful.rb +27 -1
  116. data/lib/mongoid/timestamps/created.rb +1 -1
  117. data/lib/mongoid/timestamps/updated.rb +1 -1
  118. data/lib/mongoid/touchable.rb +2 -3
  119. data/lib/mongoid/traversable.rb +5 -1
  120. data/lib/mongoid/validatable/uniqueness.rb +2 -1
  121. data/lib/mongoid/version.rb +1 -1
  122. data/lib/mongoid/warnings.rb +28 -0
  123. data/lib/mongoid.rb +2 -0
  124. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +11 -5
  125. data/spec/config/mongoid.yml +16 -0
  126. data/spec/config/mongoid_with_schema_map_uuid.yml +27 -0
  127. data/spec/integration/app_spec.rb +28 -26
  128. data/spec/integration/associations/belongs_to_spec.rb +18 -0
  129. data/spec/integration/associations/embedded_dirty_spec.rb +28 -0
  130. data/spec/integration/associations/embedded_spec.rb +15 -0
  131. data/spec/integration/associations/embeds_many_spec.rb +15 -2
  132. data/spec/integration/associations/embeds_one_spec.rb +18 -0
  133. data/spec/integration/associations/foreign_key_spec.rb +9 -0
  134. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +21 -0
  135. data/spec/integration/associations/has_one_spec.rb +97 -1
  136. data/spec/integration/associations/scope_option_spec.rb +1 -1
  137. data/spec/integration/callbacks_models.rb +95 -1
  138. data/spec/integration/callbacks_spec.rb +226 -4
  139. data/spec/integration/criteria/range_spec.rb +95 -1
  140. data/spec/integration/discriminator_key_spec.rb +115 -76
  141. data/spec/integration/dots_and_dollars_spec.rb +277 -0
  142. data/spec/integration/matcher_examples_spec.rb +20 -13
  143. data/spec/integration/matcher_operator_data/type_decimal.yml +3 -2
  144. data/spec/integration/matcher_operator_spec.rb +3 -5
  145. data/spec/integration/persistence/range_field_spec.rb +350 -0
  146. data/spec/lite_spec_helper.rb +1 -1
  147. data/spec/mongoid/association/counter_cache_spec.rb +1 -1
  148. data/spec/mongoid/association/depending_spec.rb +9 -9
  149. data/spec/mongoid/association/eager_spec.rb +2 -1
  150. data/spec/mongoid/association/embedded/embedded_in/binding_spec.rb +2 -1
  151. data/spec/mongoid/association/embedded/embedded_in/buildable_spec.rb +54 -0
  152. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +69 -9
  153. data/spec/mongoid/association/embedded/embeds_many/buildable_spec.rb +112 -0
  154. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +219 -8
  155. data/spec/mongoid/association/embedded/embeds_many_models.rb +157 -0
  156. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +12 -0
  157. data/spec/mongoid/association/embedded/embeds_many_spec.rb +68 -0
  158. data/spec/mongoid/association/embedded/embeds_one/buildable_spec.rb +25 -0
  159. data/spec/mongoid/association/embedded/embeds_one_models.rb +19 -0
  160. data/spec/mongoid/association/embedded/embeds_one_spec.rb +28 -0
  161. data/spec/mongoid/association/referenced/belongs_to/binding_spec.rb +2 -1
  162. data/spec/mongoid/association/referenced/belongs_to/buildable_spec.rb +54 -0
  163. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +15 -0
  164. data/spec/mongoid/association/referenced/belongs_to_models.rb +11 -0
  165. data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -2
  166. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +67 -4
  167. data/spec/mongoid/association/referenced/has_and_belongs_to_many_models.rb +25 -0
  168. data/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb +35 -2
  169. data/spec/mongoid/association/referenced/has_many/buildable_spec.rb +109 -0
  170. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +8 -8
  171. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +82 -13
  172. data/spec/mongoid/association/referenced/has_many_models.rb +3 -1
  173. data/spec/mongoid/association/referenced/has_many_spec.rb +25 -0
  174. data/spec/mongoid/association/referenced/has_one/buildable_spec.rb +2 -2
  175. data/spec/mongoid/association/referenced/has_one/proxy_spec.rb +107 -1
  176. data/spec/mongoid/association/referenced/has_one_models.rb +16 -0
  177. data/spec/mongoid/association/syncable_spec.rb +14 -0
  178. data/spec/mongoid/atomic/paths_spec.rb +0 -14
  179. data/spec/mongoid/atomic_spec.rb +22 -0
  180. data/spec/mongoid/attributes/nested_spec.rb +80 -11
  181. data/spec/mongoid/attributes/nested_spec_models.rb +48 -0
  182. data/spec/mongoid/attributes/projector_spec.rb +1 -5
  183. data/spec/mongoid/attributes_spec.rb +524 -27
  184. data/spec/mongoid/changeable_spec.rb +130 -13
  185. data/spec/mongoid/clients/factory_spec.rb +34 -42
  186. data/spec/mongoid/clients/options_spec.rb +1 -0
  187. data/spec/mongoid/clients/sessions_spec.rb +0 -38
  188. data/spec/mongoid/clients_spec.rb +32 -2
  189. data/spec/mongoid/config/environment_spec.rb +39 -1
  190. data/spec/mongoid/config_spec.rb +104 -13
  191. data/spec/mongoid/contextual/aggregable/memory_spec.rb +396 -158
  192. data/spec/mongoid/contextual/aggregable/memory_table.yml +88 -0
  193. data/spec/mongoid/contextual/aggregable/memory_table_spec.rb +62 -0
  194. data/spec/mongoid/contextual/map_reduce_spec.rb +2 -16
  195. data/spec/mongoid/contextual/memory_spec.rb +1337 -69
  196. data/spec/mongoid/contextual/mongo_spec.rb +1105 -172
  197. data/spec/mongoid/contextual/none_spec.rb +38 -0
  198. data/spec/mongoid/copyable_spec.rb +451 -1
  199. data/spec/mongoid/criteria/findable_spec.rb +86 -210
  200. data/spec/mongoid/criteria/includable_spec.rb +1492 -0
  201. data/spec/mongoid/criteria/includable_spec_models.rb +54 -0
  202. data/spec/mongoid/criteria/marshalable_spec.rb +18 -1
  203. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +7 -19
  204. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +134 -26
  205. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +11 -0
  206. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +11 -0
  207. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +0 -15
  208. data/spec/mongoid/criteria/queryable/extensions/numeric_spec.rb +73 -7
  209. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +11 -0
  210. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +11 -0
  211. data/spec/mongoid/criteria/queryable/optional_spec.rb +0 -484
  212. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +50 -0
  213. data/spec/mongoid/criteria/queryable/selectable_spec.rb +289 -124
  214. data/spec/mongoid/criteria/queryable/selector_spec.rb +14 -2
  215. data/spec/mongoid/criteria_spec.rb +474 -1198
  216. data/spec/mongoid/document_fields_spec.rb +173 -24
  217. data/spec/mongoid/document_spec.rb +32 -41
  218. data/spec/mongoid/errors/document_not_found_spec.rb +76 -0
  219. data/spec/mongoid/errors/invalid_field_spec.rb +1 -1
  220. data/spec/mongoid/errors/invalid_field_type_spec.rb +55 -0
  221. data/spec/mongoid/errors/mongoid_error_spec.rb +3 -1
  222. data/spec/mongoid/errors/no_environment_spec.rb +3 -3
  223. data/spec/mongoid/errors/too_many_nested_attribute_records_spec.rb +1 -1
  224. data/spec/mongoid/extensions/array_spec.rb +16 -2
  225. data/spec/mongoid/extensions/big_decimal_spec.rb +697 -212
  226. data/spec/mongoid/extensions/binary_spec.rb +44 -9
  227. data/spec/mongoid/extensions/boolean_spec.rb +68 -82
  228. data/spec/mongoid/extensions/date_class_mongoize_spec.rb +7 -3
  229. data/spec/mongoid/extensions/date_spec.rb +71 -1
  230. data/spec/mongoid/extensions/date_time_spec.rb +15 -9
  231. data/spec/mongoid/extensions/float_spec.rb +48 -76
  232. data/spec/mongoid/extensions/hash_spec.rb +30 -0
  233. data/spec/mongoid/extensions/integer_spec.rb +45 -66
  234. data/spec/mongoid/extensions/range_spec.rb +255 -54
  235. data/spec/mongoid/extensions/regexp_spec.rb +58 -33
  236. data/spec/mongoid/extensions/set_spec.rb +106 -0
  237. data/spec/mongoid/extensions/string_spec.rb +53 -25
  238. data/spec/mongoid/extensions/symbol_spec.rb +18 -25
  239. data/spec/mongoid/extensions/time_spec.rb +634 -66
  240. data/spec/mongoid/extensions/time_with_zone_spec.rb +17 -31
  241. data/spec/mongoid/factory_spec.rb +61 -1
  242. data/spec/mongoid/fields_spec.rb +321 -50
  243. data/spec/mongoid/findable_spec.rb +80 -15
  244. data/spec/mongoid/indexable/specification_spec.rb +2 -2
  245. data/spec/mongoid/indexable_spec.rb +16 -19
  246. data/spec/mongoid/interceptable_spec.rb +584 -5
  247. data/spec/mongoid/interceptable_spec_models.rb +235 -4
  248. data/spec/mongoid/matcher/extract_attribute_spec.rb +1 -5
  249. data/spec/mongoid/mongoizable_spec.rb +285 -0
  250. data/spec/mongoid/persistable/creatable_spec.rb +2 -2
  251. data/spec/mongoid/persistable/deletable_spec.rb +2 -2
  252. data/spec/mongoid/persistable/destroyable_spec.rb +2 -2
  253. data/spec/mongoid/persistable/upsertable_spec.rb +14 -0
  254. data/spec/mongoid/persistence_context_spec.rb +50 -1
  255. data/spec/mongoid/query_cache_middleware_spec.rb +0 -18
  256. data/spec/mongoid/query_cache_spec.rb +0 -154
  257. data/spec/mongoid/reloadable_spec.rb +35 -2
  258. data/spec/mongoid/scopable_spec.rb +21 -1
  259. data/spec/mongoid/shardable_spec.rb +14 -0
  260. data/spec/mongoid/stateful_spec.rb +28 -0
  261. data/spec/mongoid/timestamps_spec.rb +390 -0
  262. data/spec/mongoid/timestamps_spec_models.rb +67 -0
  263. data/spec/mongoid/touchable_spec.rb +116 -0
  264. data/spec/mongoid/touchable_spec_models.rb +12 -8
  265. data/spec/mongoid/traversable_spec.rb +4 -11
  266. data/spec/mongoid/validatable/presence_spec.rb +1 -1
  267. data/spec/mongoid/validatable/uniqueness_spec.rb +60 -31
  268. data/spec/mongoid/warnings_spec.rb +35 -0
  269. data/spec/rails/controller_extension/controller_runtime_spec.rb +2 -2
  270. data/spec/rails/mongoid_spec.rb +4 -16
  271. data/spec/shared/lib/mrss/constraints.rb +8 -16
  272. data/spec/shared/lib/mrss/docker_runner.rb +23 -3
  273. data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
  274. data/spec/shared/lib/mrss/lite_constraints.rb +32 -1
  275. data/spec/shared/share/Dockerfile.erb +34 -48
  276. data/spec/shared/shlib/config.sh +27 -0
  277. data/spec/shared/shlib/server.sh +32 -19
  278. data/spec/shared/shlib/set_env.sh +37 -0
  279. data/spec/support/constraints.rb +24 -0
  280. data/spec/support/macros.rb +39 -0
  281. data/spec/support/models/augmentation.rb +12 -0
  282. data/spec/support/models/band.rb +3 -0
  283. data/spec/support/models/catalog.rb +24 -0
  284. data/spec/support/models/circus.rb +3 -0
  285. data/spec/support/models/code.rb +2 -0
  286. data/spec/support/models/fanatic.rb +8 -0
  287. data/spec/support/models/implant.rb +9 -0
  288. data/spec/support/models/label.rb +2 -0
  289. data/spec/support/models/membership.rb +1 -0
  290. data/spec/support/models/passport.rb +9 -0
  291. data/spec/support/models/person.rb +1 -0
  292. data/spec/support/models/player.rb +2 -0
  293. data/spec/support/models/powerup.rb +12 -0
  294. data/spec/support/models/registry.rb +1 -0
  295. data/spec/support/models/school.rb +14 -0
  296. data/spec/support/models/shield.rb +18 -0
  297. data/spec/support/models/student.rb +14 -0
  298. data/spec/support/models/weapon.rb +12 -0
  299. data/spec/support/schema_maps/schema_map_aws.json +17 -0
  300. data/spec/support/schema_maps/schema_map_aws_key_alt_names.json +12 -0
  301. data/spec/support/schema_maps/schema_map_azure.json +17 -0
  302. data/spec/support/schema_maps/schema_map_azure_key_alt_names.json +12 -0
  303. data/spec/support/schema_maps/schema_map_gcp.json +17 -0
  304. data/spec/support/schema_maps/schema_map_gcp_key_alt_names.json +12 -0
  305. data/spec/support/schema_maps/schema_map_kmip.json +17 -0
  306. data/spec/support/schema_maps/schema_map_kmip_key_alt_names.json +12 -0
  307. data/spec/support/schema_maps/schema_map_local.json +18 -0
  308. data/spec/support/schema_maps/schema_map_local_key_alt_names.json +12 -0
  309. data/spec/support/spec_config.rb +4 -0
  310. data.tar.gz.sig +0 -0
  311. metadata +76 -13
  312. metadata.gz.sig +0 -0
  313. data/lib/mongoid/errors/eager_load.rb +0 -23
  314. data/lib/mongoid/errors/invalid_value.rb +0 -17
  315. 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
 
@@ -782,9 +767,11 @@ describe Mongoid::Contextual::Mongo do
782
767
 
783
768
  context "when legacy_pluck_distinct is set" do
784
769
  config_override :legacy_pluck_distinct, true
770
+ config_override :map_big_decimal_to_decimal128, true
771
+ max_bson_version '4.99.99'
785
772
 
786
773
  it "returns the distinct matching fields" do
787
- expect(context.distinct("label.sales")).to eq([ "1E2" ])
774
+ expect(context.distinct("label.sales")).to eq([ BSON::Decimal128.new('1E+2') ])
788
775
  end
789
776
  end
790
777
 
@@ -797,6 +784,435 @@ describe Mongoid::Contextual::Mongo do
797
784
  end
798
785
  end
799
786
 
787
+ describe "#tally" do
788
+ let(:fans1) { [ Fanatic.new(age:1), Fanatic.new(age:2) ] }
789
+ let(:fans2) { [ Fanatic.new(age:1), Fanatic.new(age:2) ] }
790
+ let(:fans3) { [ Fanatic.new(age:1), Fanatic.new(age:3) ] }
791
+
792
+ let(:genres1) { [ { x: 1, y: { z: 1 } }, { x: 2, y: { z: 2 } }, { y: 3 } ]}
793
+ let(:genres2) { [ { x: 1, y: { z: 1 } }, { x: 2, y: { z: 2 } }, { y: 4 } ]}
794
+ let(:genres3) { [ { x: 1, y: { z: 1 } }, { x: 3, y: { z: 3 } }, { y: 5 } ]}
795
+
796
+ let(:label1) { Label.new(name: "Atlantic") }
797
+ let(:label2) { Label.new(name: "Atlantic") }
798
+ let(:label3) { Label.new(name: "Columbia") }
799
+
800
+ before do
801
+ Band.create!(origin: "tally", name: "Depeche Mode", years: 30, sales: "1E2", label: label1, genres: genres1)
802
+ Band.create!(origin: "tally", name: "New Order", years: 30, sales: "2E3", label: label2, genres: genres2)
803
+ Band.create!(origin: "tally", name: "10,000 Maniacs", years: 30, sales: "1E2", label: label3, genres: genres3)
804
+ Band.create!(origin: "tally2", fanatics: fans1, genres: [1, 2])
805
+ Band.create!(origin: "tally2", fanatics: fans2, genres: [1, 2])
806
+ Band.create!(origin: "tally2", fanatics: fans3, genres: [1, 3])
807
+ end
808
+
809
+ let(:criteria) { Band.where(origin: "tally") }
810
+
811
+ context "when tallying a string" do
812
+ let(:tally) do
813
+ criteria.tally(:name)
814
+ end
815
+
816
+ it "returns the correct hash" do
817
+ expect(tally).to eq("Depeche Mode" => 1, "New Order" => 1, "10,000 Maniacs" => 1)
818
+ end
819
+ end
820
+
821
+ context "using an aliased field" do
822
+ let(:tally) do
823
+ criteria.tally(:years)
824
+ end
825
+
826
+ it "returns the correct hash" do
827
+ expect(tally).to eq(30 => 3)
828
+ end
829
+ end
830
+
831
+ context "when tallying a demongoizable field" do
832
+ let(:tally) do
833
+ criteria.tally(:sales)
834
+ end
835
+
836
+ it "returns the correct hash" do
837
+ expect(tally).to eq(BigDecimal("1E2") => 2, BigDecimal("2E3") => 1)
838
+ end
839
+ end
840
+
841
+ context "when tallying a localized field" do
842
+ before do
843
+ I18n.locale = :en
844
+ d1 = Dictionary.create!(description: 'en1')
845
+ d2 = Dictionary.create!(description: 'en1')
846
+ d3 = Dictionary.create!(description: 'en1')
847
+ d4 = Dictionary.create!(description: 'en2')
848
+ I18n.locale = :de
849
+ d1.description = 'de1'
850
+ d2.description = 'de1'
851
+ d3.description = 'de2'
852
+ d4.description = 'de3'
853
+ d1.save!
854
+ d2.save!
855
+ d3.save!
856
+ d4.save!
857
+
858
+ I18n.locale = :en
859
+ end
860
+
861
+ context "when getting the demongoized field" do
862
+ let(:tallied) do
863
+ Dictionary.tally(:description)
864
+ end
865
+
866
+ it "returns the translation for the current locale" do
867
+ expect(tallied).to eq("en1" => 3, "en2" => 1)
868
+ end
869
+ end
870
+
871
+ context "when getting a specific locale" do
872
+ let(:tallied) do
873
+ Dictionary.tally("description.de")
874
+ end
875
+
876
+ it "returns the translation for the the specific locale" do
877
+ expect(tallied).to eq("de1" => 2, "de2" => 1, "de3" => 1)
878
+ end
879
+ end
880
+
881
+ context "when getting the full hash" do
882
+ let(:tallied) do
883
+ Dictionary.tally("description_translations")
884
+ end
885
+
886
+ it "returns the correct hash" do
887
+ expect(tallied).to eq(
888
+ {"de" => "de1", "en" => "en1" } => 2,
889
+ {"de" => "de2", "en" => "en1" } => 1,
890
+ {"de" => "de3", "en" => "en2" } => 1
891
+ )
892
+ end
893
+ end
894
+ end
895
+
896
+ context "when tallying an embedded localized field" do
897
+
898
+ before do
899
+ I18n.locale = :en
900
+ address1a = Address.new(name: "en1")
901
+ address1b = Address.new(name: "en2")
902
+ address2a = Address.new(name: "en1")
903
+ address2b = Address.new(name: "en3")
904
+ I18n.locale = :de
905
+ address1a.name = "de1"
906
+ address1b.name = "de2"
907
+ address2a.name = "de1"
908
+ address2b.name = "de3"
909
+ Person.create!(addresses: [ address1a, address1b ])
910
+ Person.create!(addresses: [ address2a, address2b ])
911
+
912
+ I18n.locale = :en
913
+ end
914
+
915
+ context "when getting the demongoized field" do
916
+ let(:tallied) do
917
+ Person.tally("addresses.name")
918
+ end
919
+
920
+ it "returns the translation for the current locale" do
921
+ expect(tallied).to eq(
922
+ [ "en1", "en2" ] => 1,
923
+ [ "en1", "en3" ] => 1,
924
+ )
925
+ end
926
+ end
927
+
928
+ context "when getting a specific locale" do
929
+ let(:tallied) do
930
+ Person.tally("addresses.name.de")
931
+ end
932
+
933
+ it "returns the translation for the the specific locale" do
934
+ expect(tallied).to eq(
935
+ [ "de1", "de2" ] => 1,
936
+ [ "de1", "de3" ] => 1,
937
+ )
938
+ end
939
+ end
940
+
941
+ context "when getting the full hash" do
942
+ let(:tallied) do
943
+ Person.tally("addresses.name_translations")
944
+ end
945
+
946
+ it "returns the correct hash" do
947
+ expect(tallied).to eq(
948
+ [{ "de" => "de1", "en" => "en1" }, { "de" => "de2", "en" => "en2" }] => 1,
949
+ [{ "de" => "de1", "en" => "en1" }, { "de" => "de3", "en" => "en3" }] => 1,
950
+ )
951
+ end
952
+ end
953
+
954
+ end
955
+
956
+ context "when tallying an embedded field" do
957
+ let(:tally) do
958
+ criteria.tally("label.name")
959
+ end
960
+
961
+ it "returns the correct hash" do
962
+ expect(tally).to eq("Atlantic" => 2, "Columbia" => 1)
963
+ end
964
+ end
965
+
966
+ context "when tallying an element in an embeds_many field" do
967
+ let(:criteria) { Band.where(origin: "tally2") }
968
+
969
+ let(:tally) do
970
+ criteria.tally("fanatics.age")
971
+ end
972
+
973
+ it "returns the correct hash" do
974
+ expect(tally).to eq(
975
+ [1, 2] => 2,
976
+ [1, 3] => 1
977
+ )
978
+ end
979
+ end
980
+
981
+ context "when tallying an embeds_many field" do
982
+ let(:criteria) { Band.where(origin: "tally2") }
983
+
984
+ let(:tally) do
985
+ criteria.tally("fanatics")
986
+ end
987
+
988
+ it "returns the correct hash" do
989
+ expect(tally).to eq(
990
+ fans1.map(&:attributes) => 1,
991
+ fans2.map(&:attributes) => 1,
992
+ fans3.map(&:attributes) => 1,
993
+ )
994
+ end
995
+ end
996
+
997
+ context "when tallying a field of type array" do
998
+ let(:criteria) { Band.where(origin: "tally2") }
999
+
1000
+ let(:tally) do
1001
+ criteria.tally("genres")
1002
+ end
1003
+
1004
+ it "returns the correct hash" do
1005
+ expect(tally).to eq(
1006
+ [1, 2] => 2,
1007
+ [1, 3] => 1
1008
+ )
1009
+ end
1010
+ end
1011
+
1012
+ context "when tallying an element from an array of hashes" do
1013
+ let(:criteria) { Band.where(origin: "tally") }
1014
+
1015
+ let(:tally) do
1016
+ criteria.tally("genres.x")
1017
+ end
1018
+
1019
+ it "returns the correct hash without the nil keys" do
1020
+ expect(tally).to eq(
1021
+ [1, 2] => 2,
1022
+ [1, 3] => 1
1023
+ )
1024
+ end
1025
+ end
1026
+
1027
+ context "when tallying an element from an array of hashes; with duplicate" do
1028
+
1029
+ before do
1030
+ Band.create!(origin: "tally", genres: [ { x: 1 }, {x: 1} ] )
1031
+ end
1032
+
1033
+ let(:criteria) { Band.where(origin: "tally") }
1034
+
1035
+ let(:tally) do
1036
+ criteria.tally("genres.x")
1037
+ end
1038
+
1039
+ it "returns the correct hash without the nil keys" do
1040
+ expect(tally).to eq(
1041
+ [1, 2] => 2,
1042
+ [1, 3] => 1,
1043
+ [1, 1] => 1,
1044
+ )
1045
+ end
1046
+ end
1047
+
1048
+ context "when tallying an aliased field of type array" do
1049
+
1050
+ before do
1051
+ Person.create!(array: [ 1, 2 ])
1052
+ Person.create!(array: [ 1, 3 ])
1053
+ end
1054
+
1055
+ let(:tally) do
1056
+ Person.tally("array")
1057
+ end
1058
+
1059
+ it "returns the correct hash" do
1060
+ expect(tally).to eq(
1061
+ [1, 2] => 1,
1062
+ [1, 3] => 1
1063
+ )
1064
+ end
1065
+ end
1066
+
1067
+ context "when going multiple levels deep in arrays" do
1068
+ let(:criteria) { Band.where(origin: "tally") }
1069
+
1070
+ let(:tally) do
1071
+ criteria.tally("genres.y.z")
1072
+ end
1073
+
1074
+ it "returns the correct hash" do
1075
+ expect(tally).to eq(
1076
+ [1, 2] => 2,
1077
+ [1, 3] => 1
1078
+ )
1079
+ end
1080
+ end
1081
+
1082
+ context "when going multiple levels deep in an array" do
1083
+ let(:criteria) { Band.where(origin: "tally") }
1084
+
1085
+ let(:tally) do
1086
+ criteria.tally("genres.y.z")
1087
+ end
1088
+
1089
+ it "returns the correct hash" do
1090
+ expect(tally).to eq(
1091
+ [1, 2] => 2,
1092
+ [1, 3] => 1
1093
+ )
1094
+ end
1095
+ end
1096
+
1097
+ context "when tallying deeply nested arrays/embedded associations" do
1098
+
1099
+ before do
1100
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
1101
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
1102
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))) ])
1103
+ end
1104
+
1105
+ let(:tally) do
1106
+ Person.tally("addresses.code.deepest.array.y.z")
1107
+ end
1108
+
1109
+ it "returns the correct hash" do
1110
+ expect(tally).to eq(
1111
+ [ [ 1, 2 ] ] => 2,
1112
+ [ [ 1, 3 ] ] => 1
1113
+ )
1114
+ end
1115
+ end
1116
+
1117
+ context "when tallying deeply nested arrays/embedded associations" do
1118
+
1119
+ before do
1120
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))),
1121
+ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
1122
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))),
1123
+ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ])
1124
+ Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))),
1125
+ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))) ])
1126
+ end
1127
+
1128
+ let(:tally) do
1129
+ Person.tally("addresses.code.deepest.array.y.z")
1130
+ end
1131
+
1132
+ it "returns the correct hash" do
1133
+ expect(tally).to eq(
1134
+ [ [ 1, 2 ], [ 1, 2 ] ] => 2,
1135
+ [ [ 1, 3 ], [ 1, 3 ] ] => 1
1136
+ )
1137
+ end
1138
+ end
1139
+
1140
+ context "when some keys are missing" do
1141
+ before do
1142
+ 3.times { Band.create!(origin: "tally") }
1143
+ end
1144
+
1145
+ let(:tally) do
1146
+ criteria.tally(:name)
1147
+ end
1148
+
1149
+ it "returns the correct hash" do
1150
+ expect(tally).to eq(
1151
+ "Depeche Mode" => 1,
1152
+ "New Order" => 1,
1153
+ "10,000 Maniacs" => 1,
1154
+ nil => 3
1155
+ )
1156
+ end
1157
+ end
1158
+
1159
+ context "when the first element is an embeds_one" do
1160
+ before do
1161
+ Person.create!(name: Name.new(translations: [ Translation.new(language: 1), Translation.new(language: 2) ]))
1162
+ Person.create!(name: Name.new(translations: [ Translation.new(language: 1), Translation.new(language: 2) ]))
1163
+ Person.create!(name: Name.new(translations: [ Translation.new(language: 1), Translation.new(language: 3) ]))
1164
+ end
1165
+
1166
+ let(:tally) do
1167
+ Person.tally("name.translations.language")
1168
+ end
1169
+
1170
+ it "returns the correct hash" do
1171
+ expect(tally).to eq(
1172
+ [1, 2] => 2,
1173
+ [1, 3] => 1
1174
+ )
1175
+ end
1176
+ end
1177
+
1178
+ context "when tallying demongoizable values from typeless fields" do
1179
+
1180
+ let!(:person1) { Person.create!(ssn: /hello/) }
1181
+ let!(:person2) { Person.create!(ssn: BSON::Decimal128.new("1")) }
1182
+ let(:tally) { Person.tally("ssn") }
1183
+
1184
+ context "< BSON 5" do
1185
+ max_bson_version '4.99.99'
1186
+
1187
+ it "stores the correct types in the database" do
1188
+ Person.find(person1.id).attributes["ssn"].should be_a BSON::Regexp::Raw
1189
+ Person.find(person2.id).attributes["ssn"].should be_a BSON::Decimal128
1190
+ end
1191
+
1192
+ it "tallies the correct type" do
1193
+ tally.keys.map(&:class).sort do |a,b|
1194
+ a.to_s <=> b.to_s
1195
+ end.should == [BSON::Decimal128, BSON::Regexp::Raw]
1196
+ end
1197
+ end
1198
+
1199
+ context ">= BSON 5" do
1200
+ min_bson_version "5.0"
1201
+
1202
+ it "stores the correct types in the database" do
1203
+ Person.find(person1.id).ssn.should be_a BSON::Regexp::Raw
1204
+ Person.find(person2.id).ssn.should be_a BigDeimal
1205
+ end
1206
+
1207
+ it "tallies the correct type" do
1208
+ tally.keys.map(&:class).sort do |a,b|
1209
+ a.to_s <=> b.to_s
1210
+ end.should == [BigDecimal, BSON::Regexp::Raw]
1211
+ end
1212
+ end
1213
+ end
1214
+ end
1215
+
800
1216
  describe "#each" do
801
1217
 
802
1218
  before do
@@ -1005,51 +1421,15 @@ describe Mongoid::Contextual::Mongo do
1005
1421
  described_class.new(criteria)
1006
1422
  end
1007
1423
 
1008
- context "when exists? already called" do
1424
+ context "when exists? already called and query cache is enabled" do
1425
+ query_cache_enabled
1009
1426
 
1010
1427
  before do
1011
1428
  context.exists?
1012
1429
  end
1013
1430
 
1014
- it "hits the database again" do
1015
- expect(context).to receive(:view).once.and_call_original
1016
- expect(context).to be_exists
1017
- end
1018
- end
1019
- end
1020
-
1021
- context "when caching is enabled" do
1022
-
1023
- let(:criteria) do
1024
- Band.where(name: "Depeche Mode").cache
1025
- end
1026
-
1027
- let(:context) do
1028
- described_class.new(criteria)
1029
- end
1030
-
1031
- context "when the cache is loaded" do
1032
-
1033
- before do
1034
- context.to_a
1035
- end
1036
-
1037
- it "does not hit the database" do
1038
- expect(context).to receive(:view).never
1039
- expect(context).to be_exists
1040
- end
1041
- end
1042
-
1043
- context "when the cache is not loaded" do
1044
-
1045
- context "when a count has been executed" do
1046
-
1047
- before do
1048
- context.count
1049
- end
1050
-
1051
- it "does not hit the database" do
1052
- expect(context).to receive(:view).never
1431
+ it "does not hit the database again" do
1432
+ expect_no_queries do
1053
1433
  expect(context).to be_exists
1054
1434
  end
1055
1435
  end
@@ -1408,7 +1788,7 @@ describe Mongoid::Contextual::Mongo do
1408
1788
  it "deletes the document from the database" do
1409
1789
  expect {
1410
1790
  depeche.reload
1411
- }.to raise_error(Mongoid::Errors::DocumentNotFound)
1791
+ }.to raise_error(Mongoid::Errors::DocumentNotFound, /Document\(s\) not found for class Band with id\(s\)/)
1412
1792
  end
1413
1793
 
1414
1794
  context 'when a collation is specified on the criteria' do
@@ -1433,7 +1813,7 @@ describe Mongoid::Contextual::Mongo do
1433
1813
  it "deletes the document from the database" do
1434
1814
  expect {
1435
1815
  depeche.reload
1436
- }.to raise_error(Mongoid::Errors::DocumentNotFound)
1816
+ }.to raise_error(Mongoid::Errors::DocumentNotFound, /Document\(s\) not found for class Band with id\(s\)/)
1437
1817
  end
1438
1818
  end
1439
1819
  end
@@ -1470,6 +1850,10 @@ describe Mongoid::Contextual::Mongo do
1470
1850
  Band.create!(name: "New Order")
1471
1851
  end
1472
1852
 
1853
+ let!(:rolling_stones) do
1854
+ Band.create!(name: "The Rolling Stones")
1855
+ end
1856
+
1473
1857
  context "when the context is not cached" do
1474
1858
 
1475
1859
  let(:criteria) do
@@ -1510,14 +1894,14 @@ describe Mongoid::Contextual::Mongo do
1510
1894
  context "when there is sort on the context" do
1511
1895
 
1512
1896
  it "follows the main sort" do
1513
- expect(context.send(method)).to eq(new_order)
1897
+ expect(context.send(method)).to eq(rolling_stones)
1514
1898
  end
1515
1899
  end
1516
1900
 
1517
1901
  context "when subsequently calling #last" do
1518
1902
 
1519
1903
  it "returns the correct document" do
1520
- expect(context.send(method)).to eq(new_order)
1904
+ expect(context.send(method)).to eq(rolling_stones)
1521
1905
  expect(context.last).to eq(depeche_mode)
1522
1906
  end
1523
1907
  end
@@ -1542,131 +1926,611 @@ describe Mongoid::Contextual::Mongo do
1542
1926
 
1543
1927
  it 'returns the last document, sorted by _id' do
1544
1928
  expect(context.send(method)).to eq(depeche_mode)
1545
- expect(context.last).to eq(new_order)
1929
+ expect(context.last).to eq(rolling_stones)
1546
1930
  end
1547
1931
  end
1932
+ end
1933
+
1934
+ context 'when the criteria has a sort' do
1935
+
1936
+ let(:criteria) do
1937
+ Band.desc(:name)
1938
+ end
1939
+
1940
+ let(:context) do
1941
+ described_class.new(criteria)
1942
+ end
1943
+
1944
+ it 'applies the criteria sort' do
1945
+ expect(context.send(method)).to eq(rolling_stones)
1946
+ end
1548
1947
 
1549
- context 'with option { sort: :none }' do
1948
+ context 'when calling #last' do
1550
1949
 
1551
- let(:opts) do
1552
- { id_sort: :none }
1950
+ it 'applies the criteria sort' do
1951
+ expect(context.send(method)).to eq(rolling_stones)
1952
+ expect(context.last).to eq(depeche_mode)
1553
1953
  end
1954
+ end
1955
+ end
1554
1956
 
1555
- it 'does not apply the sort on _id' do
1556
- expect(context.send(method, opts)).to eq(depeche_mode)
1957
+ context "when using .sort" do
1958
+
1959
+ let(:criteria) do
1960
+ Band.all.sort(:name => -1).criteria
1961
+ end
1962
+
1963
+ let(:context) do
1964
+ described_class.new(criteria)
1965
+ end
1966
+
1967
+ context "when there is sort on the context" do
1968
+
1969
+ it "follows the main sort" do
1970
+ expect(context.send(method)).to eq(rolling_stones)
1557
1971
  end
1972
+ end
1558
1973
 
1559
- context 'when calling #last' do
1974
+ context "when subsequently calling #last" do
1560
1975
 
1561
- it 'does not apply a sort on _id' do
1562
- expect(context.send(method, opts)).to eq(depeche_mode)
1563
- expect(context.last(opts)).to eq(depeche_mode)
1564
- end
1976
+ it "returns the correct document" do
1977
+ expect(context.send(method)).to eq(rolling_stones)
1978
+ expect(context.last).to eq(depeche_mode)
1565
1979
  end
1566
1980
  end
1567
1981
  end
1568
1982
 
1569
- context 'when the criteria has a sort' do
1983
+ context "when the query cache is enabled" do
1984
+ query_cache_enabled
1570
1985
 
1571
1986
  let(:criteria) do
1572
- Band.desc(:name)
1987
+ Band.where(name: "Depeche Mode")
1573
1988
  end
1574
1989
 
1575
1990
  let(:context) do
1576
1991
  described_class.new(criteria)
1577
1992
  end
1578
1993
 
1994
+ context "when first method was called before" do
1579
1995
 
1580
- it 'applies the criteria sort' do
1581
- expect(context.send(method)).to eq(new_order)
1996
+ before do
1997
+ context.first
1998
+ end
1999
+
2000
+ it "returns the first document without touching the database" do
2001
+ expect_no_queries do
2002
+ expect(context.send(method)).to eq(depeche_mode)
2003
+ end
2004
+ end
1582
2005
  end
2006
+ end
1583
2007
 
1584
- context 'when calling #last' do
2008
+ context "when including a limit" do
1585
2009
 
1586
- it 'applies the criteria sort' do
1587
- expect(context.send(method)).to eq(new_order)
1588
- expect(context.last).to eq(depeche_mode)
2010
+ context "when the context is not cached" do
2011
+
2012
+ let(:context) do
2013
+ described_class.new(criteria)
2014
+ end
2015
+
2016
+ context "when the limit is 1" do
2017
+ let(:criteria) do
2018
+ Band.criteria
2019
+ end
2020
+
2021
+ let(:docs) do
2022
+ context.send(method, 1)
2023
+ end
2024
+
2025
+ it "returns an array of documents" do
2026
+ expect(docs).to eq([ depeche_mode ])
2027
+ end
2028
+ end
2029
+
2030
+ context "when the limit is >1" do
2031
+ let(:criteria) do
2032
+ Band.criteria
2033
+ end
2034
+
2035
+ let(:docs) do
2036
+ context.send(method, 2)
2037
+ end
2038
+
2039
+ it "returns the number of documents in order" do
2040
+ expect(docs).to eq([ depeche_mode, new_order ])
2041
+ end
2042
+ end
2043
+
2044
+ context 'when the criteria has a collation' do
2045
+ min_server_version '3.4'
2046
+
2047
+ let(:criteria) do
2048
+ Band.where(name: "DEPECHE MODE").collation(locale: 'en_US', strength: 2)
2049
+ end
2050
+
2051
+ it "returns the first matching document" do
2052
+ expect(context.send(method, 1)).to eq([ depeche_mode ])
2053
+ end
1589
2054
  end
1590
2055
  end
1591
2056
 
1592
- context 'with option { sort: :none }' do
2057
+ context "when the query cache is enabled" do
1593
2058
 
1594
- let(:opts) do
1595
- { id_sort: :none }
2059
+ let(:context) do
2060
+ described_class.new(criteria)
1596
2061
  end
1597
2062
 
1598
- it 'applies the criteria sort' do
1599
- expect(context.send(method, opts)).to eq(new_order)
2063
+ context "when calling first beforehand" do
2064
+ query_cache_enabled
2065
+
2066
+ let(:context) do
2067
+ described_class.new(criteria)
2068
+ end
2069
+
2070
+ let(:criteria) do
2071
+ Band.all
2072
+ end
2073
+
2074
+ before do
2075
+ context.first(before_limit)
2076
+ end
2077
+
2078
+ let(:docs) do
2079
+ context.send(method, limit)
2080
+ end
2081
+
2082
+ context "when getting all of the documents before" do
2083
+ let(:before_limit) { 3 }
2084
+
2085
+ context "when getting all of the documents" do
2086
+ let(:limit) { 3 }
2087
+
2088
+ it "returns all documents without touching the database" do
2089
+ expect_no_queries do
2090
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2091
+ end
2092
+ end
2093
+ end
2094
+
2095
+ context "when getting fewer documents" do
2096
+ let(:limit) { 2 }
2097
+
2098
+ it "returns the correct documents without touching the database" do
2099
+ expect_no_queries do
2100
+ expect(docs).to eq([ depeche_mode, new_order ])
2101
+ end
2102
+ end
2103
+ end
2104
+ end
2105
+
2106
+ context "when getting fewer documents before" do
2107
+ let(:before_limit) { 2 }
2108
+
2109
+ context "when getting the same number of documents" do
2110
+ let(:limit) { 2 }
2111
+
2112
+ it "returns the correct documents without touching the database" do
2113
+ expect_no_queries do
2114
+ expect(docs).to eq([ depeche_mode, new_order ])
2115
+ end
2116
+ end
2117
+ end
2118
+
2119
+ context "when getting more documents" do
2120
+ let(:limit) { 3 }
2121
+
2122
+ it "returns the correct documents and touches the database" do
2123
+ expect_query(1) do
2124
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2125
+ end
2126
+ end
2127
+ end
2128
+ end
2129
+
2130
+ context "when getting one document before" do
2131
+ let(:before_limit) { 1 }
2132
+
2133
+ context "when getting one document" do
2134
+ let(:limit) { 1 }
2135
+
2136
+ it "returns the correct documents without touching the database" do
2137
+ expect_no_queries do
2138
+ expect(docs).to eq([ depeche_mode ])
2139
+ end
2140
+ end
2141
+ end
2142
+
2143
+ context "when getting more than one document" do
2144
+ let(:limit) { 3 }
2145
+
2146
+ it "returns the correct documents and touches the database" do
2147
+ expect_query(1) do
2148
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2149
+ end
2150
+ end
2151
+ end
2152
+ end
1600
2153
  end
2154
+ end
2155
+ end
1601
2156
 
1602
- context 'when calling #last' do
2157
+ context "when calling #first then #last and the query cache is enabled" do
2158
+ query_cache_enabled
1603
2159
 
1604
- it 'applies the criteria sort' do
1605
- expect(context.send(method, opts)).to eq(new_order)
1606
- expect(context.last(opts)).to eq(depeche_mode)
2160
+ let(:context) do
2161
+ described_class.new(criteria)
2162
+ end
2163
+
2164
+ let(:criteria) do
2165
+ Band.all
2166
+ end
2167
+
2168
+ before do
2169
+ context.first(before_limit)
2170
+ end
2171
+
2172
+ let(:docs) do
2173
+ context.last(limit)
2174
+ end
2175
+
2176
+ context "when getting one from the beginning and one from the end" do
2177
+ let(:before_limit) { 2 }
2178
+ let(:limit) { 1 }
2179
+
2180
+ it "gets the correct document and hits the database" do
2181
+ expect_query(1) do
2182
+ expect(docs).to eq([rolling_stones])
1607
2183
  end
1608
2184
  end
1609
2185
  end
1610
2186
  end
2187
+ end
2188
+ end
1611
2189
 
1612
- context "when using .sort" do
2190
+ describe "#last" do
2191
+ let!(:depeche_mode) do
2192
+ Band.create!(name: "Depeche Mode")
2193
+ end
2194
+
2195
+ let!(:new_order) do
2196
+ Band.create!(name: "New Order")
2197
+ end
2198
+
2199
+ let!(:rolling_stones) do
2200
+ Band.create!(name: "The Rolling Stones")
2201
+ end
2202
+
2203
+ context "when the context is not cached" do
2204
+
2205
+ let(:criteria) do
2206
+ Band.where(name: "Depeche Mode")
2207
+ end
2208
+
2209
+ let(:context) do
2210
+ described_class.new(criteria)
2211
+ end
2212
+
2213
+ it "returns the last matching document" do
2214
+ expect(context.last).to eq(depeche_mode)
2215
+ end
2216
+
2217
+ context 'when the criteria has a collation' do
2218
+ min_server_version '3.4'
1613
2219
 
1614
2220
  let(:criteria) do
1615
- Band.all.sort(:name => -1).criteria
2221
+ Band.where(name: "DEPECHE MODE").collation(locale: 'en_US', strength: 2)
2222
+ end
2223
+
2224
+ it "returns the last matching document" do
2225
+ expect(context.last).to eq(depeche_mode)
2226
+ end
2227
+ end
2228
+ end
2229
+
2230
+ context "when using .desc" do
2231
+
2232
+ let(:criteria) do
2233
+ Band.desc(:name)
2234
+ end
2235
+
2236
+ let(:context) do
2237
+ described_class.new(criteria)
2238
+ end
2239
+
2240
+ context "when there is sort on the context" do
2241
+
2242
+ it "follows the main sort" do
2243
+ expect(context.last).to eq(depeche_mode)
2244
+ end
2245
+ end
2246
+
2247
+ context "when subsequently calling #first" do
2248
+
2249
+ it "returns the correct document" do
2250
+ expect(context.last).to eq(depeche_mode)
2251
+ expect(context.first).to eq(rolling_stones)
2252
+ end
2253
+ end
2254
+ end
2255
+
2256
+ context 'when the criteria has no sort' do
2257
+
2258
+ let(:criteria) do
2259
+ Band.all
2260
+ end
2261
+
2262
+ let(:context) do
2263
+ described_class.new(criteria)
2264
+ end
2265
+
2266
+ it 'applies a sort on _id' do
2267
+ expect(context.last).to eq(rolling_stones)
2268
+ end
2269
+
2270
+ context 'when calling #first' do
2271
+
2272
+ it 'returns the first document, sorted by _id' do
2273
+ expect(context.last).to eq(rolling_stones)
2274
+ expect(context.first).to eq(depeche_mode)
2275
+ end
2276
+ end
2277
+ end
2278
+
2279
+ context 'when the criteria has a sort' do
2280
+
2281
+ let(:criteria) do
2282
+ Band.desc(:name)
2283
+ end
2284
+
2285
+ let(:context) do
2286
+ described_class.new(criteria)
2287
+ end
2288
+
2289
+
2290
+ it 'applies the criteria sort' do
2291
+ expect(context.last).to eq(depeche_mode)
2292
+ end
2293
+
2294
+ context 'when calling #first' do
2295
+
2296
+ it 'applies the criteria sort' do
2297
+ expect(context.last).to eq(depeche_mode)
2298
+ expect(context.first).to eq(rolling_stones)
2299
+ end
2300
+ end
2301
+ end
2302
+
2303
+ context "when using .sort" do
2304
+
2305
+ let(:criteria) do
2306
+ Band.all.sort(:name => -1).criteria
2307
+ end
2308
+
2309
+ let(:context) do
2310
+ described_class.new(criteria)
2311
+ end
2312
+
2313
+ context "when there is sort on the context" do
2314
+
2315
+ it "follows the main sort" do
2316
+ expect(context.last).to eq(depeche_mode)
1616
2317
  end
2318
+ end
2319
+
2320
+ context "when subsequently calling #first" do
2321
+
2322
+ it "returns the correct document" do
2323
+ expect(context.last).to eq(depeche_mode)
2324
+ expect(context.first).to eq(rolling_stones)
2325
+ end
2326
+ end
2327
+ end
2328
+
2329
+ context "when the query cache is enabled" do
2330
+ query_cache_enabled
2331
+
2332
+ let(:criteria) do
2333
+ Band.where(name: "Depeche Mode")
2334
+ end
2335
+
2336
+ let(:context) do
2337
+ described_class.new(criteria)
2338
+ end
2339
+
2340
+ context "when last method was called before" do
2341
+
2342
+ before do
2343
+ context.last
2344
+ end
2345
+
2346
+ it "returns the last document without touching the database" do
2347
+ expect_no_queries do
2348
+ expect(context.last).to eq(depeche_mode)
2349
+ end
2350
+ end
2351
+ end
2352
+ end
2353
+
2354
+ context "when including a limit" do
2355
+
2356
+ context "when the context is not cached" do
1617
2357
 
1618
2358
  let(:context) do
1619
2359
  described_class.new(criteria)
1620
2360
  end
1621
2361
 
1622
- context "when there is sort on the context" do
2362
+ context "when the limit is 1" do
2363
+ let(:criteria) do
2364
+ Band.criteria
2365
+ end
1623
2366
 
1624
- it "follows the main sort" do
1625
- expect(context.send(method)).to eq(new_order)
2367
+ let(:docs) do
2368
+ context.last(1)
2369
+ end
2370
+
2371
+ it "returns an array of documents" do
2372
+ expect(docs).to eq([ rolling_stones ])
1626
2373
  end
1627
2374
  end
1628
2375
 
1629
- context "when subsequently calling #last" do
2376
+ context "when the limit is >1" do
2377
+ let(:criteria) do
2378
+ Band.criteria
2379
+ end
1630
2380
 
1631
- it "returns the correct document" do
1632
- expect(context.send(method)).to eq(new_order)
1633
- expect(context.last).to eq(depeche_mode)
2381
+ let(:docs) do
2382
+ context.last(2)
2383
+ end
2384
+
2385
+ it "returns the number of documents in order" do
2386
+ expect(docs).to eq([ new_order, rolling_stones ])
1634
2387
  end
1635
2388
  end
1636
- end
1637
2389
 
1638
- context "when the context is cached" do
2390
+ context 'when the criteria has a collation' do
2391
+ min_server_version '3.4'
1639
2392
 
1640
- let(:criteria) do
1641
- Band.where(name: "Depeche Mode").cache
2393
+ let(:criteria) do
2394
+ Band.where(name: "DEPECHE MODE").collation(locale: 'en_US', strength: 2)
2395
+ end
2396
+
2397
+ it "returns the first matching document" do
2398
+ expect(context.last(1)).to eq([ depeche_mode ])
2399
+ end
1642
2400
  end
2401
+ end
2402
+
2403
+ context "when the context is cached" do
1643
2404
 
1644
2405
  let(:context) do
1645
2406
  described_class.new(criteria)
1646
2407
  end
1647
2408
 
1648
- context "when the cache is loaded" do
2409
+ context "when query cache is enabled" do
2410
+ query_cache_enabled
2411
+
2412
+ let(:context) do
2413
+ described_class.new(criteria)
2414
+ end
2415
+
2416
+ let(:criteria) do
2417
+ Band.all
2418
+ end
1649
2419
 
1650
2420
  before do
1651
- context.to_a
2421
+ context.last(before_limit)
1652
2422
  end
1653
2423
 
1654
- it "returns the first document without touching the database" do
1655
- expect(context).to receive(:view).never
1656
- expect(context.send(method)).to eq(depeche_mode)
2424
+ let(:docs) do
2425
+ context.last(limit)
1657
2426
  end
1658
- end
1659
2427
 
1660
- context "when first method was called before" do
2428
+ context "when getting all of the documents before" do
2429
+ let(:before_limit) { 3 }
1661
2430
 
1662
- before do
1663
- context.first
2431
+ context "when getting all of the documents" do
2432
+ let(:limit) { 3 }
2433
+
2434
+ it "returns all documents without touching the db" do
2435
+ expect_no_queries do
2436
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2437
+ end
2438
+ end
2439
+ end
2440
+
2441
+ context "when getting fewer documents" do
2442
+ let(:limit) { 2 }
2443
+
2444
+ it "returns the correct documents without touching the db" do
2445
+ expect_no_queries do
2446
+ expect(docs).to eq([ new_order, rolling_stones ])
2447
+ end
2448
+ end
2449
+ end
1664
2450
  end
1665
2451
 
1666
- it "returns the first document without touching the database" do
1667
- expect(context).to receive(:view).never
1668
- expect(context.send(method)).to eq(depeche_mode)
2452
+ context "when getting fewer documents before" do
2453
+ let(:before_limit) { 2 }
2454
+
2455
+ context "when getting the same number of documents" do
2456
+ let(:limit) { 2 }
2457
+
2458
+ it "returns the correct documents without touching the db" do
2459
+ expect_no_queries do
2460
+ expect(docs).to eq([ new_order, rolling_stones ])
2461
+ end
2462
+ end
2463
+ end
2464
+
2465
+ context "when getting more documents" do
2466
+ let(:limit) { 3 }
2467
+
2468
+ it "returns the correct documents and touches the database" do
2469
+ expect_query(1) do
2470
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2471
+ end
2472
+ end
2473
+ end
1669
2474
  end
2475
+
2476
+ context "when getting one document before" do
2477
+ let(:before_limit) { 1 }
2478
+
2479
+ context "when getting one document" do
2480
+ let(:limit) { 1 }
2481
+
2482
+ it "returns the correct documents without touching the database" do
2483
+ expect_no_queries do
2484
+ expect(docs).to eq([ rolling_stones ])
2485
+ end
2486
+ end
2487
+ end
2488
+
2489
+ context "when getting more than one document" do
2490
+ let(:limit) { 3 }
2491
+
2492
+ it "returns the correct documents and touches the database" do
2493
+ expect_query(1) do
2494
+ expect(docs).to eq([ depeche_mode, new_order, rolling_stones ])
2495
+ end
2496
+ end
2497
+ end
2498
+ end
2499
+ end
2500
+ end
2501
+ end
2502
+
2503
+ context "when calling #last then #first and the query cache is enabled" do
2504
+ query_cache_enabled
2505
+
2506
+ let(:context) do
2507
+ described_class.new(criteria)
2508
+ end
2509
+
2510
+ let(:criteria) do
2511
+ Band.all
2512
+ end
2513
+
2514
+ before do
2515
+ context.last(before_limit)
2516
+ end
2517
+
2518
+ let(:docs) do
2519
+ context.first(limit)
2520
+ end
2521
+
2522
+ context "when getting one from the beginning and one from the end" do
2523
+ let(:before_limit) { 2 }
2524
+ let(:limit) { 1 }
2525
+
2526
+ it "hits the database" do
2527
+ expect_query(1) do
2528
+ docs
2529
+ end
2530
+ end
2531
+
2532
+ it "gets the correct document" do
2533
+ expect(docs).to eq([ depeche_mode ])
1670
2534
  end
1671
2535
  end
1672
2536
  end
@@ -1718,37 +2582,28 @@ describe Mongoid::Contextual::Mongo do
1718
2582
  described_class.new(criteria)
1719
2583
  end
1720
2584
 
1721
- it "returns the number of documents that match" do
1722
- expect(context.send(method)).to eq(2)
1723
- end
2585
+ context "when broken_view_options is false" do
2586
+ driver_config_override :broken_view_options, false
1724
2587
 
1725
- context "when calling more than once" do
1726
- it "returns the cached value for subsequent calls" do
1727
- expect(context.view).to receive(:count_documents).once.and_return(2)
1728
- 2.times { expect(context.send(method)).to eq(2) }
2588
+ it "returns the number of documents that match" do
2589
+ expect(context.send(method)).to eq(1)
1729
2590
  end
1730
2591
  end
1731
2592
 
1732
- context "when the results have been iterated over" do
2593
+ context "when broken_view_options is true" do
2594
+ driver_config_override :broken_view_options, true
1733
2595
 
1734
- before do
1735
- context.entries
1736
- end
1737
-
1738
- it "returns the cached value for all calls" do
1739
- expect(context.view).to receive(:count_documents).once.and_return(2)
2596
+ it "returns the number of documents that match" do
1740
2597
  expect(context.send(method)).to eq(2)
1741
2598
  end
2599
+ end
1742
2600
 
1743
- context "when the results have been iterated over multiple times" do
2601
+ context "when calling more than once with different limits" do
2602
+ driver_config_override :broken_view_options, false
1744
2603
 
1745
- before do
1746
- context.entries
1747
- end
1748
-
1749
- it "resets the length on each full iteration" do
1750
- expect(context.size).to eq(2)
1751
- end
2604
+ it "does not cache the value" do
2605
+ expect(context.limit(1).send(method)).to eq(1)
2606
+ expect(context.limit(2).send(method)).to eq(2)
1752
2607
  end
1753
2608
  end
1754
2609
  end
@@ -1767,10 +2622,12 @@ describe Mongoid::Contextual::Mongo do
1767
2622
  expect(context.send(method)).to eq(1)
1768
2623
  end
1769
2624
 
1770
- context "when calling more than once" do
1771
- it "returns the cached value for subsequent calls" do
1772
- expect(context.view).to receive(:count_documents).once.and_return(1)
1773
- 2.times { expect(context.send(method)).to eq(1) }
2625
+ context "when calling more than once with different skips" do
2626
+ driver_config_override :broken_view_options, false
2627
+
2628
+ it "does not cache the value" do
2629
+ expect(context.skip(0).send(method)).to eq(1)
2630
+ expect(context.skip(1).send(method)).to eq(0)
1774
2631
  end
1775
2632
  end
1776
2633
 
@@ -1823,6 +2680,80 @@ describe Mongoid::Contextual::Mongo do
1823
2680
  end
1824
2681
  end
1825
2682
 
2683
+ describe "#take" do
2684
+
2685
+ let!(:depeche_mode) do
2686
+ Band.create!(name: "Depeche Mode")
2687
+ end
2688
+
2689
+ let!(:new_order) do
2690
+ Band.create!(name: "New Order")
2691
+ end
2692
+
2693
+ let!(:rolling_stones) do
2694
+ Band.create!(name: "The Rolling Stones")
2695
+ end
2696
+
2697
+ let(:criteria) do
2698
+ Band.all
2699
+ end
2700
+
2701
+ let(:context) do
2702
+ described_class.new(criteria)
2703
+ end
2704
+
2705
+ it "takes the correct number results" do
2706
+ expect(context.take(2)).to eq([ depeche_mode, new_order ])
2707
+ end
2708
+
2709
+ it "returns an array when passing 1" do
2710
+ expect(context.take(1)).to eq([ depeche_mode ])
2711
+ end
2712
+
2713
+ it "does not return an array when not passing an argument" do
2714
+ expect(context.take).to eq(depeche_mode)
2715
+ end
2716
+
2717
+ it "returns all the documents taking more than whats in the db" do
2718
+ expect(context.take(5)).to eq([ depeche_mode, new_order, rolling_stones ])
2719
+ end
2720
+ end
2721
+
2722
+ describe "#take!" do
2723
+
2724
+ let!(:depeche_mode) do
2725
+ Band.create!(name: "Depeche Mode")
2726
+ end
2727
+
2728
+ let!(:new_order) do
2729
+ Band.create!(name: "New Order")
2730
+ end
2731
+
2732
+ let!(:rolling_stones) do
2733
+ Band.create!(name: "The Rolling Stones")
2734
+ end
2735
+
2736
+ let(:criteria) do
2737
+ Band.all
2738
+ end
2739
+
2740
+ let(:context) do
2741
+ described_class.new(criteria)
2742
+ end
2743
+
2744
+ it "takes the first document" do
2745
+ expect(context.take!).to eq(depeche_mode)
2746
+ end
2747
+
2748
+ context "when there are no documents" do
2749
+ it "raises an error" do
2750
+ expect do
2751
+ Person.take!
2752
+ end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Person./)
2753
+ end
2754
+ end
2755
+ end
2756
+
1826
2757
  describe "#map" do
1827
2758
 
1828
2759
  before do
@@ -1840,8 +2771,10 @@ describe Mongoid::Contextual::Mongo do
1840
2771
 
1841
2772
  context "when passed the symbol field name" do
1842
2773
 
1843
- it "performs mapping" do
1844
- expect(context.map(:name)).to eq ["Depeche Mode", "New Order"]
2774
+ it "raises an error" do
2775
+ expect do
2776
+ context.map(:name)
2777
+ end.to raise_error(ArgumentError)
1845
2778
  end
1846
2779
  end
1847
2780