mongoid 7.0.6 → 7.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (849) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG.md +13 -13
  5. data/README.md +23 -17
  6. data/Rakefile +2 -0
  7. data/lib/config/locales/en.yml +60 -51
  8. data/lib/mongoid.rb +10 -2
  9. data/lib/mongoid/association.rb +9 -6
  10. data/lib/mongoid/association/accessors.rb +95 -52
  11. data/lib/mongoid/association/bindable.rb +7 -5
  12. data/lib/mongoid/association/builders.rb +4 -3
  13. data/lib/mongoid/association/constrainable.rb +4 -2
  14. data/lib/mongoid/association/depending.rb +5 -2
  15. data/lib/mongoid/association/eager_loadable.rb +2 -0
  16. data/lib/mongoid/association/embedded.rb +3 -0
  17. data/lib/mongoid/association/embedded/batchable.rb +4 -2
  18. data/lib/mongoid/association/embedded/cyclic.rb +8 -6
  19. data/lib/mongoid/association/embedded/embedded_in.rb +6 -3
  20. data/lib/mongoid/association/embedded/embedded_in/binding.rb +4 -1
  21. data/lib/mongoid/association/embedded/embedded_in/buildable.rb +8 -2
  22. data/lib/mongoid/association/embedded/embedded_in/proxy.rb +15 -12
  23. data/lib/mongoid/association/embedded/embeds_many.rb +9 -5
  24. data/lib/mongoid/association/embedded/embeds_many/binding.rb +5 -2
  25. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +9 -3
  26. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +50 -47
  27. data/lib/mongoid/association/embedded/embeds_one.rb +9 -5
  28. data/lib/mongoid/association/embedded/embeds_one/binding.rb +5 -2
  29. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +10 -4
  30. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +17 -14
  31. data/lib/mongoid/association/macros.rb +38 -36
  32. data/lib/mongoid/association/many.rb +14 -11
  33. data/lib/mongoid/association/marshalable.rb +3 -1
  34. data/lib/mongoid/association/nested.rb +4 -1
  35. data/lib/mongoid/association/nested/many.rb +17 -15
  36. data/lib/mongoid/association/nested/nested_buildable.rb +6 -3
  37. data/lib/mongoid/association/nested/one.rb +11 -9
  38. data/lib/mongoid/association/one.rb +3 -1
  39. data/lib/mongoid/association/options.rb +9 -6
  40. data/lib/mongoid/association/proxy.rb +31 -14
  41. data/lib/mongoid/association/referenced.rb +3 -0
  42. data/lib/mongoid/association/referenced/auto_save.rb +4 -1
  43. data/lib/mongoid/association/referenced/belongs_to.rb +6 -3
  44. data/lib/mongoid/association/referenced/belongs_to/binding.rb +3 -1
  45. data/lib/mongoid/association/referenced/belongs_to/buildable.rb +6 -3
  46. data/lib/mongoid/association/referenced/belongs_to/eager.rb +40 -2
  47. data/lib/mongoid/association/referenced/belongs_to/proxy.rb +16 -14
  48. data/lib/mongoid/association/referenced/counter_cache.rb +2 -0
  49. data/lib/mongoid/association/referenced/eager.rb +37 -15
  50. data/lib/mongoid/association/referenced/has_and_belongs_to_many.rb +13 -6
  51. data/lib/mongoid/association/referenced/has_and_belongs_to_many/binding.rb +12 -6
  52. data/lib/mongoid/association/referenced/has_and_belongs_to_many/buildable.rb +5 -2
  53. data/lib/mongoid/association/referenced/has_and_belongs_to_many/eager.rb +2 -0
  54. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +25 -18
  55. data/lib/mongoid/association/referenced/has_many.rb +7 -4
  56. data/lib/mongoid/association/referenced/has_many/binding.rb +3 -1
  57. data/lib/mongoid/association/referenced/has_many/buildable.rb +5 -2
  58. data/lib/mongoid/association/referenced/has_many/eager.rb +2 -0
  59. data/lib/mongoid/association/referenced/has_many/enumerable.rb +11 -4
  60. data/lib/mongoid/association/referenced/has_many/proxy.rb +46 -43
  61. data/lib/mongoid/association/referenced/has_one.rb +5 -2
  62. data/lib/mongoid/association/referenced/has_one/binding.rb +4 -2
  63. data/lib/mongoid/association/referenced/has_one/buildable.rb +8 -1
  64. data/lib/mongoid/association/referenced/has_one/eager.rb +2 -0
  65. data/lib/mongoid/association/referenced/has_one/nested_builder.rb +11 -9
  66. data/lib/mongoid/association/referenced/has_one/proxy.rb +17 -15
  67. data/lib/mongoid/association/referenced/syncable.rb +7 -5
  68. data/lib/mongoid/association/reflections.rb +7 -5
  69. data/lib/mongoid/association/relatable.rb +17 -14
  70. data/lib/mongoid/atomic.rb +15 -3
  71. data/lib/mongoid/atomic/modifiers.rb +24 -1
  72. data/lib/mongoid/atomic/paths.rb +2 -0
  73. data/lib/mongoid/atomic/paths/embedded.rb +2 -0
  74. data/lib/mongoid/atomic/paths/embedded/many.rb +4 -2
  75. data/lib/mongoid/atomic/paths/embedded/one.rb +4 -2
  76. data/lib/mongoid/atomic/paths/root.rb +4 -2
  77. data/lib/mongoid/attributes.rb +14 -13
  78. data/lib/mongoid/attributes/dynamic.rb +2 -0
  79. data/lib/mongoid/attributes/nested.rb +7 -5
  80. data/lib/mongoid/attributes/processing.rb +8 -6
  81. data/lib/mongoid/attributes/readonly.rb +3 -1
  82. data/lib/mongoid/cacheable.rb +3 -1
  83. data/lib/mongoid/changeable.rb +3 -1
  84. data/lib/mongoid/clients.rb +2 -0
  85. data/lib/mongoid/clients/factory.rb +1 -0
  86. data/lib/mongoid/clients/options.rb +2 -0
  87. data/lib/mongoid/clients/sessions.rb +3 -0
  88. data/lib/mongoid/clients/storage_options.rb +2 -0
  89. data/lib/mongoid/clients/validators.rb +2 -0
  90. data/lib/mongoid/clients/validators/storage.rb +2 -0
  91. data/lib/mongoid/composable.rb +4 -2
  92. data/lib/mongoid/config.rb +53 -10
  93. data/lib/mongoid/config/environment.rb +2 -0
  94. data/lib/mongoid/config/options.rb +20 -12
  95. data/lib/mongoid/config/validators.rb +2 -0
  96. data/lib/mongoid/config/validators/client.rb +2 -0
  97. data/lib/mongoid/config/validators/option.rb +2 -0
  98. data/lib/mongoid/contextual.rb +7 -4
  99. data/lib/mongoid/contextual/aggregable/memory.rb +3 -1
  100. data/lib/mongoid/contextual/aggregable/mongo.rb +3 -1
  101. data/lib/mongoid/contextual/atomic.rb +24 -6
  102. data/lib/mongoid/contextual/command.rb +2 -0
  103. data/lib/mongoid/contextual/geo_near.rb +5 -3
  104. data/lib/mongoid/contextual/map_reduce.rb +5 -2
  105. data/lib/mongoid/contextual/memory.rb +4 -2
  106. data/lib/mongoid/contextual/mongo.rb +6 -1
  107. data/lib/mongoid/contextual/none.rb +2 -0
  108. data/lib/mongoid/contextual/queryable.rb +2 -0
  109. data/lib/mongoid/copyable.rb +3 -1
  110. data/lib/mongoid/criteria.rb +25 -4
  111. data/lib/mongoid/criteria/findable.rb +11 -1
  112. data/lib/mongoid/criteria/includable.rb +8 -6
  113. data/lib/mongoid/criteria/inspectable.rb +2 -0
  114. data/lib/mongoid/criteria/marshalable.rb +2 -0
  115. data/lib/mongoid/criteria/modifiable.rb +4 -1
  116. data/lib/mongoid/criteria/options.rb +2 -0
  117. data/lib/mongoid/criteria/permission.rb +2 -0
  118. data/lib/mongoid/criteria/queryable.rb +7 -2
  119. data/lib/mongoid/criteria/queryable/aggregable.rb +2 -0
  120. data/lib/mongoid/criteria/queryable/expandable.rb +93 -0
  121. data/lib/mongoid/criteria/queryable/extensions.rb +2 -0
  122. data/lib/mongoid/criteria/queryable/extensions/array.rb +3 -1
  123. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +3 -1
  124. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +2 -0
  125. data/lib/mongoid/criteria/queryable/extensions/date.rb +3 -1
  126. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +5 -6
  127. data/lib/mongoid/criteria/queryable/extensions/hash.rb +3 -1
  128. data/lib/mongoid/criteria/queryable/extensions/nil_class.rb +3 -1
  129. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +4 -2
  130. data/lib/mongoid/criteria/queryable/extensions/object.rb +3 -1
  131. data/lib/mongoid/criteria/queryable/extensions/range.rb +3 -1
  132. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +7 -5
  133. data/lib/mongoid/criteria/queryable/extensions/set.rb +3 -1
  134. data/lib/mongoid/criteria/queryable/extensions/string.rb +3 -1
  135. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +3 -1
  136. data/lib/mongoid/criteria/queryable/extensions/time.rb +4 -2
  137. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +3 -1
  138. data/lib/mongoid/criteria/queryable/key.rb +34 -6
  139. data/lib/mongoid/criteria/queryable/macroable.rb +3 -1
  140. data/lib/mongoid/criteria/queryable/mergeable.rb +153 -11
  141. data/lib/mongoid/criteria/queryable/optional.rb +5 -3
  142. data/lib/mongoid/criteria/queryable/options.rb +2 -0
  143. data/lib/mongoid/criteria/queryable/pipeline.rb +5 -2
  144. data/lib/mongoid/criteria/queryable/selectable.rb +374 -90
  145. data/lib/mongoid/criteria/queryable/selector.rb +38 -5
  146. data/lib/mongoid/criteria/queryable/smash.rb +2 -0
  147. data/lib/mongoid/criteria/queryable/storable.rb +233 -0
  148. data/lib/mongoid/criteria/scopable.rb +2 -0
  149. data/lib/mongoid/document.rb +2 -0
  150. data/lib/mongoid/equality.rb +3 -2
  151. data/lib/mongoid/errors.rb +3 -0
  152. data/lib/mongoid/errors/ambiguous_relationship.rb +4 -2
  153. data/lib/mongoid/errors/callback.rb +2 -0
  154. data/lib/mongoid/errors/criteria_argument_required.rb +19 -0
  155. data/lib/mongoid/errors/delete_restriction.rb +3 -2
  156. data/lib/mongoid/errors/document_not_destroyed.rb +2 -0
  157. data/lib/mongoid/errors/document_not_found.rb +2 -0
  158. data/lib/mongoid/errors/eager_load.rb +6 -2
  159. data/lib/mongoid/errors/in_memory_collation_not_supported.rb +2 -0
  160. data/lib/mongoid/errors/invalid_collection.rb +2 -0
  161. data/lib/mongoid/errors/invalid_config_option.rb +2 -0
  162. data/lib/mongoid/errors/invalid_dependent_strategy.rb +2 -0
  163. data/lib/mongoid/errors/invalid_field.rb +2 -0
  164. data/lib/mongoid/errors/invalid_field_option.rb +2 -0
  165. data/lib/mongoid/errors/invalid_find.rb +2 -0
  166. data/lib/mongoid/errors/invalid_includes.rb +2 -0
  167. data/lib/mongoid/errors/invalid_index.rb +2 -0
  168. data/lib/mongoid/errors/invalid_options.rb +4 -2
  169. data/lib/mongoid/errors/invalid_path.rb +2 -0
  170. data/lib/mongoid/errors/invalid_persistence_option.rb +2 -0
  171. data/lib/mongoid/errors/invalid_relation.rb +4 -2
  172. data/lib/mongoid/errors/invalid_relation_option.rb +4 -2
  173. data/lib/mongoid/errors/invalid_scope.rb +2 -0
  174. data/lib/mongoid/errors/invalid_session_use.rb +2 -0
  175. data/lib/mongoid/errors/invalid_set_polymorphic_relation.rb +6 -4
  176. data/lib/mongoid/errors/invalid_storage_options.rb +2 -0
  177. data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
  178. data/lib/mongoid/errors/invalid_time.rb +2 -0
  179. data/lib/mongoid/errors/invalid_value.rb +2 -0
  180. data/lib/mongoid/errors/inverse_not_found.rb +3 -1
  181. data/lib/mongoid/errors/mixed_client_configuration.rb +2 -0
  182. data/lib/mongoid/errors/mixed_relations.rb +2 -0
  183. data/lib/mongoid/errors/mongoid_error.rb +2 -0
  184. data/lib/mongoid/errors/nested_attributes_metadata_not_found.rb +3 -1
  185. data/lib/mongoid/errors/no_client_config.rb +2 -0
  186. data/lib/mongoid/errors/no_client_database.rb +2 -0
  187. data/lib/mongoid/errors/no_client_hosts.rb +2 -0
  188. data/lib/mongoid/errors/no_clients_config.rb +2 -0
  189. data/lib/mongoid/errors/no_default_client.rb +2 -0
  190. data/lib/mongoid/errors/no_environment.rb +2 -0
  191. data/lib/mongoid/errors/no_map_reduce_output.rb +2 -0
  192. data/lib/mongoid/errors/no_metadata.rb +2 -0
  193. data/lib/mongoid/errors/no_parent.rb +2 -0
  194. data/lib/mongoid/errors/readonly_attribute.rb +2 -0
  195. data/lib/mongoid/errors/readonly_document.rb +2 -0
  196. data/lib/mongoid/errors/scope_overwrite.rb +2 -0
  197. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +3 -0
  198. data/lib/mongoid/errors/unknown_attribute.rb +2 -0
  199. data/lib/mongoid/errors/unknown_model.rb +2 -0
  200. data/lib/mongoid/errors/unsaved_document.rb +2 -0
  201. data/lib/mongoid/errors/unsupported_javascript.rb +2 -0
  202. data/lib/mongoid/errors/validations.rb +2 -0
  203. data/lib/mongoid/evolvable.rb +3 -1
  204. data/lib/mongoid/extensions.rb +2 -0
  205. data/lib/mongoid/extensions/array.rb +23 -6
  206. data/lib/mongoid/extensions/big_decimal.rb +2 -0
  207. data/lib/mongoid/extensions/boolean.rb +2 -0
  208. data/lib/mongoid/extensions/date.rb +13 -3
  209. data/lib/mongoid/extensions/date_time.rb +4 -3
  210. data/lib/mongoid/extensions/decimal128.rb +2 -0
  211. data/lib/mongoid/extensions/false_class.rb +2 -0
  212. data/lib/mongoid/extensions/float.rb +5 -3
  213. data/lib/mongoid/extensions/hash.rb +51 -9
  214. data/lib/mongoid/extensions/integer.rb +5 -3
  215. data/lib/mongoid/extensions/module.rb +2 -0
  216. data/lib/mongoid/extensions/nil_class.rb +2 -0
  217. data/lib/mongoid/extensions/object.rb +16 -4
  218. data/lib/mongoid/extensions/object_id.rb +2 -0
  219. data/lib/mongoid/extensions/range.rb +2 -0
  220. data/lib/mongoid/extensions/regexp.rb +3 -1
  221. data/lib/mongoid/extensions/set.rb +2 -0
  222. data/lib/mongoid/extensions/string.rb +18 -9
  223. data/lib/mongoid/extensions/symbol.rb +2 -0
  224. data/lib/mongoid/extensions/time.rb +14 -0
  225. data/lib/mongoid/extensions/time_with_zone.rb +14 -0
  226. data/lib/mongoid/extensions/true_class.rb +2 -0
  227. data/lib/mongoid/factory.rb +28 -5
  228. data/lib/mongoid/fields.rb +5 -2
  229. data/lib/mongoid/fields/foreign_key.rb +3 -1
  230. data/lib/mongoid/fields/localized.rb +2 -0
  231. data/lib/mongoid/fields/standard.rb +6 -3
  232. data/lib/mongoid/fields/validators.rb +2 -0
  233. data/lib/mongoid/fields/validators/macro.rb +13 -2
  234. data/lib/mongoid/findable.rb +9 -4
  235. data/lib/mongoid/indexable.rb +3 -1
  236. data/lib/mongoid/indexable/specification.rb +3 -1
  237. data/lib/mongoid/indexable/validators/options.rb +2 -0
  238. data/lib/mongoid/inspectable.rb +3 -1
  239. data/lib/mongoid/interceptable.rb +9 -3
  240. data/lib/mongoid/loggable.rb +13 -7
  241. data/lib/mongoid/matchable.rb +2 -0
  242. data/lib/mongoid/matchable/all.rb +2 -0
  243. data/lib/mongoid/matchable/and.rb +2 -0
  244. data/lib/mongoid/matchable/default.rb +2 -0
  245. data/lib/mongoid/matchable/elem_match.rb +2 -0
  246. data/lib/mongoid/matchable/eq.rb +1 -0
  247. data/lib/mongoid/matchable/exists.rb +2 -0
  248. data/lib/mongoid/matchable/gt.rb +2 -0
  249. data/lib/mongoid/matchable/gte.rb +2 -0
  250. data/lib/mongoid/matchable/in.rb +2 -0
  251. data/lib/mongoid/matchable/lt.rb +2 -0
  252. data/lib/mongoid/matchable/lte.rb +2 -0
  253. data/lib/mongoid/matchable/ne.rb +2 -0
  254. data/lib/mongoid/matchable/nin.rb +2 -0
  255. data/lib/mongoid/matchable/nor.rb +2 -1
  256. data/lib/mongoid/matchable/or.rb +2 -0
  257. data/lib/mongoid/matchable/regexp.rb +5 -2
  258. data/lib/mongoid/matchable/size.rb +2 -0
  259. data/lib/mongoid/persistable.rb +133 -15
  260. data/lib/mongoid/persistable/creatable.rb +3 -1
  261. data/lib/mongoid/persistable/deletable.rb +3 -1
  262. data/lib/mongoid/persistable/destroyable.rb +3 -1
  263. data/lib/mongoid/persistable/incrementable.rb +6 -2
  264. data/lib/mongoid/persistable/logical.rb +4 -1
  265. data/lib/mongoid/persistable/poppable.rb +3 -1
  266. data/lib/mongoid/persistable/pullable.rb +3 -1
  267. data/lib/mongoid/persistable/pushable.rb +14 -3
  268. data/lib/mongoid/persistable/renamable.rb +10 -3
  269. data/lib/mongoid/persistable/savable.rb +3 -1
  270. data/lib/mongoid/persistable/settable.rb +3 -1
  271. data/lib/mongoid/persistable/unsettable.rb +8 -3
  272. data/lib/mongoid/persistable/updatable.rb +3 -1
  273. data/lib/mongoid/persistable/upsertable.rb +3 -1
  274. data/lib/mongoid/persistence_context.rb +27 -15
  275. data/lib/mongoid/positional.rb +2 -0
  276. data/lib/mongoid/query_cache.rb +10 -9
  277. data/lib/mongoid/railtie.rb +3 -1
  278. data/lib/mongoid/railties/controller_runtime.rb +4 -1
  279. data/lib/mongoid/railties/database.rake +9 -0
  280. data/lib/mongoid/reloadable.rb +4 -2
  281. data/lib/mongoid/scopable.rb +2 -1
  282. data/lib/mongoid/selectable.rb +3 -1
  283. data/lib/mongoid/serializable.rb +23 -13
  284. data/lib/mongoid/shardable.rb +59 -5
  285. data/lib/mongoid/stateful.rb +3 -1
  286. data/lib/mongoid/tasks/database.rake +12 -5
  287. data/lib/mongoid/tasks/database.rb +50 -0
  288. data/lib/mongoid/threaded.rb +2 -0
  289. data/lib/mongoid/threaded/lifecycle.rb +2 -0
  290. data/lib/mongoid/timestamps.rb +3 -1
  291. data/lib/mongoid/timestamps/created.rb +3 -1
  292. data/lib/mongoid/timestamps/created/short.rb +2 -0
  293. data/lib/mongoid/timestamps/short.rb +2 -0
  294. data/lib/mongoid/timestamps/timeless.rb +6 -2
  295. data/lib/mongoid/timestamps/updated.rb +3 -1
  296. data/lib/mongoid/timestamps/updated/short.rb +2 -0
  297. data/lib/mongoid/touchable.rb +20 -12
  298. data/lib/mongoid/traversable.rb +4 -2
  299. data/lib/mongoid/validatable.rb +7 -5
  300. data/lib/mongoid/validatable/associated.rb +4 -2
  301. data/lib/mongoid/validatable/format.rb +2 -0
  302. data/lib/mongoid/validatable/length.rb +2 -0
  303. data/lib/mongoid/validatable/localizable.rb +2 -0
  304. data/lib/mongoid/validatable/macros.rb +2 -0
  305. data/lib/mongoid/validatable/presence.rb +4 -2
  306. data/lib/mongoid/validatable/queryable.rb +2 -0
  307. data/lib/mongoid/validatable/uniqueness.rb +5 -3
  308. data/lib/mongoid/version.rb +2 -1
  309. data/lib/rails/generators/mongoid/config/config_generator.rb +2 -0
  310. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +44 -29
  311. data/lib/rails/generators/mongoid/model/model_generator.rb +3 -1
  312. data/lib/rails/generators/mongoid/model/templates/model.rb.tt +1 -1
  313. data/lib/rails/generators/mongoid_generator.rb +2 -0
  314. data/lib/rails/mongoid.rb +4 -2
  315. data/lib/support/ruby_version.rb +3 -0
  316. data/spec/app/models/account.rb +3 -0
  317. data/spec/app/models/acolyte.rb +3 -0
  318. data/spec/app/models/actor.rb +3 -0
  319. data/spec/app/models/actress.rb +3 -0
  320. data/spec/app/models/address.rb +3 -0
  321. data/spec/app/models/address_component.rb +3 -0
  322. data/spec/app/models/address_number.rb +3 -0
  323. data/spec/app/models/agency.rb +3 -0
  324. data/spec/app/models/agent.rb +3 -0
  325. data/spec/app/models/album.rb +3 -0
  326. data/spec/app/models/alert.rb +3 -0
  327. data/spec/app/models/animal.rb +3 -0
  328. data/spec/app/models/answer.rb +3 -0
  329. data/spec/app/models/appointment.rb +3 -0
  330. data/spec/app/models/array_field.rb +1 -0
  331. data/spec/app/models/article.rb +3 -0
  332. data/spec/app/models/artist.rb +3 -0
  333. data/spec/app/models/artwork.rb +3 -0
  334. data/spec/app/models/audio.rb +3 -0
  335. data/spec/app/models/augmentation.rb +3 -0
  336. data/spec/app/models/author.rb +3 -0
  337. data/spec/app/models/baby.rb +3 -0
  338. data/spec/app/models/band.rb +3 -0
  339. data/spec/app/models/bar.rb +3 -0
  340. data/spec/app/models/basic.rb +3 -0
  341. data/spec/app/models/bed.rb +3 -0
  342. data/spec/app/models/big_palette.rb +3 -0
  343. data/spec/app/models/birthday.rb +3 -0
  344. data/spec/app/models/bomb.rb +3 -0
  345. data/spec/app/models/book.rb +3 -0
  346. data/spec/app/models/breed.rb +3 -0
  347. data/spec/app/models/browser.rb +3 -0
  348. data/spec/app/models/building.rb +3 -0
  349. data/spec/app/models/building_address.rb +3 -0
  350. data/spec/app/models/bus.rb +3 -0
  351. data/spec/app/models/business.rb +3 -0
  352. data/spec/app/models/callback_test.rb +3 -0
  353. data/spec/app/models/canvas.rb +3 -0
  354. data/spec/app/models/car.rb +3 -0
  355. data/spec/app/models/cat.rb +3 -0
  356. data/spec/app/models/category.rb +3 -0
  357. data/spec/app/models/child.rb +3 -0
  358. data/spec/app/models/child_doc.rb +3 -0
  359. data/spec/app/models/church.rb +3 -0
  360. data/spec/app/models/circle.rb +3 -0
  361. data/spec/app/models/circuit.rb +3 -0
  362. data/spec/app/models/circus.rb +3 -0
  363. data/spec/app/models/code.rb +3 -0
  364. data/spec/app/models/coding.rb +4 -0
  365. data/spec/app/models/coding/pull_request.rb +12 -0
  366. data/spec/app/models/comment.rb +3 -0
  367. data/spec/app/models/company.rb +3 -0
  368. data/spec/app/models/consumption_period.rb +3 -0
  369. data/spec/app/models/contextable_item.rb +3 -0
  370. data/spec/app/models/contractor.rb +3 -0
  371. data/spec/app/models/cookie.rb +3 -0
  372. data/spec/app/models/country_code.rb +3 -0
  373. data/spec/app/models/courier_job.rb +3 -0
  374. data/spec/app/models/definition.rb +3 -0
  375. data/spec/app/models/delegating_patient.rb +16 -0
  376. data/spec/app/models/description.rb +3 -0
  377. data/spec/app/models/dictionary.rb +3 -0
  378. data/spec/app/models/division.rb +3 -0
  379. data/spec/app/models/doctor.rb +3 -0
  380. data/spec/app/models/dog.rb +3 -0
  381. data/spec/app/models/dokument.rb +3 -0
  382. data/spec/app/models/draft.rb +3 -0
  383. data/spec/app/models/dragon.rb +3 -0
  384. data/spec/app/models/driver.rb +3 -0
  385. data/spec/app/models/drug.rb +3 -0
  386. data/spec/app/models/dungeon.rb +3 -0
  387. data/spec/app/models/edit.rb +3 -0
  388. data/spec/app/models/email.rb +3 -0
  389. data/spec/app/models/employer.rb +3 -0
  390. data/spec/app/models/entry.rb +3 -0
  391. data/spec/app/models/eraser.rb +3 -0
  392. data/spec/app/models/even.rb +3 -0
  393. data/spec/app/models/event.rb +3 -0
  394. data/spec/app/models/exhibition.rb +3 -0
  395. data/spec/app/models/exhibitor.rb +3 -0
  396. data/spec/app/models/explosion.rb +3 -0
  397. data/spec/app/models/eye.rb +3 -0
  398. data/spec/app/models/eye_bowl.rb +3 -0
  399. data/spec/app/models/face.rb +3 -0
  400. data/spec/app/models/favorite.rb +3 -0
  401. data/spec/app/models/filesystem.rb +3 -0
  402. data/spec/app/models/fire_hydrant.rb +3 -0
  403. data/spec/app/models/firefox.rb +3 -0
  404. data/spec/app/models/fish.rb +3 -0
  405. data/spec/app/models/folder.rb +3 -0
  406. data/spec/app/models/folder_item.rb +3 -0
  407. data/spec/app/models/fruits.rb +3 -0
  408. data/spec/app/models/game.rb +3 -0
  409. data/spec/app/models/ghost.rb +3 -0
  410. data/spec/app/models/home.rb +3 -0
  411. data/spec/app/models/house.rb +3 -0
  412. data/spec/app/models/html_writer.rb +3 -0
  413. data/spec/app/models/id_key.rb +3 -0
  414. data/spec/app/models/image.rb +3 -0
  415. data/spec/app/models/implant.rb +3 -0
  416. data/spec/app/models/item.rb +3 -0
  417. data/spec/app/models/jar.rb +3 -0
  418. data/spec/app/models/kaleidoscope.rb +3 -0
  419. data/spec/app/models/kangaroo.rb +4 -1
  420. data/spec/app/models/label.rb +3 -0
  421. data/spec/app/models/language.rb +3 -0
  422. data/spec/app/models/lat_lng.rb +3 -0
  423. data/spec/app/models/league.rb +3 -0
  424. data/spec/app/models/learner.rb +3 -0
  425. data/spec/app/models/line_item.rb +3 -0
  426. data/spec/app/models/location.rb +3 -0
  427. data/spec/app/models/login.rb +3 -0
  428. data/spec/app/models/manufacturer.rb +3 -0
  429. data/spec/app/models/meat.rb +3 -0
  430. data/spec/app/models/membership.rb +3 -0
  431. data/spec/app/models/message.rb +3 -0
  432. data/spec/app/models/minim.rb +1 -1
  433. data/spec/app/models/mixed_drink.rb +3 -0
  434. data/spec/app/models/movie.rb +3 -0
  435. data/spec/app/models/my_hash.rb +3 -0
  436. data/spec/app/models/name.rb +3 -0
  437. data/spec/app/models/name_only.rb +9 -0
  438. data/spec/app/models/node.rb +3 -0
  439. data/spec/app/models/note.rb +3 -0
  440. data/spec/app/models/odd.rb +3 -0
  441. data/spec/app/models/ordered_post.rb +3 -0
  442. data/spec/app/models/ordered_preference.rb +3 -0
  443. data/spec/app/models/oscar.rb +3 -0
  444. data/spec/app/models/other_owner_object.rb +3 -0
  445. data/spec/app/models/override.rb +3 -0
  446. data/spec/app/models/ownable.rb +3 -0
  447. data/spec/app/models/owner.rb +3 -0
  448. data/spec/app/models/pack.rb +3 -0
  449. data/spec/app/models/page.rb +3 -0
  450. data/spec/app/models/page_question.rb +3 -0
  451. data/spec/app/models/palette.rb +3 -0
  452. data/spec/app/models/parent.rb +3 -0
  453. data/spec/app/models/parent_doc.rb +3 -0
  454. data/spec/app/models/passport.rb +3 -0
  455. data/spec/app/models/patient.rb +3 -0
  456. data/spec/app/models/pdf_writer.rb +3 -0
  457. data/spec/app/models/pencil.rb +3 -0
  458. data/spec/app/models/person.rb +3 -0
  459. data/spec/app/models/pet.rb +3 -0
  460. data/spec/app/models/pet_owner.rb +3 -0
  461. data/spec/app/models/phone.rb +3 -0
  462. data/spec/app/models/pizza.rb +3 -0
  463. data/spec/app/models/player.rb +3 -0
  464. data/spec/app/models/post.rb +3 -0
  465. data/spec/app/models/post_genre.rb +3 -0
  466. data/spec/app/models/powerup.rb +3 -0
  467. data/spec/app/models/preference.rb +3 -0
  468. data/spec/app/models/princess.rb +3 -0
  469. data/spec/app/models/product.rb +3 -0
  470. data/spec/app/models/profile.rb +3 -0
  471. data/spec/app/models/pronunciation.rb +3 -0
  472. data/spec/app/models/pub.rb +3 -0
  473. data/spec/app/models/publication.rb +5 -0
  474. data/spec/app/models/publication/encyclopedia.rb +12 -0
  475. data/spec/app/models/publication/review.rb +14 -0
  476. data/spec/app/models/purchase.rb +3 -0
  477. data/spec/app/models/question.rb +3 -0
  478. data/spec/app/models/quiz.rb +3 -0
  479. data/spec/app/models/rating.rb +3 -0
  480. data/spec/app/models/record.rb +3 -0
  481. data/spec/app/models/registry.rb +3 -0
  482. data/spec/app/models/role.rb +3 -0
  483. data/spec/app/models/root_category.rb +3 -0
  484. data/spec/app/models/sandwich.rb +3 -0
  485. data/spec/app/models/scheduler.rb +3 -0
  486. data/spec/app/models/seo.rb +3 -0
  487. data/spec/app/models/series.rb +3 -0
  488. data/spec/app/models/server.rb +3 -0
  489. data/spec/app/models/service.rb +3 -0
  490. data/spec/app/models/shape.rb +3 -0
  491. data/spec/app/models/shelf.rb +3 -0
  492. data/spec/app/models/shipment_address.rb +3 -0
  493. data/spec/app/models/shipping_container.rb +3 -0
  494. data/spec/app/models/shipping_pack.rb +3 -0
  495. data/spec/app/models/shop.rb +3 -0
  496. data/spec/app/models/short_agent.rb +3 -0
  497. data/spec/app/models/short_quiz.rb +3 -0
  498. data/spec/app/models/simple.rb +3 -0
  499. data/spec/app/models/slave.rb +3 -0
  500. data/spec/app/models/song.rb +3 -0
  501. data/spec/app/models/sound.rb +3 -0
  502. data/spec/app/models/square.rb +3 -0
  503. data/spec/app/models/staff.rb +3 -0
  504. data/spec/app/models/store_as_dup_test1.rb +3 -0
  505. data/spec/app/models/store_as_dup_test2.rb +3 -0
  506. data/spec/app/models/store_as_dup_test3.rb +1 -0
  507. data/spec/app/models/store_as_dup_test4.rb +1 -0
  508. data/spec/app/models/strategy.rb +3 -0
  509. data/spec/app/models/sub_item.rb +3 -0
  510. data/spec/app/models/subscription.rb +3 -0
  511. data/spec/app/models/survey.rb +3 -0
  512. data/spec/app/models/symptom.rb +3 -0
  513. data/spec/app/models/tag.rb +3 -0
  514. data/spec/app/models/target.rb +3 -0
  515. data/spec/app/models/template.rb +3 -0
  516. data/spec/app/models/thing.rb +3 -0
  517. data/spec/app/models/title.rb +3 -0
  518. data/spec/app/models/tool.rb +3 -0
  519. data/spec/app/models/topping.rb +3 -0
  520. data/spec/app/models/track.rb +3 -0
  521. data/spec/app/models/translation.rb +3 -0
  522. data/spec/app/models/tree.rb +3 -0
  523. data/spec/app/models/truck.rb +3 -0
  524. data/spec/app/models/updatable.rb +1 -0
  525. data/spec/app/models/user.rb +3 -0
  526. data/spec/app/models/user_account.rb +3 -0
  527. data/spec/app/models/validation_callback.rb +3 -0
  528. data/spec/app/models/vehicle.rb +3 -0
  529. data/spec/app/models/version.rb +3 -0
  530. data/spec/app/models/vertex.rb +4 -1
  531. data/spec/app/models/vet_visit.rb +3 -0
  532. data/spec/app/models/video.rb +3 -0
  533. data/spec/app/models/video_game.rb +3 -0
  534. data/spec/app/models/weapon.rb +3 -0
  535. data/spec/app/models/wiki_page.rb +3 -0
  536. data/spec/app/models/word.rb +3 -0
  537. data/spec/app/models/word_origin.rb +3 -0
  538. data/spec/app/models/writer.rb +3 -0
  539. data/spec/config/mongoid.yml +1 -0
  540. data/spec/integration/app_spec.rb +192 -0
  541. data/spec/integration/associations/belongs_to_spec.rb +3 -0
  542. data/spec/integration/associations/embedded_spec.rb +175 -0
  543. data/spec/integration/associations/foreign_key_spec.rb +99 -0
  544. data/spec/integration/associations/foreign_key_spec_models.rb +65 -0
  545. data/spec/integration/associations/reverse_population_spec.rb +35 -0
  546. data/spec/integration/associations/reverse_population_spec_models.rb +37 -0
  547. data/spec/integration/criteria/default_scope_spec.rb +21 -0
  548. data/spec/integration/criteria/logical_spec.rb +94 -0
  549. data/spec/integration/document_spec.rb +22 -0
  550. data/spec/integration/i18n_fallbacks_spec.rb +90 -0
  551. data/spec/integration/shardable_spec.rb +133 -0
  552. data/spec/lite_spec_helper.rb +20 -5
  553. data/spec/mongoid/association/accessors_spec.rb +3 -0
  554. data/spec/mongoid/association/auto_save_spec.rb +3 -0
  555. data/spec/mongoid/association/builders_spec.rb +3 -0
  556. data/spec/mongoid/association/constrainable_spec.rb +3 -0
  557. data/spec/mongoid/association/counter_cache_spec.rb +3 -0
  558. data/spec/mongoid/association/depending_spec.rb +3 -0
  559. data/spec/mongoid/association/eager_spec.rb +3 -0
  560. data/spec/mongoid/association/embedded/cyclic_spec.rb +3 -0
  561. data/spec/mongoid/association/embedded/dirty_spec.rb +3 -0
  562. data/spec/mongoid/association/embedded/embedded_in/binding_spec.rb +3 -0
  563. data/spec/mongoid/association/embedded/embedded_in/buildable_spec.rb +3 -0
  564. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +3 -0
  565. data/spec/mongoid/association/embedded/embedded_in_spec.rb +4 -1
  566. data/spec/mongoid/association/embedded/embeds_many/binding_spec.rb +3 -0
  567. data/spec/mongoid/association/embedded/embeds_many/buildable_spec.rb +3 -0
  568. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +3 -0
  569. data/spec/mongoid/association/embedded/embeds_many_models.rb +53 -0
  570. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +52 -0
  571. data/spec/mongoid/association/embedded/embeds_many_spec.rb +14 -1
  572. data/spec/mongoid/association/embedded/embeds_one/binding_spec.rb +3 -0
  573. data/spec/mongoid/association/embedded/embeds_one/buildable_spec.rb +3 -0
  574. data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +3 -0
  575. data/spec/mongoid/association/embedded/embeds_one_dnl_models.rb +3 -0
  576. data/spec/mongoid/association/embedded/embeds_one_models.rb +3 -0
  577. data/spec/mongoid/association/embedded/embeds_one_query_spec.rb +29 -0
  578. data/spec/mongoid/association/embedded/embeds_one_spec.rb +4 -3
  579. data/spec/mongoid/association/macros_spec.rb +3 -0
  580. data/spec/mongoid/association/nested/many_spec.rb +3 -0
  581. data/spec/mongoid/association/nested/one_spec.rb +3 -0
  582. data/spec/mongoid/association/options_spec.rb +3 -0
  583. data/spec/mongoid/association/polymorphic_spec.rb +3 -0
  584. data/spec/mongoid/association/referenced/belongs_to/binding_spec.rb +3 -0
  585. data/spec/mongoid/association/referenced/belongs_to/buildable_spec.rb +3 -0
  586. data/spec/mongoid/association/referenced/belongs_to/eager_spec.rb +205 -10
  587. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +3 -0
  588. data/spec/mongoid/association/referenced/belongs_to_query_spec.rb +38 -0
  589. data/spec/mongoid/association/referenced/belongs_to_spec.rb +3 -0
  590. data/spec/mongoid/association/referenced/has_and_belongs_to_many/binding_spec.rb +3 -0
  591. data/spec/mongoid/association/referenced/has_and_belongs_to_many/buildable_spec.rb +3 -0
  592. data/spec/mongoid/association/referenced/has_and_belongs_to_many/eager_spec.rb +16 -0
  593. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_persistence_spec.rb +26 -5
  594. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +39 -15
  595. data/spec/mongoid/association/referenced/has_and_belongs_to_many_models.rb +26 -2
  596. data/spec/mongoid/association/referenced/has_and_belongs_to_many_query_spec.rb +40 -0
  597. data/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb +26 -2
  598. data/spec/mongoid/association/referenced/has_many/binding_spec.rb +3 -0
  599. data/spec/mongoid/association/referenced/has_many/buildable_spec.rb +3 -0
  600. data/spec/mongoid/association/referenced/has_many/eager_spec.rb +18 -3
  601. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +3 -0
  602. data/spec/mongoid/association/referenced/has_many/proxy_query_spec.rb +1 -0
  603. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +5 -1
  604. data/spec/mongoid/association/referenced/has_many_models.rb +3 -0
  605. data/spec/mongoid/association/referenced/has_many_query_spec.rb +38 -0
  606. data/spec/mongoid/association/referenced/has_many_spec.rb +3 -0
  607. data/spec/mongoid/association/referenced/has_one/binding_spec.rb +3 -0
  608. data/spec/mongoid/association/referenced/has_one/buildable_spec.rb +3 -0
  609. data/spec/mongoid/association/referenced/has_one/eager_spec.rb +18 -3
  610. data/spec/mongoid/association/referenced/has_one/proxy_spec.rb +3 -0
  611. data/spec/mongoid/association/referenced/has_one_models.rb +3 -0
  612. data/spec/mongoid/association/referenced/has_one_query_spec.rb +38 -0
  613. data/spec/mongoid/association/referenced/has_one_spec.rb +3 -0
  614. data/spec/mongoid/association/reflections_spec.rb +3 -0
  615. data/spec/mongoid/association/syncable_spec.rb +3 -0
  616. data/spec/mongoid/association_spec.rb +3 -0
  617. data/spec/mongoid/atomic/modifiers_spec.rb +3 -0
  618. data/spec/mongoid/atomic/paths/embedded/many_spec.rb +3 -0
  619. data/spec/mongoid/atomic/paths/embedded/one_spec.rb +3 -0
  620. data/spec/mongoid/atomic/paths/root_spec.rb +3 -0
  621. data/spec/mongoid/atomic/paths_spec.rb +3 -0
  622. data/spec/mongoid/atomic_spec.rb +3 -0
  623. data/spec/mongoid/attributes/nested_spec.rb +3 -0
  624. data/spec/mongoid/attributes/readonly_spec.rb +3 -0
  625. data/spec/mongoid/attributes_spec.rb +3 -0
  626. data/spec/mongoid/cacheable_spec.rb +3 -0
  627. data/spec/mongoid/changeable_spec.rb +26 -0
  628. data/spec/mongoid/clients/factory_spec.rb +38 -20
  629. data/spec/mongoid/clients/options_spec.rb +26 -15
  630. data/spec/mongoid/clients/sessions_spec.rb +11 -4
  631. data/spec/mongoid/clients/transactions_spec.rb +26 -11
  632. data/spec/mongoid/clients_spec.rb +5 -2
  633. data/spec/mongoid/composable_spec.rb +3 -0
  634. data/spec/mongoid/config/environment_spec.rb +3 -0
  635. data/spec/mongoid/config/options_spec.rb +23 -3
  636. data/spec/mongoid/config_spec.rb +39 -0
  637. data/spec/mongoid/contextual/aggregable/memory_spec.rb +3 -0
  638. data/spec/mongoid/contextual/aggregable/mongo_spec.rb +3 -0
  639. data/spec/mongoid/contextual/atomic_spec.rb +120 -10
  640. data/spec/mongoid/contextual/geo_near_spec.rb +3 -0
  641. data/spec/mongoid/contextual/map_reduce_spec.rb +23 -5
  642. data/spec/mongoid/contextual/memory_spec.rb +3 -0
  643. data/spec/mongoid/contextual/mongo_spec.rb +81 -53
  644. data/spec/mongoid/contextual/none_spec.rb +3 -0
  645. data/spec/mongoid/copyable_spec.rb +3 -0
  646. data/spec/mongoid/copyable_spec_models.rb +3 -0
  647. data/spec/mongoid/criteria/findable_spec.rb +58 -0
  648. data/spec/mongoid/criteria/inspectable_spec.rb +3 -0
  649. data/spec/mongoid/criteria/marshalable_spec.rb +3 -0
  650. data/spec/mongoid/criteria/modifiable_spec.rb +7 -3
  651. data/spec/mongoid/criteria/options_spec.rb +3 -0
  652. data/spec/mongoid/criteria/queryable/aggregable_spec.rb +3 -0
  653. data/spec/mongoid/criteria/queryable/expandable_spec.rb +135 -0
  654. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +3 -0
  655. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +3 -0
  656. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +3 -0
  657. data/spec/mongoid/criteria/queryable/extensions/boolean_spec.rb +3 -0
  658. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +3 -0
  659. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +36 -17
  660. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +3 -0
  661. data/spec/mongoid/criteria/queryable/extensions/float_spec.rb +3 -0
  662. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +3 -0
  663. data/spec/mongoid/criteria/queryable/extensions/integer_spec.rb +3 -0
  664. data/spec/mongoid/criteria/queryable/extensions/nil_class_spec.rb +3 -0
  665. data/spec/mongoid/criteria/queryable/extensions/object_spec.rb +3 -0
  666. data/spec/mongoid/criteria/queryable/extensions/range_spec.rb +3 -0
  667. data/spec/mongoid/criteria/queryable/extensions/regexp_raw_spec.rb +3 -0
  668. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +10 -7
  669. data/spec/mongoid/criteria/queryable/extensions/set_spec.rb +3 -0
  670. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -1
  671. data/spec/mongoid/criteria/queryable/extensions/symbol_spec.rb +3 -0
  672. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +24 -16
  673. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +3 -0
  674. data/spec/mongoid/criteria/queryable/key_spec.rb +3 -0
  675. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +48 -12
  676. data/spec/mongoid/criteria/queryable/optional_spec.rb +3 -0
  677. data/spec/mongoid/criteria/queryable/options_spec.rb +3 -0
  678. data/spec/mongoid/criteria/queryable/pipeline_spec.rb +3 -0
  679. data/spec/mongoid/criteria/queryable/queryable_spec.rb +3 -0
  680. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +1348 -281
  681. data/spec/mongoid/criteria/queryable/selectable_spec.rb +996 -2159
  682. data/spec/mongoid/criteria/queryable/selector_spec.rb +3 -0
  683. data/spec/mongoid/criteria/queryable/smash_spec.rb +3 -0
  684. data/spec/mongoid/criteria/queryable/storable_spec.rb +190 -0
  685. data/spec/mongoid/criteria/scopable_spec.rb +3 -0
  686. data/spec/mongoid/criteria_spec.rb +73 -2
  687. data/spec/mongoid/document_persistence_context_spec.rb +33 -0
  688. data/spec/mongoid/document_query_spec.rb +39 -0
  689. data/spec/mongoid/document_spec.rb +3 -0
  690. data/spec/mongoid/equality_spec.rb +3 -0
  691. data/spec/mongoid/errors/ambiguous_relationship_spec.rb +6 -3
  692. data/spec/mongoid/errors/callback_spec.rb +3 -0
  693. data/spec/mongoid/errors/delete_restriction_spec.rb +3 -0
  694. data/spec/mongoid/errors/document_not_destroyed_spec.rb +3 -0
  695. data/spec/mongoid/errors/document_not_found_spec.rb +3 -0
  696. data/spec/mongoid/errors/eager_load_spec.rb +3 -0
  697. data/spec/mongoid/errors/invalid_collection_spec.rb +3 -0
  698. data/spec/mongoid/errors/invalid_config_option_spec.rb +3 -0
  699. data/spec/mongoid/errors/invalid_field_option_spec.rb +3 -0
  700. data/spec/mongoid/errors/invalid_field_spec.rb +3 -0
  701. data/spec/mongoid/errors/invalid_find_spec.rb +3 -0
  702. data/spec/mongoid/errors/invalid_includes_spec.rb +3 -0
  703. data/spec/mongoid/errors/invalid_index_spec.rb +3 -0
  704. data/spec/mongoid/errors/invalid_options_spec.rb +5 -2
  705. data/spec/mongoid/errors/invalid_path_spec.rb +3 -0
  706. data/spec/mongoid/errors/invalid_relation_spec.rb +5 -2
  707. data/spec/mongoid/errors/invalid_scope_spec.rb +3 -0
  708. data/spec/mongoid/errors/invalid_set_polymorphic_relation_spec.rb +3 -0
  709. data/spec/mongoid/errors/invalid_storage_options_spec.rb +3 -0
  710. data/spec/mongoid/errors/invalid_time_spec.rb +3 -0
  711. data/spec/mongoid/errors/inverse_not_found_spec.rb +4 -1
  712. data/spec/mongoid/errors/mixed_client_configuration_spec.rb +3 -0
  713. data/spec/mongoid/errors/mixed_relations_spec.rb +3 -0
  714. data/spec/mongoid/errors/mongoid_error_spec.rb +3 -0
  715. data/spec/mongoid/errors/nested_attributes_metadata_not_found_spec.rb +6 -3
  716. data/spec/mongoid/errors/no_client_config_spec.rb +3 -0
  717. data/spec/mongoid/errors/no_client_database_spec.rb +3 -0
  718. data/spec/mongoid/errors/no_client_hosts_spec.rb +3 -0
  719. data/spec/mongoid/errors/no_clients_config_spec.rb +3 -0
  720. data/spec/mongoid/errors/no_environment_spec.rb +3 -0
  721. data/spec/mongoid/errors/no_map_reduce_output_spec.rb +3 -0
  722. data/spec/mongoid/errors/no_metadata_spec.rb +5 -2
  723. data/spec/mongoid/errors/no_parent_spec.rb +4 -1
  724. data/spec/mongoid/errors/readonly_attribute_spec.rb +3 -0
  725. data/spec/mongoid/errors/readonly_document_spec.rb +3 -0
  726. data/spec/mongoid/errors/scope_overwrite_spec.rb +3 -0
  727. data/spec/mongoid/errors/too_many_nested_attribute_records_spec.rb +3 -0
  728. data/spec/mongoid/errors/unknown_attribute_spec.rb +3 -0
  729. data/spec/mongoid/errors/unsaved_document_spec.rb +4 -1
  730. data/spec/mongoid/errors/unsupported_javascript_spec.rb +3 -0
  731. data/spec/mongoid/errors/validations_spec.rb +3 -0
  732. data/spec/mongoid/extensions/array_spec.rb +26 -43
  733. data/spec/mongoid/extensions/big_decimal_spec.rb +3 -0
  734. data/spec/mongoid/extensions/binary_spec.rb +3 -0
  735. data/spec/mongoid/extensions/boolean_spec.rb +3 -0
  736. data/spec/mongoid/extensions/date_class_mongoize_spec.rb +336 -0
  737. data/spec/mongoid/extensions/date_spec.rb +9 -160
  738. data/spec/mongoid/extensions/date_time_spec.rb +18 -60
  739. data/spec/mongoid/extensions/decimal128_spec.rb +3 -0
  740. data/spec/mongoid/extensions/false_class_spec.rb +3 -0
  741. data/spec/mongoid/extensions/float_spec.rb +18 -3
  742. data/spec/mongoid/extensions/hash_spec.rb +106 -0
  743. data/spec/mongoid/extensions/integer_spec.rb +15 -2
  744. data/spec/mongoid/extensions/module_spec.rb +3 -0
  745. data/spec/mongoid/extensions/nil_class_spec.rb +3 -0
  746. data/spec/mongoid/extensions/object_id_spec.rb +3 -0
  747. data/spec/mongoid/extensions/object_spec.rb +14 -24
  748. data/spec/mongoid/extensions/range_spec.rb +3 -0
  749. data/spec/mongoid/extensions/regexp_spec.rb +3 -0
  750. data/spec/mongoid/extensions/set_spec.rb +3 -0
  751. data/spec/mongoid/extensions/string_spec.rb +60 -26
  752. data/spec/mongoid/extensions/symbol_spec.rb +3 -0
  753. data/spec/mongoid/extensions/time_spec.rb +30 -0
  754. data/spec/mongoid/extensions/time_with_zone_spec.rb +34 -0
  755. data/spec/mongoid/extensions/true_class_spec.rb +3 -0
  756. data/spec/mongoid/extensions_spec.rb +3 -0
  757. data/spec/mongoid/factory_spec.rb +27 -0
  758. data/spec/mongoid/fields/foreign_key_spec.rb +3 -0
  759. data/spec/mongoid/fields/localized_spec.rb +3 -0
  760. data/spec/mongoid/fields/standard_spec.rb +3 -0
  761. data/spec/mongoid/fields_spec.rb +18 -0
  762. data/spec/mongoid/findable_spec.rb +3 -0
  763. data/spec/mongoid/indexable/specification_spec.rb +3 -0
  764. data/spec/mongoid/indexable_spec.rb +9 -4
  765. data/spec/mongoid/inspectable_spec.rb +3 -0
  766. data/spec/mongoid/interceptable_spec.rb +65 -0
  767. data/spec/mongoid/interceptable_spec_models.rb +76 -0
  768. data/spec/mongoid/loggable_spec.rb +3 -0
  769. data/spec/mongoid/matchable/all_spec.rb +3 -0
  770. data/spec/mongoid/matchable/and_spec.rb +3 -0
  771. data/spec/mongoid/matchable/default_spec.rb +4 -1
  772. data/spec/mongoid/matchable/elem_match_spec.rb +3 -0
  773. data/spec/mongoid/matchable/eq_spec.rb +1 -0
  774. data/spec/mongoid/matchable/exists_spec.rb +3 -0
  775. data/spec/mongoid/matchable/gt_spec.rb +3 -0
  776. data/spec/mongoid/matchable/gte_spec.rb +3 -0
  777. data/spec/mongoid/matchable/in_spec.rb +3 -0
  778. data/spec/mongoid/matchable/lt_spec.rb +3 -0
  779. data/spec/mongoid/matchable/lte_spec.rb +3 -0
  780. data/spec/mongoid/matchable/ne_spec.rb +3 -0
  781. data/spec/mongoid/matchable/nin_spec.rb +3 -0
  782. data/spec/mongoid/matchable/nor_spec.rb +1 -0
  783. data/spec/mongoid/matchable/or_spec.rb +3 -0
  784. data/spec/mongoid/matchable/regexp_spec.rb +5 -2
  785. data/spec/mongoid/matchable/size_spec.rb +3 -0
  786. data/spec/mongoid/matchable_spec.rb +5 -2
  787. data/spec/mongoid/persistable/creatable_spec.rb +3 -0
  788. data/spec/mongoid/persistable/deletable_spec.rb +3 -0
  789. data/spec/mongoid/persistable/destroyable_spec.rb +3 -0
  790. data/spec/mongoid/persistable/incrementable_spec.rb +17 -0
  791. data/spec/mongoid/persistable/logical_spec.rb +17 -0
  792. data/spec/mongoid/persistable/poppable_spec.rb +17 -0
  793. data/spec/mongoid/persistable/pullable_spec.rb +31 -0
  794. data/spec/mongoid/persistable/pushable_spec.rb +86 -1
  795. data/spec/mongoid/persistable/renamable_spec.rb +17 -0
  796. data/spec/mongoid/persistable/savable_spec.rb +3 -0
  797. data/spec/mongoid/persistable/settable_spec.rb +17 -0
  798. data/spec/mongoid/persistable/unsettable_spec.rb +17 -0
  799. data/spec/mongoid/persistable/updatable_spec.rb +3 -0
  800. data/spec/mongoid/persistable/upsertable_spec.rb +3 -0
  801. data/spec/mongoid/persistable_spec.rb +85 -8
  802. data/spec/mongoid/persistence_context_spec.rb +3 -0
  803. data/spec/mongoid/positional_spec.rb +3 -0
  804. data/spec/mongoid/query_cache_middleware_spec.rb +48 -0
  805. data/spec/mongoid/query_cache_spec.rb +5 -45
  806. data/spec/mongoid/relations/proxy_spec.rb +4 -1
  807. data/spec/mongoid/reloadable_spec.rb +3 -0
  808. data/spec/mongoid/scopable_spec.rb +5 -1
  809. data/spec/mongoid/selectable_spec.rb +3 -0
  810. data/spec/mongoid/serializable_spec.rb +132 -18
  811. data/spec/mongoid/shardable_models.rb +61 -0
  812. data/spec/mongoid/shardable_spec.rb +72 -16
  813. data/spec/mongoid/stateful_spec.rb +3 -0
  814. data/spec/mongoid/tasks/database_rake_spec.rb +16 -13
  815. data/spec/mongoid/tasks/database_spec.rb +4 -1
  816. data/spec/mongoid/threaded_spec.rb +3 -0
  817. data/spec/mongoid/timestamps/created/short_spec.rb +3 -0
  818. data/spec/mongoid/timestamps/created_spec.rb +3 -0
  819. data/spec/mongoid/timestamps/timeless_spec.rb +3 -0
  820. data/spec/mongoid/timestamps/updated/short_spec.rb +3 -0
  821. data/spec/mongoid/timestamps/updated_spec.rb +3 -0
  822. data/spec/mongoid/timestamps_spec.rb +3 -0
  823. data/spec/mongoid/touchable_spec.rb +49 -3
  824. data/spec/mongoid/traversable_spec.rb +3 -0
  825. data/spec/mongoid/validatable/associated_spec.rb +3 -0
  826. data/spec/mongoid/validatable/format_spec.rb +3 -0
  827. data/spec/mongoid/validatable/length_spec.rb +3 -0
  828. data/spec/mongoid/validatable/numericality_spec.rb +3 -0
  829. data/spec/mongoid/validatable/presence_spec.rb +3 -0
  830. data/spec/mongoid/validatable/uniqueness_spec.rb +3 -0
  831. data/spec/mongoid/validatable_spec.rb +3 -0
  832. data/spec/mongoid_spec.rb +3 -0
  833. data/spec/rails/controller_extension/controller_runtime_spec.rb +4 -1
  834. data/spec/rails/mongoid_spec.rb +5 -2
  835. data/spec/spec_helper.rb +21 -44
  836. data/spec/support/authorization.rb +3 -0
  837. data/spec/support/child_process_helper.rb +76 -0
  838. data/spec/support/cluster_config.rb +3 -3
  839. data/spec/support/constraints.rb +185 -30
  840. data/spec/support/expectations.rb +6 -1
  841. data/spec/support/helpers.rb +11 -0
  842. data/spec/support/lite_constraints.rb +22 -0
  843. data/spec/support/macros.rb +1 -0
  844. data/spec/support/shared/time.rb +54 -0
  845. data/spec/support/spec_config.rb +15 -4
  846. metadata +544 -472
  847. metadata.gz.sig +0 -0
  848. data/lib/mongoid/criteria/queryable/forwardable.rb +0 -65
  849. data/spec/mongoid/criteria/queryable/forwardable_spec.rb +0 -87
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require "spec_helper"
2
5
 
3
6
  describe Set do
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require "spec_helper"
2
5
 
3
6
  describe String do
@@ -315,7 +318,7 @@ describe String do
315
318
  context "when provided a regex" do
316
319
 
317
320
  let(:regex) do
318
- /^[123]/
321
+ /\A[123]/.freeze
319
322
  end
320
323
 
321
324
  let(:evolved) do
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require "spec_helper"
2
5
 
3
6
  describe Symbol do
@@ -1,4 +1,7 @@
1
- require "spec_helper"
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require "lite_spec_helper"
2
5
 
3
6
  describe Time do
4
7
 
@@ -8,22 +11,27 @@ describe Time do
8
11
 
9
12
  context "when the time is not in utc" do
10
13
 
11
- let(:date) do
12
- Time.new(2010, 1, 1, 12, 0, 0)
14
+ let(:time) do
15
+ Time.new(2010, 1, 1, 14, 0, 0, '+02:00')
13
16
  end
14
17
 
15
18
  let(:evolved) do
16
- described_class.evolve(date)
19
+ described_class.evolve(time)
17
20
  end
18
21
 
19
22
  let(:expected) do
20
- Time.new(2010, 1, 1, 12, 0, 0).utc
23
+ Time.new(2010, 1, 1, 12, 0, 0, '+00:00')
21
24
  end
22
25
 
23
26
  it "returns the same time" do
24
27
  expect(evolved).to eq(expected)
25
28
  end
26
29
 
30
+ it 'does not mutate original time' do
31
+ described_class.evolve(time)
32
+ expect(time.utc_offset).to eq(7200)
33
+ end
34
+
27
35
  it "returns the time in utc" do
28
36
  expect(evolved.utc_offset).to eq(0)
29
37
  end
@@ -31,12 +39,12 @@ describe Time do
31
39
 
32
40
  context "when the time is already utc" do
33
41
 
34
- let(:date) do
42
+ let(:time) do
35
43
  Time.new(2010, 1, 1, 12, 0, 0).utc
36
44
  end
37
45
 
38
46
  let(:evolved) do
39
- described_class.evolve(date)
47
+ described_class.evolve(time)
40
48
  end
41
49
 
42
50
  let(:expected) do
@@ -57,12 +65,12 @@ describe Time do
57
65
 
58
66
  context "when the array is composed of times" do
59
67
 
60
- let(:date) do
68
+ let(:time) do
61
69
  Time.new(2010, 1, 1, 12, 0, 0)
62
70
  end
63
71
 
64
72
  let(:evolved) do
65
- described_class.evolve([ date ])
73
+ described_class.evolve([ time ])
66
74
  end
67
75
 
68
76
  let(:expected) do
@@ -80,16 +88,16 @@ describe Time do
80
88
 
81
89
  context "when the array is composed of strings" do
82
90
 
83
- let(:date) do
91
+ let(:time) do
84
92
  Time.parse("1st Jan 2010 12:00:00+01:00")
85
93
  end
86
94
 
87
95
  let(:evolved) do
88
- described_class.evolve([ date.to_s ])
96
+ described_class.evolve([ time.to_s ])
89
97
  end
90
98
 
91
99
  it "returns the strings as a times" do
92
- expect(evolved).to eq([ date.to_time ])
100
+ expect(evolved).to eq([ time.to_time ])
93
101
  end
94
102
 
95
103
  it "returns the times in utc" do
@@ -146,7 +154,7 @@ describe Time do
146
154
 
147
155
  context "when provided a range" do
148
156
 
149
- context "when the range are dates" do
157
+ context "when the range are times" do
150
158
 
151
159
  let(:min) do
152
160
  Time.new(2010, 1, 1, 12, 0, 0)
@@ -273,16 +281,16 @@ describe Time do
273
281
 
274
282
  context "when provided a string" do
275
283
 
276
- let(:date) do
284
+ let(:time) do
277
285
  Time.parse("1st Jan 2010 12:00:00+01:00")
278
286
  end
279
287
 
280
288
  let(:evolved) do
281
- described_class.evolve(date.to_s)
289
+ described_class.evolve(time.to_s)
282
290
  end
283
291
 
284
292
  it "returns the string as a time" do
285
- expect(evolved).to eq(date.to_time)
293
+ expect(evolved).to eq(time.to_time)
286
294
  end
287
295
 
288
296
  it "returns the time in utc" do
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require "spec_helper"
2
5
 
3
6
  describe ActiveSupport::TimeWithZone do
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require "spec_helper"
2
5
 
3
6
  describe Mongoid::Criteria::Queryable::Key do
@@ -1,12 +1,15 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require "spec_helper"
2
5
 
3
6
  describe Mongoid::Criteria::Queryable::Mergeable do
4
7
 
5
- describe "#intersect" do
8
+ let(:query) do
9
+ Mongoid::Query.new
10
+ end
6
11
 
7
- let(:query) do
8
- Mongoid::Query.new
9
- end
12
+ describe "#intersect" do
10
13
 
11
14
  before do
12
15
  query.intersect
@@ -19,10 +22,6 @@ describe Mongoid::Criteria::Queryable::Mergeable do
19
22
 
20
23
  describe "#override" do
21
24
 
22
- let(:query) do
23
- Mongoid::Query.new
24
- end
25
-
26
25
  before do
27
26
  query.override
28
27
  end
@@ -34,10 +33,6 @@ describe Mongoid::Criteria::Queryable::Mergeable do
34
33
 
35
34
  describe "#union" do
36
35
 
37
- let(:query) do
38
- Mongoid::Query.new
39
- end
40
-
41
36
  before do
42
37
  query.union
43
38
  end
@@ -46,4 +41,45 @@ describe Mongoid::Criteria::Queryable::Mergeable do
46
41
  expect(query.strategy).to eq(:__union__)
47
42
  end
48
43
  end
44
+
45
+ describe '#_mongoid_expand_keys' do
46
+ it 'expands simple keys' do
47
+ query.send(:_mongoid_expand_keys, {a: 1}).should == {a: 1}
48
+ end
49
+
50
+ let(:gt) do
51
+ Mongoid::Criteria::Queryable::Key.new("age", :__override__, "$gt")
52
+ end
53
+
54
+ let(:gtp) do
55
+ Mongoid::Criteria::Queryable::Key.new("age", :__override__, "$gt")
56
+ end
57
+
58
+ let(:lt) do
59
+ Mongoid::Criteria::Queryable::Key.new("age", :__override__, "$lt")
60
+ end
61
+
62
+ it 'expands Key instances' do
63
+ query.send(:_mongoid_expand_keys, {gt => 42}).should == {'age' => {'$gt' => 42}}
64
+ end
65
+
66
+ it 'expands multiple Key instances on the same field' do
67
+ query.send(:_mongoid_expand_keys, {gt => 42, lt => 50}).should == {
68
+ 'age' => {'$gt' => 42, '$lt' => 50}}
69
+ end
70
+
71
+ it 'expands simple and Key instances on the same field' do
72
+ query.send(:_mongoid_expand_keys, {'age' => 42, lt => 50}).should == {
73
+ 'age' => {'$eq' => 42, '$lt' => 50}}
74
+ end
75
+
76
+ it 'expands Key and simple instances on the same field' do
77
+ query.send(:_mongoid_expand_keys, {gt => 42, 'age' => 50}).should == {
78
+ 'age' => {'$gt' => 42, '$eq' => 50}}
79
+ end
80
+
81
+ it 'Ruby does not allow same symbol operator with different values' do
82
+ {gt => 42, gtp => 50}.should == {gtp => 50}
83
+ end
84
+ end
49
85
  end
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require "spec_helper"
2
5
 
3
6
  describe Mongoid::Criteria::Queryable::Optional do
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require "spec_helper"
2
5
 
3
6
  describe Mongoid::Criteria::Queryable::Options do
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require "spec_helper"
2
5
 
3
6
  describe Mongoid::Criteria::Queryable::Pipeline do
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require "spec_helper"
2
5
 
3
6
  describe Mongoid::Query do
@@ -9,8 +9,292 @@ describe Mongoid::Criteria::Queryable::Selectable do
9
9
  Mongoid::Query.new("id" => "_id")
10
10
  end
11
11
 
12
+ shared_examples_for 'returns a cloned query' do
13
+
14
+ it "returns a cloned query" do
15
+ expect(selection).to_not equal(query)
16
+ end
17
+ end
18
+
19
+ # Hoisting means the operator can be elided, for example
20
+ # Foo.and(a: 1) produces simply {'a' => 1}.
21
+ shared_examples_for 'a hoisting logical operation' do
22
+
23
+ let(:query) do
24
+ Mongoid::Query.new
25
+ end
26
+
27
+ context "when provided a single criterion" do
28
+
29
+ shared_examples_for 'adds the conditions to top level' do
30
+
31
+ it "adds the conditions to top level" do
32
+ expect(selection.selector).to eq(
33
+ "field" => [ 1, 2 ]
34
+ )
35
+ end
36
+
37
+ it_behaves_like 'returns a cloned query'
38
+ end
39
+
40
+ let(:selection) do
41
+ query.send(tested_method, field: [ 1, 2 ])
42
+ end
43
+
44
+ it_behaves_like 'adds the conditions to top level'
45
+
46
+ context 'when the criterion is wrapped in an array' do
47
+ let(:selection) do
48
+ query.send(tested_method, [{field: [ 1, 2 ] }])
49
+ end
50
+
51
+ it_behaves_like 'adds the conditions to top level'
52
+ end
53
+
54
+ context 'when the criterion is wrapped in a deep array with nil elements' do
55
+ let(:selection) do
56
+ query.send(tested_method, [[[{field: [ 1, 2 ] }]], [nil]])
57
+ end
58
+
59
+ it_behaves_like 'adds the conditions to top level'
60
+ end
61
+ end
62
+
63
+ context 'when argument is a Criteria' do
64
+ let(:base) do
65
+ query.where(hello: 'world')
66
+ end
67
+
68
+ let(:other) do
69
+ query.where(foo: 'bar')
70
+ end
71
+
72
+ let(:result) { base.send(tested_method, other) }
73
+
74
+ it 'combines' do
75
+ expect(result.selector).to eq(
76
+ 'hello' => 'world',
77
+ 'foo' => 'bar',
78
+ )
79
+ end
80
+ end
81
+
82
+ context "when provided a single criterion that is handled via Key" do
83
+
84
+ shared_examples_for 'adds the conditions to top level' do
85
+
86
+ it "adds the conditions to top level" do
87
+ expect(selection.selector).to eq({
88
+ "field" => {'$gt' => 3},
89
+ })
90
+ end
91
+
92
+ it_behaves_like 'returns a cloned query'
93
+ end
94
+
95
+ let(:selection) do
96
+ query.send(tested_method, :field.gt => 3)
97
+ end
98
+
99
+ it_behaves_like 'adds the conditions to top level'
100
+
101
+ context 'when the criterion is wrapped in an array' do
102
+ let(:selection) do
103
+ query.send(tested_method, [{ :field.gt => 3 }])
104
+ end
105
+
106
+ it_behaves_like 'adds the conditions to top level'
107
+ end
108
+
109
+ context 'when the criterion is wrapped in a deep array with nil elements' do
110
+ let(:selection) do
111
+ query.send(tested_method, [[[{ :field.gt => 3 }]], [nil]])
112
+ end
113
+
114
+ it_behaves_like 'adds the conditions to top level'
115
+ end
116
+
117
+ context 'when the criterion is a time' do
118
+ let(:selection) do
119
+ query.send(tested_method, :field.gte => Time.new(2020, 1, 1))
120
+ end
121
+
122
+ it 'adds the conditions' do
123
+ expect(selection.selector).to eq({
124
+ "field" => {'$gte' => Time.new(2020, 1, 1)},
125
+ })
126
+ end
127
+
128
+ it 'keeps argument type' do
129
+ selection.selector['field']['$gte'].should be_a(Time)
130
+ end
131
+ end
132
+
133
+ context 'when the criterion is a datetime' do
134
+ let(:selection) do
135
+ query.send(tested_method, :field.gte => DateTime.new(2020, 1, 1))
136
+ end
137
+
138
+ it 'adds the conditions' do
139
+ expect(selection.selector).to eq({
140
+ "field" => {'$gte' => Time.utc(2020, 1, 1)},
141
+ })
142
+ end
143
+
144
+ it 'converts argument to a time' do
145
+ selection.selector['field']['$gte'].should be_a(Time)
146
+ end
147
+ end
148
+
149
+ context 'when the criterion is a date' do
150
+ let(:selection) do
151
+ query.send(tested_method, :field.gte => Date.new(2020, 1, 1))
152
+ end
153
+
154
+ it 'adds the conditions' do
155
+ expect(selection.selector).to eq({
156
+ "field" => {'$gte' => Time.utc(2020, 1, 1)},
157
+ })
158
+ end
159
+
160
+ it 'converts argument to a time' do
161
+ selection.selector['field']['$gte'].should be_a(Time)
162
+ end
163
+ end
164
+ end
165
+
166
+ context "when provided a nested criterion" do
167
+
168
+ let(:selection) do
169
+ query.send(tested_method, :test.elem_match => { :field.in => [ 1, 2 ] })
170
+ end
171
+
172
+ it "builds the correct selector" do
173
+ expect(selection.selector).to eq({
174
+ "test" => { "$elemMatch" => { "field" => { "$in" => [ 1, 2 ] }}}
175
+ })
176
+ end
177
+
178
+ it_behaves_like 'returns a cloned query'
179
+ end
180
+
181
+ context "when chaining the criteria" do
182
+
183
+ context "when the criteria are for different fields" do
184
+
185
+ let(:selection) do
186
+ query.and(first: [ 1, 2 ]).send(tested_method, second: [ 3, 4 ])
187
+ end
188
+
189
+ it "adds the conditions to top level" do
190
+ expect(selection.selector).to eq({
191
+ "first" => [ 1, 2 ],
192
+ "second" => [ 3, 4 ],
193
+ })
194
+ end
195
+
196
+ it_behaves_like 'returns a cloned query'
197
+ end
198
+
199
+ context "when the criteria are on the same field" do
200
+
201
+ let(:selection) do
202
+ query.and(first: [ 1, 2 ]).send(tested_method, first: [ 3, 4 ])
203
+ end
204
+
205
+ it "combines via $and operator" do
206
+ expect(selection.selector).to eq({
207
+ "first" => [ 1, 2 ],
208
+ "$and" => [
209
+ { "first" => [ 3, 4 ] }
210
+ ]
211
+ })
212
+ end
213
+
214
+ it_behaves_like 'returns a cloned query'
215
+ end
216
+ end
217
+ end
218
+
219
+ # Non-hoisting means the operator is always present, for example
220
+ # Foo.or(a: 1) produces {'$or' => [{'a' => 1}]}.
221
+ shared_examples_for 'a non-hoisting logical operation' do
222
+
223
+ context 'when there is a single predicate' do
224
+ let(:query) do
225
+ Mongoid::Query.new.send(tested_method, hello: 'world')
226
+ end
227
+
228
+ it 'adds the predicate' do
229
+ expect(query.selector).to eq(expected_operator => [{'hello' => 'world'}])
230
+ end
231
+ end
232
+
233
+ context 'when the single predicate is wrapped in an array' do
234
+ let(:query) do
235
+ Mongoid::Query.new.send(tested_method, [{hello: 'world'}])
236
+ end
237
+
238
+ it 'adds the predicate' do
239
+ expect(query.selector).to eq(expected_operator => [{'hello' => 'world'}])
240
+ end
241
+ end
242
+
243
+ context 'when argument is a Criteria' do
244
+ let(:query) do
245
+ Mongoid::Query.new.where(hello: 'world')
246
+ end
247
+
248
+ let(:other) do
249
+ Mongoid::Query.new.where(foo: 'bar')
250
+ end
251
+
252
+ let(:result) { query.send(tested_method, other) }
253
+
254
+ it 'combines' do
255
+ # This is used for $or / $nor, the two conditions should remain
256
+ # as separate hashes
257
+ expect(result.selector).to eq(expected_operator => [{'hello' => 'world'}, {'foo' => 'bar'}])
258
+ end
259
+ end
260
+
261
+ context 'when argument is a mix of Criteria and hashes' do
262
+ let(:query) do
263
+ Mongoid::Query.new.where(hello: 'world')
264
+ end
265
+
266
+ let(:other1) do
267
+ Mongoid::Query.new.where(foo: 'bar')
268
+ end
269
+
270
+ let(:other2) do
271
+ {bar: 42}
272
+ end
273
+
274
+ let(:other3) do
275
+ Mongoid::Query.new.where(a: 2)
276
+ end
277
+
278
+ let(:result) { query.send(tested_method, other1, other2, other3) }
279
+
280
+ it 'combines' do
281
+ expect(result.selector).to eq(expected_operator => [
282
+ {'hello' => 'world'},
283
+ {'foo' => 'bar'},
284
+ {'bar' => 42},
285
+ {'a' => 2},
286
+ ])
287
+ end
288
+ end
289
+ end
290
+
12
291
  describe "#and" do
13
292
 
293
+ let(:tested_method) { :and }
294
+ let(:expected_operator) { '$and' }
295
+
296
+ it_behaves_like 'a hoisting logical operation'
297
+
14
298
  context "when provided no criterion" do
15
299
 
16
300
  let(:selection) do
@@ -25,9 +309,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
25
309
  expect(selection).to eq(query)
26
310
  end
27
311
 
28
- it "returns a cloned query" do
29
- expect(selection).to_not equal(query)
30
- end
312
+ it_behaves_like 'returns a cloned query'
31
313
  end
32
314
 
33
315
  context "when provided nil" do
@@ -44,103 +326,669 @@ describe Mongoid::Criteria::Queryable::Selectable do
44
326
  expect(selection).to eq(query)
45
327
  end
46
328
 
47
- it "returns a cloned query" do
48
- expect(selection).to_not equal(query)
329
+ it_behaves_like 'returns a cloned query'
330
+ end
331
+
332
+ context "when provided multiple criteria" do
333
+
334
+ context "when the criterion is already included" do
335
+
336
+ context 'simple criterion' do
337
+ let(:selection) do
338
+ query.and({ first: [ 1, 2 ] }).and({ first: [ 1, 2 ] })
339
+ end
340
+
341
+ it "adds all conditions" do
342
+ expect(selection.selector).to eq({
343
+ 'first' => [1, 2],
344
+ "$and" => [
345
+ { "first" => [ 1, 2 ] }
346
+ ]
347
+ })
348
+ end
349
+
350
+ it_behaves_like 'returns a cloned query'
351
+ end
352
+
353
+ context 'Key criterion' do
354
+ let(:selection) do
355
+ query.and({ first: [ 1, 2 ] }).and(:first.gt => 3)
356
+ end
357
+
358
+ it "adds all conditions" do
359
+ expect(selection.selector).to eq({
360
+ 'first' => [1, 2],
361
+ "$and" => [
362
+ { "first" => {'$gt' => 3} }
363
+ ]
364
+ })
365
+ end
366
+
367
+ it_behaves_like 'returns a cloned query'
368
+ end
369
+
370
+ context 'Key criterion when existing criterion is an operator' do
371
+ let(:selection) do
372
+ query.and(:first.lt => 5).and(:first.gt => 3)
373
+ end
374
+
375
+ it "adds all conditions" do
376
+ expect(selection.selector).to eq({
377
+ 'first' => {'$lt' => 5, '$gt' => 3},
378
+ })
379
+ end
380
+
381
+ it_behaves_like 'returns a cloned query'
382
+ end
383
+ end
384
+
385
+ context "when the new criteria are for different fields" do
386
+
387
+ let(:selection) do
388
+ query.and({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
389
+ end
390
+
391
+ it "adds all conditions to top level" do
392
+ expect(selection.selector).to eq({
393
+ "first" => [ 1, 2 ],
394
+ "second" => [ 3, 4 ],
395
+ })
396
+ end
397
+
398
+ it_behaves_like 'returns a cloned query'
399
+ end
400
+
401
+ context "when the new criteria are for the same field" do
402
+
403
+ context 'when criteria are simple' do
404
+ let(:selection) do
405
+ query.and({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
406
+ end
407
+
408
+ it "combines via $and operator" do
409
+ expect(selection.selector).to eq({
410
+ "first" => [ 1, 2 ],
411
+ "$and" => [
412
+ { "first" => [ 3, 4 ] }
413
+ ]
414
+ })
415
+ end
416
+
417
+ it_behaves_like 'returns a cloned query'
418
+ end
419
+
420
+ context 'when criteria are handled via Key' do
421
+ shared_examples_for 'adds the conditions to top level' do
422
+
423
+ it "adds the conditions to top level" do
424
+ expect(selection.selector).to eq({
425
+ "field" => {'$gt' => 3, '$lt' => 5},
426
+ })
427
+ end
428
+
429
+ it_behaves_like 'returns a cloned query'
430
+ end
431
+
432
+ context 'criteria are provided in the same hash' do
433
+ let(:selection) do
434
+ query.send(tested_method, :field.gt => 3, :field.lt => 5)
435
+ end
436
+
437
+ it_behaves_like 'adds the conditions to top level'
438
+ end
439
+
440
+ context 'criteria are provided in separate hashes' do
441
+ let(:selection) do
442
+ query.send(tested_method, {:field.gt => 3}, {:field.lt => 5})
443
+ end
444
+
445
+ it_behaves_like 'adds the conditions to top level'
446
+ end
447
+
448
+ context 'when the criterion is wrapped in an array' do
449
+ let(:selection) do
450
+ query.send(tested_method, [:field.gt => 3], [:field.lt => 5])
451
+ end
452
+
453
+ it_behaves_like 'adds the conditions to top level'
454
+ end
455
+ end
456
+
457
+ context 'when criteria are simple and handled via Key' do
458
+ shared_examples_for 'combines conditions with $and' do
459
+
460
+ it "combines conditions with $and" do
461
+ expect(selection.selector).to eq({
462
+ "field" => 3,
463
+ '$and' => ['field' => {'$lt' => 5}],
464
+ })
465
+ end
466
+
467
+ it_behaves_like 'returns a cloned query'
468
+ end
469
+
470
+ context 'criteria are provided in the same hash' do
471
+ let(:selection) do
472
+ query.send(tested_method, :field => 3, :field.lt => 5)
473
+ end
474
+
475
+ it_behaves_like 'combines conditions with $and'
476
+ end
477
+
478
+ context 'criteria are provided in separate hashes' do
479
+ let(:selection) do
480
+ query.send(tested_method, {:field => 3}, {:field.lt => 5})
481
+ end
482
+
483
+ it_behaves_like 'combines conditions with $and'
484
+ end
485
+
486
+ context 'when the criterion is wrapped in an array' do
487
+ let(:selection) do
488
+ query.send(tested_method, [:field => 3], [:field.lt => 5])
489
+ end
490
+
491
+ it_behaves_like 'combines conditions with $and'
492
+ end
493
+ end
494
+
495
+ context 'when criteria are handled via Key and simple' do
496
+ shared_examples_for 'combines conditions with $and' do
497
+
498
+ it "combines conditions with $and" do
499
+ expect(selection.selector).to eq({
500
+ "field" => {'$gt' => 3},
501
+ '$and' => ['field' => 5],
502
+ })
503
+ end
504
+
505
+ it_behaves_like 'returns a cloned query'
506
+ end
507
+
508
+ context 'criteria are provided in the same hash' do
509
+ let(:selection) do
510
+ query.send(tested_method, :field.gt => 3, :field => 5)
511
+ end
512
+
513
+ it_behaves_like 'combines conditions with $and'
514
+ end
515
+
516
+ context 'criteria are provided in separate hashes' do
517
+ let(:selection) do
518
+ query.send(tested_method, {:field.gt => 3}, {:field => 5})
519
+ end
520
+
521
+ it_behaves_like 'combines conditions with $and'
522
+ end
523
+
524
+ context 'when the criterion is wrapped in an array' do
525
+ let(:selection) do
526
+ query.send(tested_method, [:field.gt => 3], [:field => 5])
527
+ end
528
+
529
+ it_behaves_like 'combines conditions with $and'
530
+ end
531
+ end
49
532
  end
50
533
  end
51
534
 
52
- context "when provided a single criterion" do
535
+ context 'when argument is a Criteria' do
536
+ let(:query) do
537
+ Mongoid::Query.new.where(hello: 'world')
538
+ end
539
+
540
+ let(:result) { query.and(other) }
541
+
542
+ context 'different fields' do
543
+
544
+ let(:other) do
545
+ Mongoid::Query.new.where(foo: 'bar')
546
+ end
547
+
548
+ it 'combines both fields at top level' do
549
+ expect(result.selector).to eq('hello' => 'world', 'foo' => 'bar')
550
+ end
551
+ end
552
+
553
+ context 'same field' do
554
+
555
+ let(:other) do
556
+ Mongoid::Query.new.where(hello: /bar/)
557
+ end
558
+
559
+ it 'combines fields with $and' do
560
+ expect(result.selector).to eq('hello' => 'world', '$and' => [{'hello' => /bar/}])
561
+ end
562
+ end
563
+ end
564
+
565
+ context 'when argument is a mix of Criteria and hashes' do
566
+ let(:query) do
567
+ Mongoid::Query.new.where(hello: 'world')
568
+ end
569
+
570
+ let(:other1) do
571
+ Mongoid::Query.new.where(foo: 'bar')
572
+ end
573
+
574
+ let(:other2) do
575
+ {bar: 42}
576
+ end
577
+
578
+ let(:other3) do
579
+ Mongoid::Query.new.where(a: 2)
580
+ end
581
+
582
+ let(:result) { query.and(other1, other2, other3) }
583
+
584
+ it 'combines' do
585
+ expect(result.selector).to eq('hello' => 'world',
586
+ 'foo' => 'bar',
587
+ 'bar' => 42,
588
+ 'a' => 2,
589
+ )
590
+ end
591
+ end
592
+
593
+ context 'when Key instances are used and types involved have serializers' do
594
+ let(:time) { Time.now }
595
+
596
+ let(:query) do
597
+ Band.all.and(:created_at.gt => time)
598
+ end
599
+
600
+ let(:expected) do
601
+ {'created_at' => {'$gt' => time.utc}}
602
+ end
603
+
604
+ it 'combines and evolves' do
605
+ expect(query.selector).to eq(expected)
606
+ end
607
+ end
608
+
609
+ describe 'query shape' do
610
+ shared_examples_for 'adds most recent criterion as $and' do
611
+ let(:selector) { scope.selector }
612
+
613
+ it 'adds most recent criterion as $and' do
614
+ expect(selector).to eq('foo' => 1, '$and' => [{'foo' => 2}])
615
+ end
616
+ end
617
+
618
+ context 'and/and' do
619
+ let(:scope) do
620
+ Band.and(foo: 1).and(foo: 2)
621
+ end
622
+
623
+ it_behaves_like 'adds most recent criterion as $and'
624
+ end
625
+
626
+ context 'and/and' do
627
+ let(:scope) do
628
+ Band.and(foo: 1).and(foo: 2)
629
+ end
630
+
631
+ it_behaves_like 'adds most recent criterion as $and'
632
+ end
633
+
634
+ context 'and/where' do
635
+ let(:scope) do
636
+ Band.and(foo: 1).where(foo: 2)
637
+ end
638
+
639
+ it_behaves_like 'adds most recent criterion as $and'
640
+ end
641
+
642
+ context 'where/and' do
643
+ let(:scope) do
644
+ Band.where(foo: 1).and(foo: 2)
645
+ end
646
+
647
+ it_behaves_like 'adds most recent criterion as $and'
648
+ end
649
+
650
+ context 'where/where' do
651
+ let(:scope) do
652
+ Band.where(foo: 1).where(foo: 2)
653
+ end
654
+
655
+ it_behaves_like 'adds most recent criterion as $and'
656
+ end
657
+ end
658
+
659
+ context 'when conditions already exist in criteria' do
660
+ let(:base_selection) do
661
+ query.where(foo: 'bar')
662
+ end
663
+
664
+ context 'when hash conditions are given' do
665
+ let(:selection) do
666
+ base_selection.and(hello: 'world')
667
+ end
668
+
669
+ it 'adds new conditions to top level' do
670
+ selection.selector.should == {
671
+ 'foo' => 'bar',
672
+ 'hello' => 'world',
673
+ }
674
+ end
675
+ end
676
+
677
+ context 'when criteria conditions are given' do
678
+ let(:selection) do
679
+ base_selection.and(query.where(hello: 'world'))
680
+ end
681
+
682
+ it 'adds new conditions to top level' do
683
+ selection.selector.should == {
684
+ 'foo' => 'bar',
685
+ 'hello' => 'world',
686
+ }
687
+ end
688
+ end
689
+
690
+ context 'when complex criteria conditions are given' do
691
+ let(:selection) do
692
+ base_selection.and(query.or([one: 'one'], [two: 'two']))
693
+ end
694
+
695
+ it 'adds new conditions to top level' do
696
+ selection.selector.should == {
697
+ 'foo' => 'bar',
698
+ '$or' => [
699
+ {'one' => 'one'},
700
+ {'two' => 'two'},
701
+ ],
702
+ }
703
+ end
704
+ end
705
+ end
706
+ end
707
+
708
+ shared_examples '$or/$nor' do
709
+
710
+ it_behaves_like 'a non-hoisting logical operation'
711
+
712
+ context "when provided no arguments" do
53
713
 
54
714
  let(:selection) do
55
- query.and(field: [ 1, 2 ])
715
+ query.send(tested_method)
56
716
  end
57
717
 
58
- it "adds the $and selector" do
59
- expect(selection.selector).to eq({
60
- "$and" => [{ "field" => [ 1, 2 ] }]
61
- })
718
+ it_behaves_like 'returns a cloned query'
719
+
720
+ it "does not add any criteria" do
721
+ expect(selection.selector).to eq({})
62
722
  end
63
723
 
64
- it "returns a cloned query" do
65
- expect(selection).to_not equal(query)
724
+ it "returns the query" do
725
+ expect(selection).to eq(query)
66
726
  end
67
727
  end
68
728
 
69
- context "when provided a nested criterion" do
729
+ context "when provided nil" do
70
730
 
71
731
  let(:selection) do
72
- query.and(:test.elem_match => { :field.in => [ 1, 2 ] })
732
+ query.send(tested_method, nil)
73
733
  end
74
734
 
75
- it "adds the $and selector" do
76
- expect(selection.selector).to eq({
77
- "$and" => [{ "test" => { "$elemMatch" => { "field" => { "$in" => [ 1, 2 ] }}}}]
78
- })
735
+ it_behaves_like 'returns a cloned query'
736
+
737
+ it "does not add any criteria" do
738
+ expect(selection.selector).to eq({})
79
739
  end
80
740
 
81
- it "returns a cloned query" do
82
- expect(selection).to_not equal(query)
741
+ it "returns the query" do
742
+ expect(selection).to eq(query)
83
743
  end
84
744
  end
85
745
 
86
- context "when provided multiple criterion" do
746
+ context "when provided a single criterion" do
87
747
 
88
- context "when the criterion is already included" do
748
+ let(:selection) do
749
+ query.send(tested_method, field: [ 1, 2 ])
750
+ end
751
+
752
+ it_behaves_like 'returns a cloned query'
753
+
754
+ it "adds the $or/$nor selector" do
755
+ expect(selection.selector).to eq({
756
+ expected_operator => [{ "field" => [ 1, 2 ] }]
757
+ })
758
+ end
759
+
760
+ context 'when the criterion is wrapped in array' do
89
761
 
90
762
  let(:selection) do
91
- query.and({ first: [ 1, 2 ] }).and({ first: [ 1, 2 ] })
763
+ query.send(tested_method, [{ field: [ 1, 2 ] }])
92
764
  end
93
765
 
94
- it "does not duplicate the $and selector" do
766
+ it_behaves_like 'returns a cloned query'
767
+
768
+ it "adds the $or/$nor selector" do
95
769
  expect(selection.selector).to eq({
96
- "$and" => [
97
- { "first" => [ 1, 2 ] }
98
- ]
770
+ expected_operator => [{ "field" => [ 1, 2 ] }]
99
771
  })
100
772
  end
101
773
 
102
- it "returns a cloned query" do
103
- expect(selection).to_not equal(query)
774
+ context 'when the array has nil as one of the elements' do
775
+
776
+ let(:selection) do
777
+ query.send(tested_method, [{ field: [ 1, 2 ] }, nil])
778
+ end
779
+
780
+ it_behaves_like 'returns a cloned query'
781
+
782
+ it "adds the $or/$nor selector ignoring the nil element" do
783
+ expect(selection.selector).to eq({
784
+ expected_operator => [{ "field" => [ 1, 2 ] }]
785
+ })
786
+ end
104
787
  end
105
788
  end
106
789
 
107
- context "when the criterion are for different fields" do
790
+ context 'when query already has a condition on another field' do
108
791
 
109
792
  let(:selection) do
110
- query.and({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
793
+ query.where(foo: 'bar').send(tested_method, field: [ 1, 2 ])
111
794
  end
112
795
 
113
- it "adds the $and selector" do
796
+ it 'moves original conditions under $or/$nor' do
114
797
  expect(selection.selector).to eq({
115
- "$and" => [
798
+ expected_operator => [{'foo' => 'bar'}, { "field" => [ 1, 2 ] }]
799
+ })
800
+ end
801
+ end
802
+
803
+ context 'when query already has an $or/$nor condition and another condition' do
804
+
805
+ let(:selection) do
806
+ query.send(tested_method, field: [ 1, 2 ]).where(foo: 'bar').send(tested_method, test: 1)
807
+ end
808
+
809
+ it 'unions existing conditions' do
810
+ expect(selection.selector).to eq(
811
+ expected_operator => [
812
+ {
813
+ expected_operator => [{ "field" => [ 1, 2 ] }],
814
+ 'foo' => 'bar',
815
+ },
816
+ {'test' => 1},
817
+ ]
818
+ )
819
+ end
820
+ end
821
+ end
822
+
823
+ context "when provided multiple criteria" do
824
+
825
+ context "when the criteria are for different fields" do
826
+
827
+ let(:selection) do
828
+ query.send(tested_method, { first: [ 1, 2 ] }, { second: [ 3, 4 ] })
829
+ end
830
+
831
+ it_behaves_like 'returns a cloned query'
832
+
833
+ it "adds the $or/$nor selector" do
834
+ expect(selection.selector).to eq({
835
+ expected_operator => [
116
836
  { "first" => [ 1, 2 ] },
117
837
  { "second" => [ 3, 4 ] }
118
838
  ]
119
839
  })
120
840
  end
841
+ end
842
+
843
+ context "when the criteria uses a Key instance" do
844
+
845
+ let(:selection) do
846
+ query.send(tested_method, { first: [ 1, 2 ] }, { :second.gt => 3 })
847
+ end
848
+
849
+ it "adds the $or/$nor selector" do
850
+ expect(selection.selector).to eq({
851
+ expected_operator => [
852
+ { "first" => [ 1, 2 ] },
853
+ { "second" => { "$gt" => 3 }}
854
+ ]
855
+ })
856
+ end
857
+
858
+ it_behaves_like 'returns a cloned query'
859
+
860
+ context 'when the criterion is a time' do
861
+ let(:selection) do
862
+ query.send(tested_method, :field.gte => Time.new(2020, 1, 1))
863
+ end
864
+
865
+ it 'adds the conditions' do
866
+ expect(selection.selector).to eq(expected_operator => [
867
+ "field" => {'$gte' => Time.new(2020, 1, 1)},
868
+ ])
869
+ end
870
+
871
+ it 'keeps the type' do
872
+ selection.selector[expected_operator].first['field']['$gte'].should be_a(Time)
873
+ end
874
+ end
121
875
 
122
- it "returns a cloned query" do
123
- expect(selection).to_not equal(query)
876
+ context 'when the criterion is a datetime' do
877
+ let(:selection) do
878
+ query.send(tested_method, :field.gte => DateTime.new(2020, 1, 1))
879
+ end
880
+
881
+ it 'adds the conditions' do
882
+ expect(selection.selector).to eq(expected_operator => [
883
+ "field" => {'$gte' => Time.utc(2020, 1, 1)},
884
+ ])
885
+ end
886
+
887
+ it 'converts argument to a time' do
888
+ selection.selector[expected_operator].first['field']['$gte'].should be_a(Time)
889
+ end
890
+ end
891
+
892
+ context 'when the criterion is a date' do
893
+ let(:selection) do
894
+ query.send(tested_method, :field.gte => Date.new(2020, 1, 1))
895
+ end
896
+
897
+ it 'adds the conditions' do
898
+ expect(selection.selector).to eq(expected_operator => [
899
+ "field" => {'$gte' => Time.utc(2020, 1, 1)},
900
+ ])
901
+ end
902
+
903
+ it 'converts argument to a time' do
904
+ selection.selector[expected_operator].first['field']['$gte'].should be_a(Time)
905
+ end
124
906
  end
125
907
  end
126
908
 
127
- context "when the criterion are on the same field" do
909
+ context "when a criterion has an aliased field" do
128
910
 
129
911
  let(:selection) do
130
- query.and({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
912
+ query.send(tested_method, { id: 1 })
131
913
  end
132
914
 
133
- it "appends both $and expressions" do
915
+ it "adds the $or/$nor selector and aliases the field" do
134
916
  expect(selection.selector).to eq({
135
- "$and" => [
917
+ expected_operator => [ { "_id" => 1 } ]
918
+ })
919
+ end
920
+
921
+ it_behaves_like 'returns a cloned query'
922
+ end
923
+
924
+ context "when a criterion is wrapped in an array" do
925
+
926
+ let(:selection) do
927
+ query.send(tested_method, [{ first: [ 1, 2 ] }, { :second.gt => 3 }])
928
+ end
929
+
930
+ it_behaves_like 'returns a cloned query'
931
+
932
+ it "adds the $or/$nor selector" do
933
+ expect(selection.selector).to eq({
934
+ expected_operator => [
136
935
  { "first" => [ 1, 2 ] },
137
- { "first" => [ 3, 4 ] }
936
+ { "second" => { "$gt" => 3 }}
138
937
  ]
139
938
  })
140
939
  end
940
+ end
941
+
942
+ context "when the criteria are on the same field" do
943
+
944
+ context 'simple criteria' do
945
+ let(:selection) do
946
+ query.send(tested_method, { first: [ 1, 2 ] }, { first: [ 3, 4 ] })
947
+ end
948
+
949
+ it_behaves_like 'returns a cloned query'
950
+
951
+ it "appends both $or/$nor expressions" do
952
+ expect(selection.selector).to eq({
953
+ expected_operator => [
954
+ { "first" => [ 1, 2 ] },
955
+ { "first" => [ 3, 4 ] }
956
+ ]
957
+ })
958
+ end
959
+ end
960
+
961
+ context 'Key criteria as one argument' do
962
+ let(:selection) do
963
+ query.send(tested_method, :first.gt => 3, :first.lt => 5)
964
+ end
965
+
966
+ it_behaves_like 'returns a cloned query'
141
967
 
142
- it "returns a cloned query" do
143
- expect(selection).to_not equal(query)
968
+ it "adds all criteria" do
969
+ expect(selection.selector).to eq({
970
+ expected_operator => [
971
+ { "first" => {'$gt' => 3, '$lt' => 5} },
972
+ ]
973
+ })
974
+ end
975
+ end
976
+
977
+ context 'Key criteria as multiple arguments' do
978
+ let(:selection) do
979
+ query.send(tested_method, {:first.gt => 3}, {:first.lt => 5})
980
+ end
981
+
982
+ it_behaves_like 'returns a cloned query'
983
+
984
+ it "adds all criteria" do
985
+ expect(selection.selector).to eq({
986
+ expected_operator => [
987
+ { "first" => {'$gt' => 3} },
988
+ { "first" => {'$lt' => 5} },
989
+ ]
990
+ })
991
+ end
144
992
  end
145
993
  end
146
994
  end
@@ -150,385 +998,452 @@ describe Mongoid::Criteria::Queryable::Selectable do
150
998
  context "when the criterion are for different fields" do
151
999
 
152
1000
  let(:selection) do
153
- query.and(first: [ 1, 2 ]).and(second: [ 3, 4 ])
1001
+ query.send(tested_method, first: [ 1, 2 ]).send(tested_method, second: [ 3, 4 ])
154
1002
  end
155
1003
 
156
- it "adds the $and selectors" do
1004
+ it_behaves_like 'returns a cloned query'
1005
+
1006
+ it "adds the $or/$nor selectors" do
157
1007
  expect(selection.selector).to eq({
158
- "$and" => [
1008
+ expected_operator => [
159
1009
  { "first" => [ 1, 2 ] },
160
1010
  { "second" => [ 3, 4 ] }
161
1011
  ]
162
1012
  })
163
1013
  end
164
-
165
- it "returns a cloned query" do
166
- expect(selection).to_not equal(query)
167
- end
168
1014
  end
169
1015
 
170
1016
  context "when the criterion are on the same field" do
171
1017
 
172
1018
  let(:selection) do
173
- query.and(first: [ 1, 2 ]).and(first: [ 3, 4 ])
1019
+ query.send(tested_method, first: [ 1, 2 ]).send(tested_method, first: [ 3, 4 ])
174
1020
  end
175
1021
 
176
- it "appends both $and expressions" do
1022
+ it_behaves_like 'returns a cloned query'
1023
+
1024
+ it "appends both $or/$nor expressions" do
177
1025
  expect(selection.selector).to eq({
178
- "$and" => [
1026
+ expected_operator => [
179
1027
  { "first" => [ 1, 2 ] },
180
1028
  { "first" => [ 3, 4 ] }
181
1029
  ]
182
1030
  })
183
1031
  end
184
-
185
- it "returns a cloned query" do
186
- expect(selection).to_not equal(query)
187
- end
188
1032
  end
189
1033
  end
190
1034
  end
191
1035
 
192
1036
  describe "#or" do
193
1037
 
194
- context "when provided no criterion" do
1038
+ let(:tested_method) { :or }
1039
+ let(:expected_operator) { '$or' }
1040
+
1041
+ it_behaves_like '$or/$nor'
1042
+ end
1043
+
1044
+ describe "#nor" do
1045
+
1046
+ let(:tested_method) { :nor }
1047
+ let(:expected_operator) { '$nor' }
1048
+
1049
+ it_behaves_like '$or/$nor'
1050
+ end
1051
+
1052
+ describe "#any_of" do
1053
+
1054
+ let(:tested_method) { :any_of }
1055
+ let(:expected_operator) { '$or' }
1056
+
1057
+ it_behaves_like 'a hoisting logical operation'
1058
+
1059
+ # When multiple arguments are given to any_of, it behaves differently
1060
+ # from and.
1061
+ context 'when argument is a mix of Criteria and hashes' do
1062
+ let(:query) do
1063
+ Mongoid::Query.new.where(hello: 'world')
1064
+ end
1065
+
1066
+ let(:other1) do
1067
+ Mongoid::Query.new.where(foo: 'bar')
1068
+ end
1069
+
1070
+ let(:other2) do
1071
+ {bar: 42}
1072
+ end
1073
+
1074
+ let(:other3) do
1075
+ Mongoid::Query.new.where(a: 2)
1076
+ end
1077
+
1078
+ let(:result) { query.send(tested_method, other1, other2, other3) }
1079
+
1080
+ it 'combines' do
1081
+ expect(result.selector).to eq(
1082
+ 'hello' => 'world',
1083
+ expected_operator => [
1084
+ {'foo' => 'bar'},
1085
+ {'bar' => 42},
1086
+ {'a' => 2},
1087
+ ],
1088
+ )
1089
+ end
1090
+ end
1091
+
1092
+ context "when provided no arguments" do
195
1093
 
196
1094
  let(:selection) do
197
- query.or
1095
+ query.any_of
198
1096
  end
199
1097
 
200
- it "does not add any criterion" do
1098
+ it_behaves_like 'returns a cloned query'
1099
+
1100
+ it "does not add any criteria" do
201
1101
  expect(selection.selector).to eq({})
202
1102
  end
203
1103
 
204
1104
  it "returns the query" do
205
1105
  expect(selection).to eq(query)
206
1106
  end
207
-
208
- it "returns a cloned query" do
209
- expect(selection).to_not equal(query)
210
- end
211
1107
  end
212
1108
 
213
1109
  context "when provided nil" do
214
1110
 
215
1111
  let(:selection) do
216
- query.or(nil)
1112
+ query.any_of(nil)
217
1113
  end
218
1114
 
219
- it "does not add any criterion" do
1115
+ it_behaves_like 'returns a cloned query'
1116
+
1117
+ it "does not add any criteria" do
220
1118
  expect(selection.selector).to eq({})
221
1119
  end
222
1120
 
223
1121
  it "returns the query" do
224
1122
  expect(selection).to eq(query)
225
1123
  end
226
-
227
- it "returns a cloned query" do
228
- expect(selection).to_not equal(query)
229
- end
230
1124
  end
231
1125
 
232
1126
  context "when provided a single criterion" do
233
1127
 
234
1128
  let(:selection) do
235
- query.or(field: [ 1, 2 ])
1129
+ query.any_of(field: [ 1, 2 ])
236
1130
  end
237
1131
 
1132
+ it_behaves_like 'returns a cloned query'
1133
+
238
1134
  it "adds the $or selector" do
239
- expect(selection.selector).to eq({
240
- "$or" => [{ "field" => [ 1, 2 ] }]
241
- })
1135
+ expect(selection.selector).to eq(
1136
+ "field" => [ 1, 2 ],
1137
+ )
242
1138
  end
243
1139
 
244
- it "returns a cloned query" do
245
- expect(selection).to_not equal(query)
246
- end
247
- end
1140
+ context 'when the criterion is wrapped in array' do
248
1141
 
249
- context "when provided multiple criterion" do
1142
+ let(:selection) do
1143
+ query.any_of([{ field: [ 1, 2 ] }])
1144
+ end
1145
+
1146
+ it_behaves_like 'returns a cloned query'
1147
+
1148
+ it "adds the condition" do
1149
+ expect(selection.selector).to eq(
1150
+ "field" => [ 1, 2 ],
1151
+ )
1152
+ end
250
1153
 
251
- context "when the criterion are for different fields" do
1154
+ context 'when the array has nil as one of the elements' do
252
1155
 
253
- let(:selection) do
254
- query.or({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
255
- end
1156
+ let(:selection) do
1157
+ query.any_of([{ field: [ 1, 2 ] }, nil])
1158
+ end
256
1159
 
257
- it "adds the $or selector" do
258
- expect(selection.selector).to eq({
259
- "$or" => [
260
- { "first" => [ 1, 2 ] },
261
- { "second" => [ 3, 4 ] }
262
- ]
263
- })
264
- end
1160
+ it_behaves_like 'returns a cloned query'
265
1161
 
266
- it "returns a cloned query" do
267
- expect(selection).to_not equal(query)
1162
+ it "adds the $or selector ignoring the nil element" do
1163
+ expect(selection.selector).to eq(
1164
+ "field" => [ 1, 2 ],
1165
+ )
1166
+ end
268
1167
  end
269
1168
  end
270
1169
 
271
- context "when a criterion has a selectable key" do
1170
+ context 'when query already has a condition on another field' do
272
1171
 
273
- let(:selection) do
274
- query.or({ first: [ 1, 2 ] }, { :second.gt => 3 })
275
- end
1172
+ context 'when there is one argument' do
276
1173
 
277
- it "adds the $or selector" do
278
- expect(selection.selector).to eq({
279
- "$or" => [
280
- { "first" => [ 1, 2 ] },
281
- { "second" => { "$gt" => 3 }}
282
- ]
283
- })
1174
+ let(:selection) do
1175
+ query.where(foo: 'bar').any_of(field: [ 1, 2 ])
1176
+ end
1177
+
1178
+ it 'adds the new condition' do
1179
+ expect(selection.selector).to eq(
1180
+ 'foo' => 'bar',
1181
+ 'field' => [1, 2],
1182
+ )
1183
+ end
284
1184
  end
285
1185
 
286
- it "returns a cloned query" do
287
- expect(selection).to_not equal(query)
1186
+ context 'when there are multiple arguments' do
1187
+
1188
+ let(:selection) do
1189
+ query.where(foo: 'bar').any_of({field: [ 1, 2 ]}, {hello: 'world'})
1190
+ end
1191
+
1192
+ it 'adds the new condition' do
1193
+ expect(selection.selector).to eq(
1194
+ 'foo' => 'bar',
1195
+ '$or' => [
1196
+ {'field' => [1, 2]},
1197
+ {'hello' => 'world'},
1198
+ ],
1199
+ )
1200
+ end
288
1201
  end
289
1202
  end
290
1203
 
291
- context "when the criterion has an aliased field" do
1204
+ context 'when query already has an $or condition and another condition' do
292
1205
 
293
1206
  let(:selection) do
294
- query.or({ id: 1 })
295
- end
296
-
297
- it "adds the $or selector and aliases the field" do
298
- expect(selection.selector).to eq({
299
- "$or" => [ { "_id" => 1 } ]
300
- })
1207
+ query.or(field: [ 1, 2 ]).where(foo: 'bar').any_of(test: 1)
301
1208
  end
302
1209
 
303
- it "returns a cloned query" do
304
- expect(selection).to_not equal(query)
1210
+ it 'adds the new condition' do
1211
+ expect(selection.selector).to eq(
1212
+ '$or' => [{'field' => [1, 2]}],
1213
+ 'foo' => 'bar',
1214
+ 'test' => 1,
1215
+ )
305
1216
  end
306
1217
  end
1218
+ end
307
1219
 
308
- context "when a criterion is wrapped in an array" do
1220
+ context "when provided multiple criteria" do
1221
+
1222
+ context "when the criteria are for different fields" do
309
1223
 
310
1224
  let(:selection) do
311
- query.or([{ first: [ 1, 2 ] }, { :second.gt => 3 }])
1225
+ query.any_of({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
312
1226
  end
313
1227
 
1228
+ it_behaves_like 'returns a cloned query'
1229
+
314
1230
  it "adds the $or selector" do
315
1231
  expect(selection.selector).to eq({
316
1232
  "$or" => [
317
1233
  { "first" => [ 1, 2 ] },
318
- { "second" => { "$gt" => 3 }}
1234
+ { "second" => [ 3, 4 ] }
319
1235
  ]
320
1236
  })
321
1237
  end
322
-
323
- it "returns a cloned query" do
324
- expect(selection).to_not equal(query)
325
- end
326
1238
  end
327
1239
 
328
- context "when the criterion are on the same field" do
1240
+ context "when the criteria uses a Key instance" do
329
1241
 
330
1242
  let(:selection) do
331
- query.or({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
1243
+ query.any_of({ first: [ 1, 2 ] }, { :second.gt => 3 })
332
1244
  end
333
1245
 
334
- it "appends both $or expressions" do
1246
+ it "adds the $or selector" do
335
1247
  expect(selection.selector).to eq({
336
1248
  "$or" => [
337
1249
  { "first" => [ 1, 2 ] },
338
- { "first" => [ 3, 4 ] }
1250
+ { "second" => { "$gt" => 3 }}
339
1251
  ]
340
1252
  })
341
1253
  end
342
1254
 
343
- it "returns a cloned query" do
344
- expect(selection).to_not equal(query)
345
- end
1255
+ it_behaves_like 'returns a cloned query'
346
1256
  end
347
- end
348
1257
 
349
- context "when chaining the criterion" do
1258
+ context 'when criteria are simple and handled via Key' do
1259
+ shared_examples_for 'adds conditions with $or' do
350
1260
 
351
- context "when the criterion are for different fields" do
1261
+ it "adds conditions with $or" do
1262
+ expect(selection.selector).to eq({
1263
+ '$or' => [
1264
+ {'field' => 3},
1265
+ {'field' => {'$lt' => 5}},
1266
+ ],
1267
+ })
1268
+ end
352
1269
 
353
- let(:selection) do
354
- query.or(first: [ 1, 2 ]).or(second: [ 3, 4 ])
1270
+ it_behaves_like 'returns a cloned query'
355
1271
  end
356
1272
 
357
- it "adds the $or selectors" do
358
- expect(selection.selector).to eq({
359
- "$or" => [
360
- { "first" => [ 1, 2 ] },
361
- { "second" => [ 3, 4 ] }
362
- ]
363
- })
364
- end
1273
+ shared_examples_for 'adds one condition' do
1274
+
1275
+ it "adds one condition" do
1276
+ expect(selection.selector).to eq({
1277
+ 'field' => 3,
1278
+ '$and' => [
1279
+ {'field' => {'$lt' => 5}},
1280
+ ],
1281
+ })
1282
+ end
365
1283
 
366
- it "returns a cloned query" do
367
- expect(selection).to_not equal(query)
1284
+ it_behaves_like 'returns a cloned query'
368
1285
  end
369
- end
370
1286
 
371
- context "when the criterion are on the same field" do
1287
+ context 'criteria are provided in the same hash' do
1288
+ let(:selection) do
1289
+ query.send(tested_method, :field => 3, :field.lt => 5)
1290
+ end
372
1291
 
373
- let(:selection) do
374
- query.or(first: [ 1, 2 ]).or(first: [ 3, 4 ])
1292
+ it_behaves_like 'adds one condition'
375
1293
  end
376
1294
 
377
- it "appends both $or expressions" do
378
- expect(selection.selector).to eq({
379
- "$or" => [
380
- { "first" => [ 1, 2 ] },
381
- { "first" => [ 3, 4 ] }
382
- ]
383
- })
1295
+ context 'criteria are provided in separate hashes' do
1296
+ let(:selection) do
1297
+ query.send(tested_method, {:field => 3}, {:field.lt => 5})
1298
+ end
1299
+
1300
+ it_behaves_like 'adds conditions with $or'
384
1301
  end
385
1302
 
386
- it "returns a cloned query" do
387
- expect(selection).to_not equal(query)
1303
+ context 'when the criterion is wrapped in an array' do
1304
+ let(:selection) do
1305
+ query.send(tested_method, [:field => 3], [:field.lt => 5])
1306
+ end
1307
+
1308
+ it_behaves_like 'adds conditions with $or'
388
1309
  end
389
1310
  end
390
- end
391
- end
392
1311
 
393
- describe "#nor" do
1312
+ context 'when criteria are handled via Key and simple' do
1313
+ shared_examples_for 'adds conditions with $or' do
394
1314
 
395
- context "when provided no criterion" do
1315
+ it "adds conditions with $or" do
1316
+ expect(selection.selector).to eq({
1317
+ '$or' => [
1318
+ {'field' => {'$gt' => 3}},
1319
+ {'field' => 5},
1320
+ ],
1321
+ })
1322
+ end
396
1323
 
397
- let(:selection) do
398
- query.nor
399
- end
1324
+ it_behaves_like 'returns a cloned query'
1325
+ end
400
1326
 
401
- it "does not add any criterion" do
402
- expect(selection.selector).to eq({})
403
- end
1327
+ shared_examples_for 'adds one condition' do
404
1328
 
405
- it "returns the query" do
406
- expect(selection).to eq(query)
407
- end
1329
+ it "adds one condition" do
1330
+ expect(selection.selector).to eq({
1331
+ 'field' => {'$gt' => 3},
1332
+ '$and' => ['field' => 5],
1333
+ })
1334
+ end
408
1335
 
409
- it "returns a cloned query" do
410
- expect(selection).to_not equal(query)
411
- end
412
- end
1336
+ it_behaves_like 'returns a cloned query'
1337
+ end
413
1338
 
414
- context "when provided nil" do
1339
+ context 'criteria are provided in the same hash' do
1340
+ let(:selection) do
1341
+ query.send(tested_method, :field.gt => 3, :field => 5)
1342
+ end
415
1343
 
416
- let(:selection) do
417
- query.nor(nil)
418
- end
1344
+ it_behaves_like 'adds one condition'
1345
+ end
419
1346
 
420
- it "does not add any criterion" do
421
- expect(selection.selector).to eq({})
422
- end
1347
+ context 'criteria are provided in separate hashes' do
1348
+ let(:selection) do
1349
+ query.send(tested_method, {:field.gt => 3}, {:field => 5})
1350
+ end
423
1351
 
424
- it "returns the query" do
425
- expect(selection).to eq(query)
426
- end
1352
+ it_behaves_like 'adds conditions with $or'
1353
+ end
427
1354
 
428
- it "returns a cloned query" do
429
- expect(selection).to_not equal(query)
1355
+ context 'when the criterion is wrapped in an array' do
1356
+ let(:selection) do
1357
+ query.send(tested_method, [:field.gt => 3], [:field => 5])
1358
+ end
1359
+
1360
+ it_behaves_like 'adds conditions with $or'
1361
+ end
430
1362
  end
431
- end
432
1363
 
433
- context "when provided a single criterion" do
1364
+ context "when a criterion has an aliased field" do
434
1365
 
435
- let(:selection) do
436
- query.nor(field: [ 1, 2 ])
437
- end
1366
+ let(:selection) do
1367
+ query.any_of({ id: 1 })
1368
+ end
438
1369
 
439
- it "adds the $nor selector" do
440
- expect(selection.selector).to eq({
441
- "$nor" => [{"field" => [ 1, 2 ] }]
442
- })
443
- end
1370
+ it "adds the $or selector and aliases the field" do
1371
+ expect(selection.selector).to eq(
1372
+ "_id" => 1,
1373
+ )
1374
+ end
444
1375
 
445
- it "returns a cloned query" do
446
- expect(selection).to_not equal(query)
1376
+ it_behaves_like 'returns a cloned query'
447
1377
  end
448
- end
449
1378
 
450
- context "when provided multiple criterion" do
451
-
452
- context "when the criterion are fnor different fields" do
1379
+ context "when a criterion is wrapped in an array" do
453
1380
 
454
1381
  let(:selection) do
455
- query.nor({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
1382
+ query.any_of([{ first: [ 1, 2 ] }, { :second.gt => 3 }])
456
1383
  end
457
1384
 
458
- it "adds the $nor selector" do
1385
+ it_behaves_like 'returns a cloned query'
1386
+
1387
+ it "adds the $or selector" do
459
1388
  expect(selection.selector).to eq({
460
- "$nor" => [
1389
+ "$or" => [
461
1390
  { "first" => [ 1, 2 ] },
462
- { "second" => [ 3, 4 ] }
1391
+ { "second" => { "$gt" => 3 }}
463
1392
  ]
464
1393
  })
465
1394
  end
466
-
467
- it "returns a cloned query" do
468
- expect(selection).to_not equal(query)
469
- end
470
1395
  end
471
1396
 
472
- context "when the criterion are on the same field" do
1397
+ context "when the criteria are on the same field" do
473
1398
 
474
1399
  let(:selection) do
475
- query.nor({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
1400
+ query.any_of({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
476
1401
  end
477
1402
 
478
- it "appends both $nor expressions" do
1403
+ it_behaves_like 'returns a cloned query'
1404
+
1405
+ it "appends both $or expressions" do
479
1406
  expect(selection.selector).to eq({
480
- "$nor" => [
1407
+ "$or" => [
481
1408
  { "first" => [ 1, 2 ] },
482
1409
  { "first" => [ 3, 4 ] }
483
1410
  ]
484
1411
  })
485
1412
  end
486
-
487
- it "returns a cloned query" do
488
- expect(selection).to_not equal(query)
489
- end
490
1413
  end
491
1414
  end
492
1415
 
493
- context "when chaining the criterion" do
1416
+ context "when chaining the criteria" do
494
1417
 
495
- context "when the criterion are fnor different fields" do
1418
+ context "when the criteria are for different fields" do
496
1419
 
497
1420
  let(:selection) do
498
- query.nor(first: [ 1, 2 ]).nor(second: [ 3, 4 ])
1421
+ query.any_of(first: [ 1, 2 ]).any_of(second: [ 3, 4 ])
499
1422
  end
500
1423
 
501
- it "adds the $nor selectors" do
502
- expect(selection.selector).to eq({
503
- "$nor" => [
504
- { "first" => [ 1, 2 ] },
505
- { "second" => [ 3, 4 ] }
506
- ]
507
- })
508
- end
1424
+ it_behaves_like 'returns a cloned query'
509
1425
 
510
- it "returns a cloned query" do
511
- expect(selection).to_not equal(query)
1426
+ it "adds the conditions separately" do
1427
+ expect(selection.selector).to eq(
1428
+ "first" => [ 1, 2 ],
1429
+ "second" => [ 3, 4 ],
1430
+ )
512
1431
  end
513
1432
  end
514
1433
 
515
- context "when the criterion are on the same field" do
1434
+ context "when the criteria are on the same field" do
516
1435
 
517
1436
  let(:selection) do
518
- query.nor(first: [ 1, 2 ]).nor(first: [ 3, 4 ])
1437
+ query.any_of(first: [ 1, 2 ]).any_of(first: [ 3, 4 ])
519
1438
  end
520
1439
 
521
- it "appends both $nor expressions" do
522
- expect(selection.selector).to eq({
523
- "$nor" => [
524
- { "first" => [ 1, 2 ] },
525
- { "first" => [ 3, 4 ] }
526
- ]
527
- })
528
- end
1440
+ it_behaves_like 'returns a cloned query'
529
1441
 
530
- it "returns a cloned query" do
531
- expect(selection).to_not equal(query)
1442
+ it "adds the conditions separately" do
1443
+ expect(selection.selector).to eq(
1444
+ "first" => [ 1, 2 ],
1445
+ '$and' => [{"first" => [ 3, 4 ]}],
1446
+ )
532
1447
  end
533
1448
  end
534
1449
  end
@@ -561,27 +1476,45 @@ describe Mongoid::Criteria::Queryable::Selectable do
561
1476
  expect(query.negating).to be nil
562
1477
  end
563
1478
 
564
- context "when the following criteria is a query method" do
565
-
1479
+ shared_examples_for 'negates the next condition' do
566
1480
  let(:selection) do
567
- query.not.all(field: [ 1, 2 ])
1481
+ query.not.send(query_method, field: [ 1, 2 ])
568
1482
  end
569
1483
 
570
- it "negates the all selection" do
1484
+ it "negates the next condition" do
571
1485
  expect(selection.selector).to eq(
572
- { "field" => { "$not" => { "$all" => [ 1, 2 ] }}}
1486
+ { "field" => { "$not" => { operator => [ 1, 2 ] }}}
573
1487
  )
574
1488
  end
575
1489
 
576
- it "returns a cloned query" do
577
- expect(selection).to_not equal(query)
578
- end
1490
+ it_behaves_like 'returns a cloned query'
579
1491
 
580
1492
  it "removes the negation on the clone" do
581
1493
  expect(selection).to_not be_negating
582
1494
  end
583
1495
  end
584
1496
 
1497
+ context "when the next condition is #all" do
1498
+ let(:query_method) { :all }
1499
+ let(:operator) { '$all' }
1500
+
1501
+ it_behaves_like 'negates the next condition'
1502
+ end
1503
+
1504
+ context "when the next condition is #in" do
1505
+ let(:query_method) { :in }
1506
+ let(:operator) { '$in' }
1507
+
1508
+ it_behaves_like 'negates the next condition'
1509
+ end
1510
+
1511
+ context "when the next condition is #nin" do
1512
+ let(:query_method) { :nin }
1513
+ let(:operator) { '$nin' }
1514
+
1515
+ it_behaves_like 'negates the next condition'
1516
+ end
1517
+
585
1518
  context "when the following criteria is a gt method" do
586
1519
 
587
1520
  let(:selection) do
@@ -594,10 +1527,27 @@ describe Mongoid::Criteria::Queryable::Selectable do
594
1527
  )
595
1528
  end
596
1529
 
597
- it "returns a coned query" do
598
- expect(selection).to_not eq(query)
1530
+ it_behaves_like 'returns a cloned query'
1531
+
1532
+ it "removes the negation on the clone" do
1533
+ expect(selection).to_not be_negating
1534
+ end
1535
+ end
1536
+
1537
+ context "when the criteria uses Key" do
1538
+
1539
+ let(:selection) do
1540
+ query.not(:age.gt => 50)
1541
+ end
1542
+
1543
+ it "negates the gt selection" do
1544
+ expect(selection.selector).to eq(
1545
+ '$and' => ['$nor' => ['age' => {'$gt' => 50}]]
1546
+ )
599
1547
  end
600
1548
 
1549
+ it_behaves_like 'returns a cloned query'
1550
+
601
1551
  it "removes the negation on the clone" do
602
1552
  expect(selection).to_not be_negating
603
1553
  end
@@ -615,9 +1565,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
615
1565
  )
616
1566
  end
617
1567
 
618
- it "returns a cloned query" do
619
- expect(selection).to_not equal(query)
620
- end
1568
+ it_behaves_like 'returns a cloned query'
621
1569
 
622
1570
  it "removes the negation on the clone" do
623
1571
  expect(selection).to_not be_negating
@@ -636,10 +1584,27 @@ describe Mongoid::Criteria::Queryable::Selectable do
636
1584
  )
637
1585
  end
638
1586
 
639
- it "returns a cloned query" do
640
- expect(selection).to_not equal(query)
1587
+ it_behaves_like 'returns a cloned query'
1588
+
1589
+ it "removes the negation on the clone" do
1590
+ expect(selection).to_not be_negating
1591
+ end
1592
+ end
1593
+
1594
+ context 'when the following criteria uses string were form' do
1595
+
1596
+ let(:selection) do
1597
+ query.not.where('hello world')
1598
+ end
1599
+
1600
+ it "negates the selection with an operator" do
1601
+ expect(selection.selector).to eq(
1602
+ '$and' => [{'$nor' => [{'$where' => 'hello world'}]}]
1603
+ )
641
1604
  end
642
1605
 
1606
+ it_behaves_like 'returns a cloned query'
1607
+
643
1608
  it "removes the negation on the clone" do
644
1609
  expect(selection).to_not be_negating
645
1610
  end
@@ -661,9 +1626,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
661
1626
  expect(selection).to eq(query)
662
1627
  end
663
1628
 
664
- it "returns a cloned query" do
665
- expect(selection).to_not equal(query)
666
- end
1629
+ it_behaves_like 'returns a cloned query'
667
1630
  end
668
1631
 
669
1632
  context "when provided a single criterion" do
@@ -678,14 +1641,31 @@ describe Mongoid::Criteria::Queryable::Selectable do
678
1641
  })
679
1642
  end
680
1643
 
681
- it "returns a cloned query" do
682
- expect(selection).to_not equal(query)
1644
+ it_behaves_like 'returns a cloned query'
1645
+ end
1646
+
1647
+ context "when negating a field in the original selector" do
1648
+ let(:query) do
1649
+ Mongoid::Query.new("id" => "_id").where(field: 'foo')
683
1650
  end
1651
+
1652
+ let(:selection) do
1653
+ query.not(field: 'bar')
1654
+ end
1655
+
1656
+ it "combines the conditions" do
1657
+ expect(selection.selector).to eq({
1658
+ "field" => 'foo',
1659
+ '$and' => [{'$nor' => [{ "field" => 'bar' }]}],
1660
+ })
1661
+ end
1662
+
1663
+ it_behaves_like 'returns a cloned query'
684
1664
  end
685
1665
 
686
- context "when provided multiple criterion" do
1666
+ context "when provided multiple criteria" do
687
1667
 
688
- context "when the criterion are for different fields" do
1668
+ context "when the criteria are for different fields" do
689
1669
 
690
1670
  let(:selection) do
691
1671
  query.not(first: /1/, second: /2/)
@@ -698,9 +1678,23 @@ describe Mongoid::Criteria::Queryable::Selectable do
698
1678
  })
699
1679
  end
700
1680
 
701
- it "returns a cloned query" do
702
- expect(selection).to_not equal(query)
1681
+ it_behaves_like 'returns a cloned query'
1682
+ end
1683
+
1684
+ context "when the criteria are given in separate arguments" do
1685
+
1686
+ let(:selection) do
1687
+ query.not({first: /1/}, {second: /2/})
1688
+ end
1689
+
1690
+ it "adds the $not selectors" do
1691
+ expect(selection.selector).to eq({
1692
+ "first" => { "$not" => /1/ },
1693
+ "second" => { "$not" => /2/ }
1694
+ })
703
1695
  end
1696
+
1697
+ it_behaves_like 'returns a cloned query'
704
1698
  end
705
1699
  end
706
1700
 
@@ -719,9 +1713,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
719
1713
  })
720
1714
  end
721
1715
 
722
- it "returns a cloned query" do
723
- expect(selection).to_not equal(query)
724
- end
1716
+ it_behaves_like 'returns a cloned query'
725
1717
  end
726
1718
 
727
1719
  context "when the criterion are on the same field" do
@@ -730,15 +1722,14 @@ describe Mongoid::Criteria::Queryable::Selectable do
730
1722
  query.not(first: /1/).not(first: /2/)
731
1723
  end
732
1724
 
733
- it "overwrites the first $not selector" do
734
- expect(selection.selector).to eq({
735
- "first" => { "$not" => /2/ }
736
- })
1725
+ it "combines conditions" do
1726
+ expect(selection.selector).to eq(
1727
+ "first" => { "$not" => /1/ },
1728
+ '$and' => [{'$nor' => [{'first' => /2/}]}],
1729
+ )
737
1730
  end
738
1731
 
739
- it "returns a cloned query" do
740
- expect(selection).to_not equal(query)
741
- end
1732
+ it_behaves_like 'returns a cloned query'
742
1733
  end
743
1734
 
744
1735
  context "when the criterion are a double negative" do
@@ -753,9 +1744,85 @@ describe Mongoid::Criteria::Queryable::Selectable do
753
1744
  })
754
1745
  end
755
1746
 
756
- it "returns a cloned query" do
757
- expect(selection).to_not equal(query)
758
- end
1747
+ it_behaves_like 'returns a cloned query'
1748
+ end
1749
+ end
1750
+
1751
+ context 'when argument is a Criteria' do
1752
+ let(:query) do
1753
+ Mongoid::Query.new.where(hello: 'world')
1754
+ end
1755
+
1756
+ let(:other) do
1757
+ Mongoid::Query.new.where(foo: 'bar')
1758
+ end
1759
+
1760
+ let(:result) { query.not(other) }
1761
+
1762
+ it 'combines' do
1763
+ expect(result.selector).to eq('hello' => 'world', 'foo' => {'$ne' => 'bar'})
1764
+ end
1765
+ end
1766
+
1767
+ context 'when argument is a simple Criteria with multiple fields' do
1768
+ let(:query) do
1769
+ Mongoid::Query.new.where(hello: 'world')
1770
+ end
1771
+
1772
+ let(:other) do
1773
+ Mongoid::Query.new.where(a: 1, b: 2)
1774
+ end
1775
+
1776
+ let(:result) { query.not(other) }
1777
+
1778
+ it 'combines fields into top level criteria' do
1779
+ expect(result.selector).to eq('hello' => 'world',
1780
+ 'a' => {'$ne' => 1}, 'b' => {'$ne' => 2})
1781
+ end
1782
+ end
1783
+
1784
+ context 'when argument is a complex Criteria' do
1785
+ let(:query) do
1786
+ Mongoid::Query.new.where(hello: 'world')
1787
+ end
1788
+
1789
+ let(:other) do
1790
+ Mongoid::Query.new.where('$nor' => [{a: 1, b: 2}])
1791
+ end
1792
+
1793
+ let(:result) { query.not(other) }
1794
+
1795
+ it 'combines with $and of $nor' do
1796
+ expect(result.selector).to eq('hello' => 'world', '$and' => [{'$nor' => [{
1797
+ '$nor' => [{'a' => 1, 'b' => 2}]}]}])
1798
+ end
1799
+ end
1800
+
1801
+ context 'when argument is a mix of Criteria and hashes' do
1802
+ let(:query) do
1803
+ Mongoid::Query.new.where(hello: 'world')
1804
+ end
1805
+
1806
+ let(:other1) do
1807
+ Mongoid::Query.new.where(foo: 'bar')
1808
+ end
1809
+
1810
+ let(:other2) do
1811
+ {bar: 42}
1812
+ end
1813
+
1814
+ let(:other3) do
1815
+ Mongoid::Query.new.where(a: 2)
1816
+ end
1817
+
1818
+ let(:result) { query.not(other1, other2, other3) }
1819
+
1820
+ it 'combines' do
1821
+ expect(result.selector).to eq('hello' => 'world',
1822
+ 'foo' => {'$ne' => 'bar'},
1823
+ 'bar' => {'$ne' => 42},
1824
+ 'a' => {'$ne' => 2},
1825
+ )
759
1826
  end
760
1827
  end
761
1828
  end