mongoid 7.1.0.rc0 → 7.2.0.rc1

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 (508) 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 +6 -6
  5. data/LICENSE +1 -1
  6. data/README.md +14 -7
  7. data/Rakefile +21 -0
  8. data/lib/config/locales/en.yml +42 -6
  9. data/lib/mongoid.rb +26 -2
  10. data/lib/mongoid/association/accessors.rb +37 -2
  11. data/lib/mongoid/association/depending.rb +1 -6
  12. data/lib/mongoid/association/embedded/embeds_many.rb +2 -1
  13. data/lib/mongoid/association/embedded/embeds_one.rb +2 -1
  14. data/lib/mongoid/association/many.rb +5 -4
  15. data/lib/mongoid/association/proxy.rb +6 -4
  16. data/lib/mongoid/association/referenced/belongs_to/binding.rb +1 -1
  17. data/lib/mongoid/association/referenced/belongs_to/buildable.rb +1 -1
  18. data/lib/mongoid/association/referenced/belongs_to/eager.rb +38 -2
  19. data/lib/mongoid/association/referenced/eager.rb +29 -9
  20. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +1 -1
  21. data/lib/mongoid/association/referenced/has_many/enumerable.rb +30 -15
  22. data/lib/mongoid/association/referenced/has_many/proxy.rb +11 -3
  23. data/lib/mongoid/association/referenced/has_one/buildable.rb +1 -1
  24. data/lib/mongoid/atomic.rb +13 -3
  25. data/lib/mongoid/atomic/modifiers.rb +1 -1
  26. data/lib/mongoid/attributes.rb +28 -20
  27. data/lib/mongoid/attributes/dynamic.rb +1 -1
  28. data/lib/mongoid/clients/factory.rb +19 -2
  29. data/lib/mongoid/clients/options.rb +8 -8
  30. data/lib/mongoid/clients/sessions.rb +20 -4
  31. data/lib/mongoid/clients/storage_options.rb +5 -5
  32. data/lib/mongoid/composable.rb +1 -0
  33. data/lib/mongoid/config.rb +45 -12
  34. data/lib/mongoid/config/options.rb +5 -2
  35. data/lib/mongoid/contextual.rb +5 -4
  36. data/lib/mongoid/contextual/geo_near.rb +3 -2
  37. data/lib/mongoid/contextual/map_reduce.rb +3 -2
  38. data/lib/mongoid/contextual/mongo.rb +25 -5
  39. data/lib/mongoid/copyable.rb +2 -2
  40. data/lib/mongoid/criteria.rb +80 -11
  41. data/lib/mongoid/criteria/modifiable.rb +2 -1
  42. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
  43. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +6 -6
  44. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +12 -0
  45. data/lib/mongoid/criteria/queryable/key.rb +1 -1
  46. data/lib/mongoid/criteria/queryable/mergeable.rb +75 -8
  47. data/lib/mongoid/criteria/queryable/pipeline.rb +3 -2
  48. data/lib/mongoid/criteria/queryable/selectable.rb +141 -19
  49. data/lib/mongoid/criteria/queryable/selector.rb +3 -3
  50. data/lib/mongoid/criteria/queryable/storable.rb +106 -101
  51. data/lib/mongoid/document.rb +15 -3
  52. data/lib/mongoid/errors.rb +7 -0
  53. data/lib/mongoid/errors/eager_load.rb +2 -0
  54. data/lib/mongoid/errors/invalid_discriminator_key_target.rb +25 -0
  55. data/lib/mongoid/errors/invalid_elem_match_operator.rb +33 -0
  56. data/lib/mongoid/errors/invalid_estimated_count_criteria.rb +26 -0
  57. data/lib/mongoid/errors/invalid_expression_operator.rb +28 -0
  58. data/lib/mongoid/errors/invalid_field_operator.rb +33 -0
  59. data/lib/mongoid/errors/invalid_query.rb +41 -0
  60. data/lib/mongoid/errors/no_client_config.rb +2 -2
  61. data/lib/mongoid/errors/no_default_client.rb +1 -1
  62. data/lib/mongoid/extensions/hash.rb +4 -2
  63. data/lib/mongoid/extensions/regexp.rb +1 -1
  64. data/lib/mongoid/factory.rb +27 -10
  65. data/lib/mongoid/fields.rb +2 -1
  66. data/lib/mongoid/fields/standard.rb +2 -1
  67. data/lib/mongoid/fields/validators/macro.rb +26 -10
  68. data/lib/mongoid/findable.rb +55 -18
  69. data/lib/mongoid/indexable.rb +2 -2
  70. data/lib/mongoid/interceptable.rb +5 -1
  71. data/lib/mongoid/matchable.rb +1 -149
  72. data/lib/mongoid/matcher.rb +127 -0
  73. data/lib/mongoid/matcher/all.rb +22 -0
  74. data/lib/mongoid/matcher/and.rb +21 -0
  75. data/lib/mongoid/matcher/elem_match.rb +35 -0
  76. data/lib/mongoid/matcher/elem_match_expression.rb +20 -0
  77. data/lib/mongoid/matcher/eq.rb +11 -0
  78. data/lib/mongoid/matcher/eq_impl.rb +32 -0
  79. data/lib/mongoid/matcher/eq_impl_with_regexp.rb +21 -0
  80. data/lib/mongoid/matcher/exists.rb +15 -0
  81. data/lib/mongoid/matcher/expression.rb +40 -0
  82. data/lib/mongoid/matcher/expression_operator.rb +19 -0
  83. data/lib/mongoid/matcher/field_expression.rb +63 -0
  84. data/lib/mongoid/matcher/field_operator.rb +52 -0
  85. data/lib/mongoid/matcher/gt.rb +17 -0
  86. data/lib/mongoid/matcher/gte.rb +17 -0
  87. data/lib/mongoid/matcher/in.rb +25 -0
  88. data/lib/mongoid/matcher/lt.rb +17 -0
  89. data/lib/mongoid/matcher/lte.rb +17 -0
  90. data/lib/mongoid/matcher/ne.rb +16 -0
  91. data/lib/mongoid/matcher/nin.rb +11 -0
  92. data/lib/mongoid/matcher/nor.rb +25 -0
  93. data/lib/mongoid/matcher/not.rb +29 -0
  94. data/lib/mongoid/matcher/or.rb +21 -0
  95. data/lib/mongoid/matcher/regex.rb +41 -0
  96. data/lib/mongoid/matcher/size.rb +26 -0
  97. data/lib/mongoid/persistable/deletable.rb +1 -1
  98. data/lib/mongoid/persistable/pushable.rb +11 -2
  99. data/lib/mongoid/persistence_context.rb +6 -6
  100. data/lib/mongoid/query_cache.rb +83 -38
  101. data/lib/mongoid/railties/database.rake +7 -0
  102. data/lib/mongoid/reloadable.rb +2 -2
  103. data/lib/mongoid/serializable.rb +11 -3
  104. data/lib/mongoid/shardable.rb +56 -4
  105. data/lib/mongoid/tasks/database.rake +10 -5
  106. data/lib/mongoid/tasks/database.rb +83 -0
  107. data/lib/mongoid/timestamps/timeless.rb +3 -1
  108. data/lib/mongoid/traversable.rb +111 -4
  109. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  110. data/lib/mongoid/version.rb +1 -1
  111. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +32 -23
  112. data/lib/rails/generators/mongoid/model/templates/model.rb.tt +1 -1
  113. data/spec/integration/app_spec.rb +254 -0
  114. data/spec/integration/associations/embedded_spec.rb +148 -0
  115. data/spec/integration/associations/has_many_spec.rb +69 -0
  116. data/spec/integration/associations/has_one_spec.rb +69 -0
  117. data/spec/integration/associations/nested_attributes_assignment_spec.rb +116 -0
  118. data/spec/integration/atomic/modifiers_spec.rb +117 -0
  119. data/spec/integration/bson_regexp_raw_spec.rb +20 -0
  120. data/spec/integration/criteria/date_field_spec.rb +41 -0
  121. data/spec/integration/criteria/logical_spec.rb +13 -0
  122. data/spec/integration/discriminator_key_spec.rb +354 -0
  123. data/spec/integration/discriminator_value_spec.rb +207 -0
  124. data/spec/integration/document_spec.rb +22 -0
  125. data/spec/integration/{matchable_spec.rb → matcher_examples_spec.rb} +120 -41
  126. data/spec/integration/matcher_operator_data/all.yml +140 -0
  127. data/spec/integration/matcher_operator_data/and.yml +93 -0
  128. data/spec/integration/matcher_operator_data/elem_match.yml +363 -0
  129. data/spec/integration/matcher_operator_data/elem_match_expr.yml +213 -0
  130. data/spec/integration/matcher_operator_data/eq.yml +191 -0
  131. data/spec/integration/matcher_operator_data/exists.yml +213 -0
  132. data/spec/integration/matcher_operator_data/generic_op.yml +17 -0
  133. data/spec/integration/matcher_operator_data/gt.yml +132 -0
  134. data/spec/integration/matcher_operator_data/gte.yml +132 -0
  135. data/spec/integration/matcher_operator_data/implicit.yml +331 -0
  136. data/spec/integration/matcher_operator_data/implicit_traversal.yml +16 -0
  137. data/spec/integration/matcher_operator_data/in.yml +194 -0
  138. data/spec/integration/matcher_operator_data/invalid_op.yml +59 -0
  139. data/spec/integration/matcher_operator_data/invalid_syntax.yml +39 -0
  140. data/spec/integration/matcher_operator_data/lt.yml +132 -0
  141. data/spec/integration/matcher_operator_data/lte.yml +132 -0
  142. data/spec/integration/matcher_operator_data/multiple.yml +29 -0
  143. data/spec/integration/matcher_operator_data/ne.yml +150 -0
  144. data/spec/integration/matcher_operator_data/nin.yml +114 -0
  145. data/spec/integration/matcher_operator_data/nor.yml +126 -0
  146. data/spec/integration/matcher_operator_data/not.yml +196 -0
  147. data/spec/integration/matcher_operator_data/or.yml +137 -0
  148. data/spec/integration/matcher_operator_data/regex.yml +174 -0
  149. data/spec/integration/matcher_operator_data/regex_options.yml +72 -0
  150. data/spec/integration/matcher_operator_data/size.yml +174 -0
  151. data/spec/integration/matcher_operator_spec.rb +100 -0
  152. data/spec/integration/matcher_spec.rb +189 -0
  153. data/spec/integration/server_query_spec.rb +142 -0
  154. data/spec/integration/shardable_spec.rb +149 -0
  155. data/spec/lite_spec_helper.rb +15 -4
  156. data/spec/mongoid/association/accessors_spec.rb +238 -63
  157. data/spec/mongoid/association/depending_spec.rb +78 -26
  158. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +33 -0
  159. data/spec/mongoid/association/embedded/embeds_many_models.rb +20 -0
  160. data/spec/mongoid/association/embedded/embeds_many_spec.rb +10 -0
  161. data/spec/mongoid/association/embedded/embeds_one_models.rb +1 -0
  162. data/spec/mongoid/association/embedded/embeds_one_spec.rb +0 -2
  163. data/spec/mongoid/association/referenced/belongs_to/eager_spec.rb +193 -10
  164. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +140 -1
  165. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +146 -68
  166. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +462 -187
  167. data/spec/mongoid/association/referenced/has_many_models.rb +9 -0
  168. data/spec/mongoid/association/referenced/has_one_models.rb +9 -0
  169. data/spec/mongoid/atomic/modifiers_spec.rb +47 -0
  170. data/spec/mongoid/atomic_spec.rb +23 -0
  171. data/spec/mongoid/attributes/nested_spec.rb +1 -1
  172. data/spec/mongoid/attributes_spec.rb +19 -7
  173. data/spec/mongoid/changeable_spec.rb +23 -0
  174. data/spec/mongoid/clients/factory_spec.rb +45 -8
  175. data/spec/mongoid/clients/options_spec.rb +11 -11
  176. data/spec/mongoid/clients/sessions_spec.rb +9 -10
  177. data/spec/mongoid/clients/transactions_spec.rb +20 -8
  178. data/spec/mongoid/clients_spec.rb +2 -2
  179. data/spec/mongoid/config_spec.rb +28 -0
  180. data/spec/mongoid/contextual/atomic_spec.rb +22 -11
  181. data/spec/mongoid/contextual/geo_near_spec.rb +11 -2
  182. data/spec/mongoid/contextual/map_reduce_spec.rb +20 -5
  183. data/spec/mongoid/contextual/mongo_spec.rb +146 -69
  184. data/spec/mongoid/copyable_spec.rb +153 -1
  185. data/spec/mongoid/copyable_spec_models.rb +14 -0
  186. data/spec/mongoid/criteria/queryable/extensions/regexp_raw_spec.rb +1 -1
  187. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +7 -7
  188. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +1 -1
  189. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +19 -7
  190. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +28 -1
  191. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +45 -12
  192. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +1051 -392
  193. data/spec/mongoid/criteria/queryable/selectable_spec.rb +136 -82
  194. data/spec/mongoid/criteria/queryable/storable_spec.rb +80 -2
  195. data/spec/mongoid/criteria_spec.rb +240 -41
  196. data/spec/mongoid/document_fields_spec.rb +29 -0
  197. data/spec/mongoid/document_persistence_context_spec.rb +33 -0
  198. data/spec/mongoid/document_spec.rb +89 -15
  199. data/spec/mongoid/errors/delete_restriction_spec.rb +1 -1
  200. data/spec/mongoid/errors/no_client_config_spec.rb +2 -2
  201. data/spec/mongoid/errors/no_client_database_spec.rb +3 -3
  202. data/spec/mongoid/errors/no_client_hosts_spec.rb +3 -3
  203. data/spec/mongoid/factory_spec.rb +261 -28
  204. data/spec/mongoid/fields_spec.rb +72 -3
  205. data/spec/mongoid/findable_spec.rb +32 -0
  206. data/spec/mongoid/indexable_spec.rb +34 -6
  207. data/spec/mongoid/inspectable_spec.rb +29 -2
  208. data/spec/mongoid/interceptable_spec.rb +64 -2
  209. data/spec/mongoid/interceptable_spec_models.rb +76 -0
  210. data/spec/mongoid/matcher/extract_attribute_data/traversal.yml +259 -0
  211. data/spec/mongoid/matcher/extract_attribute_spec.rb +47 -0
  212. data/spec/mongoid/persistable/creatable_spec.rb +108 -25
  213. data/spec/mongoid/persistable/deletable_spec.rb +86 -0
  214. data/spec/mongoid/persistable/pushable_spec.rb +55 -1
  215. data/spec/mongoid/persistable/savable_spec.rb +174 -16
  216. data/spec/mongoid/query_cache_middleware_spec.rb +16 -3
  217. data/spec/mongoid/query_cache_spec.rb +498 -29
  218. data/spec/mongoid/relations/proxy_spec.rb +1 -1
  219. data/spec/mongoid/reloadable_spec.rb +72 -0
  220. data/spec/mongoid/scopable_spec.rb +2 -1
  221. data/spec/mongoid/serializable_spec.rb +149 -20
  222. data/spec/mongoid/shardable_models.rb +61 -0
  223. data/spec/mongoid/shardable_spec.rb +69 -16
  224. data/spec/mongoid/tasks/database_rake_spec.rb +13 -13
  225. data/spec/mongoid/tasks/database_spec.rb +1 -1
  226. data/spec/mongoid/traversable_spec.rb +1100 -0
  227. data/spec/spec_helper.rb +4 -33
  228. data/spec/support/child_process_helper.rb +79 -0
  229. data/spec/support/cluster_config.rb +3 -3
  230. data/spec/support/constraints.rb +50 -10
  231. data/spec/support/expectations.rb +3 -1
  232. data/spec/support/helpers.rb +11 -0
  233. data/spec/support/lite_constraints.rb +22 -0
  234. data/spec/{app → support}/models/account.rb +0 -0
  235. data/spec/{app → support}/models/acolyte.rb +0 -0
  236. data/spec/{app → support}/models/actor.rb +1 -1
  237. data/spec/{app → support}/models/actress.rb +0 -0
  238. data/spec/{app → support}/models/address.rb +0 -0
  239. data/spec/{app → support}/models/address_component.rb +0 -0
  240. data/spec/{app → support}/models/address_number.rb +0 -0
  241. data/spec/{app → support}/models/agency.rb +0 -0
  242. data/spec/{app → support}/models/agent.rb +0 -0
  243. data/spec/{app → support}/models/album.rb +0 -0
  244. data/spec/{app → support}/models/alert.rb +0 -0
  245. data/spec/{app → support}/models/animal.rb +0 -0
  246. data/spec/{app → support}/models/answer.rb +0 -0
  247. data/spec/{app → support}/models/appointment.rb +0 -0
  248. data/spec/support/models/armrest.rb +10 -0
  249. data/spec/{app → support}/models/array_field.rb +0 -0
  250. data/spec/{app → support}/models/article.rb +0 -0
  251. data/spec/{app → support}/models/artist.rb +0 -0
  252. data/spec/{app → support}/models/artwork.rb +0 -0
  253. data/spec/{app → support}/models/audio.rb +0 -0
  254. data/spec/{app → support}/models/augmentation.rb +0 -0
  255. data/spec/{app → support}/models/author.rb +0 -0
  256. data/spec/{app → support}/models/baby.rb +0 -0
  257. data/spec/{app → support}/models/band.rb +0 -0
  258. data/spec/{app → support}/models/bar.rb +0 -0
  259. data/spec/{app → support}/models/basic.rb +0 -0
  260. data/spec/{app → support}/models/bed.rb +0 -0
  261. data/spec/{app → support}/models/big_palette.rb +0 -0
  262. data/spec/{app → support}/models/birthday.rb +0 -0
  263. data/spec/{app → support}/models/bomb.rb +0 -0
  264. data/spec/{app → support}/models/book.rb +0 -0
  265. data/spec/{app → support}/models/breed.rb +0 -0
  266. data/spec/{app → support}/models/browser.rb +1 -1
  267. data/spec/{app → support}/models/building.rb +0 -0
  268. data/spec/{app → support}/models/building_address.rb +0 -0
  269. data/spec/{app → support}/models/bus.rb +0 -0
  270. data/spec/{app → support}/models/business.rb +0 -0
  271. data/spec/{app → support}/models/callback_test.rb +0 -0
  272. data/spec/{app → support}/models/canvas.rb +1 -1
  273. data/spec/{app → support}/models/car.rb +0 -0
  274. data/spec/{app → support}/models/cat.rb +0 -0
  275. data/spec/{app → support}/models/category.rb +0 -0
  276. data/spec/{app → support}/models/child.rb +0 -0
  277. data/spec/{app → support}/models/child_doc.rb +0 -0
  278. data/spec/{app → support}/models/church.rb +0 -0
  279. data/spec/{app → support}/models/circle.rb +0 -0
  280. data/spec/{app → support}/models/circuit.rb +0 -0
  281. data/spec/{app → support}/models/circus.rb +0 -0
  282. data/spec/{app → support}/models/code.rb +0 -0
  283. data/spec/support/models/coding.rb +4 -0
  284. data/spec/support/models/coding/pull_request.rb +12 -0
  285. data/spec/{app → support}/models/comment.rb +0 -0
  286. data/spec/{app → support}/models/company.rb +0 -0
  287. data/spec/{app → support}/models/consumption_period.rb +0 -0
  288. data/spec/{app → support}/models/contextable_item.rb +0 -0
  289. data/spec/{app → support}/models/contractor.rb +0 -0
  290. data/spec/{app → support}/models/cookie.rb +0 -0
  291. data/spec/{app → support}/models/country_code.rb +0 -0
  292. data/spec/{app → support}/models/courier_job.rb +0 -0
  293. data/spec/support/models/crate.rb +13 -0
  294. data/spec/support/models/deed.rb +8 -0
  295. data/spec/{app → support}/models/definition.rb +0 -0
  296. data/spec/support/models/delegating_patient.rb +16 -0
  297. data/spec/{app → support}/models/description.rb +0 -0
  298. data/spec/{app → support}/models/dictionary.rb +0 -0
  299. data/spec/{app → support}/models/division.rb +0 -0
  300. data/spec/{app → support}/models/doctor.rb +0 -0
  301. data/spec/{app → support}/models/dog.rb +0 -0
  302. data/spec/{app → support}/models/dokument.rb +0 -0
  303. data/spec/{app → support}/models/draft.rb +0 -0
  304. data/spec/{app → support}/models/dragon.rb +0 -0
  305. data/spec/{app → support}/models/driver.rb +1 -1
  306. data/spec/{app → support}/models/drug.rb +0 -0
  307. data/spec/{app → support}/models/dungeon.rb +0 -0
  308. data/spec/{app → support}/models/edit.rb +0 -0
  309. data/spec/{app → support}/models/email.rb +0 -0
  310. data/spec/{app → support}/models/employer.rb +0 -0
  311. data/spec/{app → support}/models/entry.rb +0 -0
  312. data/spec/{app → support}/models/eraser.rb +0 -0
  313. data/spec/{app → support}/models/even.rb +0 -0
  314. data/spec/{app → support}/models/event.rb +0 -0
  315. data/spec/{app → support}/models/exhibition.rb +0 -0
  316. data/spec/{app → support}/models/exhibitor.rb +0 -0
  317. data/spec/{app → support}/models/explosion.rb +0 -0
  318. data/spec/{app → support}/models/eye.rb +0 -0
  319. data/spec/{app → support}/models/eye_bowl.rb +0 -0
  320. data/spec/{app → support}/models/face.rb +0 -0
  321. data/spec/{app → support}/models/favorite.rb +0 -0
  322. data/spec/{app → support}/models/filesystem.rb +0 -0
  323. data/spec/{app → support}/models/fire_hydrant.rb +0 -0
  324. data/spec/{app → support}/models/firefox.rb +0 -0
  325. data/spec/{app → support}/models/fish.rb +0 -0
  326. data/spec/{app → support}/models/folder.rb +0 -0
  327. data/spec/{app → support}/models/folder_item.rb +0 -0
  328. data/spec/{app → support}/models/fruits.rb +0 -0
  329. data/spec/{app → support}/models/game.rb +0 -0
  330. data/spec/{app → support}/models/ghost.rb +0 -0
  331. data/spec/support/models/guitar.rb +5 -0
  332. data/spec/{app → support}/models/home.rb +0 -0
  333. data/spec/{app → support}/models/house.rb +0 -0
  334. data/spec/{app → support}/models/html_writer.rb +0 -0
  335. data/spec/{app → support}/models/id_key.rb +0 -0
  336. data/spec/support/models/idnodef.rb +8 -0
  337. data/spec/{app → support}/models/image.rb +0 -0
  338. data/spec/{app → support}/models/implant.rb +0 -0
  339. data/spec/support/models/instrument.rb +9 -0
  340. data/spec/{app → support}/models/item.rb +1 -1
  341. data/spec/{app → support}/models/jar.rb +0 -0
  342. data/spec/{app → support}/models/kaleidoscope.rb +0 -0
  343. data/spec/{app → support}/models/kangaroo.rb +0 -0
  344. data/spec/{app → support}/models/label.rb +0 -0
  345. data/spec/{app → support}/models/language.rb +0 -0
  346. data/spec/{app → support}/models/lat_lng.rb +0 -0
  347. data/spec/{app → support}/models/league.rb +0 -0
  348. data/spec/{app → support}/models/learner.rb +0 -0
  349. data/spec/{app → support}/models/line_item.rb +0 -0
  350. data/spec/{app → support}/models/location.rb +0 -0
  351. data/spec/{app → support}/models/login.rb +0 -0
  352. data/spec/{app → support}/models/manufacturer.rb +0 -0
  353. data/spec/{app → support}/models/meat.rb +0 -0
  354. data/spec/{app → support}/models/membership.rb +0 -0
  355. data/spec/{app → support}/models/message.rb +0 -0
  356. data/spec/{app → support}/models/minim.rb +0 -0
  357. data/spec/{app → support}/models/mixed_drink.rb +0 -0
  358. data/spec/support/models/mop.rb +16 -0
  359. data/spec/{app → support}/models/movie.rb +0 -0
  360. data/spec/{app → support}/models/my_hash.rb +0 -0
  361. data/spec/{app → support}/models/name.rb +0 -0
  362. data/spec/{app → support}/models/name_only.rb +0 -0
  363. data/spec/{app → support}/models/node.rb +0 -0
  364. data/spec/{app → support}/models/note.rb +0 -0
  365. data/spec/{app → support}/models/odd.rb +0 -0
  366. data/spec/{app → support}/models/ordered_post.rb +0 -0
  367. data/spec/{app → support}/models/ordered_preference.rb +0 -0
  368. data/spec/{app → support}/models/oscar.rb +0 -0
  369. data/spec/{app → support}/models/other_owner_object.rb +0 -0
  370. data/spec/{app → support}/models/override.rb +0 -0
  371. data/spec/{app → support}/models/ownable.rb +0 -0
  372. data/spec/{app → support}/models/owner.rb +2 -0
  373. data/spec/{app → support}/models/pack.rb +0 -0
  374. data/spec/{app → support}/models/page.rb +0 -0
  375. data/spec/{app → support}/models/page_question.rb +0 -0
  376. data/spec/{app → support}/models/palette.rb +1 -1
  377. data/spec/{app → support}/models/parent.rb +0 -0
  378. data/spec/{app → support}/models/parent_doc.rb +0 -0
  379. data/spec/{app → support}/models/passport.rb +1 -0
  380. data/spec/{app → support}/models/patient.rb +0 -0
  381. data/spec/{app → support}/models/pdf_writer.rb +0 -0
  382. data/spec/{app → support}/models/pencil.rb +0 -0
  383. data/spec/{app → support}/models/person.rb +1 -1
  384. data/spec/{app → support}/models/pet.rb +0 -0
  385. data/spec/{app → support}/models/pet_owner.rb +0 -0
  386. data/spec/{app → support}/models/phone.rb +1 -0
  387. data/spec/support/models/piano.rb +5 -0
  388. data/spec/{app → support}/models/pizza.rb +0 -0
  389. data/spec/{app → support}/models/player.rb +0 -0
  390. data/spec/{app → support}/models/post.rb +0 -0
  391. data/spec/{app → support}/models/post_genre.rb +0 -0
  392. data/spec/{app → support}/models/powerup.rb +0 -0
  393. data/spec/{app → support}/models/preference.rb +0 -0
  394. data/spec/{app → support}/models/princess.rb +0 -0
  395. data/spec/{app → support}/models/product.rb +0 -0
  396. data/spec/support/models/profile.rb +18 -0
  397. data/spec/{app → support}/models/pronunciation.rb +0 -0
  398. data/spec/{app → support}/models/pub.rb +0 -0
  399. data/spec/support/models/publication.rb +5 -0
  400. data/spec/support/models/publication/encyclopedia.rb +12 -0
  401. data/spec/support/models/publication/review.rb +14 -0
  402. data/spec/{app → support}/models/purchase.rb +0 -0
  403. data/spec/{app → support}/models/question.rb +0 -0
  404. data/spec/{app → support}/models/quiz.rb +0 -0
  405. data/spec/{app → support}/models/rating.rb +0 -0
  406. data/spec/{app → support}/models/record.rb +0 -0
  407. data/spec/{app → support}/models/registry.rb +0 -0
  408. data/spec/{app → support}/models/role.rb +0 -0
  409. data/spec/{app → support}/models/root_category.rb +0 -0
  410. data/spec/{app → support}/models/sandwich.rb +0 -0
  411. data/spec/{app → support}/models/scheduler.rb +0 -0
  412. data/spec/{app/models/profile.rb → support/models/scribe.rb} +2 -2
  413. data/spec/support/models/seat.rb +25 -0
  414. data/spec/{app → support}/models/seo.rb +0 -0
  415. data/spec/{app → support}/models/series.rb +0 -0
  416. data/spec/{app → support}/models/server.rb +0 -0
  417. data/spec/{app → support}/models/service.rb +0 -0
  418. data/spec/{app → support}/models/shape.rb +2 -2
  419. data/spec/{app → support}/models/shelf.rb +0 -0
  420. data/spec/{app → support}/models/shipment_address.rb +0 -0
  421. data/spec/{app → support}/models/shipping_container.rb +0 -0
  422. data/spec/{app → support}/models/shipping_pack.rb +0 -0
  423. data/spec/{app → support}/models/shop.rb +0 -0
  424. data/spec/{app → support}/models/short_agent.rb +0 -0
  425. data/spec/{app → support}/models/short_quiz.rb +0 -0
  426. data/spec/{app → support}/models/simple.rb +0 -0
  427. data/spec/{app → support}/models/slave.rb +0 -0
  428. data/spec/{app → support}/models/song.rb +0 -0
  429. data/spec/{app → support}/models/sound.rb +0 -0
  430. data/spec/{app → support}/models/square.rb +0 -0
  431. data/spec/{app → support}/models/staff.rb +0 -0
  432. data/spec/{app → support}/models/store_as_dup_test1.rb +0 -0
  433. data/spec/{app → support}/models/store_as_dup_test2.rb +0 -0
  434. data/spec/{app → support}/models/store_as_dup_test3.rb +0 -0
  435. data/spec/{app → support}/models/store_as_dup_test4.rb +0 -0
  436. data/spec/{app → support}/models/strategy.rb +0 -0
  437. data/spec/{app → support}/models/sub_item.rb +0 -0
  438. data/spec/{app → support}/models/subscription.rb +0 -0
  439. data/spec/{app → support}/models/survey.rb +0 -0
  440. data/spec/{app → support}/models/symptom.rb +0 -0
  441. data/spec/support/models/system_role.rb +7 -0
  442. data/spec/{app → support}/models/tag.rb +0 -0
  443. data/spec/{app → support}/models/target.rb +0 -0
  444. data/spec/{app → support}/models/template.rb +0 -0
  445. data/spec/{app → support}/models/thing.rb +0 -0
  446. data/spec/{app → support}/models/title.rb +0 -0
  447. data/spec/{app → support}/models/tool.rb +2 -2
  448. data/spec/{app → support}/models/topping.rb +0 -0
  449. data/spec/support/models/toy.rb +10 -0
  450. data/spec/{app → support}/models/track.rb +0 -0
  451. data/spec/{app → support}/models/translation.rb +0 -0
  452. data/spec/{app → support}/models/tree.rb +0 -0
  453. data/spec/{app → support}/models/truck.rb +2 -0
  454. data/spec/{app → support}/models/updatable.rb +0 -0
  455. data/spec/{app → support}/models/user.rb +0 -0
  456. data/spec/{app → support}/models/user_account.rb +0 -0
  457. data/spec/{app → support}/models/validation_callback.rb +0 -0
  458. data/spec/{app → support}/models/vehicle.rb +7 -2
  459. data/spec/{app → support}/models/version.rb +0 -0
  460. data/spec/{app → support}/models/vertex.rb +0 -0
  461. data/spec/{app → support}/models/vet_visit.rb +0 -0
  462. data/spec/{app → support}/models/video.rb +0 -0
  463. data/spec/{app → support}/models/video_game.rb +0 -0
  464. data/spec/{app → support}/models/weapon.rb +0 -0
  465. data/spec/{app → support}/models/wiki_page.rb +0 -0
  466. data/spec/{app → support}/models/word.rb +0 -0
  467. data/spec/{app → support}/models/word_origin.rb +0 -0
  468. data/spec/{app → support}/models/writer.rb +2 -2
  469. data/spec/support/session_registry.rb +50 -0
  470. data/spec/support/spec_config.rb +13 -5
  471. data/spec/support/spec_organizer.rb +130 -0
  472. metadata +882 -752
  473. metadata.gz.sig +0 -0
  474. data/lib/mongoid/matchable/all.rb +0 -30
  475. data/lib/mongoid/matchable/and.rb +0 -32
  476. data/lib/mongoid/matchable/default.rb +0 -121
  477. data/lib/mongoid/matchable/elem_match.rb +0 -36
  478. data/lib/mongoid/matchable/eq.rb +0 -23
  479. data/lib/mongoid/matchable/exists.rb +0 -25
  480. data/lib/mongoid/matchable/gt.rb +0 -25
  481. data/lib/mongoid/matchable/gte.rb +0 -25
  482. data/lib/mongoid/matchable/in.rb +0 -26
  483. data/lib/mongoid/matchable/lt.rb +0 -25
  484. data/lib/mongoid/matchable/lte.rb +0 -25
  485. data/lib/mongoid/matchable/ne.rb +0 -23
  486. data/lib/mongoid/matchable/nin.rb +0 -24
  487. data/lib/mongoid/matchable/nor.rb +0 -38
  488. data/lib/mongoid/matchable/or.rb +0 -35
  489. data/lib/mongoid/matchable/regexp.rb +0 -30
  490. data/lib/mongoid/matchable/size.rb +0 -23
  491. data/spec/mongoid/matchable/all_spec.rb +0 -34
  492. data/spec/mongoid/matchable/and_spec.rb +0 -190
  493. data/spec/mongoid/matchable/default_spec.rb +0 -140
  494. data/spec/mongoid/matchable/elem_match_spec.rb +0 -109
  495. data/spec/mongoid/matchable/eq_spec.rb +0 -49
  496. data/spec/mongoid/matchable/exists_spec.rb +0 -60
  497. data/spec/mongoid/matchable/gt_spec.rb +0 -89
  498. data/spec/mongoid/matchable/gte_spec.rb +0 -87
  499. data/spec/mongoid/matchable/in_spec.rb +0 -52
  500. data/spec/mongoid/matchable/lt_spec.rb +0 -88
  501. data/spec/mongoid/matchable/lte_spec.rb +0 -88
  502. data/spec/mongoid/matchable/ne_spec.rb +0 -49
  503. data/spec/mongoid/matchable/nin_spec.rb +0 -51
  504. data/spec/mongoid/matchable/nor_spec.rb +0 -210
  505. data/spec/mongoid/matchable/or_spec.rb +0 -134
  506. data/spec/mongoid/matchable/regexp_spec.rb +0 -62
  507. data/spec/mongoid/matchable/size_spec.rb +0 -28
  508. data/spec/mongoid/matchable_spec.rb +0 -856
@@ -195,7 +195,7 @@ describe Mongoid::Copyable do
195
195
  I18n.enforce_available_locales = false
196
196
  I18n.locale = 'pt_BR'
197
197
  person.addresses.type(ShipmentAddress).each { |address| address.shipping_name = "Título" }
198
- person.save
198
+ person.save!
199
199
  end
200
200
 
201
201
  after do
@@ -353,6 +353,83 @@ describe Mongoid::Copyable do
353
353
  end
354
354
  end
355
355
  end
356
+
357
+ context "when using a custom discriminator_key" do
358
+ before do
359
+ Person.discriminator_key = "dkey"
360
+ end
361
+
362
+ after do
363
+ Person.discriminator_key = nil
364
+ end
365
+
366
+ let(:copy) do
367
+ person.send(method)
368
+ end
369
+
370
+ before do
371
+ person[:versions] = [ { number: 1 } ]
372
+ end
373
+
374
+ it "copys embeds many documents" do
375
+ expect(copy.addresses).to eq(person.addresses)
376
+ end
377
+
378
+ it "copys deep embeds many documents" do
379
+ expect(copy.name.translations).to eq(person.name.translations)
380
+ end
381
+
382
+ it "sets the embedded many documents as new" do
383
+ expect(copy.addresses.first).to be_new_record
384
+ end
385
+
386
+ it "sets the deep embedded many documents as new" do
387
+ expect(copy.name.translations.first).to be_new_record
388
+ end
389
+
390
+ it "creates new embeds many instances" do
391
+ expect(copy.addresses).to_not equal(person.addresses)
392
+ end
393
+
394
+ it "creates new deep embeds many instances" do
395
+ expect(copy.name.translations).to_not equal(person.name.translations)
396
+ end
397
+
398
+ it "copys embeds one documents" do
399
+ expect(copy.name).to eq(person.name)
400
+ end
401
+
402
+ it "flags the embeds one documents as new" do
403
+ expect(copy.name).to be_new_record
404
+ end
405
+
406
+ it "creates a new embeds one instance" do
407
+ expect(copy.name).to_not equal(person.name)
408
+ end
409
+
410
+ context "when saving the copy" do
411
+
412
+ let(:reloaded) do
413
+ copy.reload
414
+ end
415
+
416
+ before do
417
+ copy.save(validate: false)
418
+ end
419
+
420
+ it "persists the attributes" do
421
+ expect(reloaded.title).to eq("Sir")
422
+ end
423
+
424
+ it "persists the embeds many relation" do
425
+ expect(reloaded.addresses).to eq(person.addresses)
426
+ end
427
+
428
+ it "persists the embeds one relation" do
429
+ expect(reloaded.name).to eq(person.name)
430
+ end
431
+ end
432
+ end
356
433
  end
357
434
 
358
435
  context "when the document is not new" do
@@ -517,6 +594,81 @@ describe Mongoid::Copyable do
517
594
  end
518
595
  end
519
596
  end
597
+
598
+ context "when cloning a document with an embedded child class and a custom discriminator value" do
599
+
600
+ before do
601
+ ShipmentAddress.discriminator_value = "dvalue"
602
+ end
603
+
604
+ after do
605
+ ShipmentAddress.discriminator_value = nil
606
+ end
607
+
608
+ let!(:shipment_address) do
609
+ person.addresses.build({}, ShipmentAddress)
610
+ end
611
+
612
+ before do
613
+ person.save
614
+ end
615
+
616
+ let!(:from_db) do
617
+ Person.find(person.id)
618
+ end
619
+
620
+ let(:copy) do
621
+ from_db.send(method)
622
+ end
623
+
624
+ it "copys embeds many documents" do
625
+ expect(copy.addresses).to eq(person.addresses)
626
+ end
627
+ end
628
+
629
+ context 'when cloning a document with embedded child that uses inheritance' do
630
+ let(:original) do
631
+ CopyableSpec::A.new(influencers: [child_cls.new])
632
+ end
633
+
634
+ let(:copy) do
635
+ original.send(method)
636
+ end
637
+
638
+ context 'embedded child is root of hierarchy' do
639
+ let(:child_cls) do
640
+ CopyableSpec::Influencer
641
+ end
642
+
643
+ before do
644
+ # When embedded class is the root in hierarchy, their
645
+ # discriminator value is not explicitly stored.
646
+ child_cls.discriminator_mapping[child_cls.name].should be nil
647
+ end
648
+
649
+ it 'works' do
650
+ copy.class.should be original.class
651
+ copy.object_id.should_not == original.object_id
652
+ end
653
+ end
654
+
655
+ context 'embedded child is leaf of hierarchy' do
656
+ let(:child_cls) do
657
+ CopyableSpec::Youtuber
658
+ end
659
+
660
+ before do
661
+ # When embedded class is a leaf in hierarchy, their
662
+ # discriminator value is explicitly stored.
663
+ child_cls.discriminator_mapping[child_cls.name].should_not be nil
664
+ end
665
+
666
+ it 'works' do
667
+ copy.class.should be original.class
668
+ copy.object_id.should_not == original.object_id
669
+ end
670
+ end
671
+ end
520
672
  end
521
673
  end
522
674
  end
@@ -6,6 +6,7 @@ module CopyableSpec
6
6
  include Mongoid::Document
7
7
 
8
8
  embeds_many :locations
9
+ embeds_many :influencers
9
10
  end
10
11
 
11
12
  class Location
@@ -17,4 +18,17 @@ module CopyableSpec
17
18
  class Building
18
19
  include Mongoid::Document
19
20
  end
21
+
22
+ class Influencer
23
+ include Mongoid::Document
24
+
25
+ embeds_many :blurbs
26
+ end
27
+
28
+ class Youtuber < Influencer
29
+ end
30
+
31
+ class Blurb
32
+ include Mongoid::Document
33
+ end
20
34
  end
@@ -3,7 +3,7 @@
3
3
 
4
4
  require "spec_helper"
5
5
 
6
- describe Mongoid::Criteria::Queryable::Extensions::Regexp::Raw do
6
+ describe Mongoid::Criteria::Queryable::Extensions::Regexp::Raw_ do
7
7
 
8
8
  describe ".evolve" do
9
9
 
@@ -11,7 +11,7 @@ describe Mongoid::Criteria::Queryable::Extensions::Regexp do
11
11
  context "when provided a regexp" do
12
12
 
13
13
  let(:regexp) do
14
- /^[123]/
14
+ /\A[123]/
15
15
  end
16
16
 
17
17
  let(:evolved) do
@@ -26,7 +26,7 @@ describe Mongoid::Criteria::Queryable::Extensions::Regexp do
26
26
  context "when provided a string" do
27
27
 
28
28
  let(:regexp) do
29
- "^[123]"
29
+ "\\A[123]"
30
30
  end
31
31
 
32
32
  let(:evolved) do
@@ -34,7 +34,7 @@ describe Mongoid::Criteria::Queryable::Extensions::Regexp do
34
34
  end
35
35
 
36
36
  it "returns the converted regexp" do
37
- expect(evolved).to eq(/^[123]/)
37
+ expect(evolved).to eq(/\A[123]/)
38
38
  end
39
39
  end
40
40
 
@@ -43,7 +43,7 @@ describe Mongoid::Criteria::Queryable::Extensions::Regexp do
43
43
  context "when the elements are regexps" do
44
44
 
45
45
  let(:regexp) do
46
- /^[123]/
46
+ /\A[123]/
47
47
  end
48
48
 
49
49
  let(:array) do
@@ -66,7 +66,7 @@ describe Mongoid::Criteria::Queryable::Extensions::Regexp do
66
66
  context "when the elements are strings" do
67
67
 
68
68
  let(:regexp) do
69
- "^[123]"
69
+ "\\A[123]"
70
70
  end
71
71
 
72
72
  let(:evolved) do
@@ -74,7 +74,7 @@ describe Mongoid::Criteria::Queryable::Extensions::Regexp do
74
74
  end
75
75
 
76
76
  it "returns the regexps" do
77
- expect(evolved).to eq([ /^[123]/ ])
77
+ expect(evolved).to eq([ /\A[123]/ ])
78
78
  end
79
79
  end
80
80
  end
@@ -83,7 +83,7 @@ describe Mongoid::Criteria::Queryable::Extensions::Regexp do
83
83
  describe "#regexp?" do
84
84
 
85
85
  let(:regexp) do
86
- /^[123]/
86
+ /\A[123]/
87
87
  end
88
88
 
89
89
  it "returns true" do
@@ -318,7 +318,7 @@ describe String do
318
318
  context "when provided a regex" do
319
319
 
320
320
  let(:regex) do
321
- /^[123]/
321
+ /\A[123]/.freeze
322
322
  end
323
323
 
324
324
  let(:evolved) do
@@ -354,23 +354,35 @@ describe Time do
354
354
 
355
355
  describe "#__evolve_date__" do
356
356
 
357
- let(:time) do
358
- Time.new(2010, 1, 1, 12, 0, 0)
359
- end
360
-
361
357
  let(:evolved) do
362
358
  time.__evolve_date__
363
359
  end
364
360
 
365
- it "returns midnight utc" do
366
- expect(evolved).to eq(Time.utc(2010, 1, 1, 0, 0, 0))
361
+ context 'beginning of day' do
362
+ let(:time) do
363
+ Time.new(2010, 1, 1, 0, 0, 1).freeze
364
+ end
365
+
366
+ it "returns midnight utc" do
367
+ expect(evolved).to eq(Time.utc(2010, 1, 1, 0, 0, 0))
368
+ end
369
+ end
370
+
371
+ context 'end of day' do
372
+ let(:time) do
373
+ Time.new(2010, 1, 1, 23, 59, 59).freeze
374
+ end
375
+
376
+ it "returns midnight utc" do
377
+ expect(evolved).to eq(Time.utc(2010, 1, 1, 0, 0, 0))
378
+ end
367
379
  end
368
380
  end
369
381
 
370
382
  describe "#__evolve_time__" do
371
383
 
372
384
  let(:time) do
373
- Time.new(2010, 1, 1, 12, 0, 0)
385
+ Time.new(2010, 1, 1, 12, 0, 0).freeze
374
386
  end
375
387
 
376
388
  let(:evolved) do
@@ -325,10 +325,37 @@ describe ActiveSupport::TimeWithZone do
325
325
  end
326
326
  end
327
327
 
328
+ describe "#__evolve_date__" do
329
+
330
+ let(:evolved) do
331
+ time.__evolve_date__
332
+ end
333
+
334
+ context 'beginning of day' do
335
+ let(:time) do
336
+ time_zone.local(2010, 1, 1, 0, 0, 1).freeze
337
+ end
338
+
339
+ it "returns midnight utc" do
340
+ expect(evolved).to eq(Time.utc(2010, 1, 1, 0, 0, 0))
341
+ end
342
+ end
343
+
344
+ context 'end of day' do
345
+ let(:time) do
346
+ time_zone.local(2010, 1, 1, 23, 59, 59).freeze
347
+ end
348
+
349
+ it "returns midnight utc" do
350
+ expect(evolved).to eq(Time.utc(2010, 1, 1, 0, 0, 0))
351
+ end
352
+ end
353
+ end
354
+
328
355
  describe "#__evolve_time__" do
329
356
 
330
357
  let(:date) do
331
- time_zone.local(2010, 1, 1, 12, 0, 0)
358
+ time_zone.local(2010, 1, 1, 12, 0, 0).freeze
332
359
  end
333
360
 
334
361
  let(:evolved) do
@@ -5,11 +5,11 @@ require "spec_helper"
5
5
 
6
6
  describe Mongoid::Criteria::Queryable::Mergeable do
7
7
 
8
- describe "#intersect" do
8
+ let(:query) do
9
+ Mongoid::Query.new
10
+ end
9
11
 
10
- let(:query) do
11
- Mongoid::Query.new
12
- end
12
+ describe "#intersect" do
13
13
 
14
14
  before do
15
15
  query.intersect
@@ -22,10 +22,6 @@ describe Mongoid::Criteria::Queryable::Mergeable do
22
22
 
23
23
  describe "#override" do
24
24
 
25
- let(:query) do
26
- Mongoid::Query.new
27
- end
28
-
29
25
  before do
30
26
  query.override
31
27
  end
@@ -37,10 +33,6 @@ describe Mongoid::Criteria::Queryable::Mergeable do
37
33
 
38
34
  describe "#union" do
39
35
 
40
- let(:query) do
41
- Mongoid::Query.new
42
- end
43
-
44
36
  before do
45
37
  query.union
46
38
  end
@@ -49,4 +41,45 @@ describe Mongoid::Criteria::Queryable::Mergeable do
49
41
  expect(query.strategy).to eq(:__union__)
50
42
  end
51
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
52
85
  end
@@ -16,110 +16,12 @@ describe Mongoid::Criteria::Queryable::Selectable do
16
16
  end
17
17
  end
18
18
 
19
- shared_examples_for 'a non-combining logical operation' do
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
20
22
 
21
- context 'when there is a single predicate' do
22
- let(:query) do
23
- Mongoid::Query.new.send(tested_method, hello: 'world')
24
- end
25
-
26
- it 'adds the predicate' do
27
- expect(query.selector).to eq(expected_operator => [{'hello' => 'world'}])
28
- end
29
- end
30
-
31
- context 'when the single predicate is wrapped in an array' do
32
- let(:query) do
33
- Mongoid::Query.new.send(tested_method, [{hello: 'world'}])
34
- end
35
-
36
- it 'adds the predicate' do
37
- expect(query.selector).to eq(expected_operator => [{'hello' => 'world'}])
38
- end
39
- end
40
-
41
- context 'when argument is a Criteria' do
42
- let(:query) do
43
- Mongoid::Query.new.where(hello: 'world')
44
- end
45
-
46
- let(:other) do
47
- Mongoid::Query.new.where(foo: 'bar')
48
- end
49
-
50
- let(:result) { query.send(tested_method, other) }
51
-
52
- it 'combines' do
53
- # This is used for $or / $nor, the two conditions should remain
54
- # as separate hashes
55
- expect(result.selector).to eq(expected_operator => [{'hello' => 'world'}, {'foo' => 'bar'}])
56
- end
57
- end
58
-
59
- context 'when argument is a mix of Criteria and hashes' do
60
- let(:query) do
61
- Mongoid::Query.new.where(hello: 'world')
62
- end
63
-
64
- let(:other1) do
65
- Mongoid::Query.new.where(foo: 'bar')
66
- end
67
-
68
- let(:other2) do
69
- {bar: 42}
70
- end
71
-
72
- let(:other3) do
73
- Mongoid::Query.new.where(a: 2)
74
- end
75
-
76
- let(:result) { query.send(tested_method, other1, other2, other3) }
77
-
78
- it 'combines' do
79
- expect(result.selector).to eq(expected_operator => [
80
- {'hello' => 'world'},
81
- {'foo' => 'bar'},
82
- {'bar' => 42},
83
- {'a' => 2},
84
- ])
85
- end
86
- end
87
- end
88
-
89
- describe "#and" do
90
-
91
- context "when provided no criterion" do
92
-
93
- let(:selection) do
94
- query.and
95
- end
96
-
97
- it "does not add any criterion" do
98
- expect(selection.selector).to eq({})
99
- end
100
-
101
- it "returns the query" do
102
- expect(selection).to eq(query)
103
- end
104
-
105
- it_behaves_like 'returns a cloned query'
106
- end
107
-
108
- context "when provided nil" do
109
-
110
- let(:selection) do
111
- query.and(nil)
112
- end
113
-
114
- it "does not add any criterion" do
115
- expect(selection.selector).to eq({})
116
- end
117
-
118
- it "returns the query" do
119
- expect(selection).to eq(query)
120
- end
121
-
122
- it_behaves_like 'returns a cloned query'
23
+ let(:query) do
24
+ Mongoid::Query.new
123
25
  end
124
26
 
125
27
  context "when provided a single criterion" do
@@ -127,23 +29,23 @@ describe Mongoid::Criteria::Queryable::Selectable do
127
29
  shared_examples_for 'adds the conditions to top level' do
128
30
 
129
31
  it "adds the conditions to top level" do
130
- expect(selection.selector).to eq({
32
+ expect(selection.selector).to eq(
131
33
  "field" => [ 1, 2 ]
132
- })
34
+ )
133
35
  end
134
36
 
135
37
  it_behaves_like 'returns a cloned query'
136
38
  end
137
39
 
138
40
  let(:selection) do
139
- query.and(field: [ 1, 2 ])
41
+ query.send(tested_method, field: [ 1, 2 ])
140
42
  end
141
43
 
142
44
  it_behaves_like 'adds the conditions to top level'
143
45
 
144
46
  context 'when the criterion is wrapped in an array' do
145
47
  let(:selection) do
146
- query.and([{field: [ 1, 2 ] }])
48
+ query.send(tested_method, [{field: [ 1, 2 ] }])
147
49
  end
148
50
 
149
51
  it_behaves_like 'adds the conditions to top level'
@@ -151,20 +53,39 @@ describe Mongoid::Criteria::Queryable::Selectable do
151
53
 
152
54
  context 'when the criterion is wrapped in a deep array with nil elements' do
153
55
  let(:selection) do
154
- query.and([[[{field: [ 1, 2 ] }]], [nil]])
56
+ query.send(tested_method, [[[{field: [ 1, 2 ] }]], [nil]])
155
57
  end
156
58
 
157
59
  it_behaves_like 'adds the conditions to top level'
158
60
  end
159
61
  end
160
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
+
161
82
  context "when provided a single criterion that is handled via Key" do
162
83
 
163
84
  shared_examples_for 'adds the conditions to top level' do
164
85
 
165
86
  it "adds the conditions to top level" do
166
87
  expect(selection.selector).to eq({
167
- "field" => {'$gt' => 3 },
88
+ "field" => {'$gt' => 3},
168
89
  })
169
90
  end
170
91
 
@@ -172,14 +93,14 @@ describe Mongoid::Criteria::Queryable::Selectable do
172
93
  end
173
94
 
174
95
  let(:selection) do
175
- query.and(:field.gt => 3)
96
+ query.send(tested_method, :field.gt => 3)
176
97
  end
177
98
 
178
99
  it_behaves_like 'adds the conditions to top level'
179
100
 
180
101
  context 'when the criterion is wrapped in an array' do
181
102
  let(:selection) do
182
- query.and([{ :field.gt => 3 }])
103
+ query.send(tested_method, [{ :field.gt => 3 }])
183
104
  end
184
105
 
185
106
  it_behaves_like 'adds the conditions to top level'
@@ -187,81 +108,74 @@ describe Mongoid::Criteria::Queryable::Selectable do
187
108
 
188
109
  context 'when the criterion is wrapped in a deep array with nil elements' do
189
110
  let(:selection) do
190
- query.and([[[{ :field.gt => 3 }]], [nil]])
111
+ query.send(tested_method, [[[{ :field.gt => 3 }]], [nil]])
191
112
  end
192
113
 
193
114
  it_behaves_like 'adds the conditions to top level'
194
115
  end
195
- end
196
-
197
- context "when provided a nested criterion" do
198
-
199
- let(:selection) do
200
- query.and(:test.elem_match => { :field.in => [ 1, 2 ] })
201
- end
202
-
203
- it "builds the correct selector" do
204
- expect(selection.selector).to eq({
205
- "test" => { "$elemMatch" => { "field" => { "$in" => [ 1, 2 ] }}}
206
- })
207
- end
208
-
209
- it_behaves_like 'returns a cloned query'
210
- end
211
-
212
- context "when provided multiple criteria" do
213
-
214
- context "when the criteria is already included" do
215
116
 
117
+ context 'when the criterion is a time' do
216
118
  let(:selection) do
217
- query.and({ first: [ 1, 2 ] }).and({ first: [ 1, 2 ] })
119
+ query.send(tested_method, :field.gte => Time.new(2020, 1, 1))
218
120
  end
219
121
 
220
- it "adds all conditions" do
122
+ it 'adds the conditions' do
221
123
  expect(selection.selector).to eq({
222
- 'first' => [1, 2],
223
- "$and" => [
224
- { "first" => [ 1, 2 ] }
225
- ]
124
+ "field" => {'$gte' => Time.new(2020, 1, 1)},
226
125
  })
227
126
  end
228
127
 
229
- it_behaves_like 'returns a cloned query'
128
+ it 'keeps argument type' do
129
+ selection.selector['field']['$gte'].should be_a(Time)
130
+ end
230
131
  end
231
132
 
232
- context "when the new criterion is for different fields" do
233
-
133
+ context 'when the criterion is a datetime' do
234
134
  let(:selection) do
235
- query.and({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
135
+ query.send(tested_method, :field.gte => DateTime.new(2020, 1, 1))
236
136
  end
237
137
 
238
- it "adds all conditions to top level" do
138
+ it 'adds the conditions' do
239
139
  expect(selection.selector).to eq({
240
- "first" => [ 1, 2 ],
241
- "second" => [ 3, 4 ],
140
+ "field" => {'$gte' => Time.utc(2020, 1, 1)},
242
141
  })
243
142
  end
244
143
 
245
- it_behaves_like 'returns a cloned query'
144
+ it 'converts argument to a time' do
145
+ selection.selector['field']['$gte'].should be_a(Time)
146
+ end
246
147
  end
247
148
 
248
- context "when the new criterion is for the same field" do
249
-
149
+ context 'when the criterion is a date' do
250
150
  let(:selection) do
251
- query.and({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
151
+ query.send(tested_method, :field.gte => Date.new(2020, 1, 1))
252
152
  end
253
153
 
254
- it "combines via $and operator" do
154
+ it 'adds the conditions' do
255
155
  expect(selection.selector).to eq({
256
- "first" => [ 1, 2 ],
257
- "$and" => [
258
- { "first" => [ 3, 4 ] }
259
- ]
156
+ "field" => {'$gte' => Time.utc(2020, 1, 1)},
260
157
  })
261
158
  end
262
159
 
263
- it_behaves_like 'returns a cloned query'
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
+ })
264
176
  end
177
+
178
+ it_behaves_like 'returns a cloned query'
265
179
  end
266
180
 
267
181
  context "when chaining the criteria" do
@@ -269,7 +183,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
269
183
  context "when the criteria are for different fields" do
270
184
 
271
185
  let(:selection) do
272
- query.and(first: [ 1, 2 ]).and(second: [ 3, 4 ])
186
+ query.and(first: [ 1, 2 ]).send(tested_method, second: [ 3, 4 ])
273
187
  end
274
188
 
275
189
  it "adds the conditions to top level" do
@@ -285,7 +199,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
285
199
  context "when the criteria are on the same field" do
286
200
 
287
201
  let(:selection) do
288
- query.and(first: [ 1, 2 ]).and(first: [ 3, 4 ])
202
+ query.and(first: [ 1, 2 ]).send(tested_method, first: [ 3, 4 ])
289
203
  end
290
204
 
291
205
  it "combines via $and operator" do
@@ -300,34 +214,47 @@ describe Mongoid::Criteria::Queryable::Selectable do
300
214
  it_behaves_like 'returns a cloned query'
301
215
  end
302
216
  end
217
+ end
303
218
 
304
- context 'when argument is a Criteria' do
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
305
224
  let(:query) do
306
- Mongoid::Query.new.where(hello: 'world')
225
+ Mongoid::Query.new.send(tested_method, hello: 'world')
307
226
  end
308
227
 
309
- let(:result) { query.and(other) }
228
+ it 'adds the predicate' do
229
+ expect(query.selector).to eq(expected_operator => [{'hello' => 'world'}])
230
+ end
231
+ end
310
232
 
311
- context 'different fields' do
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
312
237
 
313
- let(:other) do
314
- Mongoid::Query.new.where(foo: 'bar')
315
- end
238
+ it 'adds the predicate' do
239
+ expect(query.selector).to eq(expected_operator => [{'hello' => 'world'}])
240
+ end
241
+ end
316
242
 
317
- it 'combines both fields at top level' do
318
- expect(result.selector).to eq('hello' => 'world', 'foo' => 'bar')
319
- end
243
+ context 'when argument is a Criteria' do
244
+ let(:query) do
245
+ Mongoid::Query.new.where(hello: 'world')
320
246
  end
321
247
 
322
- context 'same field' do
248
+ let(:other) do
249
+ Mongoid::Query.new.where(foo: 'bar')
250
+ end
323
251
 
324
- let(:other) do
325
- Mongoid::Query.new.where(hello: /bar/)
326
- end
252
+ let(:result) { query.send(tested_method, other) }
327
253
 
328
- it 'combines fields with $and' do
329
- expect(result.selector).to eq('hello' => 'world', '$and' => [{'hello' => /bar/}])
330
- end
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'}])
331
258
  end
332
259
  end
333
260
 
@@ -348,40 +275,344 @@ describe Mongoid::Criteria::Queryable::Selectable do
348
275
  Mongoid::Query.new.where(a: 2)
349
276
  end
350
277
 
351
- let(:result) { query.and(other1, other2, other3) }
278
+ let(:result) { query.send(tested_method, other1, other2, other3) }
352
279
 
353
280
  it 'combines' do
354
- expect(result.selector).to eq('hello' => 'world',
355
- 'foo' => 'bar',
356
- 'bar' => 42,
357
- 'a' => 2,
358
- )
281
+ expect(result.selector).to eq(expected_operator => [
282
+ {'hello' => 'world'},
283
+ {'foo' => 'bar'},
284
+ {'bar' => 42},
285
+ {'a' => 2},
286
+ ])
359
287
  end
360
288
  end
289
+ end
361
290
 
362
- context 'when Key instances are used and types involved have serializers' do
363
- let(:time) { Time.now }
291
+ describe "#and" do
364
292
 
365
- let(:query) do
366
- Band.all.and(:created_at.gt => time)
293
+ let(:tested_method) { :and }
294
+ let(:expected_operator) { '$and' }
295
+
296
+ it_behaves_like 'a hoisting logical operation'
297
+
298
+ context "when provided no criterion" do
299
+
300
+ let(:selection) do
301
+ query.and
367
302
  end
368
303
 
369
- let(:expected) do
370
- {'created_at' => {'$gt' => time.utc}}
304
+ it "does not add any criterion" do
305
+ expect(selection.selector).to eq({})
371
306
  end
372
307
 
373
- it 'combines and evolves' do
374
- expect(query.selector).to eq(expected)
308
+ it "returns the query" do
309
+ expect(selection).to eq(query)
375
310
  end
311
+
312
+ it_behaves_like 'returns a cloned query'
376
313
  end
377
314
 
378
- describe 'query shape' do
379
- shared_examples_for 'adds most recent criterion as $and' do
380
- let(:selector) { scope.selector }
315
+ context "when provided nil" do
381
316
 
382
- it 'adds most recent criterion as $and' do
383
- expect(selector).to eq('foo' => 1, '$and' => [{'foo' => 2}])
384
- end
317
+ let(:selection) do
318
+ query.and(nil)
319
+ end
320
+
321
+ it "does not add any criterion" do
322
+ expect(selection.selector).to eq({})
323
+ end
324
+
325
+ it "returns the query" do
326
+ expect(selection).to eq(query)
327
+ end
328
+
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
532
+ end
533
+ end
534
+
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
385
616
  end
386
617
 
387
618
  context 'and/and' do
@@ -405,38 +636,463 @@ describe Mongoid::Criteria::Queryable::Selectable do
405
636
  Band.and(foo: 1).where(foo: 2)
406
637
  end
407
638
 
408
- it_behaves_like 'adds most recent criterion as $and'
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
713
+
714
+ let(:selection) do
715
+ query.send(tested_method)
716
+ end
717
+
718
+ it_behaves_like 'returns a cloned query'
719
+
720
+ it "does not add any criteria" do
721
+ expect(selection.selector).to eq({})
722
+ end
723
+
724
+ it "returns the query" do
725
+ expect(selection).to eq(query)
726
+ end
727
+ end
728
+
729
+ context "when provided nil" do
730
+
731
+ let(:selection) do
732
+ query.send(tested_method, nil)
733
+ end
734
+
735
+ it_behaves_like 'returns a cloned query'
736
+
737
+ it "does not add any criteria" do
738
+ expect(selection.selector).to eq({})
739
+ end
740
+
741
+ it "returns the query" do
742
+ expect(selection).to eq(query)
743
+ end
744
+ end
745
+
746
+ context "when provided a single criterion" do
747
+
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
761
+
762
+ let(:selection) do
763
+ query.send(tested_method, [{ field: [ 1, 2 ] }])
764
+ end
765
+
766
+ it_behaves_like 'returns a cloned query'
767
+
768
+ it "adds the $or/$nor selector" do
769
+ expect(selection.selector).to eq({
770
+ expected_operator => [{ "field" => [ 1, 2 ] }]
771
+ })
772
+ end
773
+
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
787
+ end
788
+ end
789
+
790
+ context 'when query already has a condition on another field' do
791
+
792
+ let(:selection) do
793
+ query.where(foo: 'bar').send(tested_method, field: [ 1, 2 ])
794
+ end
795
+
796
+ it 'moves original conditions under $or/$nor' do
797
+ expect(selection.selector).to eq({
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 => [
836
+ { "first" => [ 1, 2 ] },
837
+ { "second" => [ 3, 4 ] }
838
+ ]
839
+ })
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
875
+
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
906
+ end
907
+ end
908
+
909
+ context "when a criterion has an aliased field" do
910
+
911
+ let(:selection) do
912
+ query.send(tested_method, { id: 1 })
913
+ end
914
+
915
+ it "adds the $or/$nor selector and aliases the field" do
916
+ expect(selection.selector).to eq({
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 => [
935
+ { "first" => [ 1, 2 ] },
936
+ { "second" => { "$gt" => 3 }}
937
+ ]
938
+ })
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'
967
+
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
992
+ end
993
+ end
994
+ end
995
+
996
+ context "when chaining the criterion" do
997
+
998
+ context "when the criterion are for different fields" do
999
+
1000
+ let(:selection) do
1001
+ query.send(tested_method, first: [ 1, 2 ]).send(tested_method, second: [ 3, 4 ])
1002
+ end
1003
+
1004
+ it_behaves_like 'returns a cloned query'
1005
+
1006
+ it "adds the $or/$nor selectors" do
1007
+ expect(selection.selector).to eq({
1008
+ expected_operator => [
1009
+ { "first" => [ 1, 2 ] },
1010
+ { "second" => [ 3, 4 ] }
1011
+ ]
1012
+ })
1013
+ end
1014
+ end
1015
+
1016
+ context "when the criterion are on the same field" do
1017
+
1018
+ let(:selection) do
1019
+ query.send(tested_method, first: [ 1, 2 ]).send(tested_method, first: [ 3, 4 ])
1020
+ end
1021
+
1022
+ it_behaves_like 'returns a cloned query'
1023
+
1024
+ it "appends both $or/$nor expressions" do
1025
+ expect(selection.selector).to eq({
1026
+ expected_operator => [
1027
+ { "first" => [ 1, 2 ] },
1028
+ { "first" => [ 3, 4 ] }
1029
+ ]
1030
+ })
1031
+ end
1032
+ end
1033
+ end
1034
+ end
1035
+
1036
+ describe "#or" do
1037
+
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')
409
1068
  end
410
1069
 
411
- context 'where/and' do
412
- let(:scope) do
413
- Band.where(foo: 1).and(foo: 2)
414
- end
1070
+ let(:other2) do
1071
+ {bar: 42}
1072
+ end
415
1073
 
416
- it_behaves_like 'adds most recent criterion as $and'
1074
+ let(:other3) do
1075
+ Mongoid::Query.new.where(a: 2)
417
1076
  end
418
1077
 
419
- context 'where/where' do
420
- let(:scope) do
421
- Band.where(foo: 1).where(foo: 2)
422
- end
1078
+ let(:result) { query.send(tested_method, other1, other2, other3) }
423
1079
 
424
- it_behaves_like 'adds most recent criterion as $and'
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
+ )
425
1089
  end
426
1090
  end
427
- end
428
-
429
- describe "#or" do
430
-
431
- let(:tested_method) { :or }
432
- let(:expected_operator) { '$or' }
433
-
434
- it_behaves_like 'a non-combining logical operation'
435
1091
 
436
1092
  context "when provided no arguments" do
437
1093
 
438
1094
  let(:selection) do
439
- query.or
1095
+ query.any_of
440
1096
  end
441
1097
 
442
1098
  it_behaves_like 'returns a cloned query'
@@ -453,7 +1109,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
453
1109
  context "when provided nil" do
454
1110
 
455
1111
  let(:selection) do
456
- query.or(nil)
1112
+ query.any_of(nil)
457
1113
  end
458
1114
 
459
1115
  it_behaves_like 'returns a cloned query'
@@ -470,75 +1126,92 @@ describe Mongoid::Criteria::Queryable::Selectable do
470
1126
  context "when provided a single criterion" do
471
1127
 
472
1128
  let(:selection) do
473
- query.or(field: [ 1, 2 ])
1129
+ query.any_of(field: [ 1, 2 ])
474
1130
  end
475
1131
 
476
1132
  it_behaves_like 'returns a cloned query'
477
1133
 
478
1134
  it "adds the $or selector" do
479
- expect(selection.selector).to eq({
480
- "$or" => [{ "field" => [ 1, 2 ] }]
481
- })
1135
+ expect(selection.selector).to eq(
1136
+ "field" => [ 1, 2 ],
1137
+ )
482
1138
  end
483
1139
 
484
1140
  context 'when the criterion is wrapped in array' do
485
1141
 
486
1142
  let(:selection) do
487
- query.or([{ field: [ 1, 2 ] }])
1143
+ query.any_of([{ field: [ 1, 2 ] }])
488
1144
  end
489
1145
 
490
1146
  it_behaves_like 'returns a cloned query'
491
1147
 
492
- it "adds the $or selector" do
493
- expect(selection.selector).to eq({
494
- "$or" => [{ "field" => [ 1, 2 ] }]
495
- })
1148
+ it "adds the condition" do
1149
+ expect(selection.selector).to eq(
1150
+ "field" => [ 1, 2 ],
1151
+ )
496
1152
  end
497
1153
 
498
1154
  context 'when the array has nil as one of the elements' do
499
1155
 
500
1156
  let(:selection) do
501
- query.or([{ field: [ 1, 2 ] }, nil])
1157
+ query.any_of([{ field: [ 1, 2 ] }, nil])
502
1158
  end
503
1159
 
504
1160
  it_behaves_like 'returns a cloned query'
505
1161
 
506
1162
  it "adds the $or selector ignoring the nil element" do
507
- expect(selection.selector).to eq({
508
- "$or" => [{ "field" => [ 1, 2 ] }]
509
- })
1163
+ expect(selection.selector).to eq(
1164
+ "field" => [ 1, 2 ],
1165
+ )
510
1166
  end
511
1167
  end
512
1168
  end
513
1169
 
514
1170
  context 'when query already has a condition on another field' do
515
1171
 
516
- let(:selection) do
517
- query.where(foo: 'bar').or(field: [ 1, 2 ])
1172
+ context 'when there is one argument' do
1173
+
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
518
1184
  end
519
1185
 
520
- it 'moves original conditions under $or' do
521
- expect(selection.selector).to eq({
522
- "$or" => [{'foo' => 'bar'}, { "field" => [ 1, 2 ] }]
523
- })
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
524
1201
  end
525
1202
  end
526
1203
 
527
1204
  context 'when query already has an $or condition and another condition' do
528
1205
 
529
1206
  let(:selection) do
530
- query.or(field: [ 1, 2 ]).where(foo: 'bar').or(test: 1)
1207
+ query.or(field: [ 1, 2 ]).where(foo: 'bar').any_of(test: 1)
531
1208
  end
532
1209
 
533
- it 'unions existing conditions' do
1210
+ it 'adds the new condition' do
534
1211
  expect(selection.selector).to eq(
535
- '$or' => [
536
- {
537
- "$or" => [{ "field" => [ 1, 2 ] }],
538
- 'foo' => 'bar',
539
- },
540
- {'test' => 1},
541
- ]
1212
+ '$or' => [{'field' => [1, 2]}],
1213
+ 'foo' => 'bar',
1214
+ 'test' => 1,
542
1215
  )
543
1216
  end
544
1217
  end
@@ -549,7 +1222,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
549
1222
  context "when the criteria are for different fields" do
550
1223
 
551
1224
  let(:selection) do
552
- query.or({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
1225
+ query.any_of({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
553
1226
  end
554
1227
 
555
1228
  it_behaves_like 'returns a cloned query'
@@ -567,7 +1240,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
567
1240
  context "when the criteria uses a Key instance" do
568
1241
 
569
1242
  let(:selection) do
570
- query.or({ first: [ 1, 2 ] }, { :second.gt => 3 })
1243
+ query.any_of({ first: [ 1, 2 ] }, { :second.gt => 3 })
571
1244
  end
572
1245
 
573
1246
  it "adds the $or selector" do
@@ -582,229 +1255,196 @@ describe Mongoid::Criteria::Queryable::Selectable do
582
1255
  it_behaves_like 'returns a cloned query'
583
1256
  end
584
1257
 
585
- context "when a criterion has an aliased field" do
1258
+ context 'when criteria are simple and handled via Key' do
1259
+ shared_examples_for 'adds conditions with $or' do
586
1260
 
587
- let(:selection) do
588
- query.or({ id: 1 })
589
- end
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
590
1269
 
591
- it "adds the $or selector and aliases the field" do
592
- expect(selection.selector).to eq({
593
- "$or" => [ { "_id" => 1 } ]
594
- })
1270
+ it_behaves_like 'returns a cloned query'
595
1271
  end
596
1272
 
597
- it_behaves_like 'returns a cloned query'
598
- end
1273
+ shared_examples_for 'adds one condition' do
599
1274
 
600
- context "when a criterion is wrapped in an array" do
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
601
1283
 
602
- let(:selection) do
603
- query.or([{ first: [ 1, 2 ] }, { :second.gt => 3 }])
1284
+ it_behaves_like 'returns a cloned query'
604
1285
  end
605
1286
 
606
- it_behaves_like 'returns a cloned query'
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
607
1291
 
608
- it "adds the $or selector" do
609
- expect(selection.selector).to eq({
610
- "$or" => [
611
- { "first" => [ 1, 2 ] },
612
- { "second" => { "$gt" => 3 }}
613
- ]
614
- })
1292
+ it_behaves_like 'adds one condition'
615
1293
  end
616
- end
617
1294
 
618
- context "when the criteria are on the same field" do
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
619
1299
 
620
- let(:selection) do
621
- query.or({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
1300
+ it_behaves_like 'adds conditions with $or'
622
1301
  end
623
1302
 
624
- it_behaves_like 'returns a cloned 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
625
1307
 
626
- it "appends both $or expressions" do
627
- expect(selection.selector).to eq({
628
- "$or" => [
629
- { "first" => [ 1, 2 ] },
630
- { "first" => [ 3, 4 ] }
631
- ]
632
- })
1308
+ it_behaves_like 'adds conditions with $or'
633
1309
  end
634
1310
  end
635
- end
636
1311
 
637
- context "when chaining the criterion" do
1312
+ context 'when criteria are handled via Key and simple' do
1313
+ shared_examples_for 'adds conditions with $or' do
638
1314
 
639
- context "when the criterion are for different fields" 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
640
1323
 
641
- let(:selection) do
642
- query.or(first: [ 1, 2 ]).or(second: [ 3, 4 ])
1324
+ it_behaves_like 'returns a cloned query'
643
1325
  end
644
1326
 
645
- it_behaves_like 'returns a cloned query'
646
-
647
- it "adds the $or selectors" do
648
- expect(selection.selector).to eq({
649
- "$or" => [
650
- { "first" => [ 1, 2 ] },
651
- { "second" => [ 3, 4 ] }
652
- ]
653
- })
654
- end
655
- end
1327
+ shared_examples_for 'adds one condition' do
656
1328
 
657
- context "when the criterion are on the same field" do
1329
+ it "adds one condition" do
1330
+ expect(selection.selector).to eq({
1331
+ 'field' => {'$gt' => 3},
1332
+ '$and' => ['field' => 5],
1333
+ })
1334
+ end
658
1335
 
659
- let(:selection) do
660
- query.or(first: [ 1, 2 ]).or(first: [ 3, 4 ])
1336
+ it_behaves_like 'returns a cloned query'
661
1337
  end
662
1338
 
663
- it_behaves_like 'returns a cloned query'
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
664
1343
 
665
- it "appends both $or expressions" do
666
- expect(selection.selector).to eq({
667
- "$or" => [
668
- { "first" => [ 1, 2 ] },
669
- { "first" => [ 3, 4 ] }
670
- ]
671
- })
1344
+ it_behaves_like 'adds one condition'
672
1345
  end
673
- end
674
- end
675
- end
676
-
677
- describe "#nor" do
678
-
679
- let(:tested_method) { :nor }
680
- let(:expected_operator) { '$nor' }
681
-
682
- it_behaves_like 'a non-combining logical operation'
683
-
684
- context "when provided no criterion" do
685
-
686
- let(:selection) do
687
- query.nor
688
- end
689
-
690
- it "does not add any criterion" do
691
- expect(selection.selector).to eq({})
692
- end
693
-
694
- it "returns the query" do
695
- expect(selection).to eq(query)
696
- end
697
-
698
- it_behaves_like 'returns a cloned query'
699
- end
700
1346
 
701
- context "when provided nil" do
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
702
1351
 
703
- let(:selection) do
704
- query.nor(nil)
705
- end
1352
+ it_behaves_like 'adds conditions with $or'
1353
+ end
706
1354
 
707
- it "does not add any criterion" do
708
- expect(selection.selector).to eq({})
709
- end
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
710
1359
 
711
- it "returns the query" do
712
- expect(selection).to eq(query)
1360
+ it_behaves_like 'adds conditions with $or'
1361
+ end
713
1362
  end
714
1363
 
715
- it_behaves_like 'returns a cloned query'
716
- end
1364
+ context "when a criterion has an aliased field" do
717
1365
 
718
- context "when provided a single criterion" do
1366
+ let(:selection) do
1367
+ query.any_of({ id: 1 })
1368
+ end
719
1369
 
720
- let(:selection) do
721
- query.nor(field: [ 1, 2 ])
722
- end
1370
+ it "adds the $or selector and aliases the field" do
1371
+ expect(selection.selector).to eq(
1372
+ "_id" => 1,
1373
+ )
1374
+ end
723
1375
 
724
- it "adds the $nor selector" do
725
- expect(selection.selector).to eq({
726
- "$nor" => [{"field" => [ 1, 2 ] }]
727
- })
1376
+ it_behaves_like 'returns a cloned query'
728
1377
  end
729
1378
 
730
- it_behaves_like 'returns a cloned query'
731
- end
732
-
733
- context "when provided multiple criterion" do
734
-
735
- context "when the criterion are fnor different fields" do
1379
+ context "when a criterion is wrapped in an array" do
736
1380
 
737
1381
  let(:selection) do
738
- query.nor({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
1382
+ query.any_of([{ first: [ 1, 2 ] }, { :second.gt => 3 }])
739
1383
  end
740
1384
 
741
- it "adds the $nor selector" do
1385
+ it_behaves_like 'returns a cloned query'
1386
+
1387
+ it "adds the $or selector" do
742
1388
  expect(selection.selector).to eq({
743
- "$nor" => [
1389
+ "$or" => [
744
1390
  { "first" => [ 1, 2 ] },
745
- { "second" => [ 3, 4 ] }
1391
+ { "second" => { "$gt" => 3 }}
746
1392
  ]
747
1393
  })
748
1394
  end
749
-
750
- it_behaves_like 'returns a cloned query'
751
1395
  end
752
1396
 
753
- context "when the criterion are on the same field" do
1397
+ context "when the criteria are on the same field" do
754
1398
 
755
1399
  let(:selection) do
756
- query.nor({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
1400
+ query.any_of({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
757
1401
  end
758
1402
 
759
- it "appends both $nor expressions" do
1403
+ it_behaves_like 'returns a cloned query'
1404
+
1405
+ it "appends both $or expressions" do
760
1406
  expect(selection.selector).to eq({
761
- "$nor" => [
1407
+ "$or" => [
762
1408
  { "first" => [ 1, 2 ] },
763
1409
  { "first" => [ 3, 4 ] }
764
1410
  ]
765
1411
  })
766
1412
  end
767
-
768
- it_behaves_like 'returns a cloned query'
769
1413
  end
770
1414
  end
771
1415
 
772
- context "when chaining the criterion" do
1416
+ context "when chaining the criteria" do
773
1417
 
774
- context "when the criterion are fnor different fields" do
1418
+ context "when the criteria are for different fields" do
775
1419
 
776
1420
  let(:selection) do
777
- query.nor(first: [ 1, 2 ]).nor(second: [ 3, 4 ])
778
- end
779
-
780
- it "adds the $nor selectors" do
781
- expect(selection.selector).to eq({
782
- "$nor" => [
783
- { "first" => [ 1, 2 ] },
784
- { "second" => [ 3, 4 ] }
785
- ]
786
- })
1421
+ query.any_of(first: [ 1, 2 ]).any_of(second: [ 3, 4 ])
787
1422
  end
788
1423
 
789
1424
  it_behaves_like 'returns a cloned query'
1425
+
1426
+ it "adds the conditions separately" do
1427
+ expect(selection.selector).to eq(
1428
+ "first" => [ 1, 2 ],
1429
+ "second" => [ 3, 4 ],
1430
+ )
1431
+ end
790
1432
  end
791
1433
 
792
- context "when the criterion are on the same field" do
1434
+ context "when the criteria are on the same field" do
793
1435
 
794
1436
  let(:selection) do
795
- query.nor(first: [ 1, 2 ]).nor(first: [ 3, 4 ])
796
- end
797
-
798
- it "appends both $nor expressions" do
799
- expect(selection.selector).to eq({
800
- "$nor" => [
801
- { "first" => [ 1, 2 ] },
802
- { "first" => [ 3, 4 ] }
803
- ]
804
- })
1437
+ query.any_of(first: [ 1, 2 ]).any_of(first: [ 3, 4 ])
805
1438
  end
806
1439
 
807
1440
  it_behaves_like 'returns a cloned query'
1441
+
1442
+ it "adds the conditions separately" do
1443
+ expect(selection.selector).to eq(
1444
+ "first" => [ 1, 2 ],
1445
+ '$and' => [{"first" => [ 3, 4 ]}],
1446
+ )
1447
+ end
808
1448
  end
809
1449
  end
810
1450
  end
@@ -894,6 +1534,25 @@ describe Mongoid::Criteria::Queryable::Selectable do
894
1534
  end
895
1535
  end
896
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
+ )
1547
+ end
1548
+
1549
+ it_behaves_like 'returns a cloned query'
1550
+
1551
+ it "removes the negation on the clone" do
1552
+ expect(selection).to_not be_negating
1553
+ end
1554
+ end
1555
+
897
1556
  context "when the following criteria is a where" do
898
1557
 
899
1558
  let(:selection) do