mongoid 7.4.0 → 8.0.1

Sign up to get free protection for your applications and to get access to all the features.
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