reform 2.2.4
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 +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +11 -0
- data/CHANGES.md +415 -0
- data/Gemfile +19 -0
- data/LICENSE.txt +22 -0
- data/README.md +339 -0
- data/Rakefile +15 -0
- data/TODO.md +45 -0
- data/gemfiles/Gemfile.disposable-0.3 +6 -0
- data/lib/reform.rb +8 -0
- data/lib/reform/contract.rb +77 -0
- data/lib/reform/contract/errors.rb +43 -0
- data/lib/reform/contract/validate.rb +33 -0
- data/lib/reform/form.rb +94 -0
- data/lib/reform/form/call.rb +23 -0
- data/lib/reform/form/coercion.rb +3 -0
- data/lib/reform/form/composition.rb +34 -0
- data/lib/reform/form/dry.rb +67 -0
- data/lib/reform/form/module.rb +27 -0
- data/lib/reform/form/mongoid.rb +37 -0
- data/lib/reform/form/orm.rb +26 -0
- data/lib/reform/form/populator.rb +123 -0
- data/lib/reform/form/prepopulate.rb +24 -0
- data/lib/reform/form/validate.rb +60 -0
- data/lib/reform/mongoid.rb +4 -0
- data/lib/reform/validation.rb +40 -0
- data/lib/reform/validation/groups.rb +73 -0
- data/lib/reform/version.rb +3 -0
- data/reform.gemspec +29 -0
- data/test/benchmarking.rb +26 -0
- data/test/call_test.rb +23 -0
- data/test/changed_test.rb +41 -0
- data/test/coercion_test.rb +66 -0
- data/test/composition_test.rb +149 -0
- data/test/contract_test.rb +77 -0
- data/test/default_test.rb +22 -0
- data/test/deprecation_test.rb +27 -0
- data/test/deserialize_test.rb +104 -0
- data/test/errors_test.rb +165 -0
- data/test/feature_test.rb +65 -0
- data/test/fixtures/dry_error_messages.yml +44 -0
- data/test/form_option_test.rb +24 -0
- data/test/form_test.rb +57 -0
- data/test/from_test.rb +75 -0
- data/test/inherit_test.rb +119 -0
- data/test/module_test.rb +142 -0
- data/test/parse_pipeline_test.rb +15 -0
- data/test/populate_test.rb +270 -0
- data/test/populator_skip_test.rb +28 -0
- data/test/prepopulator_test.rb +112 -0
- data/test/read_only_test.rb +3 -0
- data/test/readable_test.rb +30 -0
- data/test/readonly_test.rb +14 -0
- data/test/reform_test.rb +223 -0
- data/test/save_test.rb +89 -0
- data/test/setup_test.rb +48 -0
- data/test/skip_if_test.rb +74 -0
- data/test/skip_setter_and_getter_test.rb +54 -0
- data/test/test_helper.rb +49 -0
- data/test/validate_test.rb +420 -0
- data/test/validation/dry_test.rb +60 -0
- data/test/validation/dry_validation_test.rb +352 -0
- data/test/validation/errors.yml +4 -0
- data/test/virtual_test.rb +24 -0
- data/test/writeable_test.rb +29 -0
- metadata +265 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
# require "test_helper"
|
2
|
+
# require "reform/form/dry"
|
3
|
+
|
4
|
+
# class BlaTest < MiniTest::Spec
|
5
|
+
# class CartForm < Reform::Form
|
6
|
+
# include Reform::Form::Dry::Validations
|
7
|
+
|
8
|
+
# property :user_id
|
9
|
+
|
10
|
+
# collection :variants do
|
11
|
+
# property :id
|
12
|
+
# end
|
13
|
+
|
14
|
+
|
15
|
+
# validation :default do
|
16
|
+
# key(:user_id).required
|
17
|
+
|
18
|
+
# key(:variants).schema do
|
19
|
+
# each do
|
20
|
+
# key(:id).required
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
|
24
|
+
# configure do
|
25
|
+
# config.messages_file = 'test/validation/errors.yml'
|
26
|
+
|
27
|
+
# option :form
|
28
|
+
# # message need to be defined on fixtures/dry_error_messages
|
29
|
+
# # d-v expects you to define your custome messages on the .yml file
|
30
|
+
|
31
|
+
|
32
|
+
# def form_access_validation?(value)
|
33
|
+
# raise value.inspect
|
34
|
+
# form.title == 'Reform'
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
|
38
|
+
# rule(form_present: [:form]) do |form|
|
39
|
+
# form.user_id == "hallo"
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
|
44
|
+
# it do
|
45
|
+
# cart = Struct.new(:user_id, :variants).new(1, [Struct.new(:id).new])
|
46
|
+
|
47
|
+
# form = CartForm.new(cart)
|
48
|
+
# form.validate(user_id: 2, variants: [{id: 3}])
|
49
|
+
# puts form.errors.inspect
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
|
53
|
+
|
54
|
+
# # current_id: BlaTest
|
55
|
+
# # cart: {
|
56
|
+
# # carts: {
|
57
|
+
# # products: [{current_id}]
|
58
|
+
# # }
|
59
|
+
|
60
|
+
# # }
|
@@ -0,0 +1,352 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "reform/form/dry"
|
3
|
+
|
4
|
+
class DryValidationDefaultGroupTest < Minitest::Spec
|
5
|
+
Session = Struct.new(:username, :email, :password, :confirm_password)
|
6
|
+
|
7
|
+
class SessionForm < Reform::Form
|
8
|
+
include Reform::Form::Dry
|
9
|
+
|
10
|
+
property :username
|
11
|
+
property :email
|
12
|
+
property :password
|
13
|
+
property :confirm_password
|
14
|
+
|
15
|
+
validation do
|
16
|
+
key(:username).required
|
17
|
+
key(:email).required
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
let (:form) { SessionForm.new(Session.new) }
|
22
|
+
|
23
|
+
# valid.
|
24
|
+
it do
|
25
|
+
form.validate(username: "Helloween",
|
26
|
+
email: "yep").must_equal true
|
27
|
+
form.errors.messages.inspect.must_equal "{}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class ValidationGroupsTest < MiniTest::Spec
|
32
|
+
describe "basic validations" do
|
33
|
+
Session = Struct.new(:username, :email, :password, :confirm_password)
|
34
|
+
|
35
|
+
class SessionForm < Reform::Form
|
36
|
+
include Reform::Form::Dry::Validations
|
37
|
+
|
38
|
+
property :username
|
39
|
+
property :email
|
40
|
+
property :password
|
41
|
+
property :confirm_password
|
42
|
+
|
43
|
+
validation :default do
|
44
|
+
key(:username).required
|
45
|
+
key(:email).required
|
46
|
+
end
|
47
|
+
|
48
|
+
validation :email, if: :default do
|
49
|
+
key(:email).required(min_size?: 3)
|
50
|
+
end
|
51
|
+
|
52
|
+
validation :nested, if: :default do
|
53
|
+
key(:password).required(min_size?: 2)
|
54
|
+
end
|
55
|
+
|
56
|
+
validation :confirm, if: :default, after: :email do
|
57
|
+
key(:confirm_password).required(min_size?: 2)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
let (:form) { SessionForm.new(Session.new) }
|
62
|
+
|
63
|
+
# valid.
|
64
|
+
it do
|
65
|
+
form.validate({ username: "Helloween",
|
66
|
+
email: "yep",
|
67
|
+
password: "99",
|
68
|
+
confirm_password: "99" }).must_equal true
|
69
|
+
form.errors.messages.inspect.must_equal "{}"
|
70
|
+
end
|
71
|
+
|
72
|
+
# invalid.
|
73
|
+
it do
|
74
|
+
form.validate({}).must_equal false
|
75
|
+
form.errors.messages.inspect.must_equal "{:username=>[\"is missing\"], :email=>[\"is missing\"]}"
|
76
|
+
end
|
77
|
+
|
78
|
+
# partially invalid.
|
79
|
+
# 2nd group fails.
|
80
|
+
it do
|
81
|
+
form.validate(username: "Helloween", email: "yo", confirm_password:"9").must_equal false
|
82
|
+
form.errors.messages.inspect.must_equal "{:email=>[\"size cannot be less than 3\"], :confirm_password=>[\"size cannot be less than 2\"], :password=>[\"is missing\", \"size cannot be less than 2\"]}"
|
83
|
+
end
|
84
|
+
# 3rd group fails.
|
85
|
+
it do
|
86
|
+
form.validate(username: "Helloween", email: "yo!", confirm_password:"9").must_equal false
|
87
|
+
form.errors.messages.inspect
|
88
|
+
.must_equal "{:confirm_password=>[\"size cannot be less than 2\"], :password=>[\"is missing\", \"size cannot be less than 2\"]}"
|
89
|
+
end
|
90
|
+
# 4th group with after: fails.
|
91
|
+
it do
|
92
|
+
form.validate(username: "Helloween", email: "yo!", password: "", confirm_password: "9").must_equal false
|
93
|
+
form.errors.messages.inspect.must_equal "{:confirm_password=>[\"size cannot be less than 2\"], :password=>[\"must be filled\", \"size cannot be less than 2\"]}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "Nested validations" do
|
98
|
+
class AlbumForm < Reform::Form
|
99
|
+
include Reform::Form::Dry::Validations
|
100
|
+
|
101
|
+
property :title
|
102
|
+
|
103
|
+
property :hit do
|
104
|
+
property :title
|
105
|
+
|
106
|
+
# FIX ME: this doesn't work now, @apotonick said he knows why
|
107
|
+
# The error is that this validation block act as an AM:V instead of the Dry one.
|
108
|
+
# validation :default do
|
109
|
+
# key(:title, &:filled?)
|
110
|
+
# end
|
111
|
+
end
|
112
|
+
|
113
|
+
collection :songs do
|
114
|
+
property :title
|
115
|
+
end
|
116
|
+
|
117
|
+
property :band do
|
118
|
+
property :name
|
119
|
+
property :label do
|
120
|
+
property :name
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
validation :default do
|
125
|
+
|
126
|
+
key(:title).required
|
127
|
+
|
128
|
+
key(:band).schema do
|
129
|
+
key(:name).required
|
130
|
+
key(:label).schema do
|
131
|
+
key(:name).required
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
configure do
|
136
|
+
option :form
|
137
|
+
# message need to be defined on fixtures/dry_error_messages
|
138
|
+
# d-v expects you to define your custome messages on the .yml file
|
139
|
+
def good_musical_taste?(value)
|
140
|
+
value != 'Nickelback'
|
141
|
+
end
|
142
|
+
|
143
|
+
def form_access_validation?(value)
|
144
|
+
form.title == 'Reform'
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
key(:title).required(:good_musical_taste?)
|
149
|
+
key(:title).required(:form_access_validation?)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
let (:album) do
|
154
|
+
OpenStruct.new(
|
155
|
+
:title => "Blackhawks Over Los Angeles",
|
156
|
+
:hit => song,
|
157
|
+
:songs => songs,
|
158
|
+
:band => Struct.new(:name, :label).new("Epitaph", OpenStruct.new),
|
159
|
+
)
|
160
|
+
end
|
161
|
+
let (:song) { OpenStruct.new(:title => "Downtown") }
|
162
|
+
let (:songs) { [ song = OpenStruct.new(:title => "Calling"), song ] }
|
163
|
+
let (:form) { AlbumForm.new(album) }
|
164
|
+
|
165
|
+
# correct #validate.
|
166
|
+
it do
|
167
|
+
result = form.validate(
|
168
|
+
"title" => "Reform",
|
169
|
+
"songs" => [
|
170
|
+
{"title" => "Fallout"},
|
171
|
+
{"title" => "Roxanne", "composer" => {"name" => "Sting"}}
|
172
|
+
],
|
173
|
+
"band" => {"label" => {"name" => "Epitaph"}},
|
174
|
+
)
|
175
|
+
|
176
|
+
result.must_equal true
|
177
|
+
form.errors.messages.inspect.must_equal "{}"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
describe "fails with :validate, :validates and :validates_with" do
|
183
|
+
|
184
|
+
it "throws a goddamn error" do
|
185
|
+
e = proc do
|
186
|
+
class FailingForm < Reform::Form
|
187
|
+
include Reform::Form::Dry::Validations
|
188
|
+
|
189
|
+
property :username
|
190
|
+
|
191
|
+
validation :email do
|
192
|
+
validates(:email, &:filled?)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end.must_raise(NoMethodError)
|
196
|
+
# e.message.must_equal 'validates() is not supported by Dry Validation backend.'
|
197
|
+
|
198
|
+
e = proc do
|
199
|
+
class FailingForm < Reform::Form
|
200
|
+
include Reform::Form::Dry::Validations
|
201
|
+
|
202
|
+
property :username
|
203
|
+
|
204
|
+
validation :email do
|
205
|
+
validate(:email, &:filled?)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end.must_raise(NoMethodError)
|
209
|
+
# e.message.must_equal 'validate() is not supported by Dry Validation backend.'
|
210
|
+
|
211
|
+
e = proc do
|
212
|
+
class FailingForm < Reform::Form
|
213
|
+
include Reform::Form::Dry::Validations
|
214
|
+
|
215
|
+
property :username
|
216
|
+
|
217
|
+
validation :email do
|
218
|
+
validates_with(:email, &:filled?)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end.must_raise(NoMethodError)
|
222
|
+
# e.message.must_equal (NoMethodError)'validates_with() is not supported by Dry Validation backend.'
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
|
227
|
+
# describe "same-named group" do
|
228
|
+
# class OverwritingForm < Reform::Form
|
229
|
+
# include Reform::Form::Dry::Validations
|
230
|
+
|
231
|
+
# property :username
|
232
|
+
# property :email
|
233
|
+
|
234
|
+
# validation :email do # FIX ME: is this working for other validator or just bugging here?
|
235
|
+
# key(:email, &:filled?) # it's not considered, overitten
|
236
|
+
# end
|
237
|
+
|
238
|
+
# validation :email do # just another group.
|
239
|
+
# key(:username, &:filled?)
|
240
|
+
# end
|
241
|
+
# end
|
242
|
+
|
243
|
+
# let (:form) { OverwritingForm.new(Session.new) }
|
244
|
+
|
245
|
+
# # valid.
|
246
|
+
# it do
|
247
|
+
# form.validate({username: "Helloween"}).must_equal true
|
248
|
+
# end
|
249
|
+
|
250
|
+
# # invalid.
|
251
|
+
# it "whoo" do
|
252
|
+
# form.validate({}).must_equal false
|
253
|
+
# form.errors.messages.inspect.must_equal "{:username=>[\"username can't be blank\"]}"
|
254
|
+
# end
|
255
|
+
# end
|
256
|
+
|
257
|
+
|
258
|
+
describe "inherit: true in same group" do
|
259
|
+
class InheritSameGroupForm < Reform::Form
|
260
|
+
include Reform::Form::Dry::Validations
|
261
|
+
|
262
|
+
property :username
|
263
|
+
property :email
|
264
|
+
|
265
|
+
validation :email do
|
266
|
+
key(:email).required
|
267
|
+
end
|
268
|
+
|
269
|
+
validation :email, inherit: true do # extends the above.
|
270
|
+
key(:username).required
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
let (:form) { InheritSameGroupForm.new(Session.new) }
|
275
|
+
|
276
|
+
# valid.
|
277
|
+
it do
|
278
|
+
form.validate({username: "Helloween", email: 9}).must_equal true
|
279
|
+
end
|
280
|
+
|
281
|
+
# invalid.
|
282
|
+
it do
|
283
|
+
form.validate({}).must_equal false
|
284
|
+
form.errors.messages.inspect.must_equal "{:email=>[\"is missing\"], :username=>[\"is missing\"]}"
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
|
289
|
+
describe "if: with lambda" do
|
290
|
+
class IfWithLambdaForm < Reform::Form
|
291
|
+
include Reform::Form::Dry::Validations # ::build_errors.
|
292
|
+
|
293
|
+
property :username
|
294
|
+
property :email
|
295
|
+
property :password
|
296
|
+
|
297
|
+
validation :email do
|
298
|
+
key(:email).required
|
299
|
+
end
|
300
|
+
|
301
|
+
# run this is :email group is true.
|
302
|
+
validation :after_email, if: lambda { |results| results[:email]==true } do # extends the above.
|
303
|
+
key(:username).required
|
304
|
+
end
|
305
|
+
|
306
|
+
# block gets evaled in form instance context.
|
307
|
+
validation :password, if: lambda { |results| email == "john@trb.org" } do
|
308
|
+
key(:password).required
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
let (:form) { IfWithLambdaForm.new(Session.new) }
|
313
|
+
|
314
|
+
# valid.
|
315
|
+
it do
|
316
|
+
form.validate({username: "Strung Out", email: 9}).must_equal true
|
317
|
+
end
|
318
|
+
|
319
|
+
# invalid.
|
320
|
+
it do
|
321
|
+
form.validate({email: 9}).must_equal false
|
322
|
+
form.errors.messages.inspect.must_equal "{:username=>[\"is missing\"]}"
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
|
327
|
+
# Currenty dry-v don't support that option, it doesn't make sense
|
328
|
+
# I've talked to @solnic and he plans to add a "hint" feature to show
|
329
|
+
# more errors messages than only those that have failed.
|
330
|
+
#
|
331
|
+
# describe "multiple errors for property" do
|
332
|
+
# class MultipleErrorsForPropertyForm < Reform::Form
|
333
|
+
# include Reform::Form::Dry::Validations
|
334
|
+
|
335
|
+
# property :username
|
336
|
+
|
337
|
+
# validation :default do
|
338
|
+
# key(:username) do |username|
|
339
|
+
# username.filled? | (username.min_size?(2) & username.max_size?(3))
|
340
|
+
# end
|
341
|
+
# end
|
342
|
+
# end
|
343
|
+
|
344
|
+
# let (:form) { MultipleErrorsForPropertyForm.new(Session.new) }
|
345
|
+
|
346
|
+
# # valid.
|
347
|
+
# it do
|
348
|
+
# form.validate({username: ""}).must_equal false
|
349
|
+
# form.errors.messages.inspect.must_equal "{:username=>[\"username must be filled\", \"username is not proper size\"]}"
|
350
|
+
# end
|
351
|
+
# end
|
352
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class VirtualTest < MiniTest::Spec
|
4
|
+
class CreditCardForm < Reform::Form
|
5
|
+
property :credit_card_number, virtual: true # no read, no write, it's virtual.
|
6
|
+
end
|
7
|
+
|
8
|
+
let (:form) { CreditCardForm.new(Object.new) }
|
9
|
+
|
10
|
+
it {
|
11
|
+
form.validate("credit_card_number" => "123")
|
12
|
+
|
13
|
+
form.credit_card_number.must_equal "123" # this is still readable in the UI.
|
14
|
+
|
15
|
+
form.sync
|
16
|
+
|
17
|
+
hash = {}
|
18
|
+
form.save do |nested|
|
19
|
+
hash = nested
|
20
|
+
end
|
21
|
+
|
22
|
+
hash.must_equal("credit_card_number"=> "123")
|
23
|
+
}
|
24
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class WriteableTest < MiniTest::Spec
|
4
|
+
Location = Struct.new(:country)
|
5
|
+
|
6
|
+
class LocationForm < Reform::Form
|
7
|
+
property :country, writeable: false
|
8
|
+
end
|
9
|
+
|
10
|
+
let (:loc) { Location.new("Australia") }
|
11
|
+
let (:form) { LocationForm.new(loc) }
|
12
|
+
|
13
|
+
it do
|
14
|
+
form.country.must_equal "Australia"
|
15
|
+
|
16
|
+
form.validate("country" => "Germany") # this usually won't change when submitting.
|
17
|
+
form.country.must_equal "Germany"
|
18
|
+
|
19
|
+
form.sync
|
20
|
+
loc.country.must_equal "Australia" # the writer wasn't called.
|
21
|
+
|
22
|
+
hash = {}
|
23
|
+
form.save do |nested|
|
24
|
+
hash = nested
|
25
|
+
end
|
26
|
+
|
27
|
+
hash.must_equal("country"=> "Germany")
|
28
|
+
end
|
29
|
+
end
|