reform 2.3.0.rc1 → 2.3.0.rc2

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +30 -0
  4. data/.rubocop_todo.yml +460 -0
  5. data/.travis.yml +26 -11
  6. data/CHANGES.md +25 -2
  7. data/Gemfile +6 -3
  8. data/ISSUE_TEMPLATE.md +1 -1
  9. data/README.md +2 -4
  10. data/Rakefile +18 -9
  11. data/lib/reform/contract.rb +7 -7
  12. data/lib/reform/contract/custom_error.rb +41 -0
  13. data/lib/reform/contract/validate.rb +9 -5
  14. data/lib/reform/errors.rb +27 -15
  15. data/lib/reform/form.rb +22 -11
  16. data/lib/reform/form/call.rb +1 -1
  17. data/lib/reform/form/composition.rb +2 -2
  18. data/lib/reform/form/dry.rb +10 -86
  19. data/lib/reform/form/dry/input_hash.rb +37 -0
  20. data/lib/reform/form/dry/new_api.rb +58 -0
  21. data/lib/reform/form/dry/old_api.rb +61 -0
  22. data/lib/reform/form/populator.rb +9 -11
  23. data/lib/reform/form/prepopulate.rb +3 -2
  24. data/lib/reform/form/validate.rb +19 -12
  25. data/lib/reform/result.rb +36 -9
  26. data/lib/reform/validation.rb +10 -8
  27. data/lib/reform/validation/groups.rb +2 -3
  28. data/lib/reform/version.rb +1 -1
  29. data/reform.gemspec +10 -9
  30. data/test/benchmarking.rb +10 -11
  31. data/test/call_new_api.rb +23 -0
  32. data/test/{call_test.rb → call_old_api.rb} +3 -3
  33. data/test/changed_test.rb +7 -7
  34. data/test/coercion_test.rb +50 -18
  35. data/test/composition_new_api.rb +186 -0
  36. data/test/{composition_test.rb → composition_old_api.rb} +23 -26
  37. data/test/contract/custom_error_test.rb +55 -0
  38. data/test/contract_new_api.rb +77 -0
  39. data/test/{contract_test.rb → contract_old_api.rb} +8 -8
  40. data/test/default_test.rb +1 -1
  41. data/test/deserialize_test.rb +8 -11
  42. data/test/errors_new_api.rb +225 -0
  43. data/test/errors_old_api.rb +230 -0
  44. data/test/feature_test.rb +7 -9
  45. data/test/fixtures/dry_error_messages.yml +5 -2
  46. data/test/fixtures/dry_new_api_error_messages.yml +104 -0
  47. data/test/form_new_api.rb +57 -0
  48. data/test/{form_test.rb → form_old_api.rb} +2 -2
  49. data/test/form_option_new_api.rb +24 -0
  50. data/test/{form_option_test.rb → form_option_old_api.rb} +1 -1
  51. data/test/from_test.rb +8 -12
  52. data/test/inherit_new_api.rb +105 -0
  53. data/test/{inherit_test.rb → inherit_old_api.rb} +10 -17
  54. data/test/module_new_api.rb +137 -0
  55. data/test/{module_test.rb → module_old_api.rb} +19 -15
  56. data/test/parse_option_test.rb +5 -5
  57. data/test/parse_pipeline_test.rb +2 -2
  58. data/test/populate_new_api.rb +304 -0
  59. data/test/{populate_test.rb → populate_old_api.rb} +28 -34
  60. data/test/populator_skip_test.rb +1 -2
  61. data/test/prepopulator_test.rb +5 -6
  62. data/test/read_only_test.rb +12 -1
  63. data/test/readable_test.rb +5 -5
  64. data/test/reform_new_api.rb +204 -0
  65. data/test/{reform_test.rb → reform_old_api.rb} +17 -23
  66. data/test/save_new_api.rb +101 -0
  67. data/test/{save_test.rb → save_old_api.rb} +10 -13
  68. data/test/setup_test.rb +6 -6
  69. data/test/{skip_if_test.rb → skip_if_new_api.rb} +20 -9
  70. data/test/skip_if_old_api.rb +92 -0
  71. data/test/skip_setter_and_getter_test.rb +2 -3
  72. data/test/test_helper.rb +13 -5
  73. data/test/validate_new_api.rb +408 -0
  74. data/test/{validate_test.rb → validate_old_api.rb} +43 -53
  75. data/test/validation/dry_validation_new_api.rb +826 -0
  76. data/test/validation/{dry_validation_test.rb → dry_validation_old_api.rb} +223 -116
  77. data/test/validation/result_test.rb +20 -22
  78. data/test/validation_library_provided_test.rb +3 -3
  79. data/test/virtual_test.rb +46 -6
  80. data/test/writeable_test.rb +7 -7
  81. metadata +101 -51
  82. data/test/errors_test.rb +0 -180
  83. data/test/readonly_test.rb +0 -14
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  require "test_helper"
3
2
  require "reform/form/dry"
4
3
  require "reform/form/coercion"
@@ -41,74 +40,73 @@ class DryValidationErrorsAPITest < Minitest::Spec
41
40
 
42
41
  validation do
43
42
  configure do
44
- config.messages_file = 'test/fixtures/dry_error_messages.yml'
43
+ config.messages_file = "test/fixtures/dry_error_messages.yml"
45
44
  end
46
45
 
47
46
  required(:title).filled
48
47
  end
49
48
  end
50
-
51
49
  end
52
50
 
53
- let (:form) { AlbumForm.new(Album.new(nil, Artist.new(nil, Label.new), [Song.new(nil), Song.new(nil)])) }
51
+ let(:form) { AlbumForm.new(Album.new(nil, Artist.new(nil, Label.new), [Song.new(nil), Song.new(nil)])) }
54
52
 
55
53
  it "everything wrong" do
56
- result = form.({ title: nil, artist: { email: "" }, songs: [{ title: "Clams have feelings too" }, { title: "" }] })
54
+ result = form.(title: nil, artist: {email: ""}, songs: [{title: "Clams have feelings too"}, {title: ""}])
57
55
 
58
56
  result.success?.must_equal false
59
57
 
60
58
  # errors.messages
61
- form.errors.messages.must_equal({:title=>["must be filled", "size cannot be less than 2"], :"artist.email"=>["must be filled"], :"artist.label.location"=>["must be filled"], :"songs.title"=>["must be filled"]})
62
- form.artist.errors.messages.must_equal({:email=>["must be filled"], :"label.location"=>["must be filled"]})
63
- form.artist.label.errors.messages.must_equal({:location=>["must be filled"]})
59
+ form.errors.messages.must_equal(title: ["must be filled", "size cannot be less than 2"], "artist.email": ["must be filled"], "artist.label.location": ["must be filled"], "songs.title": ["must be filled"])
60
+ form.artist.errors.messages.must_equal(email: ["must be filled"], "label.location": ["must be filled"])
61
+ form.artist.label.errors.messages.must_equal(location: ["must be filled"])
64
62
  form.songs[0].errors.messages.must_equal({})
65
- form.songs[1].errors.messages.must_equal({:title=>["must be filled"]})
63
+ form.songs[1].errors.messages.must_equal(title: ["must be filled"])
66
64
 
67
65
  # #errors[]
68
- form.errors[:nonsense].must_be_nil
66
+ form.errors[:nonsense].must_equal []
69
67
  form.errors[:title].must_equal ["must be filled", "size cannot be less than 2"]
70
68
  form.artist.errors[:email].must_equal ["must be filled"]
71
69
  form.artist.label.errors[:location].must_equal ["must be filled"]
72
- form.songs[0].errors[:title].must_be_nil
70
+ form.songs[0].errors[:title].must_equal []
73
71
  form.songs[1].errors[:title].must_equal ["must be filled"]
74
72
 
75
73
  # #to_result
76
- form.to_result.errors.must_equal({:title=>["must be filled"]})
77
- form.to_result.messages.must_equal({:title=>["must be filled", "size cannot be less than 2"]})
78
- form.to_result.hints.must_equal({:title=>["size cannot be less than 2"]})
79
- form.artist.to_result.errors.must_equal({:email=>["must be filled"]})
80
- form.artist.to_result.messages.must_equal({:email=>["must be filled"]})
81
- form.artist.to_result.hints.must_equal({:email=>[]})
82
- form.artist.label.to_result.errors.must_equal({:location=>["must be filled"]})
83
- form.artist.label.to_result.messages.must_equal({:location=>["must be filled"]})
84
- form.artist.label.to_result.hints.must_equal({:location=>[]})
74
+ form.to_result.errors.must_equal(title: ["must be filled"])
75
+ form.to_result.messages.must_equal(title: ["must be filled", "size cannot be less than 2"])
76
+ form.to_result.hints.must_equal(title: ["size cannot be less than 2"])
77
+ form.artist.to_result.errors.must_equal(email: ["must be filled"])
78
+ form.artist.to_result.messages.must_equal(email: ["must be filled"])
79
+ form.artist.to_result.hints.must_equal(email: [])
80
+ form.artist.label.to_result.errors.must_equal(location: ["must be filled"])
81
+ form.artist.label.to_result.messages.must_equal(location: ["must be filled"])
82
+ form.artist.label.to_result.hints.must_equal(location: [])
85
83
  form.songs[0].to_result.errors.must_equal({})
86
84
  form.songs[0].to_result.messages.must_equal({})
87
85
  form.songs[0].to_result.hints.must_equal({})
88
- form.songs[1].to_result.errors.must_equal({:title=>["must be filled"]})
89
- form.songs[1].to_result.messages.must_equal({:title=>["must be filled"]})
90
- form.songs[1].to_result.hints.must_equal({:title=>[]})
91
- form.songs[1].to_result.errors(locale: :de).must_equal({:title=>["muss abgefüllt sein"]})
92
- form.songs[1].to_result.messages(locale: :de).must_equal({:title=>["muss abgefüllt sein"]})
93
- form.songs[1].to_result.hints(locale: :de).must_equal({:title=>[]})
86
+ form.songs[1].to_result.errors.must_equal(title: ["must be filled"])
87
+ form.songs[1].to_result.messages.must_equal(title: ["must be filled"])
88
+ form.songs[1].to_result.hints.must_equal(title: [])
89
+ form.songs[1].to_result.errors(locale: :de).must_equal(title: ["muss abgefüllt sein"])
90
+ form.songs[1].to_result.messages(locale: :de).must_equal(title: ["muss abgefüllt sein"])
91
+ form.songs[1].to_result.hints(locale: :de).must_equal(title: [])
94
92
  end
95
93
 
96
94
  it "only nested property is invalid." do
97
- result = form.({ title: "Black Star", artist: { email: "" } })
95
+ result = form.(title: "Black Star", artist: {email: ""})
98
96
 
99
97
  result.success?.must_equal false
100
98
 
101
99
  # errors.messages
102
- form.errors.messages.must_equal({:"artist.email"=>["must be filled"], :"artist.label.location"=>["must be filled"], :"songs.title"=>["must be filled"]})
103
- form.artist.errors.messages.must_equal({:email=>["must be filled"], :"label.location"=>["must be filled"]})
104
- form.artist.label.errors.messages.must_equal({:location=>["must be filled"]})
100
+ form.errors.messages.must_equal("artist.email": ["must be filled"], "artist.label.location": ["must be filled"], "songs.title": ["must be filled"])
101
+ form.artist.errors.messages.must_equal(email: ["must be filled"], "label.location": ["must be filled"])
102
+ form.artist.label.errors.messages.must_equal(location: ["must be filled"])
105
103
  end
106
104
 
107
105
  it "nested collection invalid" do
108
- result = form.({ title: "Black Star", artist: { email: "uhm", label: { location: "Hannover" } }, songs: [ { title: "" } ] })
106
+ result = form.(title: "Black Star", artist: {email: "uhm", label: {location: "Hannover"}}, songs: [{title: ""}])
109
107
 
110
108
  result.success?.must_equal false
111
- form.errors.messages.must_equal({:"songs.title"=>["must be filled"]})
109
+ form.errors.messages.must_equal("songs.title": ["must be filled"])
112
110
  end
113
111
 
114
112
  #---
@@ -129,11 +127,11 @@ class DryValidationErrorsAPITest < Minitest::Spec
129
127
 
130
128
  it do
131
129
  form = CollectionExternalValidationsForm.new(Album.new(nil, nil, [Song.new, Song.new]))
132
- form.validate(songs: [ { title: "Liar"}, { title: ""} ])
130
+ form.validate(songs: [{title: "Liar"}, {title: ""}])
133
131
 
134
- form.errors.messages.must_equal({:"songs.title"=>["must be filled"]})
132
+ form.errors.messages.must_equal("songs.title": ["must be filled"])
135
133
  form.songs[0].errors.messages.must_equal({})
136
- form.songs[1].errors.messages.must_equal({:title=>["must be filled"]})
134
+ form.songs[1].errors.messages.must_equal(title: ["must be filled"])
137
135
  end
138
136
  end
139
137
 
@@ -153,7 +151,7 @@ class DryValidationExplicitSchemaTest < Minitest::Spec
153
151
  validation schema: SessionSchema
154
152
  end
155
153
 
156
- let (:form) { SessionForm.new(Session.new) }
154
+ let(:form) { SessionForm.new(Session.new) }
157
155
 
158
156
  # valid.
159
157
  it do
@@ -177,8 +175,8 @@ class DryValidationDefaultGroupTest < Minitest::Spec
177
175
  property :email
178
176
  property :password
179
177
  property :confirm_password
180
- property :starts_at, type: Types::Form::DateTime
181
- property :active, type: Types::Form::Bool
178
+ property :starts_at, type: DRY_TYPES_CONSTANT::DateTime
179
+ property :active, type: DRY_TYPES_CONSTANT::Bool
182
180
  property :color
183
181
 
184
182
  validation do
@@ -192,7 +190,7 @@ class DryValidationDefaultGroupTest < Minitest::Spec
192
190
  required(:confirm_password).filled
193
191
  end
194
192
 
195
- validation name: :dynamic_args, with: { form: true } do
193
+ validation name: :dynamic_args, with: {form: true} do
196
194
  configure do
197
195
  def colors
198
196
  form.colors
@@ -206,7 +204,7 @@ class DryValidationDefaultGroupTest < Minitest::Spec
206
204
  end
207
205
  end
208
206
 
209
- let (:form) { SessionForm.new(Session.new) }
207
+ let(:form) { SessionForm.new(Session.new) }
210
208
 
211
209
  # valid.
212
210
  it do
@@ -214,16 +212,16 @@ class DryValidationDefaultGroupTest < Minitest::Spec
214
212
  email: "yep",
215
213
  starts_at: "01/01/2000 - 11:00",
216
214
  active: "true",
217
- confirm_password: 'pA55w0rd').must_equal true
215
+ confirm_password: "pA55w0rd").must_equal true
218
216
  form.errors.messages.inspect.must_equal "{}"
219
217
  end
220
218
 
221
219
  it "invalid" do
222
220
  form.validate(username: "Helloween",
223
221
  email: "yep",
224
- active: 'hello',
222
+ active: "hello",
225
223
  starts_at: "01/01/2000 - 11:00",
226
- color: 'purple').must_equal false
224
+ color: "purple").must_equal false
227
225
  form.errors.messages.inspect.must_equal "{:active=>[\"must be boolean\"], :confirm_password=>[\"must be filled\"], :color=>[\"must be one of: red orange green\"]}"
228
226
  end
229
227
  end
@@ -231,10 +229,9 @@ end
231
229
  class ValidationGroupsTest < MiniTest::Spec
232
230
  describe "basic validations" do
233
231
  Session = Struct.new(:username, :email, :password, :confirm_password, :special_class)
234
- SomeClass= Struct.new(:id)
232
+ SomeClass = Struct.new(:id)
235
233
 
236
234
  class SessionForm < TestForm
237
-
238
235
  property :username
239
236
  property :email
240
237
  property :password
@@ -251,7 +248,7 @@ class ValidationGroupsTest < MiniTest::Spec
251
248
  required(:email).filled(min_size?: 3)
252
249
  end
253
250
 
254
- validation name: :nested, if: :default do
251
+ validation name: :password, if: :email do
255
252
  required(:password).filled(min_size?: 2)
256
253
  end
257
254
 
@@ -260,35 +257,35 @@ class ValidationGroupsTest < MiniTest::Spec
260
257
  end
261
258
  end
262
259
 
263
- let (:form) { SessionForm.new(Session.new) }
260
+ let(:form) { SessionForm.new(Session.new) }
264
261
 
265
262
  # valid.
266
263
  it do
267
- form.validate({ username: "Helloween",
268
- special_class: SomeClass.new(id: 15),
269
- email: "yep",
270
- password: "99",
271
- confirm_password: "99" }).must_equal true
264
+ form.validate(username: "Helloween",
265
+ special_class: SomeClass.new(id: 15),
266
+ email: "yep",
267
+ password: "99",
268
+ confirm_password: "99").must_equal true
272
269
  form.errors.messages.inspect.must_equal "{}"
273
270
  end
274
271
 
275
272
  # invalid.
276
273
  it do
277
274
  form.validate({}).must_equal false
278
- form.errors.messages.must_equal({:username=>["must be filled"], :email=>["must be filled"], :special_class=>["must be filled", "must be ValidationGroupsTest::SomeClass"]})
275
+ form.errors.messages.must_equal({username: ["must be filled"], email: ["must be filled"], special_class: ["must be filled", "must be ValidationGroupsTest::SomeClass"]})
279
276
  end
280
277
 
281
278
  # partially invalid.
282
279
  # 2nd group fails.
283
280
  it do
284
- form.validate(username: "Helloween", email: "yo", confirm_password:"9", special_class: SomeClass.new(id: 15)).must_equal false
285
- form.errors.messages.inspect.must_equal "{:email=>[\"size cannot be less than 3\"], :confirm_password=>[\"size cannot be less than 2\"], :password=>[\"must be filled\", \"size cannot be less than 2\"]}"
281
+ form.validate(username: "Helloween", email: "yo", confirm_password: "9", special_class: SomeClass.new(id: 15)).must_equal false
282
+ form.errors.messages.inspect.must_equal "{:email=>[\"size cannot be less than 3\"], :confirm_password=>[\"size cannot be less than 2\"]}"
286
283
  end
287
284
  # 3rd group fails.
288
285
  it do
289
- form.validate(username: "Helloween", email: "yo!", confirm_password:"9", special_class: SomeClass.new(id: 15)).must_equal false
286
+ form.validate(username: "Helloween", email: "yo!", confirm_password: "9", special_class: SomeClass.new(id: 15)).must_equal false
290
287
  form.errors.messages.inspect
291
- .must_equal "{:confirm_password=>[\"size cannot be less than 2\"], :password=>[\"must be filled\", \"size cannot be less than 2\"]}"
288
+ .must_equal "{:confirm_password=>[\"size cannot be less than 2\"], :password=>[\"must be filled\", \"size cannot be less than 2\"]}"
292
289
  end
293
290
  # 4th group with after: fails.
294
291
  it do
@@ -303,7 +300,7 @@ class ValidationGroupsTest < MiniTest::Spec
303
300
  class SessionForm < TestForm
304
301
  property :username
305
302
 
306
- validation name: :default, with: { user: OpenStruct.new(name: "Nick") } do
303
+ validation name: :default, with: {user: OpenStruct.new(name: "Nick")} do
307
304
  configure do
308
305
  def users_name
309
306
  user.name
@@ -313,17 +310,17 @@ class ValidationGroupsTest < MiniTest::Spec
313
310
  end
314
311
  end
315
312
 
316
- let (:form) { SessionForm.new(Session.new) }
313
+ let(:form) { SessionForm.new(Session.new) }
317
314
 
318
315
  # valid.
319
316
  it do
320
- form.validate({ username: "Nick" }).must_equal true
317
+ form.validate(username: "Nick").must_equal true
321
318
  form.errors.messages.inspect.must_equal "{}"
322
319
  end
323
320
 
324
321
  # invalid.
325
322
  it do
326
- form.validate({ username: 'Fred'}).must_equal false
323
+ form.validate(username: "Fred").must_equal false
327
324
  form.errors.messages.inspect.must_equal "{:username=>[\"must be equal to Nick\"]}"
328
325
  end
329
326
  end
@@ -336,15 +333,14 @@ class ValidationGroupsTest < MiniTest::Spec
336
333
 
337
334
  MySchema = Dry::Validation.Schema do
338
335
  configure do
339
- config.messages_file = 'test/fixtures/dry_error_messages.yml'
336
+ config.messages_file = "test/fixtures/dry_error_messages.yml"
340
337
 
341
338
  def good_musical_taste?(val)
342
339
  val.is_a? String
343
340
  end
344
-
345
341
  end
346
342
 
347
- required(:password).filled(:min_size? => 6)
343
+ required(:password).filled(min_size?: 6)
348
344
  end
349
345
 
350
346
  class Session2Form < TestForm
@@ -358,22 +354,22 @@ class ValidationGroupsTest < MiniTest::Spec
358
354
  end
359
355
  end
360
356
 
361
- let (:form) { Session2Form.new(Session2.new) }
357
+ let(:form) { Session2Form.new(Session2.new) }
362
358
 
363
359
  # valid.
364
360
  it do
365
- form.validate({ username: "Helloween", email: "yep", password: "extrasafe" }).must_equal true
361
+ form.validate(username: "Helloween", email: "yep", password: "extrasafe").must_equal true
366
362
  form.errors.messages.inspect.must_equal "{}"
367
363
  end
368
364
 
369
365
  # invalid.
370
366
  it do
371
367
  form.validate({}).must_equal false
372
- form.errors.messages.must_equal({:password=>["must be filled", "size cannot be less than 6"], :username=>["must be filled"], :email=>["must be filled", "you're a bad person"]})
368
+ form.errors.messages.must_equal(password: ["must be filled", "size cannot be less than 6"], username: ["must be filled"], email: ["must be filled", "you're a bad person"])
373
369
  end
374
370
 
375
371
  it do
376
- form.validate({email: 1}).must_equal false
372
+ form.validate(email: 1).must_equal false
377
373
  form.errors.messages.inspect.must_equal "{:password=>[\"must be filled\", \"size cannot be less than 6\"], :username=>[\"must be filled\"], :email=>[\"you're a bad person\"]}"
378
374
  end
379
375
  end
@@ -416,7 +412,7 @@ class ValidationGroupsTest < MiniTest::Spec
416
412
  # message need to be defined on fixtures/dry_error_messages
417
413
  # d-v expects you to define your custome messages on the .yml file
418
414
  def good_musical_taste?(value)
419
- value != 'Nickelback'
415
+ value != "Nickelback"
420
416
  end
421
417
  end
422
418
 
@@ -434,46 +430,45 @@ class ValidationGroupsTest < MiniTest::Spec
434
430
  required(:name).filled
435
431
  end
436
432
  end
437
-
438
433
  end
439
434
  end
440
435
 
441
- let (:album) do
436
+ let(:album) do
442
437
  OpenStruct.new(
443
- :hit => OpenStruct.new,
444
- :songs => [OpenStruct.new, OpenStruct.new],
445
- :band => Struct.new(:name, :label).new("", OpenStruct.new),
446
- :producers => [OpenStruct.new, OpenStruct.new, OpenStruct.new],
438
+ hit: OpenStruct.new,
439
+ songs: [OpenStruct.new, OpenStruct.new],
440
+ band: Struct.new(:name, :label).new("", OpenStruct.new),
441
+ producers: [OpenStruct.new, OpenStruct.new, OpenStruct.new],
447
442
  )
448
443
  end
449
444
 
450
- let (:form) { AlbumForm.new(album) }
445
+ let(:form) { AlbumForm.new(album) }
451
446
 
452
447
  it "maps errors to form objects correctly" do
453
448
  result = form.validate(
454
449
  "title" => "Nickelback",
455
- "songs" => [ {"title" => ""}, {"title" => ""} ],
450
+ "songs" => [{"title" => ""}, {"title" => ""}],
456
451
  "band" => {"size" => "", "label" => {"location" => ""}},
457
- "producers" => [{"name" => ''}, {"name" => 'something lovely'}]
452
+ "producers" => [{"name" => ""}, {"name" => "something lovely"}]
458
453
  )
459
454
 
460
455
  result.must_equal false
461
456
  # from nested validation
462
- form.errors.messages.must_equal({:title=>["you're a bad person"], :"hit.title"=>["must be filled"], :"songs.title"=>["must be filled"], :"producers.name"=>["must be filled"], :"band.name"=>["must be filled"], :"band.label.location"=>["must be filled"]})
457
+ form.errors.messages.must_equal(title: ["you're a bad person"], "hit.title": ["must be filled"], "songs.title": ["must be filled"], "producers.name": ["must be filled"], "band.name": ["must be filled"], "band.label.location": ["must be filled"])
463
458
 
464
459
  # songs have their own validation.
465
- form.songs[0].errors.messages.must_equal({:title=>["must be filled"]})
460
+ form.songs[0].errors.messages.must_equal(title: ["must be filled"])
466
461
  # hit got its own validation group.
467
- form.hit.errors.messages.must_equal({:title=>["must be filled"]})
462
+ form.hit.errors.messages.must_equal(title: ["must be filled"])
468
463
 
469
- form.band.label.errors.messages.must_equal({:location=>["must be filled"]})
470
- form.band.errors.messages.must_equal({:name=>["must be filled"], :"label.location"=>["must be filled"]})
471
- form.producers[0].errors.messages.must_equal({:name=>["must be filled"]})
464
+ form.band.label.errors.messages.must_equal(location: ["must be filled"])
465
+ form.band.errors.messages.must_equal(name: ["must be filled"], "label.location": ["must be filled"])
466
+ form.producers[0].errors.messages.must_equal(name: ["must be filled"])
472
467
 
473
468
  # TODO: use the same form structure as the top one and do the same test against messages, errors and hints.
474
- form.producers[0].to_result.errors.must_equal({:name=>["must be filled"]})
475
- form.producers[0].to_result.messages.must_equal({:name=>["must be filled"]})
476
- form.producers[0].to_result.hints.must_equal({:name=>[]})
469
+ form.producers[0].to_result.errors.must_equal(name: ["must be filled"])
470
+ form.producers[0].to_result.messages.must_equal(name: ["must be filled"])
471
+ form.producers[0].to_result.hints.must_equal(name: [])
477
472
  end
478
473
 
479
474
  # FIXME: fix the "must be filled error"
@@ -481,9 +476,9 @@ class ValidationGroupsTest < MiniTest::Spec
481
476
  it "renders full messages correctly" do
482
477
  result = form.validate(
483
478
  "title" => "",
484
- "songs" => [ {"title" => ""}, {"title" => ""} ],
479
+ "songs" => [{"title" => ""}, {"title" => ""}],
485
480
  "band" => {"size" => "", "label" => {"name" => ""}},
486
- "producers" => [{"name" => ''}, {"name" => ''}, {"name" => 'something lovely'}]
481
+ "producers" => [{"name" => ""}, {"name" => ""}, {"name" => "something lovely"}]
487
482
  )
488
483
 
489
484
  result.must_equal false
@@ -505,7 +500,7 @@ class ValidationGroupsTest < MiniTest::Spec
505
500
 
506
501
  validation do
507
502
  configure do
508
- config.messages_file = 'test/fixtures/dry_error_messages.yml'
503
+ config.messages_file = "test/fixtures/dry_error_messages.yml"
509
504
  end
510
505
 
511
506
  required(:title).filled
@@ -519,24 +514,24 @@ class ValidationGroupsTest < MiniTest::Spec
519
514
  end
520
515
  end
521
516
 
522
- let (:form) { AlbumFormWith1NestedVal.new(album) }
517
+ let(:form) { AlbumFormWith1NestedVal.new(album) }
523
518
 
524
519
  it "allows to access dry's result semantics per nested form" do
525
- result = form.validate(
520
+ form.validate(
526
521
  "title" => "",
527
- "songs" => [ {"title" => ""}, {"title" => ""} ],
522
+ "songs" => [{"title" => ""}, {"title" => ""}],
528
523
  "band" => {"size" => "", "label" => {"name" => ""}},
529
- "producers" => [{"name" => ''}, {"name" => ''}, {"name" => 'something lovely'}]
524
+ "producers" => [{"name" => ""}, {"name" => ""}, {"name" => "something lovely"}]
530
525
  )
531
526
 
532
- form.to_result.errors.must_equal({:title=>["must be filled"]})
533
- form.band.to_result.errors.must_equal({:name=>["must be filled"]})
534
- form.band.label.to_result.errors.must_equal({:location=>["must be filled"]})
527
+ form.to_result.errors.must_equal(title: ["must be filled"])
528
+ form.band.to_result.errors.must_equal(name: ["must be filled"])
529
+ form.band.label.to_result.errors.must_equal(location: ["must be filled"])
535
530
 
536
531
  # with locale: "de"
537
- form.to_result.errors(locale: :de).must_equal({:title=>["muss abgefüllt sein"]})
538
- form.band.to_result.errors(locale: :de).must_equal({:name=>["muss abgefüllt sein"]})
539
- form.band.label.to_result.errors(locale: :de).must_equal({:location=>["muss abgefüllt sein"]})
532
+ form.to_result.errors(locale: :de).must_equal(title: ["muss abgefüllt sein"])
533
+ form.band.to_result.errors(locale: :de).must_equal(name: ["muss abgefüllt sein"])
534
+ form.band.label.to_result.errors(locale: :de).must_equal(location: ["muss abgefüllt sein"])
540
535
  end
541
536
  end
542
537
  end
@@ -557,7 +552,7 @@ class ValidationGroupsTest < MiniTest::Spec
557
552
  # end
558
553
  # end
559
554
 
560
- # let (:form) { OverwritingForm.new(Session.new) }
555
+ # let(:form) { OverwritingForm.new(Session.new) }
561
556
 
562
557
  # # valid.
563
558
  # it do
@@ -571,36 +566,41 @@ class ValidationGroupsTest < MiniTest::Spec
571
566
  # end
572
567
  # end
573
568
 
574
-
575
569
  describe "inherit: true in same group" do
576
570
  class InheritSameGroupForm < TestForm
577
571
  property :username
578
572
  property :email
573
+ property :full_name, virtual: true
579
574
 
580
- validation name: :email do
581
- required(:email).filled
582
- end
575
+ validation name: :username do
576
+ configure do
577
+ config.messages_file = "test/fixtures/dry_error_messages.yml"
578
+ end
583
579
 
584
- validation name: :email, inherit: true do # extends the above.
585
580
  required(:username).filled
581
+ required(:full_name).filled
582
+ end
583
+
584
+ validation name: :username, inherit: true do # overrides and extends the above.
585
+ optional(:username).maybe
586
+ required(:email).filled
586
587
  end
587
588
  end
588
589
 
589
- let (:form) { InheritSameGroupForm.new(Session.new) }
590
+ let(:form) { InheritSameGroupForm.new(Session.new) }
590
591
 
591
592
  # valid.
592
593
  it do
593
- form.validate({username: "Helloween", email: 9}).must_equal true
594
+ form.validate(full_name: "My name", email: 9).must_equal true
594
595
  end
595
596
 
596
597
  # invalid.
597
598
  it do
598
599
  form.validate({}).must_equal false
599
- form.errors.messages.inspect.must_equal "{:email=>[\"must be filled\"], :username=>[\"must be filled\"]}"
600
+ form.errors.messages.must_equal email: ["must be filled"], full_name: ["must be filled"]
600
601
  end
601
602
  end
602
603
 
603
-
604
604
  describe "if: with lambda" do
605
605
  class IfWithLambdaForm < TestForm
606
606
  property :username
@@ -612,30 +612,137 @@ class ValidationGroupsTest < MiniTest::Spec
612
612
  end
613
613
 
614
614
  # run this is :email group is true.
615
- validation name: :after_email, if: lambda { |results| results[:email].success? } do # extends the above.
615
+ validation name: :after_email, if: ->(results) { results[:email].success? } do # extends the above.
616
616
  required(:username).filled
617
617
  end
618
618
 
619
619
  # block gets evaled in form instance context.
620
- validation name: :password, if: lambda { |results| email == "john@trb.org" } do
620
+ validation name: :password, if: ->(results) { email == "john@trb.org" } do
621
621
  required(:password).filled
622
622
  end
623
623
  end
624
624
 
625
- let (:form) { IfWithLambdaForm.new(Session.new) }
625
+ let(:form) { IfWithLambdaForm.new(Session.new) }
626
626
 
627
627
  # valid.
628
628
  it do
629
- form.validate({username: "Strung Out", email: 9}).must_equal true
629
+ form.validate(username: "Strung Out", email: 9).must_equal true
630
630
  end
631
631
 
632
632
  # invalid.
633
633
  it do
634
- form.validate({email: 9}).must_equal false
634
+ form.validate(email: 9).must_equal false
635
635
  form.errors.messages.inspect.must_equal "{:username=>[\"must be filled\"]}"
636
636
  end
637
637
  end
638
638
 
639
+ class NestedSchemaValidationTest < MiniTest::Spec
640
+ AddressSchema = Dry::Validation.Schema do
641
+ required(:company).filled(:int?)
642
+ end
643
+
644
+ class OrderForm < TestForm
645
+ property :delivery_address do
646
+ property :company
647
+ end
648
+
649
+ validation do
650
+ required(:delivery_address).schema(AddressSchema)
651
+ end
652
+ end
653
+
654
+ let(:company) { Struct.new(:company) }
655
+ let(:order) { Struct.new(:delivery_address) }
656
+ let(:form) { OrderForm.new(order.new(company.new)) }
657
+
658
+ it "has company error" do
659
+ form.validate(delivery_address: {company: "not int"}).must_equal false
660
+ form.errors.messages.must_equal(:"delivery_address.company" => ["must be an integer"])
661
+ end
662
+ end
663
+
664
+ class NestedSchemaValidationWithFormTest < MiniTest::Spec
665
+ class CompanyForm < TestForm
666
+ property :company
667
+
668
+ validation do
669
+ required(:company).filled(:int?)
670
+ end
671
+ end
672
+
673
+ class OrderFormWithForm < TestForm
674
+ property :delivery_address, form: CompanyForm
675
+ end
676
+
677
+ let(:company) { Struct.new(:company) }
678
+ let(:order) { Struct.new(:delivery_address) }
679
+ let(:form) { OrderFormWithForm.new(order.new(company.new)) }
680
+
681
+ it "has company error" do
682
+ form.validate(delivery_address: {company: "not int"}).must_equal false
683
+ form.errors.messages.must_equal(:"delivery_address.company" => ["must be an integer"])
684
+ end
685
+ end
686
+
687
+ class CollectionPropertyWithCustomRuleTest < MiniTest::Spec
688
+ Artist = Struct.new(:first_name, :last_name)
689
+ Song = Struct.new(:title, :enabled)
690
+ Album = Struct.new(:title, :songs, :artist)
691
+
692
+ class AlbumForm < TestForm
693
+ property :title
694
+
695
+ collection :songs, virtual: true, populate_if_empty: Song do
696
+ property :title
697
+ property :enabled
698
+
699
+ validation do
700
+ required(:title).filled
701
+ end
702
+ end
703
+
704
+ property :artist, populate_if_empty: Artist do
705
+ property :first_name
706
+ property :last_name
707
+ end
708
+
709
+ validation do
710
+ configure do
711
+ config.messages_file = "test/fixtures/dry_error_messages.yml"
712
+
713
+ def a_song?(value)
714
+ value.any? { |el| el && el[:enabled] }
715
+ end
716
+
717
+ def with_last_name?(value)
718
+ !value[:last_name].nil?
719
+ end
720
+ end
721
+
722
+ required(:songs).filled(:a_song?)
723
+ required(:artist).filled(:with_last_name?)
724
+ end
725
+ end
726
+
727
+ it "validates fails and shows the correct errors" do
728
+ form = AlbumForm.new(Album.new(nil, [], nil))
729
+ form.validate(
730
+ "songs" => [
731
+ {"title" => "One", "enabled" => false},
732
+ {"title" => nil, "enabled" => false},
733
+ {"title" => "Three", "enabled" => false}
734
+ ],
735
+ "artist" => {"last_name" => nil}
736
+ ).must_equal false
737
+ form.songs.size.must_equal 3
738
+
739
+ form.errors.messages.must_equal(
740
+ :songs => ["must have at least one enabled song"],
741
+ :artist => ["must have last name"],
742
+ :"songs.title" => ["must be filled"]
743
+ )
744
+ end
745
+ end
639
746
 
640
747
  # Currenty dry-v don't support that option, it doesn't make sense
641
748
  # I've talked to @solnic and he plans to add a "hint" feature to show
@@ -654,7 +761,7 @@ class ValidationGroupsTest < MiniTest::Spec
654
761
  # end
655
762
  # end
656
763
 
657
- # let (:form) { MultipleErrorsForPropertyForm.new(Session.new) }
764
+ # let(:form) { MultipleErrorsForPropertyForm.new(Session.new) }
658
765
 
659
766
  # # valid.
660
767
  # it do