mongoid 5.4.0 → 6.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +3 -3
  4. data/Rakefile +26 -0
  5. data/lib/config/locales/en.yml +40 -0
  6. data/lib/mongoid/atomic/modifiers.rb +2 -2
  7. data/lib/mongoid/atomic.rb +5 -5
  8. data/lib/mongoid/attributes/readonly.rb +22 -0
  9. data/lib/mongoid/attributes.rb +22 -21
  10. data/lib/mongoid/cacheable.rb +36 -0
  11. data/lib/mongoid/changeable.rb +36 -0
  12. data/lib/mongoid/clients/options.rb +55 -250
  13. data/lib/mongoid/clients/sessions.rb +113 -0
  14. data/lib/mongoid/clients/storage_options.rb +2 -69
  15. data/lib/mongoid/clients.rb +10 -63
  16. data/lib/mongoid/composable.rb +29 -2
  17. data/lib/mongoid/config.rb +1 -0
  18. data/lib/mongoid/contextual/aggregable/mongo.rb +1 -1
  19. data/lib/mongoid/contextual/atomic.rb +4 -4
  20. data/lib/mongoid/contextual/map_reduce.rb +7 -3
  21. data/lib/mongoid/contextual/memory.rb +9 -4
  22. data/lib/mongoid/contextual/mongo.rb +65 -30
  23. data/lib/mongoid/contextual/none.rb +12 -0
  24. data/lib/mongoid/copyable.rb +13 -6
  25. data/lib/mongoid/criteria/marshalable.rb +2 -2
  26. data/lib/mongoid/criteria/modifiable.rb +29 -3
  27. data/lib/mongoid/criteria/options.rb +25 -0
  28. data/lib/mongoid/criteria/queryable/aggregable.rb +120 -0
  29. data/lib/mongoid/criteria/queryable/extensions/array.rb +185 -0
  30. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +37 -0
  31. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +34 -0
  32. data/lib/mongoid/criteria/queryable/extensions/date.rb +63 -0
  33. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +53 -0
  34. data/lib/mongoid/criteria/queryable/extensions/hash.rb +200 -0
  35. data/lib/mongoid/criteria/queryable/extensions/nil_class.rb +86 -0
  36. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +90 -0
  37. data/lib/mongoid/criteria/queryable/extensions/object.rb +206 -0
  38. data/lib/mongoid/criteria/queryable/extensions/range.rb +70 -0
  39. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +79 -0
  40. data/lib/mongoid/criteria/queryable/extensions/set.rb +34 -0
  41. data/lib/mongoid/criteria/queryable/extensions/string.rb +137 -0
  42. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +79 -0
  43. data/lib/mongoid/criteria/queryable/extensions/time.rb +60 -0
  44. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +54 -0
  45. data/lib/mongoid/criteria/queryable/extensions.rb +28 -0
  46. data/lib/mongoid/criteria/queryable/forwardable.rb +65 -0
  47. data/lib/mongoid/criteria/queryable/key.rb +103 -0
  48. data/lib/mongoid/criteria/queryable/macroable.rb +27 -0
  49. data/lib/mongoid/criteria/queryable/mergeable.rb +273 -0
  50. data/lib/mongoid/criteria/queryable/optional.rb +429 -0
  51. data/lib/mongoid/criteria/queryable/options.rb +153 -0
  52. data/lib/mongoid/criteria/queryable/pipeline.rb +111 -0
  53. data/lib/mongoid/criteria/queryable/selectable.rb +689 -0
  54. data/lib/mongoid/criteria/queryable/selector.rb +212 -0
  55. data/lib/mongoid/criteria/queryable/smash.rb +104 -0
  56. data/lib/mongoid/criteria/queryable.rb +87 -0
  57. data/lib/mongoid/criteria.rb +6 -2
  58. data/lib/mongoid/document.rb +34 -41
  59. data/lib/mongoid/errors/ambiguous_relationship.rb +1 -1
  60. data/lib/mongoid/errors/in_memory_collation_not_supported.rb +1 -1
  61. data/lib/mongoid/errors/invalid_field.rb +2 -2
  62. data/lib/mongoid/errors/invalid_persistence_option.rb +29 -0
  63. data/lib/mongoid/errors/invalid_relation.rb +66 -0
  64. data/lib/mongoid/errors/invalid_session_use.rb +24 -0
  65. data/lib/mongoid/errors.rb +3 -0
  66. data/lib/mongoid/evolvable.rb +1 -1
  67. data/lib/mongoid/extensions/big_decimal.rb +17 -8
  68. data/lib/mongoid/extensions/date.rb +4 -1
  69. data/lib/mongoid/extensions/decimal128.rb +3 -3
  70. data/lib/mongoid/extensions/hash.rb +1 -0
  71. data/lib/mongoid/extensions/regexp.rb +1 -0
  72. data/lib/mongoid/extensions/string.rb +6 -3
  73. data/lib/mongoid/extensions/time.rb +4 -1
  74. data/lib/mongoid/extensions.rb +0 -4
  75. data/lib/mongoid/factory.rb +2 -1
  76. data/lib/mongoid/fields/validators/macro.rb +18 -0
  77. data/lib/mongoid/findable.rb +2 -2
  78. data/lib/mongoid/indexable.rb +16 -14
  79. data/lib/mongoid/interceptable.rb +9 -22
  80. data/lib/mongoid/matchable/all.rb +2 -2
  81. data/lib/mongoid/matchable/and.rb +3 -3
  82. data/lib/mongoid/matchable/default.rb +2 -2
  83. data/lib/mongoid/matchable/elem_match.rb +28 -0
  84. data/lib/mongoid/matchable/exists.rb +2 -2
  85. data/lib/mongoid/matchable/gt.rb +4 -2
  86. data/lib/mongoid/matchable/gte.rb +4 -2
  87. data/lib/mongoid/matchable/in.rb +2 -2
  88. data/lib/mongoid/matchable/lt.rb +4 -2
  89. data/lib/mongoid/matchable/lte.rb +4 -2
  90. data/lib/mongoid/matchable/ne.rb +2 -2
  91. data/lib/mongoid/matchable/nin.rb +2 -2
  92. data/lib/mongoid/matchable/nor.rb +37 -0
  93. data/lib/mongoid/matchable/or.rb +3 -3
  94. data/lib/mongoid/matchable/regexp.rb +3 -3
  95. data/lib/mongoid/matchable/size.rb +2 -2
  96. data/lib/mongoid/matchable.rb +16 -7
  97. data/lib/mongoid/persistable/creatable.rb +5 -3
  98. data/lib/mongoid/persistable/deletable.rb +5 -3
  99. data/lib/mongoid/persistable/destroyable.rb +1 -5
  100. data/lib/mongoid/persistable/settable.rb +5 -5
  101. data/lib/mongoid/persistable/updatable.rb +7 -14
  102. data/lib/mongoid/persistable/upsertable.rb +2 -1
  103. data/lib/mongoid/persistable.rb +4 -6
  104. data/lib/mongoid/persistence_context.rb +220 -0
  105. data/lib/mongoid/query_cache.rb +67 -23
  106. data/lib/mongoid/railtie.rb +17 -1
  107. data/lib/mongoid/railties/controller_runtime.rb +86 -0
  108. data/lib/mongoid/relations/accessors.rb +3 -0
  109. data/lib/mongoid/relations/auto_save.rb +12 -4
  110. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +4 -4
  111. data/lib/mongoid/relations/counter_cache.rb +15 -5
  112. data/lib/mongoid/relations/eager/base.rb +3 -3
  113. data/lib/mongoid/relations/eager/has_and_belongs_to_many.rb +2 -2
  114. data/lib/mongoid/relations/eager/has_many.rb +1 -1
  115. data/lib/mongoid/relations/eager.rb +6 -11
  116. data/lib/mongoid/relations/embedded/batchable.rb +20 -18
  117. data/lib/mongoid/relations/embedded/in.rb +13 -1
  118. data/lib/mongoid/relations/embedded/many.rb +51 -10
  119. data/lib/mongoid/relations/embedded/one.rb +14 -1
  120. data/lib/mongoid/relations/macros.rb +9 -1
  121. data/lib/mongoid/relations/many.rb +4 -0
  122. data/lib/mongoid/relations/metadata.rb +3 -3
  123. data/lib/mongoid/relations/options.rb +2 -2
  124. data/lib/mongoid/relations/proxy.rb +1 -31
  125. data/lib/mongoid/relations/referenced/in.rb +19 -10
  126. data/lib/mongoid/relations/referenced/many.rb +30 -26
  127. data/lib/mongoid/relations/referenced/many_to_many.rb +20 -13
  128. data/lib/mongoid/relations/referenced/one.rb +15 -1
  129. data/lib/mongoid/relations/synchronization.rb +12 -12
  130. data/lib/mongoid/relations/targets/enumerable.rb +24 -4
  131. data/lib/mongoid/relations/touchable.rb +7 -4
  132. data/lib/mongoid/reloadable.rb +2 -2
  133. data/lib/mongoid/scopable.rb +3 -3
  134. data/lib/mongoid/serializable.rb +1 -1
  135. data/lib/mongoid/stateful.rb +1 -0
  136. data/lib/mongoid/tasks/database.rb +3 -2
  137. data/lib/mongoid/threaded.rb +74 -0
  138. data/lib/mongoid/traversable.rb +1 -1
  139. data/lib/mongoid/validatable/uniqueness.rb +1 -2
  140. data/lib/mongoid/version.rb +1 -1
  141. data/lib/mongoid.rb +6 -6
  142. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +18 -3
  143. data/spec/app/models/agent.rb +2 -0
  144. data/spec/app/models/album.rb +5 -1
  145. data/spec/app/models/array_field.rb +7 -0
  146. data/spec/app/models/artist.rb +21 -0
  147. data/spec/app/models/band.rb +3 -0
  148. data/spec/app/models/book.rb +2 -1
  149. data/spec/app/models/delegating_patient.rb +16 -0
  150. data/spec/app/models/dokument.rb +1 -0
  151. data/spec/app/models/ordered_post.rb +5 -0
  152. data/spec/app/models/oscar.rb +1 -2
  153. data/spec/app/models/page.rb +1 -1
  154. data/spec/app/models/person.rb +3 -3
  155. data/spec/app/models/princess.rb +2 -0
  156. data/spec/app/models/record.rb +1 -0
  157. data/spec/app/models/subscription.rb +1 -0
  158. data/spec/app/models/thing.rb +1 -1
  159. data/spec/config/mongoid.yml +15 -0
  160. data/spec/integration/document_spec.rb +22 -0
  161. data/spec/mongoid/atomic/modifiers_spec.rb +3 -3
  162. data/spec/mongoid/atomic_spec.rb +5 -5
  163. data/spec/mongoid/attributes/nested_spec.rb +18 -14
  164. data/spec/mongoid/attributes/readonly_spec.rb +87 -44
  165. data/spec/mongoid/attributes_spec.rb +90 -5
  166. data/spec/mongoid/cacheable_spec.rb +112 -0
  167. data/spec/mongoid/changeable_spec.rb +58 -0
  168. data/spec/mongoid/clients/factory_spec.rb +80 -28
  169. data/spec/mongoid/clients/options_spec.rb +396 -95
  170. data/spec/mongoid/clients/sessions_spec.rb +334 -0
  171. data/spec/mongoid/clients_spec.rb +243 -101
  172. data/spec/mongoid/composable_spec.rb +7 -0
  173. data/spec/mongoid/config_spec.rb +67 -11
  174. data/spec/mongoid/contextual/atomic_spec.rb +3 -3
  175. data/spec/mongoid/contextual/geo_near_spec.rb +1 -0
  176. data/spec/mongoid/contextual/mongo_spec.rb +275 -22
  177. data/spec/mongoid/contextual/none_spec.rb +15 -0
  178. data/spec/mongoid/copyable_spec.rb +13 -4
  179. data/spec/mongoid/criteria/modifiable_spec.rb +297 -16
  180. data/spec/mongoid/criteria/options_spec.rb +29 -0
  181. data/spec/mongoid/criteria/queryable/aggregable_spec.rb +370 -0
  182. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +523 -0
  183. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +59 -0
  184. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +58 -0
  185. data/spec/mongoid/criteria/queryable/extensions/boolean_spec.rb +213 -0
  186. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +330 -0
  187. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +405 -0
  188. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +58 -0
  189. data/spec/mongoid/criteria/queryable/extensions/float_spec.rb +65 -0
  190. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +327 -0
  191. data/spec/mongoid/criteria/queryable/extensions/integer_spec.rb +65 -0
  192. data/spec/mongoid/criteria/queryable/extensions/nil_class_spec.rb +77 -0
  193. data/spec/mongoid/criteria/queryable/extensions/object_spec.rb +108 -0
  194. data/spec/mongoid/criteria/queryable/extensions/range_spec.rb +309 -0
  195. data/spec/mongoid/{extensions/origin → criteria/queryable/extensions}/regexp_raw_spec.rb +2 -2
  196. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +90 -0
  197. data/spec/mongoid/criteria/queryable/extensions/set_spec.rb +39 -0
  198. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +302 -0
  199. data/spec/mongoid/criteria/queryable/extensions/symbol_spec.rb +167 -0
  200. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +376 -0
  201. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +347 -0
  202. data/spec/mongoid/criteria/queryable/forwardable_spec.rb +87 -0
  203. data/spec/mongoid/criteria/queryable/key_spec.rb +52 -0
  204. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +49 -0
  205. data/spec/mongoid/criteria/queryable/optional_spec.rb +1799 -0
  206. data/spec/mongoid/criteria/queryable/options_spec.rb +360 -0
  207. data/spec/mongoid/criteria/queryable/pipeline_spec.rb +200 -0
  208. data/spec/mongoid/criteria/queryable/queryable_spec.rb +137 -0
  209. data/spec/mongoid/criteria/queryable/selectable_spec.rb +4242 -0
  210. data/spec/mongoid/criteria/queryable/selector_spec.rb +844 -0
  211. data/spec/mongoid/criteria/queryable/smash_spec.rb +30 -0
  212. data/spec/mongoid/criteria/scopable_spec.rb +81 -0
  213. data/spec/mongoid/criteria_spec.rb +156 -22
  214. data/spec/mongoid/document_spec.rb +100 -90
  215. data/spec/mongoid/errors/invalid_relation_spec.rb +37 -0
  216. data/spec/mongoid/errors/mongoid_error_spec.rb +6 -3
  217. data/spec/mongoid/extensions/big_decimal_spec.rb +321 -19
  218. data/spec/mongoid/extensions/boolean_spec.rb +14 -0
  219. data/spec/mongoid/extensions/date_spec.rb +2 -6
  220. data/spec/mongoid/extensions/date_time_spec.rb +2 -6
  221. data/spec/mongoid/extensions/decimal128_spec.rb +1 -1
  222. data/spec/mongoid/extensions/float_spec.rb +8 -1
  223. data/spec/mongoid/extensions/hash_spec.rb +15 -0
  224. data/spec/mongoid/extensions/integer_spec.rb +8 -1
  225. data/spec/mongoid/extensions/object_spec.rb +11 -0
  226. data/spec/mongoid/extensions/regexp_spec.rb +23 -0
  227. data/spec/mongoid/extensions/string_spec.rb +53 -4
  228. data/spec/mongoid/extensions/time_spec.rb +2 -6
  229. data/spec/mongoid/extensions/time_with_zone_spec.rb +2 -6
  230. data/spec/mongoid/factory_spec.rb +11 -0
  231. data/spec/mongoid/fields_spec.rb +1 -1
  232. data/spec/mongoid/findable_spec.rb +47 -2
  233. data/spec/mongoid/indexable_spec.rb +15 -3
  234. data/spec/mongoid/interceptable_spec.rb +85 -19
  235. data/spec/mongoid/matchable/all_spec.rb +4 -4
  236. data/spec/mongoid/matchable/and_spec.rb +10 -10
  237. data/spec/mongoid/matchable/default_spec.rb +12 -12
  238. data/spec/mongoid/matchable/elem_match_spec.rb +86 -0
  239. data/spec/mongoid/matchable/exists_spec.rb +5 -5
  240. data/spec/mongoid/matchable/gt_spec.rb +18 -7
  241. data/spec/mongoid/matchable/gte_spec.rb +17 -7
  242. data/spec/mongoid/matchable/in_spec.rb +5 -5
  243. data/spec/mongoid/matchable/lt_spec.rb +18 -7
  244. data/spec/mongoid/matchable/lte_spec.rb +18 -7
  245. data/spec/mongoid/matchable/ne_spec.rb +5 -5
  246. data/spec/mongoid/matchable/nin_spec.rb +5 -5
  247. data/spec/mongoid/matchable/nor_spec.rb +209 -0
  248. data/spec/mongoid/matchable/or_spec.rb +7 -7
  249. data/spec/mongoid/matchable/regexp_spec.rb +5 -5
  250. data/spec/mongoid/matchable/size_spec.rb +3 -3
  251. data/spec/mongoid/matchable_spec.rb +199 -54
  252. data/spec/mongoid/persistable/creatable_spec.rb +7 -2
  253. data/spec/mongoid/persistable/deletable_spec.rb +35 -1
  254. data/spec/mongoid/persistable/destroyable_spec.rb +25 -2
  255. data/spec/mongoid/persistable/incrementable_spec.rb +6 -6
  256. data/spec/mongoid/persistable/savable_spec.rb +34 -29
  257. data/spec/mongoid/persistable/settable_spec.rb +77 -27
  258. data/spec/mongoid/persistable/updatable_spec.rb +182 -3
  259. data/spec/mongoid/persistable_spec.rb +16 -16
  260. data/spec/mongoid/persistence_context_spec.rb +694 -0
  261. data/spec/mongoid/positional_spec.rb +1 -1
  262. data/spec/mongoid/query_cache_spec.rb +170 -12
  263. data/spec/mongoid/relations/accessors_spec.rb +1 -1
  264. data/spec/mongoid/relations/auto_save_spec.rb +39 -6
  265. data/spec/mongoid/relations/bindings/referenced/many_to_many_spec.rb +4 -4
  266. data/spec/mongoid/relations/builders_spec.rb +37 -10
  267. data/spec/mongoid/relations/counter_cache_spec.rb +64 -3
  268. data/spec/mongoid/relations/eager/has_and_belongs_to_many_spec.rb +16 -0
  269. data/spec/mongoid/relations/eager_spec.rb +40 -0
  270. data/spec/mongoid/relations/embedded/many_spec.rb +305 -59
  271. data/spec/mongoid/relations/embedded/one_spec.rb +2 -1
  272. data/spec/mongoid/relations/macros_spec.rb +415 -7
  273. data/spec/mongoid/relations/metadata_spec.rb +15 -1
  274. data/spec/mongoid/relations/proxy_spec.rb +27 -1
  275. data/spec/mongoid/relations/referenced/in_spec.rb +41 -1
  276. data/spec/mongoid/relations/referenced/many_spec.rb +35 -25
  277. data/spec/mongoid/relations/referenced/many_to_many_spec.rb +14 -26
  278. data/spec/mongoid/relations/synchronization_spec.rb +48 -2
  279. data/spec/mongoid/relations/targets/enumerable_spec.rb +108 -0
  280. data/spec/mongoid/relations/touchable_spec.rb +40 -0
  281. data/spec/mongoid/reloadable_spec.rb +51 -0
  282. data/spec/mongoid/scopable_spec.rb +13 -0
  283. data/spec/mongoid/serializable_spec.rb +0 -50
  284. data/spec/mongoid/threaded_spec.rb +68 -0
  285. data/spec/mongoid/validatable/presence_spec.rb +1 -1
  286. data/spec/mongoid/validatable/uniqueness_spec.rb +18 -9
  287. data/spec/mongoid/validatable_spec.rb +16 -0
  288. data/spec/rails/controller_extension/controller_runtime_spec.rb +110 -0
  289. data/spec/spec_helper.rb +101 -8
  290. data/spec/support/cluster_config.rb +158 -0
  291. data/spec/support/constraints.rb +101 -0
  292. data/spec/support/macros.rb +20 -0
  293. data/spec/support/session_registry.rb +50 -0
  294. data/spec/support/spec_config.rb +42 -0
  295. data.tar.gz.sig +0 -0
  296. metadata +163 -61
  297. metadata.gz.sig +0 -0
  298. data/lib/mongoid/clients/thread_options.rb +0 -19
  299. data/lib/mongoid/extensions/origin/regexp_raw.rb +0 -43
  300. data/lib/mongoid/railties/document.rb +0 -12
  301. data/spec/mongoid/railties/document_spec.rb +0 -24
@@ -30,7 +30,7 @@ describe Mongoid::Persistable::Destroyable do
30
30
  it "destroys the document from the collection" do
31
31
  expect {
32
32
  Person.find(person.id)
33
- }.to raise_error
33
+ }.to raise_error(Mongoid::Errors::DocumentNotFound)
34
34
  end
35
35
 
36
36
  it "returns true" do
@@ -164,7 +164,11 @@ describe Mongoid::Persistable::Destroyable do
164
164
  end
165
165
 
166
166
  before do
167
- expect(album).to receive(:set_parent_name).and_return(false)
167
+ Album.before_destroy(:set_parent_name_fail)
168
+ end
169
+
170
+ after do
171
+ Album.reset_callbacks(:destroy)
168
172
  end
169
173
 
170
174
  it "raises an exception" do
@@ -218,6 +222,25 @@ describe Mongoid::Persistable::Destroyable do
218
222
  end
219
223
  end
220
224
 
225
+ context 'when the write concern is unacknowledged' do
226
+
227
+ before do
228
+ Person.create(title: 'miss')
229
+ end
230
+
231
+ let!(:removed) do
232
+ Person.with(write: { w: 0 }) { |klass| klass.destroy_all(title: "sir") }
233
+ end
234
+
235
+ it "removes the matching documents" do
236
+ expect(Person.where(title: 'miss').count).to eq(1)
237
+ end
238
+
239
+ it "returns 0" do
240
+ expect(removed).to eq(0)
241
+ end
242
+ end
243
+
221
244
  context 'when removing a list of embedded documents' do
222
245
 
223
246
  context 'when the embedded documents list is reversed in memory' do
@@ -66,15 +66,15 @@ describe Mongoid::Persistable::Incrementable do
66
66
  context "when providing big decimal values" do
67
67
 
68
68
  let(:five) do
69
- BigDecimal.new("5.0")
69
+ BigDecimal("5.0")
70
70
  end
71
71
 
72
72
  let(:neg_ten) do
73
- BigDecimal.new("-10.0")
73
+ BigDecimal("-10.0")
74
74
  end
75
75
 
76
76
  let(:thirty) do
77
- BigDecimal.new("30.0")
77
+ BigDecimal("30.0")
78
78
  end
79
79
 
80
80
  let!(:inc) do
@@ -151,15 +151,15 @@ describe Mongoid::Persistable::Incrementable do
151
151
  context "when providing big decimal values" do
152
152
 
153
153
  let(:five) do
154
- BigDecimal.new("5.0")
154
+ BigDecimal("5.0")
155
155
  end
156
156
 
157
157
  let(:neg_ten) do
158
- BigDecimal.new("-10.0")
158
+ BigDecimal("-10.0")
159
159
  end
160
160
 
161
161
  let(:thirty) do
162
- BigDecimal.new("30.0")
162
+ BigDecimal("30.0")
163
163
  end
164
164
 
165
165
  let!(:inc) do
@@ -190,7 +190,7 @@ describe Mongoid::Persistable::Savable do
190
190
  "name.first_name" => "Ryan"
191
191
  },
192
192
  "$push"=> {
193
- "addresses" => {'$each' => [ { "_id" => address.id, "street" => "Bond St" } ]}
193
+ "addresses" => { '$each' => [ { "_id" => address.id, "street" => "Bond St" } ] }
194
194
  }
195
195
  })
196
196
  end
@@ -295,20 +295,37 @@ describe Mongoid::Persistable::Savable do
295
295
  end
296
296
  end
297
297
 
298
- context "when the document is readonly" do
299
-
300
- let(:person) do
301
- Person.only(:title).first
302
- end
298
+ context "when the changed attribute is not writable" do
303
299
 
304
300
  before do
305
301
  Person.create(title: "sir")
306
302
  end
307
303
 
304
+ let(:person) do
305
+ Person.only(:title).first
306
+ end
307
+
308
308
  it "raises an error" do
309
309
  expect {
310
+ person.username = 'unloaded-attribute'
310
311
  person.save
311
- }.to raise_error(Mongoid::Errors::ReadonlyDocument)
312
+ }.to raise_error(Mongoid::Errors::ReadonlyAttribute)
313
+ end
314
+
315
+ context 'when the changed attribute is aliased' do
316
+
317
+ before do
318
+ Person.create(at: Time.now)
319
+ end
320
+
321
+ let(:person) do
322
+ Person.only(:at).first
323
+ end
324
+
325
+ it "saves the document" do
326
+ person.aliased_timestamp = Time.now
327
+ expect(person.save(validate: false)).to be true
328
+ end
312
329
  end
313
330
  end
314
331
 
@@ -353,12 +370,17 @@ describe Mongoid::Persistable::Savable do
353
370
  end
354
371
 
355
372
  before do
373
+ Person.index({ ssn: 1 }, { unique: true })
356
374
  Person.create_indexes
357
375
  Person.create!(ssn: "555-55-9999")
358
376
  end
359
377
 
360
- it "raises an error" do
361
- expect { person.save! }.to raise_error
378
+ after do
379
+ Person.collection.drop
380
+ end
381
+
382
+ it "raises an OperationFailure" do
383
+ expect { person.save! }.to raise_error(Mongo::Error::OperationFailure)
362
384
  end
363
385
  end
364
386
  end
@@ -374,12 +396,12 @@ describe Mongoid::Persistable::Savable do
374
396
  end
375
397
 
376
398
  it "raises an error with multiple save attempts" do
377
- expect { subject.save! }.to raise_error
378
- expect { subject.save! }.to raise_error
399
+ expect { service.save! }.to raise_error(Mongoid::Errors::Validations)
400
+ expect { service.save! }.to raise_error(Mongoid::Errors::Validations)
379
401
  end
380
402
  end
381
403
 
382
- context "when a callback returns false" do
404
+ context "when a callback aborts the callback chain" do
383
405
 
384
406
  let(:oscar) do
385
407
  Oscar.new
@@ -495,22 +517,5 @@ describe Mongoid::Persistable::Savable do
495
517
  expect(from_db.shapes.first.canvas).to eq(firefox)
496
518
  end
497
519
  end
498
-
499
- context "when the document is readonly" do
500
-
501
- let(:person) do
502
- Person.only(:title).first
503
- end
504
-
505
- before do
506
- Person.create(title: "sir")
507
- end
508
-
509
- it "raises an error" do
510
- expect {
511
- person.save!
512
- }.to raise_error(Mongoid::Errors::ReadonlyDocument)
513
- end
514
- end
515
520
  end
516
521
  end
@@ -230,6 +230,22 @@ describe Mongoid::Persistable::Settable do
230
230
  end
231
231
  end
232
232
 
233
+ context 'when the field is assigned with nil' do
234
+
235
+ before do
236
+ church.location = nil
237
+ church.set('location.neighborhood' => 'Kreuzberg')
238
+ end
239
+
240
+ it 'updates the hash while keeping existing key and values locally' do
241
+ expect(church.location).to eq({'neighborhood' => 'Kreuzberg'})
242
+ end
243
+
244
+ it 'updates the hash in the database' do
245
+ expect(church.reload.location).to eq({'neighborhood' => 'Kreuzberg'})
246
+ end
247
+ end
248
+
233
249
  context 'when the field type is String' do
234
250
 
235
251
  before do
@@ -261,48 +277,82 @@ describe Mongoid::Persistable::Settable do
261
277
  expect(church.reload.location).to eq({ 'city' => 'Berlin', 'street' => 'Yorckstr.'})
262
278
  end
263
279
  end
264
- end
265
280
 
266
- context 'when the field is a nested hash' do
281
+ context 'when the field is a nested hash' do
267
282
 
268
- context 'when a leaf value in the nested hash is updated' do
283
+ context 'when the field is reset to an empty hash' do
269
284
 
270
- let(:church) do
271
- Church.new.tap do |a|
272
- a.location = {'address' => {'city' => 'Berlin', 'street' => 'Yorckstr'}}
273
- a.name = 'Church1'
274
- a.save
285
+ before do
286
+ church.set('location' => {})
275
287
  end
276
- end
277
288
 
278
- before do
279
- church.set('location.address.city' => 'Munich')
280
- end
289
+ it 'updates the field locally' do
290
+ expect(church.location).to eq({})
291
+ end
281
292
 
282
- it 'does not reset the nested hash' do
283
- expect(church.name).to eq('Church1')
284
- expect(church.location).to eql({'address' => {'city' => 'Munich', 'street' => 'Yorckstr'}})
293
+ it 'updates the field in the database' do
294
+ expect(church.reload.location).to eq({})
295
+ end
285
296
  end
286
- end
287
297
 
298
+ context 'when a leaf value in the nested hash is updated' do
288
299
 
289
- context 'when the nested hash is many levels deep' do
300
+ let(:church) do
301
+ Church.new.tap do |a|
302
+ a.location = {'address' => {'city' => 'Berlin', 'street' => 'Yorckstr'}}
303
+ a.name = 'Church1'
304
+ a.save
305
+ end
306
+ end
290
307
 
291
- let(:church) do
292
- Church.new.tap do |a|
293
- a.location = {'address' => {'state' => {'address' => {'city' => 'Berlin', 'street' => 'Yorckstr'}}}}
294
- a.name = 'Church1'
295
- a.save
308
+ before do
309
+ church.set('location.address.city' => 'Munich')
310
+ end
311
+
312
+ it 'does not reset the nested hash' do
313
+ expect(church.name).to eq('Church1')
314
+ expect(church.location).to eql({'address' => {'city' => 'Munich', 'street' => 'Yorckstr'}})
296
315
  end
297
316
  end
298
317
 
299
- before do
300
- church.set('location.address.state.address.city' => 'Munich')
318
+ context 'when a leaf value in the nested hash is updated to a number' do
319
+
320
+ let(:church) do
321
+ Church.new.tap do |a|
322
+ a.location = {'address' => {'city' => 'Berlin', 'street' => 'Yorckstr'}}
323
+ a.name = 'Church1'
324
+ a.save
325
+ end
326
+ end
327
+
328
+ before do
329
+ church.set('location.address.city' => 12345)
330
+ end
331
+
332
+ it 'updates the nested value to the correct value' do
333
+ expect(church.name).to eq('Church1')
334
+ expect(church.location).to eql({'address' => {'city' => 12345, 'street' => 'Yorckstr'}})
335
+ end
301
336
  end
302
337
 
303
- it 'does not reset the nested hash' do
304
- expect(church.name).to eq('Church1')
305
- expect(church.location).to eql({'address' => {'state' => {'address' => {'city' => 'Munich', 'street' => 'Yorckstr'}}}})
338
+ context 'when the nested hash is many levels deep' do
339
+
340
+ let(:church) do
341
+ Church.new.tap do |a|
342
+ a.location = {'address' => {'state' => {'address' => {'city' => 'Berlin', 'street' => 'Yorckstr'}}}}
343
+ a.name = 'Church1'
344
+ a.save
345
+ end
346
+ end
347
+
348
+ before do
349
+ church.set('location.address.state.address.city' => 'Munich')
350
+ end
351
+
352
+ it 'does not reset the nested hash' do
353
+ expect(church.name).to eq('Church1')
354
+ expect(church.location).to eql({'address' => {'state' => {'address' => {'city' => 'Munich', 'street' => 'Yorckstr'}}}})
355
+ end
306
356
  end
307
357
  end
308
358
  end
@@ -112,6 +112,24 @@ describe Mongoid::Persistable::Updatable do
112
112
  end
113
113
  end
114
114
 
115
+ context "when dynamic attributes are not enabled" do
116
+
117
+ it "raises exception for an unknown attribute " do
118
+ account = Account.create
119
+
120
+ expect {
121
+ account.update_attribute(:somethingnew, "somethingnew")
122
+ }.to raise_error(Mongoid::Errors::UnknownAttribute)
123
+ end
124
+
125
+ it "will update value of aliased field" do
126
+ person = Person.create
127
+ person.update_attribute(:t, "test_value")
128
+ expect(person.reload.t).to eq "test_value"
129
+ expect(person.test).to eq "test_value"
130
+ end
131
+ end
132
+
115
133
  context "when provided a symbol attribute name" do
116
134
 
117
135
  let(:post) do
@@ -168,7 +186,7 @@ describe Mongoid::Persistable::Updatable do
168
186
  it "raises an error" do
169
187
  expect {
170
188
  post.update_attribute(:title, "something")
171
- }.to raise_error
189
+ }.to raise_error(RuntimeError)
172
190
  end
173
191
  end
174
192
  end
@@ -269,6 +287,167 @@ describe Mongoid::Persistable::Updatable do
269
287
  expect(from_db.reload.name).to eq("home")
270
288
  end
271
289
  end
290
+
291
+ context 'when the field is read-only' do
292
+
293
+ before do
294
+ Person.attr_readonly :species
295
+ end
296
+
297
+ after do
298
+ Person.readonly_attributes.reject! { |a| a.to_s == 'species' }
299
+ end
300
+
301
+ let(:person) do
302
+ Person.create(species: :human)
303
+ end
304
+
305
+ it 'raises an error when trying to set the attribute' do
306
+ expect {
307
+ person.update_attribute(:species, :reptile)
308
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
309
+ end
310
+
311
+ context 'when referring to the attribute with a string' do
312
+
313
+ it 'raises an error when trying to set the attribute' do
314
+ expect {
315
+ person.update_attribute('species', :reptile)
316
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
317
+ end
318
+ end
319
+
320
+ context 'when the field is aliased' do
321
+
322
+ before do
323
+ Person.attr_readonly :at
324
+ end
325
+
326
+ after do
327
+ Person.readonly_attributes.reject! { |a| a.to_s == 'at' }
328
+ end
329
+
330
+ it 'raises an error when trying to set the attribute using the db name' do
331
+ expect {
332
+ person.update_attribute(:at, Time.now)
333
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
334
+ end
335
+
336
+ it 'raises an error when trying to set the attribute using the aliased name' do
337
+ expect {
338
+ person.update_attribute(:aliased_timestamp, Time.now)
339
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
340
+ end
341
+ end
342
+ end
343
+
344
+ context 'when the field is loaded explicitly' do
345
+
346
+ before do
347
+ Person.create(title: 'Captain')
348
+ end
349
+
350
+ context 'when the loaded attribute is updated' do
351
+
352
+ let(:person) do
353
+ Person.only(:title).first.tap do |_person|
354
+ _person.update_attribute(:title, 'Esteemed')
355
+ end
356
+ end
357
+
358
+ it 'allows the field to be updated' do
359
+ expect(person.title).to eq('Esteemed')
360
+ end
361
+
362
+ it 'persists the updated field' do
363
+ expect(person.reload.title).to eq('Esteemed')
364
+ end
365
+ end
366
+
367
+ context 'when the an attribute other than the loaded one is updated' do
368
+
369
+ let(:person) do
370
+ Person.only(:title).first
371
+ end
372
+
373
+ it 'does not allow the field to be updated' do
374
+ expect {
375
+ person.update_attribute(:age, 20)
376
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
377
+ end
378
+
379
+ it 'does not persist the change' do
380
+ expect(person.reload.age).to eq(100)
381
+ end
382
+
383
+ context 'when referring to the attribute with a string' do
384
+
385
+ it 'does not allow the field to be updated' do
386
+ expect {
387
+ person.update_attribute('age', 20)
388
+ }.to raise_exception(Mongoid::Errors::ReadonlyAttribute)
389
+ end
390
+
391
+ it 'does not persist the change' do
392
+ expect(person.reload.age).to eq(100)
393
+ end
394
+ end
395
+ end
396
+ end
397
+
398
+ context 'when fields are explicitly not loaded' do
399
+
400
+ before do
401
+ Person.create(title: 'Captain')
402
+ end
403
+
404
+ context 'when the loaded attribute is updated' do
405
+
406
+ let(:person) do
407
+ Person.without(:age).first.tap do |_person|
408
+ _person.update_attribute(:title, 'Esteemed')
409
+ end
410
+ end
411
+
412
+ it 'allows the field to be updated' do
413
+ expect(person.title).to eq('Esteemed')
414
+ end
415
+
416
+ it 'persists the updated field' do
417
+ expect(person.reload.title).to eq('Esteemed')
418
+ end
419
+ end
420
+
421
+ context 'when the non-loaded attribute is updated' do
422
+
423
+ let(:person) do
424
+ Person.without(:title).first
425
+ end
426
+
427
+ it 'does not allow the field to be updated' do
428
+ expect {
429
+ person.update_attribute(:title, 'Esteemed')
430
+ }.to raise_exception(ActiveModel::MissingAttributeError)
431
+ end
432
+
433
+ it 'does not persist the change' do
434
+ expect(person.reload.title).to eq('Captain')
435
+ end
436
+
437
+ context 'when referring to the attribute with a string' do
438
+
439
+ it 'does not allow the field to be updated' do
440
+ expect {
441
+ person.update_attribute('title', 'Esteemed')
442
+ }.to raise_exception(ActiveModel::MissingAttributeError)
443
+ end
444
+
445
+ it 'does not persist the change' do
446
+ expect(person.reload.title).to eq('Captain')
447
+ end
448
+ end
449
+ end
450
+ end
272
451
  end
273
452
 
274
453
  [:update_attributes, :update].each do |method|
@@ -360,7 +539,7 @@ describe Mongoid::Persistable::Updatable do
360
539
  it "raises an error" do
361
540
  expect {
362
541
  person.send(method, title: "something")
363
- }.to raise_error
542
+ }.to raise_error(RuntimeError)
364
543
  end
365
544
  end
366
545
 
@@ -543,7 +722,7 @@ describe Mongoid::Persistable::Updatable do
543
722
 
544
723
  describe "#update!" do
545
724
 
546
- context "when a callback returns false" do
725
+ context "when a callback aborts the callback chain" do
547
726
 
548
727
  let(:oscar) do
549
728
  Oscar.new
@@ -52,16 +52,16 @@ describe Mongoid::Persistable do
52
52
  context "when not chaining the operations" do
53
53
 
54
54
  let(:operations) do
55
- {
55
+ [{
56
56
  "$inc" => { "member_count" => 10 },
57
57
  "$bit" => { "likes" => { :and => 13 }},
58
58
  "$set" => { "name" => "Placebo" },
59
- "$unset" => { "origin" => true }
60
- }
59
+ "$unset" => { "origin" => true }},
60
+ { session: nil }]
61
61
  end
62
62
 
63
63
  before do
64
- expect_any_instance_of(Mongo::Collection::View).to receive(:update_one).with(operations).and_call_original
64
+ expect_any_instance_of(Mongo::Collection::View).to receive(:update_one).with(*operations).and_call_original
65
65
  end
66
66
 
67
67
  let!(:update) do
@@ -79,16 +79,16 @@ describe Mongoid::Persistable do
79
79
  context "when chaining the operations" do
80
80
 
81
81
  let(:operations) do
82
- {
82
+ [{
83
83
  "$inc" => { "member_count" => 10 },
84
84
  "$bit" => { "likes" => { :and => 13 }},
85
85
  "$set" => { "name" => "Placebo" },
86
- "$unset" => { "origin" => true }
87
- }
86
+ "$unset" => { "origin" => true }},
87
+ { :session => nil } ]
88
88
  end
89
89
 
90
90
  before do
91
- expect_any_instance_of(Mongo::Collection::View).to receive(:update_one).with(operations).and_call_original
91
+ expect_any_instance_of(Mongo::Collection::View).to receive(:update_one).with(*operations).and_call_original
92
92
  end
93
93
 
94
94
  let!(:update) do
@@ -107,16 +107,16 @@ describe Mongoid::Persistable do
107
107
  context "when given multiple operations of the same type" do
108
108
 
109
109
  let(:operations) do
110
- {
110
+ [{
111
111
  "$inc" => { "member_count" => 10, "other_count" => 10 },
112
112
  "$bit" => { "likes" => { :and => 13 }},
113
113
  "$set" => { "name" => "Placebo" },
114
- "$unset" => { "origin" => true }
115
- }
114
+ "$unset" => { "origin" => true }},
115
+ { session: nil }]
116
116
  end
117
117
 
118
118
  before do
119
- expect_any_instance_of(Mongo::Collection::View).to receive(:update_one).with(operations).and_call_original
119
+ expect_any_instance_of(Mongo::Collection::View).to receive(:update_one).with(*operations).and_call_original
120
120
  end
121
121
 
122
122
  let!(:update) do
@@ -144,16 +144,16 @@ describe Mongoid::Persistable do
144
144
  context "when expecting the document to be yielded" do
145
145
 
146
146
  let(:operations) do
147
- {
147
+ [{
148
148
  "$inc" => { "member_count" => 10 },
149
149
  "$bit" => { "likes" => { :and => 13 }},
150
150
  "$set" => { "name" => "Placebo" },
151
- "$unset" => { "origin" => true }
152
- }
151
+ "$unset" => { "origin" => true }},
152
+ { session: nil }]
153
153
  end
154
154
 
155
155
  before do
156
- expect_any_instance_of(Mongo::Collection::View).to receive(:update_one).with(operations).and_call_original
156
+ expect_any_instance_of(Mongo::Collection::View).to receive(:update_one).with(*operations).and_call_original
157
157
  end
158
158
 
159
159
  let!(:update) do