mongoid 5.4.0 → 6.4.8

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 (301) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +3 -3
  4. data/Rakefile +26 -0
  5. data/lib/config/locales/en.yml +40 -0
  6. data/lib/mongoid/atomic/modifiers.rb +2 -2
  7. data/lib/mongoid/atomic.rb +5 -5
  8. data/lib/mongoid/attributes/readonly.rb +22 -0
  9. data/lib/mongoid/attributes.rb +22 -21
  10. data/lib/mongoid/cacheable.rb +36 -0
  11. data/lib/mongoid/changeable.rb +36 -0
  12. data/lib/mongoid/clients/options.rb +55 -250
  13. data/lib/mongoid/clients/sessions.rb +113 -0
  14. data/lib/mongoid/clients/storage_options.rb +2 -69
  15. data/lib/mongoid/clients.rb +10 -63
  16. data/lib/mongoid/composable.rb +29 -2
  17. data/lib/mongoid/config.rb +1 -0
  18. data/lib/mongoid/contextual/aggregable/mongo.rb +1 -1
  19. data/lib/mongoid/contextual/atomic.rb +4 -4
  20. data/lib/mongoid/contextual/map_reduce.rb +7 -3
  21. data/lib/mongoid/contextual/memory.rb +9 -4
  22. data/lib/mongoid/contextual/mongo.rb +65 -30
  23. data/lib/mongoid/contextual/none.rb +12 -0
  24. data/lib/mongoid/copyable.rb +13 -6
  25. data/lib/mongoid/criteria/marshalable.rb +2 -2
  26. data/lib/mongoid/criteria/modifiable.rb +29 -3
  27. data/lib/mongoid/criteria/options.rb +25 -0
  28. data/lib/mongoid/criteria/queryable/aggregable.rb +120 -0
  29. data/lib/mongoid/criteria/queryable/extensions/array.rb +185 -0
  30. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +37 -0
  31. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +34 -0
  32. data/lib/mongoid/criteria/queryable/extensions/date.rb +63 -0
  33. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +53 -0
  34. data/lib/mongoid/criteria/queryable/extensions/hash.rb +200 -0
  35. data/lib/mongoid/criteria/queryable/extensions/nil_class.rb +86 -0
  36. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +90 -0
  37. data/lib/mongoid/criteria/queryable/extensions/object.rb +206 -0
  38. data/lib/mongoid/criteria/queryable/extensions/range.rb +70 -0
  39. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +79 -0
  40. data/lib/mongoid/criteria/queryable/extensions/set.rb +34 -0
  41. data/lib/mongoid/criteria/queryable/extensions/string.rb +137 -0
  42. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +79 -0
  43. data/lib/mongoid/criteria/queryable/extensions/time.rb +60 -0
  44. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +54 -0
  45. data/lib/mongoid/criteria/queryable/extensions.rb +28 -0
  46. data/lib/mongoid/criteria/queryable/forwardable.rb +65 -0
  47. data/lib/mongoid/criteria/queryable/key.rb +103 -0
  48. data/lib/mongoid/criteria/queryable/macroable.rb +27 -0
  49. data/lib/mongoid/criteria/queryable/mergeable.rb +273 -0
  50. data/lib/mongoid/criteria/queryable/optional.rb +429 -0
  51. data/lib/mongoid/criteria/queryable/options.rb +153 -0
  52. data/lib/mongoid/criteria/queryable/pipeline.rb +111 -0
  53. data/lib/mongoid/criteria/queryable/selectable.rb +689 -0
  54. data/lib/mongoid/criteria/queryable/selector.rb +212 -0
  55. data/lib/mongoid/criteria/queryable/smash.rb +104 -0
  56. data/lib/mongoid/criteria/queryable.rb +87 -0
  57. data/lib/mongoid/criteria.rb +6 -2
  58. data/lib/mongoid/document.rb +34 -41
  59. data/lib/mongoid/errors/ambiguous_relationship.rb +1 -1
  60. data/lib/mongoid/errors/in_memory_collation_not_supported.rb +1 -1
  61. data/lib/mongoid/errors/invalid_field.rb +2 -2
  62. data/lib/mongoid/errors/invalid_persistence_option.rb +29 -0
  63. data/lib/mongoid/errors/invalid_relation.rb +66 -0
  64. data/lib/mongoid/errors/invalid_session_use.rb +24 -0
  65. data/lib/mongoid/errors.rb +3 -0
  66. data/lib/mongoid/evolvable.rb +1 -1
  67. data/lib/mongoid/extensions/big_decimal.rb +17 -8
  68. data/lib/mongoid/extensions/date.rb +4 -1
  69. data/lib/mongoid/extensions/decimal128.rb +3 -3
  70. data/lib/mongoid/extensions/hash.rb +1 -0
  71. data/lib/mongoid/extensions/regexp.rb +1 -0
  72. data/lib/mongoid/extensions/string.rb +6 -3
  73. data/lib/mongoid/extensions/time.rb +4 -1
  74. data/lib/mongoid/extensions.rb +0 -4
  75. data/lib/mongoid/factory.rb +2 -1
  76. data/lib/mongoid/fields/validators/macro.rb +18 -0
  77. data/lib/mongoid/findable.rb +2 -2
  78. data/lib/mongoid/indexable.rb +16 -14
  79. data/lib/mongoid/interceptable.rb +9 -22
  80. data/lib/mongoid/matchable/all.rb +2 -2
  81. data/lib/mongoid/matchable/and.rb +3 -3
  82. data/lib/mongoid/matchable/default.rb +2 -2
  83. data/lib/mongoid/matchable/elem_match.rb +28 -0
  84. data/lib/mongoid/matchable/exists.rb +2 -2
  85. data/lib/mongoid/matchable/gt.rb +4 -2
  86. data/lib/mongoid/matchable/gte.rb +4 -2
  87. data/lib/mongoid/matchable/in.rb +2 -2
  88. data/lib/mongoid/matchable/lt.rb +4 -2
  89. data/lib/mongoid/matchable/lte.rb +4 -2
  90. data/lib/mongoid/matchable/ne.rb +2 -2
  91. data/lib/mongoid/matchable/nin.rb +2 -2
  92. data/lib/mongoid/matchable/nor.rb +37 -0
  93. data/lib/mongoid/matchable/or.rb +3 -3
  94. data/lib/mongoid/matchable/regexp.rb +3 -3
  95. data/lib/mongoid/matchable/size.rb +2 -2
  96. data/lib/mongoid/matchable.rb +16 -7
  97. data/lib/mongoid/persistable/creatable.rb +5 -3
  98. data/lib/mongoid/persistable/deletable.rb +5 -3
  99. data/lib/mongoid/persistable/destroyable.rb +1 -5
  100. data/lib/mongoid/persistable/settable.rb +5 -5
  101. data/lib/mongoid/persistable/updatable.rb +7 -14
  102. data/lib/mongoid/persistable/upsertable.rb +2 -1
  103. data/lib/mongoid/persistable.rb +4 -6
  104. data/lib/mongoid/persistence_context.rb +220 -0
  105. data/lib/mongoid/query_cache.rb +67 -23
  106. data/lib/mongoid/railtie.rb +17 -1
  107. data/lib/mongoid/railties/controller_runtime.rb +86 -0
  108. data/lib/mongoid/relations/accessors.rb +3 -0
  109. data/lib/mongoid/relations/auto_save.rb +12 -4
  110. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +4 -4
  111. data/lib/mongoid/relations/counter_cache.rb +15 -5
  112. data/lib/mongoid/relations/eager/base.rb +3 -3
  113. data/lib/mongoid/relations/eager/has_and_belongs_to_many.rb +2 -2
  114. data/lib/mongoid/relations/eager/has_many.rb +1 -1
  115. data/lib/mongoid/relations/eager.rb +6 -11
  116. data/lib/mongoid/relations/embedded/batchable.rb +20 -18
  117. data/lib/mongoid/relations/embedded/in.rb +13 -1
  118. data/lib/mongoid/relations/embedded/many.rb +51 -10
  119. data/lib/mongoid/relations/embedded/one.rb +14 -1
  120. data/lib/mongoid/relations/macros.rb +9 -1
  121. data/lib/mongoid/relations/many.rb +4 -0
  122. data/lib/mongoid/relations/metadata.rb +3 -3
  123. data/lib/mongoid/relations/options.rb +2 -2
  124. data/lib/mongoid/relations/proxy.rb +1 -31
  125. data/lib/mongoid/relations/referenced/in.rb +19 -10
  126. data/lib/mongoid/relations/referenced/many.rb +30 -26
  127. data/lib/mongoid/relations/referenced/many_to_many.rb +20 -13
  128. data/lib/mongoid/relations/referenced/one.rb +15 -1
  129. data/lib/mongoid/relations/synchronization.rb +12 -12
  130. data/lib/mongoid/relations/targets/enumerable.rb +24 -4
  131. data/lib/mongoid/relations/touchable.rb +7 -4
  132. data/lib/mongoid/reloadable.rb +2 -2
  133. data/lib/mongoid/scopable.rb +3 -3
  134. data/lib/mongoid/serializable.rb +1 -1
  135. data/lib/mongoid/stateful.rb +1 -0
  136. data/lib/mongoid/tasks/database.rb +3 -2
  137. data/lib/mongoid/threaded.rb +74 -0
  138. data/lib/mongoid/traversable.rb +1 -1
  139. data/lib/mongoid/validatable/uniqueness.rb +1 -2
  140. data/lib/mongoid/version.rb +1 -1
  141. data/lib/mongoid.rb +6 -6
  142. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +18 -3
  143. data/spec/app/models/agent.rb +2 -0
  144. data/spec/app/models/album.rb +5 -1
  145. data/spec/app/models/array_field.rb +7 -0
  146. data/spec/app/models/artist.rb +21 -0
  147. data/spec/app/models/band.rb +3 -0
  148. data/spec/app/models/book.rb +2 -1
  149. data/spec/app/models/delegating_patient.rb +16 -0
  150. data/spec/app/models/dokument.rb +1 -0
  151. data/spec/app/models/ordered_post.rb +5 -0
  152. data/spec/app/models/oscar.rb +1 -2
  153. data/spec/app/models/page.rb +1 -1
  154. data/spec/app/models/person.rb +3 -3
  155. data/spec/app/models/princess.rb +2 -0
  156. data/spec/app/models/record.rb +1 -0
  157. data/spec/app/models/subscription.rb +1 -0
  158. data/spec/app/models/thing.rb +1 -1
  159. data/spec/config/mongoid.yml +15 -0
  160. data/spec/integration/document_spec.rb +22 -0
  161. data/spec/mongoid/atomic/modifiers_spec.rb +3 -3
  162. data/spec/mongoid/atomic_spec.rb +5 -5
  163. data/spec/mongoid/attributes/nested_spec.rb +18 -14
  164. data/spec/mongoid/attributes/readonly_spec.rb +87 -44
  165. data/spec/mongoid/attributes_spec.rb +90 -5
  166. data/spec/mongoid/cacheable_spec.rb +112 -0
  167. data/spec/mongoid/changeable_spec.rb +58 -0
  168. data/spec/mongoid/clients/factory_spec.rb +80 -28
  169. data/spec/mongoid/clients/options_spec.rb +396 -95
  170. data/spec/mongoid/clients/sessions_spec.rb +334 -0
  171. data/spec/mongoid/clients_spec.rb +243 -101
  172. data/spec/mongoid/composable_spec.rb +7 -0
  173. data/spec/mongoid/config_spec.rb +67 -11
  174. data/spec/mongoid/contextual/atomic_spec.rb +3 -3
  175. data/spec/mongoid/contextual/geo_near_spec.rb +1 -0
  176. data/spec/mongoid/contextual/mongo_spec.rb +275 -22
  177. data/spec/mongoid/contextual/none_spec.rb +15 -0
  178. data/spec/mongoid/copyable_spec.rb +13 -4
  179. data/spec/mongoid/criteria/modifiable_spec.rb +297 -16
  180. data/spec/mongoid/criteria/options_spec.rb +29 -0
  181. data/spec/mongoid/criteria/queryable/aggregable_spec.rb +370 -0
  182. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +523 -0
  183. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +59 -0
  184. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +58 -0
  185. data/spec/mongoid/criteria/queryable/extensions/boolean_spec.rb +213 -0
  186. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +330 -0
  187. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +405 -0
  188. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +58 -0
  189. data/spec/mongoid/criteria/queryable/extensions/float_spec.rb +65 -0
  190. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +327 -0
  191. data/spec/mongoid/criteria/queryable/extensions/integer_spec.rb +65 -0
  192. data/spec/mongoid/criteria/queryable/extensions/nil_class_spec.rb +77 -0
  193. data/spec/mongoid/criteria/queryable/extensions/object_spec.rb +108 -0
  194. data/spec/mongoid/criteria/queryable/extensions/range_spec.rb +309 -0
  195. data/spec/mongoid/{extensions/origin → criteria/queryable/extensions}/regexp_raw_spec.rb +2 -2
  196. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +90 -0
  197. data/spec/mongoid/criteria/queryable/extensions/set_spec.rb +39 -0
  198. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +302 -0
  199. data/spec/mongoid/criteria/queryable/extensions/symbol_spec.rb +167 -0
  200. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +376 -0
  201. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +347 -0
  202. data/spec/mongoid/criteria/queryable/forwardable_spec.rb +87 -0
  203. data/spec/mongoid/criteria/queryable/key_spec.rb +52 -0
  204. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +49 -0
  205. data/spec/mongoid/criteria/queryable/optional_spec.rb +1799 -0
  206. data/spec/mongoid/criteria/queryable/options_spec.rb +360 -0
  207. data/spec/mongoid/criteria/queryable/pipeline_spec.rb +200 -0
  208. data/spec/mongoid/criteria/queryable/queryable_spec.rb +137 -0
  209. data/spec/mongoid/criteria/queryable/selectable_spec.rb +4242 -0
  210. data/spec/mongoid/criteria/queryable/selector_spec.rb +844 -0
  211. data/spec/mongoid/criteria/queryable/smash_spec.rb +30 -0
  212. data/spec/mongoid/criteria/scopable_spec.rb +81 -0
  213. data/spec/mongoid/criteria_spec.rb +156 -22
  214. data/spec/mongoid/document_spec.rb +100 -90
  215. data/spec/mongoid/errors/invalid_relation_spec.rb +37 -0
  216. data/spec/mongoid/errors/mongoid_error_spec.rb +6 -3
  217. data/spec/mongoid/extensions/big_decimal_spec.rb +321 -19
  218. data/spec/mongoid/extensions/boolean_spec.rb +14 -0
  219. data/spec/mongoid/extensions/date_spec.rb +2 -6
  220. data/spec/mongoid/extensions/date_time_spec.rb +2 -6
  221. data/spec/mongoid/extensions/decimal128_spec.rb +1 -1
  222. data/spec/mongoid/extensions/float_spec.rb +8 -1
  223. data/spec/mongoid/extensions/hash_spec.rb +15 -0
  224. data/spec/mongoid/extensions/integer_spec.rb +8 -1
  225. data/spec/mongoid/extensions/object_spec.rb +11 -0
  226. data/spec/mongoid/extensions/regexp_spec.rb +23 -0
  227. data/spec/mongoid/extensions/string_spec.rb +53 -4
  228. data/spec/mongoid/extensions/time_spec.rb +2 -6
  229. data/spec/mongoid/extensions/time_with_zone_spec.rb +2 -6
  230. data/spec/mongoid/factory_spec.rb +11 -0
  231. data/spec/mongoid/fields_spec.rb +1 -1
  232. data/spec/mongoid/findable_spec.rb +47 -2
  233. data/spec/mongoid/indexable_spec.rb +15 -3
  234. data/spec/mongoid/interceptable_spec.rb +85 -19
  235. data/spec/mongoid/matchable/all_spec.rb +4 -4
  236. data/spec/mongoid/matchable/and_spec.rb +10 -10
  237. data/spec/mongoid/matchable/default_spec.rb +12 -12
  238. data/spec/mongoid/matchable/elem_match_spec.rb +86 -0
  239. data/spec/mongoid/matchable/exists_spec.rb +5 -5
  240. data/spec/mongoid/matchable/gt_spec.rb +18 -7
  241. data/spec/mongoid/matchable/gte_spec.rb +17 -7
  242. data/spec/mongoid/matchable/in_spec.rb +5 -5
  243. data/spec/mongoid/matchable/lt_spec.rb +18 -7
  244. data/spec/mongoid/matchable/lte_spec.rb +18 -7
  245. data/spec/mongoid/matchable/ne_spec.rb +5 -5
  246. data/spec/mongoid/matchable/nin_spec.rb +5 -5
  247. data/spec/mongoid/matchable/nor_spec.rb +209 -0
  248. data/spec/mongoid/matchable/or_spec.rb +7 -7
  249. data/spec/mongoid/matchable/regexp_spec.rb +5 -5
  250. data/spec/mongoid/matchable/size_spec.rb +3 -3
  251. data/spec/mongoid/matchable_spec.rb +199 -54
  252. data/spec/mongoid/persistable/creatable_spec.rb +7 -2
  253. data/spec/mongoid/persistable/deletable_spec.rb +35 -1
  254. data/spec/mongoid/persistable/destroyable_spec.rb +25 -2
  255. data/spec/mongoid/persistable/incrementable_spec.rb +6 -6
  256. data/spec/mongoid/persistable/savable_spec.rb +34 -29
  257. data/spec/mongoid/persistable/settable_spec.rb +77 -27
  258. data/spec/mongoid/persistable/updatable_spec.rb +182 -3
  259. data/spec/mongoid/persistable_spec.rb +16 -16
  260. data/spec/mongoid/persistence_context_spec.rb +694 -0
  261. data/spec/mongoid/positional_spec.rb +1 -1
  262. data/spec/mongoid/query_cache_spec.rb +170 -12
  263. data/spec/mongoid/relations/accessors_spec.rb +1 -1
  264. data/spec/mongoid/relations/auto_save_spec.rb +39 -6
  265. data/spec/mongoid/relations/bindings/referenced/many_to_many_spec.rb +4 -4
  266. data/spec/mongoid/relations/builders_spec.rb +37 -10
  267. data/spec/mongoid/relations/counter_cache_spec.rb +64 -3
  268. data/spec/mongoid/relations/eager/has_and_belongs_to_many_spec.rb +16 -0
  269. data/spec/mongoid/relations/eager_spec.rb +40 -0
  270. data/spec/mongoid/relations/embedded/many_spec.rb +305 -59
  271. data/spec/mongoid/relations/embedded/one_spec.rb +2 -1
  272. data/spec/mongoid/relations/macros_spec.rb +415 -7
  273. data/spec/mongoid/relations/metadata_spec.rb +15 -1
  274. data/spec/mongoid/relations/proxy_spec.rb +27 -1
  275. data/spec/mongoid/relations/referenced/in_spec.rb +41 -1
  276. data/spec/mongoid/relations/referenced/many_spec.rb +35 -25
  277. data/spec/mongoid/relations/referenced/many_to_many_spec.rb +14 -26
  278. data/spec/mongoid/relations/synchronization_spec.rb +48 -2
  279. data/spec/mongoid/relations/targets/enumerable_spec.rb +108 -0
  280. data/spec/mongoid/relations/touchable_spec.rb +40 -0
  281. data/spec/mongoid/reloadable_spec.rb +51 -0
  282. data/spec/mongoid/scopable_spec.rb +13 -0
  283. data/spec/mongoid/serializable_spec.rb +0 -50
  284. data/spec/mongoid/threaded_spec.rb +68 -0
  285. data/spec/mongoid/validatable/presence_spec.rb +1 -1
  286. data/spec/mongoid/validatable/uniqueness_spec.rb +18 -9
  287. data/spec/mongoid/validatable_spec.rb +16 -0
  288. data/spec/rails/controller_extension/controller_runtime_spec.rb +110 -0
  289. data/spec/spec_helper.rb +101 -8
  290. data/spec/support/cluster_config.rb +158 -0
  291. data/spec/support/constraints.rb +101 -0
  292. data/spec/support/macros.rb +20 -0
  293. data/spec/support/session_registry.rb +50 -0
  294. data/spec/support/spec_config.rb +42 -0
  295. data.tar.gz.sig +0 -0
  296. metadata +163 -61
  297. metadata.gz.sig +0 -0
  298. data/lib/mongoid/clients/thread_options.rb +0 -19
  299. data/lib/mongoid/extensions/origin/regexp_raw.rb +0 -43
  300. data/lib/mongoid/railties/document.rb +0 -12
  301. data/spec/mongoid/railties/document_spec.rb +0 -24
@@ -280,7 +280,7 @@ describe Mongoid::Validatable::PresenceValidator do
280
280
  end
281
281
 
282
282
  it "does not save the relation" do
283
- expect { game.reload }.to raise_error
283
+ expect { game.reload }.to raise_error(Mongoid::Errors::DocumentNotFound)
284
284
  end
285
285
  end
286
286
  end
@@ -19,34 +19,41 @@ describe Mongoid::Validatable::UniquenessValidator do
19
19
  context "when persisting to another collection" do
20
20
 
21
21
  before do
22
- Dictionary.with(collection: "dicts").create(name: "websters")
22
+ Dictionary.with(collection: "dicts") do |klass|
23
+ klass.create(name: "websters")
24
+ end
23
25
  end
24
26
 
25
27
  context "when the document is not valid" do
26
28
 
27
29
  let(:websters) do
28
- Dictionary.with(collection: "dicts").new(name: "websters")
30
+ object = nil
31
+ valid = Dictionary.with(collection: "dicts") do |klass|
32
+ object = klass.new(name: "websters")
33
+ object.valid?
34
+ end
35
+ { :valid => valid, :object => object }
29
36
  end
30
37
 
31
38
  it "performs the validation on the correct collection" do
32
- expect(websters).to_not be_valid
39
+ expect(websters[:valid]).to be(false)
33
40
  end
34
41
 
35
42
  it "adds the uniqueness error" do
36
- websters.valid?
37
- expect(websters.errors[:name]).to_not be_nil
43
+ expect(websters[:object].errors[:name]).to_not be_nil
38
44
  end
39
45
 
40
46
  it "clears the persistence options in the thread local" do
41
- websters.valid?
42
- expect(Dictionary.persistence_options).to be_nil
47
+ expect(Dictionary.persistence_context).to eq(Mongoid::PersistenceContext.new(Dictionary))
43
48
  end
44
49
  end
45
50
 
46
51
  context "when the document is valid" do
47
52
 
48
53
  let(:oxford) do
49
- Dictionary.with(collection: "dicts").new(name: "oxford")
54
+ Dictionary.with(collection: "dicts") do |klass|
55
+ klass.new(name: "oxford")
56
+ end
50
57
  end
51
58
 
52
59
  it "performs the validation on the correct collection" do
@@ -2417,6 +2424,7 @@ describe Mongoid::Validatable::UniquenessValidator do
2417
2424
 
2418
2425
  before do
2419
2426
  Person.validates_uniqueness_of(:username)
2427
+ Person.index({ ssn: 1 }, { unique: true })
2420
2428
  Person.create_indexes
2421
2429
  end
2422
2430
 
@@ -2426,12 +2434,13 @@ describe Mongoid::Validatable::UniquenessValidator do
2426
2434
 
2427
2435
  after do
2428
2436
  Person.reset_callbacks(:validate)
2437
+ Person.collection.drop
2429
2438
  end
2430
2439
 
2431
2440
  it "transfers the options to the cloned client" do
2432
2441
  expect {
2433
2442
  Person.create!(ssn: "132-11-1111", username: "asdfsdfA")
2434
- }.to raise_error
2443
+ }.to raise_error(Mongo::Error::OperationFailure)
2435
2444
  end
2436
2445
  end
2437
2446
  end
@@ -69,6 +69,22 @@ describe Mongoid::Validatable do
69
69
  it "uses the provided context" do
70
70
  expect(account).to be_valid(:update)
71
71
  end
72
+
73
+ context 'when multiple contexts are provided' do
74
+
75
+ let(:princess) do
76
+ Princess.new
77
+ end
78
+
79
+ let(:validation) do
80
+ princess.valid?([:create, :update])
81
+ end
82
+
83
+ it 'validates using each context' do
84
+ expect(validation).to be(false)
85
+ expect(princess.errors.messages.keys).to eq([:color, :name])
86
+ end
87
+ end
72
88
  end
73
89
 
74
90
  context "when not provided a context" do
@@ -0,0 +1,110 @@
1
+ require "spec_helper"
2
+ require "mongoid/railties/controller_runtime"
3
+
4
+ describe "Mongoid::Railties::ControllerRuntime" do
5
+ controller_runtime = Mongoid::Railties::ControllerRuntime
6
+ collector = controller_runtime::Collector
7
+
8
+ def set_metric value
9
+ Thread.current["Mongoid.controller_runtime"] = value
10
+ end
11
+
12
+ def clear_metric!
13
+ set_metric 0
14
+ end
15
+
16
+ describe "Collector" do
17
+
18
+ it "stores the metric in thread-safe manner" do
19
+ clear_metric!
20
+ expect(collector.runtime).to eq(0)
21
+ set_metric 42
22
+ expect(collector.runtime).to eq(42)
23
+ end
24
+
25
+ it "sets metric on both succeeded and failed" do
26
+ instance = collector.new
27
+ event_payload = OpenStruct.new duration: 42
28
+
29
+ clear_metric!
30
+ instance.succeeded event_payload
31
+ expect(collector.runtime).to eq(42)
32
+
33
+ clear_metric!
34
+ instance.failed event_payload
35
+ expect(collector.runtime).to eq(42)
36
+ end
37
+
38
+ it "resets the metric and returns the value" do
39
+ clear_metric!
40
+ expect(collector.reset_runtime).to eq(0)
41
+ set_metric 42
42
+ expect(collector.reset_runtime).to eq(42)
43
+ expect(collector.runtime).to eq(0)
44
+ end
45
+
46
+ end
47
+
48
+ reference_controller_class = Class.new do
49
+ def process_action *_
50
+ @process_action = true
51
+ end
52
+
53
+ def cleanup_view_runtime *_
54
+ @cleanup_view_runtime.call
55
+ end
56
+
57
+ def append_info_to_payload *_
58
+ @append_info_to_payload = true
59
+ end
60
+
61
+ def self.log_process_action *_
62
+ @log_process_action.call
63
+ end
64
+ end
65
+
66
+ controller_class = Class.new reference_controller_class do
67
+ include controller_runtime::ControllerExtension
68
+ end
69
+
70
+ let(:controller){ controller_class.new }
71
+
72
+ it "resets the metric before each action" do
73
+ set_metric 42
74
+ controller.send(:process_action, 'foo')
75
+ expect(collector.runtime).to be(0)
76
+ expect(controller.instance_variable_get "@process_action").to be(true)
77
+ end
78
+
79
+ it "strips the metric of other sources of the runtime" do
80
+ set_metric 1
81
+ controller.instance_variable_set "@cleanup_view_runtime", ->{
82
+ controller.instance_variable_set "@cleanup_view_runtime", true
83
+ set_metric 13
84
+ 42
85
+ }
86
+ returned = controller.send :cleanup_view_runtime
87
+ expect(controller.instance_variable_get "@cleanup_view_runtime").to be(true)
88
+ expect(controller.mongoid_runtime).to eq(14)
89
+ expect(returned).to be(29)
90
+ end
91
+
92
+ it "appends the metric to payload" do
93
+ payload = {}
94
+ set_metric 42
95
+ controller.send :append_info_to_payload, payload
96
+ expect(controller.instance_variable_get "@append_info_to_payload").to be(true)
97
+ expect(payload[:mongoid_runtime]).to eq(42)
98
+ end
99
+
100
+ it "adds metric to log message" do
101
+ controller_class.instance_variable_set "@log_process_action", ->{
102
+ controller_class.instance_variable_set "@log_process_action", true
103
+ []
104
+ }
105
+ messages = controller_class.log_process_action mongoid_runtime: 42.101
106
+ expect(controller_class.instance_variable_get "@log_process_action").to be(true)
107
+ expect(messages).to eq(["MongoDB: 42.1ms"])
108
+ end
109
+
110
+ end
data/spec/spec_helper.rb CHANGED
@@ -34,9 +34,14 @@ end
34
34
 
35
35
  require 'support/authorization'
36
36
  require 'support/expectations'
37
+ require 'support/macros'
38
+ require 'support/spec_config'
39
+ require 'support/cluster_config'
40
+ require 'support/constraints'
41
+ require 'support/session_registry'
37
42
 
38
43
  # Give MongoDB time to start up on the travis ci environment.
39
- if (ENV['CI'] == 'travis')
44
+ if (ENV['CI'] == 'travis' || ENV['CI'] == 'evergreen')
40
45
  starting = true
41
46
  client = Mongo::Client.new(['127.0.0.1:27017'])
42
47
  while starting
@@ -64,6 +69,9 @@ CONFIG = {
64
69
  auth_source: Mongo::Database::ADMIN,
65
70
  }
66
71
  }
72
+ },
73
+ options: {
74
+ belongs_to_required_by_default: false
67
75
  }
68
76
  }
69
77
 
@@ -71,24 +79,29 @@ def non_legacy_server?
71
79
  Mongoid::Clients.default.cluster.servers.first.features.write_command_enabled?
72
80
  end
73
81
 
82
+ def testing_replica_set?
83
+ Mongoid::Clients.default.cluster.replica_set?
84
+ end
85
+
86
+ def collation_supported?
87
+ Mongoid::Clients.default.cluster.next_primary.features.collation_enabled?
88
+ end
89
+ alias :decimal128_supported? :collation_supported?
90
+
74
91
  def testing_locally?
75
92
  !(ENV['CI'] == 'travis')
76
93
  end
77
94
 
78
- def testing_replica_set?
79
- Mongoid::Clients.default.cluster.replica_set?
95
+ def array_filters_supported?
96
+ Mongoid::Clients.default.cluster.next_primary.features.array_filters_enabled?
80
97
  end
98
+ alias :sessions_supported? :array_filters_supported?
81
99
 
82
100
  # Set the database that the spec suite connects to.
83
101
  Mongoid.configure do |config|
84
102
  config.load_configuration(CONFIG)
85
103
  end
86
104
 
87
- def collation_supported?
88
- Mongoid::Clients.default.cluster.next_primary.features.collation_enabled?
89
- end
90
- alias :decimal128_supported? :collation_supported?
91
-
92
105
  # Autoload every model for the test suite that sits in spec/app/models.
93
106
  Dir[ File.join(MODELS, "*.rb") ].sort.each do |file|
94
107
  name = File.basename(file, ".rb")
@@ -105,6 +118,12 @@ module MyApp
105
118
  end
106
119
  end
107
120
 
121
+ module Mongoid
122
+ class Query
123
+ include Mongoid::Criteria::Queryable
124
+ end
125
+ end
126
+
108
127
  ActiveSupport::Inflector.inflections do |inflect|
109
128
  inflect.irregular("canvas", "canvases")
110
129
  inflect.singular("address_components", "address_component")
@@ -113,8 +132,13 @@ end
113
132
  I18n.config.enforce_available_locales = false
114
133
 
115
134
  RSpec.configure do |config|
135
+ config.expect_with(:rspec) do |c|
136
+ c.syntax = [:should, :expect]
137
+ end
138
+
116
139
  config.raise_errors_for_deprecations!
117
140
  config.include(Mongoid::Expectations)
141
+ config.extend(Constraints)
118
142
 
119
143
  config.before(:suite) do
120
144
  client = Mongo::Client.new(["#{HOST}:#{PORT}"])
@@ -132,3 +156,72 @@ RSpec.configure do |config|
132
156
  Mongoid.purge!
133
157
  end
134
158
  end
159
+
160
+ # A subscriber to be used with the Ruby driver for testing.
161
+ #
162
+ # @since 6.4.0
163
+ class EventSubscriber
164
+
165
+ # The started events.
166
+ #
167
+ # @since 6.4.0
168
+ attr_reader :started_events
169
+
170
+ # The succeeded events.
171
+ #
172
+ # @since 6.4.0
173
+ attr_reader :succeeded_events
174
+
175
+ # The failed events.
176
+ #
177
+ # @since 6.4.0
178
+ attr_reader :failed_events
179
+
180
+ # Create the test event subscriber.
181
+ #
182
+ # @example Create the subscriber
183
+ # EventSubscriber.new
184
+ #
185
+ # @since 6.4.0
186
+ def initialize
187
+ @started_events = []
188
+ @succeeded_events = []
189
+ @failed_events = []
190
+ end
191
+
192
+ # Cache the succeeded event.
193
+ #
194
+ # @param [ Event ] event The event.
195
+ #
196
+ # @since 6.4.0
197
+ def succeeded(event)
198
+ @succeeded_events.push(event)
199
+ end
200
+
201
+ # Cache the started event.
202
+ #
203
+ # @param [ Event ] event The event.
204
+ #
205
+ # @since 6.4.0
206
+ def started(event)
207
+ @started_events.push(event)
208
+ end
209
+
210
+ # Cache the failed event.
211
+ #
212
+ # @param [ Event ] event The event.
213
+ #
214
+ # @since 6.4.0
215
+ def failed(event)
216
+ @failed_events.push(event)
217
+ end
218
+
219
+ # Clear all cached events.
220
+ #
221
+ # @since 6.4.0
222
+ def clear_events!
223
+ @started_events = []
224
+ @succeeded_events = []
225
+ @failed_events = []
226
+ end
227
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'singleton'
5
+
6
+ class ClusterConfig
7
+ include Singleton
8
+
9
+ def single_server?
10
+ determine_cluster_config
11
+ @single_server
12
+ end
13
+
14
+ def replica_set_name
15
+ determine_cluster_config
16
+ @replica_set_name
17
+ end
18
+
19
+ def server_version
20
+ determine_cluster_config
21
+ @server_version
22
+ end
23
+
24
+ def short_server_version
25
+ server_version.split('.')[0..1].join('.')
26
+ end
27
+
28
+ def fcv
29
+ determine_cluster_config
30
+ @fcv
31
+ end
32
+
33
+ # Per https://jira.mongodb.org/browse/SERVER-39052, working with FCV
34
+ # in sharded topologies is annoying. Also, FCV doesn't exist in servers
35
+ # less than 3.4. This method returns FCV on 3.4+ servers when in single
36
+ # or RS topologies, and otherwise returns the major.minor server version.
37
+ def fcv_ish
38
+ if server_version >= '3.4' && topology != :sharded
39
+ fcv
40
+ else
41
+ if short_server_version == '4.1'
42
+ '4.2'
43
+ else
44
+ short_server_version
45
+ end
46
+ end
47
+ end
48
+
49
+ def primary_address
50
+ determine_cluster_config
51
+ @primary_address
52
+ end
53
+
54
+ def primary_address_str
55
+ determine_cluster_config
56
+ @primary_address.seed
57
+ end
58
+
59
+ def primary_address_host
60
+ both = primary_address_str
61
+ both.split(':').first
62
+ end
63
+
64
+ def primary_address_port
65
+ both = primary_address_str
66
+ both.split(':')[1] || 27017
67
+ end
68
+
69
+ def primary_description
70
+ determine_cluster_config
71
+ @primary_description
72
+ end
73
+
74
+ # Try running a command on the admin database to see if the mongod was
75
+ # started with auth.
76
+ def auth_enabled?
77
+ if @auth_enabled.nil?
78
+ @auth_enabled = begin
79
+ basic_client.use(:admin).command(getCmdLineOpts: 1).first["argv"].include?("--auth")
80
+ rescue => e
81
+ e.message =~ /(not authorized)|(unauthorized)|(no users authenticated)|(requires authentication)/
82
+ end
83
+ end
84
+ @auth_enabled
85
+ end
86
+
87
+ def topology
88
+ determine_cluster_config
89
+ @topology
90
+ end
91
+
92
+ def storage_engine
93
+ @storage_engine ||= begin
94
+ # 2.6 does not have wired tiger
95
+ if short_server_version == '2.6'
96
+ :mmapv1
97
+ else
98
+ client = ClientRegistry.instance.global_client('root_authorized')
99
+ if topology == :sharded
100
+ shards = client.use(:admin).command(listShards: 1).first
101
+ shard = shards['shards'].first
102
+ address_str = shard['host'].sub(/^.*\//, '').sub(/,.*/, '')
103
+ client = ClusterTools.instance.direct_client(address_str,
104
+ SpecConfig.instance.test_options.merge(SpecConfig.instance.auth_options).merge(connect: :direct))
105
+ end
106
+ rv = client.use(:admin).command(serverStatus: 1).first
107
+ rv = rv['storageEngine']['name']
108
+ rv_map = {
109
+ 'wiredTiger' => :wired_tiger,
110
+ 'mmapv1' => :mmapv1,
111
+ }
112
+ rv_map[rv] || rv
113
+ end
114
+ end
115
+ end
116
+
117
+ private
118
+
119
+ def determine_cluster_config
120
+ return if @primary_address
121
+
122
+ # Run all commands to figure out the cluster configuration from the same
123
+ # client. This is somewhat wasteful when running a single test, but reduces
124
+ # test runtime for the suite overall because all commands are sent on the
125
+ # same connection rather than each command connecting to the cluster by
126
+ # itself.
127
+ client = Mongoid::Clients.default
128
+
129
+ primary = client.cluster.next_primary
130
+ @primary_address = primary.address
131
+ @primary_description = primary.description
132
+ @replica_set_name = client.cluster.topology.replica_set_name
133
+
134
+ @topology ||= begin
135
+ topology = client.cluster.topology.class.name.sub(/.*::/, '')
136
+ topology = topology.gsub(/([A-Z])/) { |match| '_' + match.downcase }.sub(/^_/, '')
137
+ if topology =~ /^replica_set/
138
+ topology = 'replica_set'
139
+ end
140
+ topology.to_sym
141
+ end
142
+
143
+ @single_server = client.cluster.send(:servers_list).length == 1
144
+
145
+ @server_version = client.database.command(buildInfo: 1).first['version']
146
+
147
+ if @topology != :sharded && short_server_version >= '3.4'
148
+ rv = client.use(:admin).command(getParameter: 1, featureCompatibilityVersion: 1).first['featureCompatibilityVersion']
149
+ @fcv = rv['version'] || rv
150
+ end
151
+ end
152
+
153
+ def basic_client
154
+ # Do not cache the result here so that if the client gets closed,
155
+ # client registry reconnects it in subsequent tests
156
+ ClientRegistry.instance.global_client('basic')
157
+ end
158
+ end
@@ -0,0 +1,101 @@
1
+ module Constraints
2
+ RAILS_VERSION = ActiveSupport.version.to_s.split('.')[0..1].join('.').freeze
3
+
4
+ def min_rails_version(version)
5
+ unless version =~ /^\d+\.\d+$/
6
+ raise ArgumentError, "Version can only be major.minor: #{version}"
7
+ end
8
+
9
+ before do
10
+ if version > RAILS_VERSION
11
+ skip "Rails version #{version} or higher required, we have #{RAILS_VERSION}"
12
+ end
13
+ end
14
+ end
15
+
16
+ def max_rails_version(version)
17
+ unless version =~ /^\d+\.\d+$/
18
+ raise ArgumentError, "Version can only be major.minor: #{version}"
19
+ end
20
+
21
+ before do
22
+ if version < RAILS_VERSION
23
+ skip "Rails version #{version} or lower required, we have #{RAILS_VERSION}"
24
+ end
25
+ end
26
+ end
27
+
28
+ def min_server_version(version)
29
+ unless version =~ /^\d+\.\d+$/
30
+ raise ArgumentError, "Version can only be major.minor: #{version}"
31
+ end
32
+
33
+ before do
34
+ if version > ClusterConfig.instance.server_version
35
+ skip "Server version #{version} or higher required, we have #{ClusterConfig.instance.server_version}"
36
+ end
37
+ end
38
+ end
39
+
40
+ def max_server_version(version)
41
+ unless version =~ /^\d+\.\d+$/
42
+ raise ArgumentError, "Version can only be major.minor: #{version}"
43
+ end
44
+
45
+ before do
46
+ if version < ClusterConfig.instance.short_server_version
47
+ skip "Server version #{version} or lower required, we have #{ClusterConfig.instance.server_version}"
48
+ end
49
+ end
50
+ end
51
+
52
+ def require_topology(*topologies)
53
+ topologies = topologies.map { |t| t.to_s }
54
+ invalid_topologies = topologies - %w(single replica_set sharded)
55
+ unless invalid_topologies.empty?
56
+ raise ArgumentError, "Invalid topologies requested: #{invalid_topologies.join(', ')}"
57
+ end
58
+ before do
59
+ topology = Mongoid.default_client.cluster.topology.class.name.sub(/.*::/, '')
60
+ topology = topology.gsub(/([A-Z])/) { |match| '_' + match.downcase }.sub(/^_/, '')
61
+ if topology =~ /^replica_set/
62
+ topology = 'replica_set'
63
+ end
64
+ unless topologies.include?(topology)
65
+ skip "Topology #{topologies.join(' or ')} required, we have #{topology}"
66
+ end
67
+ end
68
+ end
69
+
70
+ def max_example_run_time(timeout)
71
+ around do |example|
72
+ TimeoutInterrupt.timeout(timeout) do
73
+ example.run
74
+ end
75
+ end
76
+ end
77
+
78
+ def require_transaction_support
79
+ min_server_version '4.0'
80
+ require_topology :replica_set
81
+ end
82
+
83
+ def require_scram_sha_256_support
84
+ before do
85
+ $mongo_server_features ||= begin
86
+ scanned_client_server!.features
87
+ end
88
+ unless $mongo_server_features.scram_sha_256_enabled?
89
+ skip "SCRAM SHA 256 is not enabled on the server"
90
+ end
91
+ end
92
+ end
93
+
94
+ def require_ssl
95
+ before do
96
+ unless SpecConfig.instance.ssl?
97
+ skip "SSL not enabled"
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongoid
4
+ module Macros
5
+ def use_spec_mongoid_config
6
+ around do |example|
7
+ config_path = File.join(File.dirname(__FILE__), "..", "config", "mongoid.yml")
8
+
9
+ Mongoid::Clients.clear
10
+ Mongoid.load!(config_path, :test)
11
+
12
+ begin
13
+ example.run
14
+ ensure
15
+ Mongoid::Config.reset
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,50 @@
1
+ require 'singleton'
2
+
3
+ module Mongo
4
+ class Client
5
+ alias :get_session_without_tracking :get_session
6
+
7
+ def get_session(options = {})
8
+ get_session_without_tracking(options).tap do |session|
9
+ SessionRegistry.instance.register(session)
10
+ end
11
+ end
12
+ end
13
+
14
+ class Session
15
+ alias :end_session_without_tracking :end_session
16
+
17
+ def end_session
18
+ SessionRegistry.instance.unregister(self)
19
+ end_session_without_tracking
20
+ end
21
+ end
22
+ end
23
+
24
+
25
+ class SessionRegistry
26
+ include Singleton
27
+
28
+ def initialize
29
+ @registry = {}
30
+ end
31
+
32
+ def register(session)
33
+ @registry[session.session_id] = session if session
34
+ end
35
+
36
+ def unregister(session)
37
+ @registry.delete(session.session_id)
38
+ end
39
+
40
+ def verify_sessions_ended!
41
+ unless @registry.empty?
42
+ sessions = @registry.map { |_, session| session }
43
+ raise "Session registry contains live sessions: #{sessions.join(', ')}"
44
+ end
45
+ end
46
+
47
+ def clear_registry
48
+ @registry = {}
49
+ end
50
+ end