reform 2.3.0.rc1 → 2.5.0
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.
- checksums.yaml +5 -5
- data/.gitignore +5 -1
- data/.travis.yml +7 -11
- data/CHANGES.md +43 -3
- data/Gemfile +2 -5
- data/ISSUE_TEMPLATE.md +1 -1
- data/LICENSE.txt +1 -1
- data/README.md +7 -9
- data/Rakefile +6 -10
- data/lib/reform/contract.rb +7 -7
- data/lib/reform/contract/custom_error.rb +41 -0
- data/lib/reform/contract/validate.rb +10 -6
- data/lib/reform/errors.rb +27 -15
- data/lib/reform/form.rb +22 -11
- data/lib/reform/form/call.rb +1 -1
- data/lib/reform/form/composition.rb +2 -2
- data/lib/reform/form/dry.rb +22 -60
- data/lib/reform/form/dry/input_hash.rb +37 -0
- data/lib/reform/form/populator.rb +9 -11
- data/lib/reform/form/prepopulate.rb +3 -2
- data/lib/reform/form/validate.rb +19 -12
- data/lib/reform/result.rb +36 -9
- data/lib/reform/validation.rb +10 -8
- data/lib/reform/validation/groups.rb +2 -4
- data/lib/reform/version.rb +1 -1
- data/reform.gemspec +9 -9
- data/test/benchmarking.rb +10 -11
- data/test/call_test.rb +8 -8
- data/test/changed_test.rb +13 -13
- data/test/coercion_test.rb +56 -24
- data/test/composition_test.rb +49 -51
- data/test/contract/custom_error_test.rb +55 -0
- data/test/contract_test.rb +18 -18
- data/test/default_test.rb +3 -3
- data/test/deserialize_test.rb +14 -17
- data/test/docs/validation_test.rb +134 -0
- data/test/errors_test.rb +131 -86
- data/test/feature_test.rb +9 -11
- data/test/fixtures/dry_error_messages.yml +65 -52
- data/test/form_option_test.rb +3 -3
- data/test/form_test.rb +6 -6
- data/test/from_test.rb +17 -21
- data/test/inherit_test.rb +28 -35
- data/test/module_test.rb +23 -28
- data/test/parse_option_test.rb +12 -12
- data/test/parse_pipeline_test.rb +3 -3
- data/test/populate_test.rb +146 -93
- data/test/populator_skip_test.rb +3 -4
- data/test/prepopulator_test.rb +20 -21
- data/test/read_only_test.rb +12 -1
- data/test/readable_test.rb +7 -7
- data/test/reform_test.rb +38 -42
- data/test/save_test.rb +16 -19
- data/test/setup_test.rb +15 -15
- data/test/skip_if_test.rb +30 -19
- data/test/skip_setter_and_getter_test.rb +8 -9
- data/test/test_helper.rb +12 -5
- data/test/validate_test.rb +160 -140
- data/test/validation/dry_validation_test.rb +407 -236
- data/test/validation/result_test.rb +29 -31
- data/test/validation_library_provided_test.rb +3 -3
- data/test/virtual_test.rb +46 -6
- data/test/writeable_test.rb +13 -13
- metadata +32 -29
- data/test/readonly_test.rb +0 -14
@@ -1,8 +1,6 @@
|
|
1
|
-
# encoding: utf-8
|
2
1
|
require "test_helper"
|
3
2
|
require "reform/form/dry"
|
4
3
|
require "reform/form/coercion"
|
5
|
-
|
6
4
|
#---
|
7
5
|
# one "nested" Schema per form.
|
8
6
|
class DryValidationErrorsAPITest < Minitest::Spec
|
@@ -15,22 +13,23 @@ class DryValidationErrorsAPITest < Minitest::Spec
|
|
15
13
|
property :title
|
16
14
|
|
17
15
|
validation do
|
18
|
-
|
19
|
-
|
16
|
+
params do
|
17
|
+
required(:title).filled(min_size?: 2)
|
18
|
+
end
|
20
19
|
end
|
21
20
|
|
22
21
|
property :artist do
|
23
22
|
property :email
|
24
23
|
|
25
24
|
validation do
|
26
|
-
required(:email).filled
|
25
|
+
params { required(:email).filled }
|
27
26
|
end
|
28
27
|
|
29
28
|
property :label do
|
30
29
|
property :location
|
31
30
|
|
32
31
|
validation do
|
33
|
-
required(:location).filled
|
32
|
+
params { required(:location).filled }
|
34
33
|
end
|
35
34
|
end
|
36
35
|
end
|
@@ -40,75 +39,73 @@ class DryValidationErrorsAPITest < Minitest::Spec
|
|
40
39
|
property :title
|
41
40
|
|
42
41
|
validation do
|
43
|
-
|
44
|
-
config.messages_file = 'test/fixtures/dry_error_messages.yml'
|
45
|
-
end
|
42
|
+
config.messages.load_paths << "test/fixtures/dry_error_messages.yml"
|
46
43
|
|
47
|
-
required(:title).filled
|
44
|
+
params { required(:title).filled }
|
48
45
|
end
|
49
46
|
end
|
50
|
-
|
51
47
|
end
|
52
48
|
|
53
|
-
let
|
49
|
+
let(:form) { AlbumForm.new(Album.new(nil, Artist.new(nil, Label.new), [Song.new(nil), Song.new(nil)])) }
|
54
50
|
|
55
51
|
it "everything wrong" do
|
56
|
-
result = form.(
|
52
|
+
result = form.(title: nil, artist: {email: ""}, songs: [{title: "Clams have feelings too"}, {title: ""}])
|
57
53
|
|
58
|
-
result.success
|
54
|
+
assert_equal result.success?, false
|
59
55
|
|
60
|
-
|
61
|
-
form.errors.messages
|
62
|
-
form.artist.errors.messages
|
63
|
-
form.
|
64
|
-
form.songs[
|
65
|
-
form.songs[1].errors.messages.must_equal({:title=>["must be filled"]})
|
56
|
+
assert_equal form.errors.messages, 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"]
|
57
|
+
assert_equal form.artist.errors.messages, email: ["must be filled"], "label.location": ["must be filled"]
|
58
|
+
assert_equal form.artist.label.errors.messages, location: ["must be filled"]
|
59
|
+
assert_equal form.songs[0].errors.messages, {}
|
60
|
+
assert_equal form.songs[1].errors.messages, title: ["must be filled"]
|
66
61
|
|
67
62
|
# #errors[]
|
68
|
-
form.errors[:nonsense]
|
69
|
-
form.errors[:title]
|
70
|
-
form.artist.errors[:email]
|
71
|
-
form.artist.label.errors[:location]
|
72
|
-
form.songs[0].errors[:title]
|
73
|
-
form.songs[1].errors[:title]
|
63
|
+
assert_equal form.errors[:nonsense], []
|
64
|
+
assert_equal form.errors[:title], ["must be filled", "size cannot be less than 2"]
|
65
|
+
assert_equal form.artist.errors[:email], ["must be filled"]
|
66
|
+
assert_equal form.artist.label.errors[:location], ["must be filled"]
|
67
|
+
assert_equal form.songs[0].errors[:title], []
|
68
|
+
assert_equal form.songs[1].errors[:title], ["must be filled"]
|
74
69
|
|
75
70
|
# #to_result
|
76
|
-
form.to_result.errors
|
77
|
-
form.to_result.messages
|
78
|
-
form.to_result.hints
|
79
|
-
form.artist.to_result.errors
|
80
|
-
form.artist.to_result.messages
|
81
|
-
form.artist.to_result.hints
|
82
|
-
form.artist.label.to_result.errors
|
83
|
-
form.artist.label.to_result.messages
|
84
|
-
form.artist.label.to_result.hints
|
85
|
-
form.songs[0].to_result.errors
|
86
|
-
form.songs[0].to_result.messages
|
87
|
-
form.songs[0].to_result.hints
|
88
|
-
form.songs[1].to_result.errors
|
89
|
-
form.songs[1].to_result.messages
|
90
|
-
form.songs[1].to_result.hints
|
91
|
-
form.songs[1].to_result.errors(locale: :de)
|
92
|
-
|
93
|
-
|
71
|
+
assert_equal form.to_result.errors, title: ["must be filled"]
|
72
|
+
assert_equal form.to_result.messages, title: ["must be filled", "size cannot be less than 2"]
|
73
|
+
assert_equal form.to_result.hints, title: ["size cannot be less than 2"]
|
74
|
+
assert_equal form.artist.to_result.errors, email: ["must be filled"]
|
75
|
+
assert_equal form.artist.to_result.messages, email: ["must be filled"]
|
76
|
+
assert_equal form.artist.to_result.hints, {}
|
77
|
+
assert_equal form.artist.label.to_result.errors, location: ["must be filled"]
|
78
|
+
assert_equal form.artist.label.to_result.messages, location: ["must be filled"]
|
79
|
+
assert_equal form.artist.label.to_result.hints, {}
|
80
|
+
assert_equal form.songs[0].to_result.errors, {}
|
81
|
+
assert_equal form.songs[0].to_result.messages, {}
|
82
|
+
assert_equal form.songs[0].to_result.hints, {}
|
83
|
+
assert_equal form.songs[1].to_result.errors, title: ["must be filled"]
|
84
|
+
assert_equal form.songs[1].to_result.messages, title: ["must be filled"]
|
85
|
+
assert_equal form.songs[1].to_result.hints, {}
|
86
|
+
assert_equal form.songs[1].to_result.errors(locale: :de), title: ["muss abgefüllt sein"]
|
87
|
+
# seems like dry-v when calling Dry::Schema::Result#messages locale option is ignored
|
88
|
+
# started a topic in their forum https://discourse.dry-rb.org/t/dry-result-messages-ignore-locale-option/910
|
89
|
+
# assert_equal form.songs[1].to_result.messages(locale: :de), (title: ["muss abgefüllt sein"])
|
90
|
+
assert_equal form.songs[1].to_result.hints(locale: :de), ({})
|
94
91
|
end
|
95
92
|
|
96
93
|
it "only nested property is invalid." do
|
97
|
-
result = form.(
|
94
|
+
result = form.(title: "Black Star", artist: {email: ""})
|
98
95
|
|
99
|
-
result.success
|
96
|
+
assert_equal result.success?, false
|
100
97
|
|
101
98
|
# errors.messages
|
102
|
-
form.errors.messages
|
103
|
-
form.artist.errors.messages
|
104
|
-
form.artist.label.errors.messages
|
99
|
+
assert_equal form.errors.messages, "artist.email": ["must be filled"], "artist.label.location": ["must be filled"], "songs.title": ["must be filled"]
|
100
|
+
assert_equal form.artist.errors.messages, email: ["must be filled"], "label.location": ["must be filled"]
|
101
|
+
assert_equal form.artist.label.errors.messages, location: ["must be filled"]
|
105
102
|
end
|
106
103
|
|
107
104
|
it "nested collection invalid" do
|
108
|
-
result = form.(
|
105
|
+
result = form.(title: "Black Star", artist: {email: "uhm", label: {location: "Hannover"}}, songs: [{title: ""}])
|
109
106
|
|
110
|
-
result.success
|
111
|
-
form.errors.messages
|
107
|
+
assert_equal result.success?, false
|
108
|
+
assert_equal form.errors.messages, "songs.title": ["must be filled"]
|
112
109
|
end
|
113
110
|
|
114
111
|
#---
|
@@ -119,9 +116,11 @@ class DryValidationErrorsAPITest < Minitest::Spec
|
|
119
116
|
end
|
120
117
|
|
121
118
|
validation do
|
122
|
-
|
123
|
-
|
124
|
-
|
119
|
+
params do
|
120
|
+
required(:songs).each do
|
121
|
+
schema do
|
122
|
+
required(:title).filled
|
123
|
+
end
|
125
124
|
end
|
126
125
|
end
|
127
126
|
end
|
@@ -129,19 +128,21 @@ class DryValidationErrorsAPITest < Minitest::Spec
|
|
129
128
|
|
130
129
|
it do
|
131
130
|
form = CollectionExternalValidationsForm.new(Album.new(nil, nil, [Song.new, Song.new]))
|
132
|
-
form.validate(songs: [
|
131
|
+
form.validate(songs: [{title: "Liar"}, {title: ""}])
|
133
132
|
|
134
|
-
form.errors.messages
|
135
|
-
form.songs[0].errors.messages
|
136
|
-
form.songs[1].errors.messages
|
133
|
+
assert_equal form.errors.messages, "songs.title": ["must be filled"]
|
134
|
+
assert_equal form.songs[0].errors.messages, {}
|
135
|
+
assert_equal form.songs[1].errors.messages, title: ["must be filled"]
|
137
136
|
end
|
138
137
|
end
|
139
138
|
|
140
139
|
class DryValidationExplicitSchemaTest < Minitest::Spec
|
141
140
|
Session = Struct.new(:name, :email)
|
142
|
-
|
143
|
-
|
144
|
-
|
141
|
+
SessionContract = Dry::Validation.Contract do
|
142
|
+
params do
|
143
|
+
required(:name).filled
|
144
|
+
required(:email).filled
|
145
|
+
end
|
145
146
|
end
|
146
147
|
|
147
148
|
class SessionForm < TestForm
|
@@ -150,20 +151,20 @@ class DryValidationExplicitSchemaTest < Minitest::Spec
|
|
150
151
|
property :name
|
151
152
|
property :email
|
152
153
|
|
153
|
-
validation
|
154
|
+
validation contract: SessionContract
|
154
155
|
end
|
155
156
|
|
156
|
-
let
|
157
|
+
let(:form) { SessionForm.new(Session.new) }
|
157
158
|
|
158
159
|
# valid.
|
159
160
|
it do
|
160
|
-
form.validate(name: "Helloween", email: "yep")
|
161
|
-
form.errors.messages.inspect
|
161
|
+
assert form.validate(name: "Helloween", email: "yep")
|
162
|
+
assert_equal form.errors.messages.inspect, "{}"
|
162
163
|
end
|
163
164
|
|
164
165
|
it "invalid" do
|
165
|
-
form.validate(name: "", email: "yep")
|
166
|
-
form.errors.messages.inspect
|
166
|
+
assert_equal form.validate(name: "", email: "yep"), false
|
167
|
+
assert_equal form.errors.messages.inspect, "{:name=>[\"must be filled\"]}"
|
167
168
|
end
|
168
169
|
end
|
169
170
|
|
@@ -177,28 +178,31 @@ class DryValidationDefaultGroupTest < Minitest::Spec
|
|
177
178
|
property :email
|
178
179
|
property :password
|
179
180
|
property :confirm_password
|
180
|
-
property :starts_at, type: Types::
|
181
|
-
property :active, type: Types::
|
181
|
+
property :starts_at, type: Types::Params::DateTime
|
182
|
+
property :active, type: Types::Params::Bool
|
182
183
|
property :color
|
183
184
|
|
184
185
|
validation do
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
186
|
+
params do
|
187
|
+
required(:username).filled
|
188
|
+
required(:email).filled
|
189
|
+
required(:starts_at).filled(:date_time?)
|
190
|
+
required(:active).filled(:bool?)
|
191
|
+
end
|
189
192
|
end
|
190
193
|
|
191
194
|
validation name: :another_block do
|
192
|
-
required(:confirm_password).filled
|
195
|
+
params { required(:confirm_password).filled }
|
193
196
|
end
|
194
197
|
|
195
|
-
validation name: :dynamic_args
|
196
|
-
|
197
|
-
|
198
|
-
|
198
|
+
validation name: :dynamic_args do
|
199
|
+
option :form
|
200
|
+
params { optional(:color) }
|
201
|
+
rule(:color) do
|
202
|
+
if value
|
203
|
+
key.failure("must be one of: #{form.colors}") unless form.colors.include? value
|
199
204
|
end
|
200
205
|
end
|
201
|
-
required(:color).maybe(included_in?: colors)
|
202
206
|
end
|
203
207
|
|
204
208
|
def colors
|
@@ -206,35 +210,40 @@ class DryValidationDefaultGroupTest < Minitest::Spec
|
|
206
210
|
end
|
207
211
|
end
|
208
212
|
|
209
|
-
let
|
213
|
+
let(:form) { SessionForm.new(Session.new) }
|
210
214
|
|
211
215
|
# valid.
|
212
216
|
it do
|
213
|
-
form.validate(
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
217
|
+
assert form.validate(
|
218
|
+
username: "Helloween",
|
219
|
+
email: "yep",
|
220
|
+
starts_at: "01/01/2000 - 11:00",
|
221
|
+
active: "true",
|
222
|
+
confirm_password: "pA55w0rd"
|
223
|
+
)
|
224
|
+
assert form.active
|
225
|
+
assert_equal "{}", form.errors.messages.inspect
|
219
226
|
end
|
220
227
|
|
221
228
|
it "invalid" do
|
222
|
-
form.validate(
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
229
|
+
assert_equal form.validate(
|
230
|
+
username: "Helloween",
|
231
|
+
email: "yep",
|
232
|
+
active: "1",
|
233
|
+
starts_at: "01/01/2000 - 11:00",
|
234
|
+
color: "purple"
|
235
|
+
), false
|
236
|
+
assert form.active
|
237
|
+
assert_equal form.errors.messages.inspect, "{:confirm_password=>[\"must be filled\"], :color=>[\"must be one of: red orange green\"]}"
|
228
238
|
end
|
229
239
|
end
|
230
240
|
|
231
241
|
class ValidationGroupsTest < MiniTest::Spec
|
232
242
|
describe "basic validations" do
|
233
243
|
Session = Struct.new(:username, :email, :password, :confirm_password, :special_class)
|
234
|
-
SomeClass= Struct.new(:id)
|
244
|
+
SomeClass = Struct.new(:id)
|
235
245
|
|
236
246
|
class SessionForm < TestForm
|
237
|
-
|
238
247
|
property :username
|
239
248
|
property :email
|
240
249
|
property :password
|
@@ -242,58 +251,61 @@ class ValidationGroupsTest < MiniTest::Spec
|
|
242
251
|
property :special_class
|
243
252
|
|
244
253
|
validation do
|
245
|
-
|
246
|
-
|
247
|
-
|
254
|
+
params do
|
255
|
+
required(:username).filled
|
256
|
+
required(:email).filled
|
257
|
+
required(:special_class).filled(type?: SomeClass)
|
258
|
+
end
|
248
259
|
end
|
249
260
|
|
250
261
|
validation name: :email, if: :default do
|
251
|
-
required(:email).filled(min_size?: 3)
|
262
|
+
params { required(:email).filled(min_size?: 3) }
|
252
263
|
end
|
253
264
|
|
254
|
-
validation name: :
|
255
|
-
required(:password).filled(min_size?: 2)
|
265
|
+
validation name: :password, if: :email do
|
266
|
+
params { required(:password).filled(min_size?: 2) }
|
256
267
|
end
|
257
268
|
|
258
269
|
validation name: :confirm, if: :default, after: :email do
|
259
|
-
required(:confirm_password).filled(min_size?: 2)
|
270
|
+
params { required(:confirm_password).filled(min_size?: 2) }
|
260
271
|
end
|
261
272
|
end
|
262
273
|
|
263
|
-
let
|
274
|
+
let(:form) { SessionForm.new(Session.new) }
|
264
275
|
|
265
276
|
# valid.
|
266
277
|
it do
|
267
|
-
form.validate(
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
278
|
+
assert form.validate(
|
279
|
+
username: "Helloween",
|
280
|
+
special_class: SomeClass.new(id: 15),
|
281
|
+
email: "yep",
|
282
|
+
password: "99",
|
283
|
+
confirm_password: "99"
|
284
|
+
)
|
285
|
+
assert_equal form.errors.messages.inspect, "{}"
|
273
286
|
end
|
274
287
|
|
275
288
|
# invalid.
|
276
289
|
it do
|
277
|
-
form.validate({})
|
278
|
-
form.errors.messages
|
290
|
+
assert_equal form.validate({}), false
|
291
|
+
assert_equal form.errors.messages, username: ["must be filled"], email: ["must be filled"], special_class: ["must be filled", "must be ValidationGroupsTest::SomeClass"]
|
279
292
|
end
|
280
293
|
|
281
294
|
# partially invalid.
|
282
295
|
# 2nd group fails.
|
283
296
|
it do
|
284
|
-
form.validate(username: "Helloween", email: "yo", confirm_password:"9", special_class: SomeClass.new(id: 15))
|
285
|
-
form.errors.messages.inspect
|
297
|
+
assert_equal form.validate(username: "Helloween", email: "yo", confirm_password: "9", special_class: SomeClass.new(id: 15)), false
|
298
|
+
assert_equal form.errors.messages.inspect, "{:email=>[\"size cannot be less than 3\"], :confirm_password=>[\"size cannot be less than 2\"]}"
|
286
299
|
end
|
287
300
|
# 3rd group fails.
|
288
301
|
it do
|
289
|
-
form.validate(username: "Helloween", email: "yo!", confirm_password:"9", special_class: SomeClass.new(id: 15))
|
290
|
-
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\"]}"
|
302
|
+
assert_equal form.validate(username: "Helloween", email: "yo!", confirm_password: "9", special_class: SomeClass.new(id: 15)), false
|
303
|
+
assert_equal form.errors.messages.inspect, "{:confirm_password=>[\"size cannot be less than 2\"], :password=>[\"must be filled\", \"size cannot be less than 2\"]}"
|
292
304
|
end
|
293
305
|
# 4th group with after: fails.
|
294
306
|
it do
|
295
|
-
form.validate(username: "Helloween", email: "yo!", password: "1", confirm_password: "9", special_class: SomeClass.new(id: 15))
|
296
|
-
form.errors.messages.inspect
|
307
|
+
assert_equal form.validate(username: "Helloween", email: "yo!", password: "1", confirm_password: "9", special_class: SomeClass.new(id: 15)), false
|
308
|
+
assert_equal form.errors.messages.inspect, "{:confirm_password=>[\"size cannot be less than 2\"], :password=>[\"size cannot be less than 2\"]}"
|
297
309
|
end
|
298
310
|
end
|
299
311
|
|
@@ -303,48 +315,41 @@ class ValidationGroupsTest < MiniTest::Spec
|
|
303
315
|
class SessionForm < TestForm
|
304
316
|
property :username
|
305
317
|
|
306
|
-
validation name: :default, with: {
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
318
|
+
validation name: :default, with: {user: OpenStruct.new(name: "Nick")} do
|
319
|
+
option :user
|
320
|
+
params do
|
321
|
+
required(:username).filled
|
322
|
+
end
|
323
|
+
rule(:username) do
|
324
|
+
key.failure("must be equal to #{user.name}") unless user.name == value
|
311
325
|
end
|
312
|
-
required(:username).filled(eql?: users_name)
|
313
326
|
end
|
314
327
|
end
|
315
328
|
|
316
|
-
let
|
329
|
+
let(:form) { SessionForm.new(Session.new) }
|
317
330
|
|
318
331
|
# valid.
|
319
332
|
it do
|
320
|
-
form.validate(
|
321
|
-
form.errors.messages.inspect
|
333
|
+
assert form.validate(username: "Nick")
|
334
|
+
assert_equal form.errors.messages.inspect, "{}"
|
322
335
|
end
|
323
336
|
|
324
337
|
# invalid.
|
325
338
|
it do
|
326
|
-
form.validate(
|
327
|
-
form.errors.messages.inspect
|
339
|
+
assert_equal form.validate(username: "Fred"), false
|
340
|
+
assert_equal form.errors.messages.inspect, "{:username=>[\"must be equal to Nick\"]}"
|
328
341
|
end
|
329
342
|
end
|
330
343
|
end
|
331
344
|
|
332
345
|
#---
|
333
|
-
#- validation( schema: MySchema )
|
334
346
|
describe "with custom schema" do
|
335
347
|
Session2 = Struct.new(:username, :email, :password)
|
336
348
|
|
337
|
-
|
338
|
-
|
339
|
-
config.messages_file = 'test/fixtures/dry_error_messages.yml'
|
340
|
-
|
341
|
-
def good_musical_taste?(val)
|
342
|
-
val.is_a? String
|
343
|
-
end
|
349
|
+
MyContract = Dry::Schema.Params do
|
350
|
+
config.messages.load_paths << "test/fixtures/dry_error_messages.yml"
|
344
351
|
|
345
|
-
|
346
|
-
|
347
|
-
required(:password).filled(:min_size? => 6)
|
352
|
+
required(:password).filled(min_size?: 6)
|
348
353
|
end
|
349
354
|
|
350
355
|
class Session2Form < TestForm
|
@@ -352,29 +357,38 @@ class ValidationGroupsTest < MiniTest::Spec
|
|
352
357
|
property :email
|
353
358
|
property :password
|
354
359
|
|
355
|
-
validation
|
356
|
-
|
357
|
-
|
360
|
+
validation contract: MyContract do
|
361
|
+
params do
|
362
|
+
required(:username).filled
|
363
|
+
required(:email).filled
|
364
|
+
end
|
365
|
+
|
366
|
+
rule(:email) do
|
367
|
+
key.failure(:good_musical_taste?) unless value.is_a? String
|
368
|
+
end
|
358
369
|
end
|
359
370
|
end
|
360
371
|
|
361
|
-
let
|
372
|
+
let(:form) { Session2Form.new(Session2.new) }
|
362
373
|
|
363
374
|
# valid.
|
364
375
|
it do
|
365
|
-
|
366
|
-
form.
|
376
|
+
skip "waiting dry-v to add this as feature https://github.com/dry-rb/dry-schema/issues/33"
|
377
|
+
assert form.validate(username: "Helloween", email: "yep", password: "extrasafe")
|
378
|
+
assert_equal form.errors.messages.inspect, "{}"
|
367
379
|
end
|
368
380
|
|
369
381
|
# invalid.
|
370
382
|
it do
|
371
|
-
|
372
|
-
form.
|
383
|
+
skip "waiting dry-v to add this as feature https://github.com/dry-rb/dry-schema/issues/33"
|
384
|
+
assert_equal form.validate({}), false
|
385
|
+
assert_equal form.errors.messages, password: ["must be filled", "size cannot be less than 6"], username: ["must be filled"], email: ["must be filled", "you're a bad person"]
|
373
386
|
end
|
374
387
|
|
375
388
|
it do
|
376
|
-
|
377
|
-
form.
|
389
|
+
skip "waiting dry-v to add this as feature https://github.com/dry-rb/dry-schema/issues/33"
|
390
|
+
assert_equal form.validate(email: 1), false
|
391
|
+
assert_equal form.errors.messages.inspect, "{:password=>[\"must be filled\", \"size cannot be less than 6\"], :username=>[\"must be filled\"], :email=>[\"you're a bad person\"]}"
|
378
392
|
end
|
379
393
|
end
|
380
394
|
|
@@ -386,7 +400,7 @@ class ValidationGroupsTest < MiniTest::Spec
|
|
386
400
|
property :title
|
387
401
|
|
388
402
|
validation do
|
389
|
-
required(:title).filled
|
403
|
+
params { required(:title).filled }
|
390
404
|
end
|
391
405
|
end
|
392
406
|
|
@@ -394,7 +408,7 @@ class ValidationGroupsTest < MiniTest::Spec
|
|
394
408
|
property :title
|
395
409
|
|
396
410
|
validation do
|
397
|
-
required(:title).filled
|
411
|
+
params { required(:title).filled }
|
398
412
|
end
|
399
413
|
end
|
400
414
|
|
@@ -411,69 +425,63 @@ class ValidationGroupsTest < MiniTest::Spec
|
|
411
425
|
end
|
412
426
|
|
413
427
|
validation do
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
428
|
+
config.messages.load_paths << "test/fixtures/dry_error_messages.yml"
|
429
|
+
params do
|
430
|
+
required(:title).filled
|
431
|
+
required(:band).hash do
|
432
|
+
required(:name).filled
|
433
|
+
required(:label).hash do
|
434
|
+
required(:location).filled
|
435
|
+
end
|
420
436
|
end
|
421
|
-
end
|
422
437
|
|
423
|
-
|
424
|
-
|
425
|
-
required(:band).schema do
|
426
|
-
required(:name).filled
|
427
|
-
required(:label).schema do
|
428
|
-
required(:location).filled
|
438
|
+
required(:producers).each do
|
439
|
+
hash { required(:name).filled }
|
429
440
|
end
|
430
441
|
end
|
431
442
|
|
432
|
-
|
433
|
-
|
434
|
-
required(:name).filled
|
435
|
-
end
|
443
|
+
rule(:title) do
|
444
|
+
key.failure(:good_musical_taste?) unless value != "Nickelback"
|
436
445
|
end
|
437
|
-
|
438
446
|
end
|
439
447
|
end
|
440
448
|
|
441
|
-
let
|
449
|
+
let(:album) do
|
442
450
|
OpenStruct.new(
|
443
|
-
:
|
444
|
-
:
|
445
|
-
:
|
446
|
-
:
|
451
|
+
hit: OpenStruct.new,
|
452
|
+
songs: [OpenStruct.new, OpenStruct.new],
|
453
|
+
band: Struct.new(:name, :label).new("", OpenStruct.new),
|
454
|
+
producers: [OpenStruct.new, OpenStruct.new, OpenStruct.new],
|
447
455
|
)
|
448
456
|
end
|
449
457
|
|
450
|
-
let
|
458
|
+
let(:form) { AlbumForm.new(album) }
|
451
459
|
|
452
460
|
it "maps errors to form objects correctly" do
|
453
461
|
result = form.validate(
|
454
462
|
"title" => "Nickelback",
|
455
|
-
"songs" => [
|
463
|
+
"songs" => [{"title" => ""}, {"title" => ""}],
|
456
464
|
"band" => {"size" => "", "label" => {"location" => ""}},
|
457
|
-
"producers" => [{"name" =>
|
465
|
+
"producers" => [{"name" => ""}, {"name" => "something lovely"}]
|
458
466
|
)
|
459
467
|
|
460
|
-
result
|
468
|
+
assert_equal result, false
|
461
469
|
# from nested validation
|
462
|
-
form.errors.messages
|
470
|
+
assert_equal form.errors.messages, 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
471
|
|
464
472
|
# songs have their own validation.
|
465
|
-
form.songs[0].errors.messages
|
473
|
+
assert_equal form.songs[0].errors.messages, title: ["must be filled"]
|
466
474
|
# hit got its own validation group.
|
467
|
-
form.hit.errors.messages
|
475
|
+
assert_equal form.hit.errors.messages, title: ["must be filled"]
|
468
476
|
|
469
|
-
form.band.label.errors.messages
|
470
|
-
form.band.errors.messages
|
471
|
-
form.producers[0].errors.messages
|
477
|
+
assert_equal form.band.label.errors.messages, location: ["must be filled"]
|
478
|
+
assert_equal form.band.errors.messages, name: ["must be filled"], "label.location": ["must be filled"]
|
479
|
+
assert_equal form.producers[0].errors.messages, name: ["must be filled"]
|
472
480
|
|
473
481
|
# 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
|
475
|
-
form.producers[0].to_result.messages
|
476
|
-
form.producers[0].to_result.hints
|
482
|
+
assert_equal form.producers[0].to_result.errors, name: ["must be filled"]
|
483
|
+
assert_equal form.producers[0].to_result.messages, name: ["must be filled"]
|
484
|
+
assert_equal form.producers[0].to_result.hints, {}
|
477
485
|
end
|
478
486
|
|
479
487
|
# FIXME: fix the "must be filled error"
|
@@ -481,16 +489,16 @@ class ValidationGroupsTest < MiniTest::Spec
|
|
481
489
|
it "renders full messages correctly" do
|
482
490
|
result = form.validate(
|
483
491
|
"title" => "",
|
484
|
-
"songs" => [
|
492
|
+
"songs" => [{"title" => ""}, {"title" => ""}],
|
485
493
|
"band" => {"size" => "", "label" => {"name" => ""}},
|
486
|
-
"producers" => [{"name" =>
|
494
|
+
"producers" => [{"name" => ""}, {"name" => ""}, {"name" => "something lovely"}]
|
487
495
|
)
|
488
496
|
|
489
|
-
result
|
490
|
-
form.band.errors.full_messages
|
491
|
-
form.band.label.errors.full_messages
|
492
|
-
form.producers.first.errors.full_messages
|
493
|
-
form.errors.full_messages
|
497
|
+
assert_equal result, false
|
498
|
+
assert_equal form.band.errors.full_messages, ["Name must be filled", "Label Location must be filled"]
|
499
|
+
assert_equal form.band.label.errors.full_messages, ["Location must be filled"]
|
500
|
+
assert_equal form.producers.first.errors.full_messages, ["Name must be filled"]
|
501
|
+
assert_equal form.errors.full_messages, ["Title must be filled", "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"]
|
494
502
|
end
|
495
503
|
|
496
504
|
describe "only 1 nested validation" do
|
@@ -504,39 +512,39 @@ class ValidationGroupsTest < MiniTest::Spec
|
|
504
512
|
end
|
505
513
|
|
506
514
|
validation do
|
507
|
-
|
508
|
-
config.messages_file = 'test/fixtures/dry_error_messages.yml'
|
509
|
-
end
|
515
|
+
config.messages.load_paths << "test/fixtures/dry_error_messages.yml"
|
510
516
|
|
511
|
-
|
517
|
+
params do
|
518
|
+
required(:title).filled
|
512
519
|
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
520
|
+
required(:band).schema do
|
521
|
+
required(:name).filled
|
522
|
+
required(:label).schema do
|
523
|
+
required(:location).filled
|
524
|
+
end
|
517
525
|
end
|
518
526
|
end
|
519
527
|
end
|
520
528
|
end
|
521
529
|
|
522
|
-
let
|
530
|
+
let(:form) { AlbumFormWith1NestedVal.new(album) }
|
523
531
|
|
524
532
|
it "allows to access dry's result semantics per nested form" do
|
525
|
-
|
526
|
-
"title"
|
527
|
-
"songs"
|
528
|
-
"band"
|
529
|
-
"producers" => [{"name" =>
|
533
|
+
form.validate(
|
534
|
+
"title" => "",
|
535
|
+
"songs" => [{"title" => ""}, {"title" => ""}],
|
536
|
+
"band" => {"size" => "", "label" => {"name" => ""}},
|
537
|
+
"producers" => [{"name" => ""}, {"name" => ""}, {"name" => "something lovely"}]
|
530
538
|
)
|
531
539
|
|
532
|
-
form.to_result.errors
|
533
|
-
form.band.to_result.errors
|
534
|
-
form.band.label.to_result.errors
|
540
|
+
assert_equal form.to_result.errors, title: ["must be filled"]
|
541
|
+
assert_equal form.band.to_result.errors, name: ["must be filled"]
|
542
|
+
assert_equal form.band.label.to_result.errors, location: ["must be filled"]
|
535
543
|
|
536
544
|
# with locale: "de"
|
537
|
-
form.to_result.errors(locale: :de)
|
538
|
-
form.band.to_result.errors(locale: :de)
|
539
|
-
form.band.label.to_result.errors(locale: :de)
|
545
|
+
assert_equal form.to_result.errors(locale: :de), title: ["muss abgefüllt sein"]
|
546
|
+
assert_equal form.band.to_result.errors(locale: :de), name: ["muss abgefüllt sein"]
|
547
|
+
assert_equal form.band.label.to_result.errors(locale: :de), location: ["muss abgefüllt sein"]
|
540
548
|
end
|
541
549
|
end
|
542
550
|
end
|
@@ -557,7 +565,7 @@ class ValidationGroupsTest < MiniTest::Spec
|
|
557
565
|
# end
|
558
566
|
# end
|
559
567
|
|
560
|
-
# let
|
568
|
+
# let(:form) { OverwritingForm.new(Session.new) }
|
561
569
|
|
562
570
|
# # valid.
|
563
571
|
# it do
|
@@ -571,36 +579,43 @@ class ValidationGroupsTest < MiniTest::Spec
|
|
571
579
|
# end
|
572
580
|
# end
|
573
581
|
|
574
|
-
|
575
582
|
describe "inherit: true in same group" do
|
576
583
|
class InheritSameGroupForm < TestForm
|
577
584
|
property :username
|
578
585
|
property :email
|
586
|
+
property :full_name, virtual: true
|
579
587
|
|
580
|
-
validation name: :
|
581
|
-
|
588
|
+
validation name: :username do
|
589
|
+
params do
|
590
|
+
required(:username).filled
|
591
|
+
required(:full_name).filled
|
592
|
+
end
|
582
593
|
end
|
583
594
|
|
584
|
-
validation name: :
|
585
|
-
|
595
|
+
validation name: :username, inherit: true do # extends the above.
|
596
|
+
params do
|
597
|
+
optional(:username).maybe(:string)
|
598
|
+
required(:email).filled
|
599
|
+
end
|
586
600
|
end
|
587
601
|
end
|
588
602
|
|
589
|
-
let
|
603
|
+
let(:form) { InheritSameGroupForm.new(Session.new) }
|
590
604
|
|
591
605
|
# valid.
|
592
606
|
it do
|
593
|
-
|
607
|
+
skip "waiting dry-v to add this as feature https://github.com/dry-rb/dry-schema/issues/33"
|
608
|
+
assert form.validate(email: 9)
|
594
609
|
end
|
595
610
|
|
596
611
|
# invalid.
|
597
612
|
it do
|
598
|
-
|
599
|
-
form.
|
613
|
+
skip "waiting dry-v to add this as feature https://github.com/dry-rb/dry-schema/issues/33"
|
614
|
+
assert_equal form.validate({}), false
|
615
|
+
assert_equal form.errors.messages, email: ["must be filled"], full_name: ["must be filled"]
|
600
616
|
end
|
601
617
|
end
|
602
618
|
|
603
|
-
|
604
619
|
describe "if: with lambda" do
|
605
620
|
class IfWithLambdaForm < TestForm
|
606
621
|
property :username
|
@@ -608,34 +623,190 @@ class ValidationGroupsTest < MiniTest::Spec
|
|
608
623
|
property :password
|
609
624
|
|
610
625
|
validation name: :email do
|
611
|
-
required(:email).filled
|
626
|
+
params { required(:email).filled }
|
612
627
|
end
|
613
628
|
|
614
629
|
# run this is :email group is true.
|
615
|
-
validation name: :after_email, if:
|
616
|
-
required(:username).filled
|
630
|
+
validation name: :after_email, if: ->(results) { results[:email].success? } do # extends the above.
|
631
|
+
params { required(:username).filled }
|
617
632
|
end
|
618
633
|
|
619
634
|
# block gets evaled in form instance context.
|
620
|
-
validation name: :password, if:
|
621
|
-
required(:password).filled
|
635
|
+
validation name: :password, if: ->(results) { email == "john@trb.org" } do
|
636
|
+
params { required(:password).filled }
|
622
637
|
end
|
623
638
|
end
|
624
639
|
|
625
|
-
let
|
640
|
+
let(:form) { IfWithLambdaForm.new(Session.new) }
|
626
641
|
|
627
642
|
# valid.
|
628
643
|
it do
|
629
|
-
form.validate(
|
644
|
+
assert form.validate(username: "Strung Out", email: 9)
|
630
645
|
end
|
631
646
|
|
632
647
|
# invalid.
|
633
648
|
it do
|
634
|
-
form.validate(
|
635
|
-
form.errors.messages.inspect
|
649
|
+
assert_equal form.validate(email: 9), false
|
650
|
+
assert_equal form.errors.messages.inspect, "{:username=>[\"must be filled\"]}"
|
651
|
+
end
|
652
|
+
end
|
653
|
+
|
654
|
+
class NestedSchemaValidationTest < MiniTest::Spec
|
655
|
+
AddressSchema = Dry::Schema.Params do
|
656
|
+
required(:company).filled(:int?)
|
657
|
+
end
|
658
|
+
|
659
|
+
class OrderForm < TestForm
|
660
|
+
property :delivery_address do
|
661
|
+
property :company
|
662
|
+
end
|
663
|
+
|
664
|
+
validation do
|
665
|
+
params { required(:delivery_address).schema(AddressSchema) }
|
666
|
+
end
|
667
|
+
end
|
668
|
+
|
669
|
+
let(:company) { Struct.new(:company) }
|
670
|
+
let(:order) { Struct.new(:delivery_address) }
|
671
|
+
let(:form) { OrderForm.new(order.new(company.new)) }
|
672
|
+
|
673
|
+
it "has company error" do
|
674
|
+
assert_equal form.validate(delivery_address: {company: "not int"}), false
|
675
|
+
assert_equal form.errors.messages, :"delivery_address.company" => ["must be an integer"]
|
636
676
|
end
|
637
677
|
end
|
638
678
|
|
679
|
+
class NestedSchemaValidationWithFormTest < MiniTest::Spec
|
680
|
+
class CompanyForm < TestForm
|
681
|
+
property :company
|
682
|
+
|
683
|
+
validation do
|
684
|
+
params { required(:company).filled(:int?) }
|
685
|
+
end
|
686
|
+
end
|
687
|
+
|
688
|
+
class OrderFormWithForm < TestForm
|
689
|
+
property :delivery_address, form: CompanyForm
|
690
|
+
end
|
691
|
+
|
692
|
+
let(:company) { Struct.new(:company) }
|
693
|
+
let(:order) { Struct.new(:delivery_address) }
|
694
|
+
let(:form) { OrderFormWithForm.new(order.new(company.new)) }
|
695
|
+
|
696
|
+
it "has company error" do
|
697
|
+
assert_equal form.validate(delivery_address: {company: "not int"}), false
|
698
|
+
assert_equal form.errors.messages, :"delivery_address.company" => ["must be an integer"]
|
699
|
+
end
|
700
|
+
end
|
701
|
+
|
702
|
+
class CollectionPropertyWithCustomRuleTest < MiniTest::Spec
|
703
|
+
Artist = Struct.new(:first_name, :last_name)
|
704
|
+
Song = Struct.new(:title, :enabled)
|
705
|
+
Album = Struct.new(:title, :songs, :artist)
|
706
|
+
|
707
|
+
class AlbumForm < TestForm
|
708
|
+
property :title
|
709
|
+
|
710
|
+
collection :songs, virtual: true, populate_if_empty: Song do
|
711
|
+
property :title
|
712
|
+
property :enabled
|
713
|
+
|
714
|
+
validation do
|
715
|
+
params { required(:title).filled }
|
716
|
+
end
|
717
|
+
end
|
718
|
+
|
719
|
+
property :artist, populate_if_empty: Artist do
|
720
|
+
property :first_name
|
721
|
+
property :last_name
|
722
|
+
end
|
723
|
+
|
724
|
+
validation do
|
725
|
+
config.messages.load_paths << "test/fixtures/dry_error_messages.yml"
|
726
|
+
|
727
|
+
params do
|
728
|
+
required(:songs).filled
|
729
|
+
required(:artist).filled
|
730
|
+
end
|
731
|
+
|
732
|
+
rule(:songs) do
|
733
|
+
key.failure(:a_song?) unless value.any? { |el| el && el[:enabled] }
|
734
|
+
end
|
735
|
+
|
736
|
+
rule(:artist) do
|
737
|
+
key.failure(:with_last_name?) unless value[:last_name]
|
738
|
+
end
|
739
|
+
end
|
740
|
+
end
|
741
|
+
|
742
|
+
it "validates fails and shows the correct errors" do
|
743
|
+
form = AlbumForm.new(Album.new(nil, [], nil))
|
744
|
+
assert_equal form.validate(
|
745
|
+
"songs" => [
|
746
|
+
{"title" => "One", "enabled" => false},
|
747
|
+
{"title" => nil, "enabled" => false},
|
748
|
+
{"title" => "Three", "enabled" => false}
|
749
|
+
],
|
750
|
+
"artist" => {"last_name" => nil}
|
751
|
+
), false
|
752
|
+
assert_equal form.songs.size, 3
|
753
|
+
|
754
|
+
assert_equal form.errors.messages, {
|
755
|
+
:songs => ["must have at least one enabled song"],
|
756
|
+
:artist => ["must have last name"],
|
757
|
+
:"songs.title" => ["must be filled"]
|
758
|
+
}
|
759
|
+
end
|
760
|
+
end
|
761
|
+
|
762
|
+
class DryVWithSchemaAndParams < MiniTest::Spec
|
763
|
+
Foo = Struct.new(:age)
|
764
|
+
|
765
|
+
class ParamsForm < TestForm
|
766
|
+
property :age
|
767
|
+
|
768
|
+
validation do
|
769
|
+
params { required(:age).value(:integer) }
|
770
|
+
|
771
|
+
rule(:age) { key.failure("value exceeded") if value > 999 }
|
772
|
+
end
|
773
|
+
end
|
774
|
+
|
775
|
+
class SchemaForm < TestForm
|
776
|
+
property :age
|
777
|
+
|
778
|
+
validation do
|
779
|
+
schema { required(:age).value(:integer) }
|
780
|
+
|
781
|
+
rule(:age) { key.failure("value exceeded") if value > 999 }
|
782
|
+
end
|
783
|
+
end
|
784
|
+
|
785
|
+
it "using params" do
|
786
|
+
model = Foo.new
|
787
|
+
form = ParamsForm.new(model)
|
788
|
+
assert form.validate(age: "99")
|
789
|
+
form.sync
|
790
|
+
assert_equal model.age, "99"
|
791
|
+
|
792
|
+
form = ParamsForm.new(Foo.new)
|
793
|
+
assert_equal form.validate(age: "1000"), false
|
794
|
+
assert_equal form.errors.messages, age: ["value exceeded"]
|
795
|
+
end
|
796
|
+
|
797
|
+
it "using schema" do
|
798
|
+
model = Foo.new
|
799
|
+
form = SchemaForm.new(model)
|
800
|
+
assert_equal form.validate(age: "99"), false
|
801
|
+
assert form.validate(age: 99)
|
802
|
+
form.sync
|
803
|
+
assert_equal model.age, 99
|
804
|
+
|
805
|
+
form = SchemaForm.new(Foo.new)
|
806
|
+
assert_equal form.validate(age: 1000), false
|
807
|
+
assert_equal form.errors.messages, age: ["value exceeded"]
|
808
|
+
end
|
809
|
+
end
|
639
810
|
|
640
811
|
# Currenty dry-v don't support that option, it doesn't make sense
|
641
812
|
# I've talked to @solnic and he plans to add a "hint" feature to show
|
@@ -654,7 +825,7 @@ class ValidationGroupsTest < MiniTest::Spec
|
|
654
825
|
# end
|
655
826
|
# end
|
656
827
|
|
657
|
-
# let
|
828
|
+
# let(:form) { MultipleErrorsForPropertyForm.new(Session.new) }
|
658
829
|
|
659
830
|
# # valid.
|
660
831
|
# it do
|