mongoid 7.1.0.rc0 → 7.2.0.rc1

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