form_input 0.9.0.pre1
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 +5 -0
- data/LICENSE +19 -0
- data/README.md +3160 -0
- data/Rakefile +19 -0
- data/example/controllers/ramaze/press_release.rb +104 -0
- data/example/controllers/ramaze/profile.rb +38 -0
- data/example/controllers/sinatra/press_release.rb +114 -0
- data/example/controllers/sinatra/profile.rb +39 -0
- data/example/forms/change_password_form.rb +17 -0
- data/example/forms/login_form.rb +14 -0
- data/example/forms/lost_password_form.rb +14 -0
- data/example/forms/new_password_form.rb +15 -0
- data/example/forms/password_form.rb +18 -0
- data/example/forms/press_release_form.rb +153 -0
- data/example/forms/profile_form.rb +21 -0
- data/example/forms/signup_form.rb +25 -0
- data/example/views/press_release.slim +65 -0
- data/example/views/profile.slim +28 -0
- data/example/views/snippets/form_block.slim +27 -0
- data/example/views/snippets/form_chunked.slim +25 -0
- data/example/views/snippets/form_hidden.slim +21 -0
- data/example/views/snippets/form_panel.slim +89 -0
- data/form_input.gemspec +32 -0
- data/lib/form_input/core.rb +1165 -0
- data/lib/form_input/localize.rb +49 -0
- data/lib/form_input/r18n/cs.yml +97 -0
- data/lib/form_input/r18n/en.yml +70 -0
- data/lib/form_input/r18n/pl.yml +122 -0
- data/lib/form_input/r18n/sk.yml +120 -0
- data/lib/form_input/r18n.rb +163 -0
- data/lib/form_input/steps.rb +365 -0
- data/lib/form_input/types.rb +176 -0
- data/lib/form_input/version.rb +12 -0
- data/lib/form_input.rb +5 -0
- data/test/helper.rb +21 -0
- data/test/localize/en.yml +63 -0
- data/test/r18n/cs.yml +60 -0
- data/test/r18n/xx.yml +51 -0
- data/test/reference/cs.txt +352 -0
- data/test/reference/cs.yml +14 -0
- data/test/reference/en.txt +76 -0
- data/test/reference/en.yml +8 -0
- data/test/reference/pl.txt +440 -0
- data/test/reference/pl.yml +16 -0
- data/test/reference/sk.txt +352 -0
- data/test/reference/sk.yml +14 -0
- data/test/test_core.rb +1272 -0
- data/test/test_localize.rb +27 -0
- data/test/test_r18n.rb +373 -0
- data/test/test_steps.rb +482 -0
- data/test/test_types.rb +307 -0
- metadata +145 -0
@@ -0,0 +1,1165 @@
|
|
1
|
+
# Form input.
|
2
|
+
|
3
|
+
# Class for easy form input processing and management.
|
4
|
+
class FormInput
|
5
|
+
|
6
|
+
# Constants.
|
7
|
+
|
8
|
+
# Default size limit applied to all input.
|
9
|
+
DEFAULT_SIZE_LIMIT = 255
|
10
|
+
|
11
|
+
# Default input filter applied to all input.
|
12
|
+
DEFAULT_FILTER = ->{ gsub( /\s+/, ' ' ).strip }
|
13
|
+
|
14
|
+
# Minimum hash key value we allow by default.
|
15
|
+
DEFAULT_MIN_KEY = 0
|
16
|
+
|
17
|
+
# Maximum hash key value we allow by default.
|
18
|
+
DEFAULT_MAX_KEY = ( 1 << 64 ) - 1
|
19
|
+
|
20
|
+
# Encoding we convert all input request parameters into.
|
21
|
+
DEFAULT_ENCODING = Encoding::UTF_8
|
22
|
+
|
23
|
+
# Hash mapping error codes to default error messages.
|
24
|
+
DEFAULT_ERROR_MESSAGES = {
|
25
|
+
required_scalar: "%p is required",
|
26
|
+
required_array: "%p are required",
|
27
|
+
not_array: "%p are not an array",
|
28
|
+
not_hash: "%p are not a hash",
|
29
|
+
not_string: "%p is not a string",
|
30
|
+
match_key: "%p contain invalid key",
|
31
|
+
invalid_key: "%p contain invalid key",
|
32
|
+
min_key: "%p contain too small key",
|
33
|
+
max_key: "%p contain too large key",
|
34
|
+
min_count: "%p must have at least",
|
35
|
+
max_count: "%p may have at most",
|
36
|
+
value_type: "%p like this is not valid",
|
37
|
+
element_type: "%p contain invalid value",
|
38
|
+
min_limit: "%p must be at least",
|
39
|
+
max_limit: "%p may be at most",
|
40
|
+
inf_limit: "%p must be greater than",
|
41
|
+
sup_limit: "%p must be less than",
|
42
|
+
invalid_encoding: "%p must use valid encoding",
|
43
|
+
invalid_characters: "%p may not contain invalid characters",
|
44
|
+
min_size: "%p must have at least",
|
45
|
+
max_size: "%p may have at most",
|
46
|
+
min_bytesize: "%p must have at least",
|
47
|
+
max_bytesize: "%p may have at most",
|
48
|
+
reject_msg: "%p like this is not allowed",
|
49
|
+
match_msg: "%p like this is not valid",
|
50
|
+
}
|
51
|
+
|
52
|
+
# Form parameter.
|
53
|
+
class Parameter
|
54
|
+
|
55
|
+
# Form this parameter belongs to.
|
56
|
+
attr_reader :form
|
57
|
+
|
58
|
+
# Name of the parameter as we use it internally in our code.
|
59
|
+
attr_reader :name
|
60
|
+
|
61
|
+
# Name of the parameter as we use it in the form fields and url queries.
|
62
|
+
attr_reader :code
|
63
|
+
|
64
|
+
# Additional parameter options.
|
65
|
+
attr_reader :opts
|
66
|
+
|
67
|
+
# Initialize new parameter.
|
68
|
+
def initialize( name, code, opts )
|
69
|
+
@name = name.freeze
|
70
|
+
@code = code.freeze
|
71
|
+
@opts = opts.freeze
|
72
|
+
end
|
73
|
+
|
74
|
+
# Allow copies to evaluate tags again.
|
75
|
+
def initialize_dup( other )
|
76
|
+
super
|
77
|
+
@tags = nil
|
78
|
+
end
|
79
|
+
|
80
|
+
# Bind self to given form instance. Can be done only once.
|
81
|
+
def bind( form )
|
82
|
+
fail "parameter #{name} is already bound" if @form
|
83
|
+
@form = form
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
87
|
+
# Get the value of this parameter. Always nil for unbound parameters.
|
88
|
+
def value
|
89
|
+
form ? form[ name ] : nil
|
90
|
+
end
|
91
|
+
|
92
|
+
# Format given value for form/URL output, applying the formatting filter as necessary.
|
93
|
+
def format_value( value )
|
94
|
+
if format.nil? or value.nil? or ( value.is_a?( String ) and type = self[ :class ] and type != String )
|
95
|
+
value.to_s
|
96
|
+
else
|
97
|
+
value.instance_exec( &format ).to_s
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Get value of this parameter for use in form/URL, with all scalar values converted to strings.
|
102
|
+
def form_value
|
103
|
+
if array?
|
104
|
+
[ *value ].map{ |x| format_value( x ) }
|
105
|
+
elsif hash?
|
106
|
+
Hash[ [ *value ].map{ |k, v| [ k.to_s, format_value( v ) ] } ]
|
107
|
+
else
|
108
|
+
format_value( value )
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Test if given parameter has value of correct type.
|
113
|
+
def correct?
|
114
|
+
case v = value
|
115
|
+
when nil
|
116
|
+
true
|
117
|
+
when String
|
118
|
+
scalar?
|
119
|
+
when Array
|
120
|
+
array?
|
121
|
+
when Hash
|
122
|
+
hash?
|
123
|
+
end or ( scalar? && [ *self[ :class ] ].any?{ |x| v.is_a?( x ) } )
|
124
|
+
end
|
125
|
+
|
126
|
+
# Test if given parameter has value of incorrect type.
|
127
|
+
def incorrect?
|
128
|
+
not correct?
|
129
|
+
end
|
130
|
+
|
131
|
+
# Test if given parameter has blank value.
|
132
|
+
def blank?
|
133
|
+
case v = value
|
134
|
+
when nil
|
135
|
+
true
|
136
|
+
when String
|
137
|
+
v.empty? or !! ( v.valid_encoding? && v =~ /\A\s*\z/ )
|
138
|
+
when Array, Hash
|
139
|
+
v.empty?
|
140
|
+
else
|
141
|
+
false
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Test if given parameter has empty value.
|
146
|
+
def empty?
|
147
|
+
case v = value
|
148
|
+
when nil
|
149
|
+
true
|
150
|
+
when String, Array, Hash
|
151
|
+
v.empty?
|
152
|
+
else
|
153
|
+
false
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Test if given parameter has non-empty value.
|
158
|
+
def filled?
|
159
|
+
not empty?
|
160
|
+
end
|
161
|
+
|
162
|
+
# Get the proper name for use in form names, adding [] to array and [key] to hash parameters.
|
163
|
+
def form_name( key = nil )
|
164
|
+
if array?
|
165
|
+
"#{code}[]"
|
166
|
+
elsif hash?
|
167
|
+
fail( ArgumentError, "missing hash key" ) if key.nil?
|
168
|
+
"#{code}[#{key}]"
|
169
|
+
else
|
170
|
+
code.to_s
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Test if given value is the selected value, for use in form selects.
|
175
|
+
def selected?( value )
|
176
|
+
if empty?
|
177
|
+
false
|
178
|
+
elsif array?
|
179
|
+
self.value.include?( value )
|
180
|
+
elsif hash?
|
181
|
+
false
|
182
|
+
else
|
183
|
+
self.value == value
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Get the name of the parameter to be displayed to the user, or nil if there is none.
|
188
|
+
def title
|
189
|
+
self[ :title ]
|
190
|
+
end
|
191
|
+
|
192
|
+
# Get the title for use in form. Fallbacks to normal title and code if no form title was specified.
|
193
|
+
def form_title
|
194
|
+
self[ :form_title ] || title || code.to_s
|
195
|
+
end
|
196
|
+
|
197
|
+
# Get the title for use in error messages. Fallbacks to normal title and code if no form title was specified.
|
198
|
+
def error_title
|
199
|
+
self[ :error_title ] || title || code.to_s
|
200
|
+
end
|
201
|
+
|
202
|
+
# Get list of errors reported for this paramater. Always empty for unbound parameters.
|
203
|
+
def errors
|
204
|
+
form ? form.errors_for( name ) : []
|
205
|
+
end
|
206
|
+
|
207
|
+
# Get first error reported for this parameter. Always nil for unbound parameters.
|
208
|
+
def error
|
209
|
+
errors.first
|
210
|
+
end
|
211
|
+
|
212
|
+
# Test if this parameter had no errors reported.
|
213
|
+
def valid?
|
214
|
+
errors.empty?
|
215
|
+
end
|
216
|
+
|
217
|
+
# Test if this parameter had some errors reported.
|
218
|
+
def invalid?
|
219
|
+
not valid?
|
220
|
+
end
|
221
|
+
|
222
|
+
# Test if the parameter is required.
|
223
|
+
def required?
|
224
|
+
!! self[ :required ]
|
225
|
+
end
|
226
|
+
|
227
|
+
# Test if the parameter is optional.
|
228
|
+
def optional?
|
229
|
+
not required?
|
230
|
+
end
|
231
|
+
|
232
|
+
# Test if the parameter is disabled.
|
233
|
+
def disabled?
|
234
|
+
!! self[ :disabled ]
|
235
|
+
end
|
236
|
+
|
237
|
+
# Test if the parameter is enabled.
|
238
|
+
def enabled?
|
239
|
+
not disabled?
|
240
|
+
end
|
241
|
+
|
242
|
+
# Get type of the parameter. Defaults to :text.
|
243
|
+
def type
|
244
|
+
self[ :type ] || :text
|
245
|
+
end
|
246
|
+
|
247
|
+
# Test if the parameter is hidden.
|
248
|
+
def hidden?
|
249
|
+
type == :hidden
|
250
|
+
end
|
251
|
+
|
252
|
+
# Test if the parameter is to be ignored on output.
|
253
|
+
def ignored?
|
254
|
+
type == :ignore
|
255
|
+
end
|
256
|
+
|
257
|
+
# Test if the parameter is visible.
|
258
|
+
def visible?
|
259
|
+
not ( hidden? || ignored? )
|
260
|
+
end
|
261
|
+
|
262
|
+
# Test if this is an array parameter.
|
263
|
+
def array?
|
264
|
+
!! self[ :array ]
|
265
|
+
end
|
266
|
+
|
267
|
+
# Test if this is a hash parameter.
|
268
|
+
def hash?
|
269
|
+
!! self[ :hash ]
|
270
|
+
end
|
271
|
+
|
272
|
+
# Test if this is a scalar parameter.
|
273
|
+
def scalar?
|
274
|
+
not ( array? || hash? )
|
275
|
+
end
|
276
|
+
|
277
|
+
# Get list of tags of this parameter.
|
278
|
+
def tags
|
279
|
+
@tags ||= [ *self[ :tag ], *self[ :tags ] ]
|
280
|
+
end
|
281
|
+
|
282
|
+
# Test if the parameter is tagged with some of given tags, or any tag if the argument list is empty.
|
283
|
+
def tagged?( *tags )
|
284
|
+
t = self.tags
|
285
|
+
if tags.empty?
|
286
|
+
not t.empty?
|
287
|
+
else
|
288
|
+
tags.flatten.any?{ |x| t.include? x }
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
# Test if the parameter is not tagged with any of given tags, or any tag if the argument list is empty.
|
293
|
+
def untagged?( *tags )
|
294
|
+
not tagged?( *tags )
|
295
|
+
end
|
296
|
+
|
297
|
+
# Get input filter for this paramater, if any.
|
298
|
+
def filter
|
299
|
+
opts[ :filter ]
|
300
|
+
end
|
301
|
+
|
302
|
+
# Get input transform for this paramater, if any.
|
303
|
+
def transform
|
304
|
+
opts[ :transform ]
|
305
|
+
end
|
306
|
+
|
307
|
+
# Get output filter for this paramater, if any.
|
308
|
+
def format
|
309
|
+
opts[ :format ]
|
310
|
+
end
|
311
|
+
|
312
|
+
# Get data relevant for this parameter, if any. Returns empty array if there are none.
|
313
|
+
def data
|
314
|
+
self[ :data ] || []
|
315
|
+
end
|
316
|
+
|
317
|
+
# Methods affected by localization, put in separate module for easier overloading.
|
318
|
+
module LocaleMethods
|
319
|
+
|
320
|
+
# Get value of arbitrary option. Automatically resolves call blocks.
|
321
|
+
def []( name )
|
322
|
+
o = opts[ name ]
|
323
|
+
o = instance_exec( &o ) if o.is_a?( Proc )
|
324
|
+
o
|
325
|
+
end
|
326
|
+
|
327
|
+
# Format the error report message. Default implementation includes simple pluralizer.
|
328
|
+
# String %p in the message is automatically replaced with error title.
|
329
|
+
# Can be redefined to provide correctly localized error messages.
|
330
|
+
def format_error_message( msg, count = nil, singular = nil, plural = "#{singular}s" )
|
331
|
+
msg = DEFAULT_ERROR_MESSAGES[ msg ] || msg.to_s
|
332
|
+
msg += " #{count}" if count
|
333
|
+
msg += " #{ count == 1 ? singular : plural }" if singular
|
334
|
+
msg.gsub( '%p', error_title )
|
335
|
+
end
|
336
|
+
|
337
|
+
# Report an error concerning this parameter.
|
338
|
+
# String %p in the message is automatically replaced with error title.
|
339
|
+
# Returns self for chaining.
|
340
|
+
def report( msg, *args )
|
341
|
+
form.report( name, format_error_message( msg, *args ) ) if form
|
342
|
+
self
|
343
|
+
end
|
344
|
+
|
345
|
+
end
|
346
|
+
|
347
|
+
include LocaleMethods
|
348
|
+
|
349
|
+
# Validation.
|
350
|
+
|
351
|
+
# Validate this parameter. Does nothing if it was found invalid already.
|
352
|
+
def validate
|
353
|
+
return if invalid?
|
354
|
+
|
355
|
+
# First of all, make sure required parameters are present and not empty.
|
356
|
+
|
357
|
+
if required? && empty?
|
358
|
+
report( self[ :required_msg ] || ( scalar? ? :required_scalar : :required_array ) )
|
359
|
+
return
|
360
|
+
end
|
361
|
+
|
362
|
+
# Otherwise empty parameters are considered correct, as long as the type is correct.
|
363
|
+
|
364
|
+
return if empty? && correct?
|
365
|
+
|
366
|
+
# Make sure the parameter value contains only valid data.
|
367
|
+
|
368
|
+
return unless if array?
|
369
|
+
validate_array( value )
|
370
|
+
elsif hash?
|
371
|
+
validate_hash( value )
|
372
|
+
else
|
373
|
+
validate_value( value )
|
374
|
+
end
|
375
|
+
|
376
|
+
# Finally, invoke the custom check callback if there is any.
|
377
|
+
|
378
|
+
if check = opts[ :check ]
|
379
|
+
instance_exec( &check )
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
private
|
384
|
+
|
385
|
+
# Validate given array.
|
386
|
+
# Return true if the entire array validated correctly, nil or false otherwise.
|
387
|
+
def validate_array( value )
|
388
|
+
|
389
|
+
# Make sure it's an array in the first place.
|
390
|
+
|
391
|
+
unless value.is_a? Array
|
392
|
+
report( :not_array )
|
393
|
+
return
|
394
|
+
end
|
395
|
+
|
396
|
+
# Enforce array limits.
|
397
|
+
|
398
|
+
return unless validate_count( value )
|
399
|
+
|
400
|
+
# Now validate array elements. If we detect problems, don't bother with the rest.
|
401
|
+
|
402
|
+
value.all?{ |v| validate_value( v ) }
|
403
|
+
end
|
404
|
+
|
405
|
+
# Validate given hash.
|
406
|
+
# Return true if the entire hash validated correctly, nil or false otherwise.
|
407
|
+
def validate_hash( value )
|
408
|
+
|
409
|
+
# Make sure it's a hash in the first place.
|
410
|
+
|
411
|
+
unless value.is_a? Hash
|
412
|
+
report( :not_hash )
|
413
|
+
return
|
414
|
+
end
|
415
|
+
|
416
|
+
# Enforce hash limits.
|
417
|
+
|
418
|
+
return unless validate_count( value )
|
419
|
+
|
420
|
+
# Now validate hash keys and values. If we detect problems, don't bother with the rest.
|
421
|
+
|
422
|
+
value.all?{ |k, v| validate_key( k ) && validate_value( v ) }
|
423
|
+
end
|
424
|
+
|
425
|
+
# Validate given hash key.
|
426
|
+
# Return true if it validated correctly, nil or false otherwise.
|
427
|
+
def validate_key( value )
|
428
|
+
|
429
|
+
# If there is a key pattern specified, make sure the key matches.
|
430
|
+
|
431
|
+
if patterns = self[ :match_key ]
|
432
|
+
unless [ *patterns ].all?{ |x| value.to_s =~ x }
|
433
|
+
report( :match_key )
|
434
|
+
return
|
435
|
+
end
|
436
|
+
return true
|
437
|
+
end
|
438
|
+
|
439
|
+
# Otherwise make sure it's an integer.
|
440
|
+
|
441
|
+
unless value.is_a? Integer
|
442
|
+
report( :invalid_key )
|
443
|
+
return
|
444
|
+
end
|
445
|
+
|
446
|
+
# Make sure it is within allowed limits.
|
447
|
+
|
448
|
+
if limit = self[ :min_key ] and value < limit
|
449
|
+
report( :min_key )
|
450
|
+
return
|
451
|
+
end
|
452
|
+
|
453
|
+
if limit = self[ :max_key ] and value > limit
|
454
|
+
report( :max_key )
|
455
|
+
return
|
456
|
+
end
|
457
|
+
|
458
|
+
true
|
459
|
+
end
|
460
|
+
|
461
|
+
# Validate container limits.
|
462
|
+
# Return true if it validated correctly, nil or false otherwise.
|
463
|
+
def validate_count( value )
|
464
|
+
|
465
|
+
if limit = self[ :min_count ] and value.count < limit
|
466
|
+
report( :min_count, limit, 'element' )
|
467
|
+
return
|
468
|
+
end
|
469
|
+
|
470
|
+
if limit = self[ :max_count ] and value.count > limit
|
471
|
+
report( :max_count, limit, 'element' )
|
472
|
+
return
|
473
|
+
end
|
474
|
+
|
475
|
+
true
|
476
|
+
end
|
477
|
+
|
478
|
+
# Validate given scalar value.
|
479
|
+
# Return true if it validated correctly, nil or false otherwise.
|
480
|
+
def validate_value( value )
|
481
|
+
|
482
|
+
# First apply the type tests.
|
483
|
+
|
484
|
+
if type = self[ :class ] and type != String
|
485
|
+
unless [ *type ].any?{ |x| value.is_a?( x ) }
|
486
|
+
report( scalar? ? :value_type : :element_type )
|
487
|
+
return
|
488
|
+
end
|
489
|
+
else
|
490
|
+
return unless validate_string( value )
|
491
|
+
end
|
492
|
+
|
493
|
+
# Then enforce any value limits.
|
494
|
+
|
495
|
+
if limit = self[ :min ] and value.to_f < limit.to_f
|
496
|
+
report( :min_limit, limit )
|
497
|
+
return
|
498
|
+
end
|
499
|
+
|
500
|
+
if limit = self[ :max ] and value.to_f > limit.to_f
|
501
|
+
report( :max_limit, limit )
|
502
|
+
return
|
503
|
+
end
|
504
|
+
|
505
|
+
if limit = self[ :inf ] and value.to_f <= limit.to_f
|
506
|
+
report( :inf_limit, limit )
|
507
|
+
return
|
508
|
+
end
|
509
|
+
|
510
|
+
if limit = self[ :sup ] and value.to_f >= limit.to_f
|
511
|
+
report( :sup_limit, limit )
|
512
|
+
return
|
513
|
+
end
|
514
|
+
|
515
|
+
# Finally, invoke the custom callback if there is any.
|
516
|
+
|
517
|
+
if test = opts[ :test ]
|
518
|
+
instance_exec( value, &test )
|
519
|
+
return unless valid?
|
520
|
+
end
|
521
|
+
|
522
|
+
true
|
523
|
+
end
|
524
|
+
|
525
|
+
# Validate given string.
|
526
|
+
# Return true if it validated correctly, nil or false otherwise.
|
527
|
+
def validate_string( value )
|
528
|
+
|
529
|
+
# Make sure it's a string in the first place.
|
530
|
+
|
531
|
+
unless value.is_a? String
|
532
|
+
report( scalar? ? :not_string : :element_type )
|
533
|
+
return
|
534
|
+
end
|
535
|
+
|
536
|
+
# Make sure the string contains only valid data.
|
537
|
+
|
538
|
+
unless value.valid_encoding? && ( value.encoding == DEFAULT_ENCODING || value.ascii_only? )
|
539
|
+
report( :invalid_encoding )
|
540
|
+
return
|
541
|
+
end
|
542
|
+
|
543
|
+
unless value =~ /\A(\p{Graph}|[ \t\r\n])*\z/u
|
544
|
+
report( :invalid_characters )
|
545
|
+
return
|
546
|
+
end
|
547
|
+
|
548
|
+
# Enforce any size limits.
|
549
|
+
|
550
|
+
if limit = self[ :min_size ] and value.size < limit
|
551
|
+
report( :min_size, limit, 'character' )
|
552
|
+
return
|
553
|
+
end
|
554
|
+
|
555
|
+
if limit = self[ :min_bytesize ] and value.bytesize < limit
|
556
|
+
report( :min_bytesize, limit, 'byte' )
|
557
|
+
return
|
558
|
+
end
|
559
|
+
|
560
|
+
if limit = self[ :max_size ] and value.size > limit
|
561
|
+
report( :max_size, limit, 'character' )
|
562
|
+
return
|
563
|
+
end
|
564
|
+
|
565
|
+
if limit = self[ :max_bytesize ] and value.bytesize > limit
|
566
|
+
report( :max_bytesize, limit, 'byte' )
|
567
|
+
return
|
568
|
+
end
|
569
|
+
|
570
|
+
# Finally make sure the format is valid.
|
571
|
+
|
572
|
+
if patterns = self[ :reject ]
|
573
|
+
if [ *patterns ].any?{ |x| value =~ x }
|
574
|
+
report( self[ :reject_msg ] || self[ :msg ] || :reject_msg )
|
575
|
+
return
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
if patterns = self[ :match ]
|
580
|
+
unless [ *patterns ].all?{ |x| value =~ x }
|
581
|
+
report( self[ :match_msg ] || self[ :msg ] || :match_msg )
|
582
|
+
return
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
true
|
587
|
+
end
|
588
|
+
|
589
|
+
end
|
590
|
+
|
591
|
+
# Class methods.
|
592
|
+
|
593
|
+
class << self
|
594
|
+
|
595
|
+
# Create standalone copy of form parameters in case someone inherits an existing form.
|
596
|
+
def inherited( into )
|
597
|
+
into.instance_variable_set( '@params', form_params.dup )
|
598
|
+
end
|
599
|
+
|
600
|
+
# Get hash mapping parameter names to parameters themselves.
|
601
|
+
def form_params
|
602
|
+
@params ||= {}
|
603
|
+
end
|
604
|
+
|
605
|
+
# Get given parameter(s), hash style.
|
606
|
+
def []( *names )
|
607
|
+
if names.count == 1
|
608
|
+
form_params[ names.first ]
|
609
|
+
else
|
610
|
+
form_params.values_at( *names )
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
# Add given parameter to the form, after performing basic validity checks.
|
615
|
+
def add( param )
|
616
|
+
name = param.name
|
617
|
+
|
618
|
+
fail ArgumentError, "duplicate parameter #{name}" if form_params[ name ]
|
619
|
+
fail ArgumentError, "invalid parameter name #{name}" if method_defined?( name )
|
620
|
+
|
621
|
+
self.send( :attr_accessor, name )
|
622
|
+
|
623
|
+
form_params[ name ] = param
|
624
|
+
end
|
625
|
+
private :add
|
626
|
+
|
627
|
+
# Copy given/all form parameters.
|
628
|
+
# Returns self for chaining.
|
629
|
+
def copy( source, opts = {} )
|
630
|
+
case source
|
631
|
+
when Parameter
|
632
|
+
add( Parameter.new(
|
633
|
+
opts[ :name ] || source.name,
|
634
|
+
opts[ :code ] || opts[ :name ] || source.code,
|
635
|
+
source.opts.merge( opts )
|
636
|
+
) )
|
637
|
+
when Array
|
638
|
+
source.each{ |x| copy( x, opts ) }
|
639
|
+
when Class
|
640
|
+
fail ArgumentError, "invalid source form #{source.inspect}" unless source < FormInput
|
641
|
+
copy( source.form_params.values, opts )
|
642
|
+
else
|
643
|
+
fail ArgumentError, "invalid source parameter #{source.inspect}"
|
644
|
+
end
|
645
|
+
self
|
646
|
+
end
|
647
|
+
|
648
|
+
# Define form parameter with given name, code, title, maximum size, options, and filter block.
|
649
|
+
# All fields except name are optional. In case the code is missing, name is used instead.
|
650
|
+
# If no size limits are specified, 255 characters and bytes limits are applied by default.
|
651
|
+
# If no filter is explicitly defined, default filter squeezing and stripping whitespace is applied.
|
652
|
+
# Returns self for chaining.
|
653
|
+
def param( name, *args, &block )
|
654
|
+
|
655
|
+
# Fetch arguments.
|
656
|
+
|
657
|
+
code = name
|
658
|
+
code = args.shift if args.first.is_a? Symbol
|
659
|
+
|
660
|
+
title = args.shift if args.first.is_a? String
|
661
|
+
|
662
|
+
size = args.shift if args.first.is_a? Numeric
|
663
|
+
|
664
|
+
opts = {}
|
665
|
+
opts.merge!( args.shift ) while args.first.is_a? Hash
|
666
|
+
|
667
|
+
fail ArgumentError, "invalid arguments #{args}" unless args.empty?
|
668
|
+
|
669
|
+
# Set the title.
|
670
|
+
|
671
|
+
opts[ :title ] = title.freeze if title
|
672
|
+
|
673
|
+
# Set input filter.
|
674
|
+
|
675
|
+
opts[ :filter ] = block if block
|
676
|
+
opts[ :filter ] = DEFAULT_FILTER unless opts.key?( :filter )
|
677
|
+
|
678
|
+
# Enforce default size limits for any input processed.
|
679
|
+
|
680
|
+
limit = DEFAULT_SIZE_LIMIT
|
681
|
+
|
682
|
+
size = ( opts[ :max_size ] ||= size || limit )
|
683
|
+
opts[ :max_bytesize ] ||= limit if size.is_a?( Proc ) or size <= limit
|
684
|
+
|
685
|
+
# Set default key limits for hash parameters.
|
686
|
+
|
687
|
+
if opts[ :hash ]
|
688
|
+
opts[ :min_key ] ||= DEFAULT_MIN_KEY
|
689
|
+
opts[ :max_key ] ||= DEFAULT_MAX_KEY
|
690
|
+
end
|
691
|
+
|
692
|
+
# Define parameter.
|
693
|
+
|
694
|
+
add( Parameter.new( name, code, opts ) )
|
695
|
+
self
|
696
|
+
end
|
697
|
+
|
698
|
+
# Like param, except this defines required parameter.
|
699
|
+
def param!( name, *args, &block )
|
700
|
+
param( name, *args, required: true, &block )
|
701
|
+
end
|
702
|
+
|
703
|
+
# Like param, except that it defines array parameter.
|
704
|
+
def array( name, *args, &block )
|
705
|
+
param( name, *args, array: true, &block )
|
706
|
+
end
|
707
|
+
|
708
|
+
# Like param!, except that it defines required array parameter.
|
709
|
+
def array!( name, *args, &block )
|
710
|
+
param!( name, *args, array: true, &block )
|
711
|
+
end
|
712
|
+
|
713
|
+
# Like param, except that it defines hash parameter.
|
714
|
+
def hash( name, *args, &block )
|
715
|
+
param( name, *args, hash: true, &block )
|
716
|
+
end
|
717
|
+
|
718
|
+
# Like param!, except that it defines required hash parameter.
|
719
|
+
def hash!( name, *args, &block )
|
720
|
+
param!( name, *args, hash: true, &block )
|
721
|
+
end
|
722
|
+
|
723
|
+
# Create new form from request with external values.
|
724
|
+
def from_request( request )
|
725
|
+
new.import( request )
|
726
|
+
end
|
727
|
+
|
728
|
+
# Create new form from hash with external values.
|
729
|
+
def from_params( params )
|
730
|
+
new.import( params )
|
731
|
+
end
|
732
|
+
|
733
|
+
# Create new form from hash with internal values.
|
734
|
+
def from_hash( hash )
|
735
|
+
new.set( hash )
|
736
|
+
end
|
737
|
+
|
738
|
+
end
|
739
|
+
|
740
|
+
# Instantiation and access.
|
741
|
+
|
742
|
+
# Get copy of parameter hash with parameters bound to this form.
|
743
|
+
def bound_params
|
744
|
+
hash = {}
|
745
|
+
self.class.form_params.each{ |name, param| hash[ name ] = param.dup.bind( self ) }
|
746
|
+
hash.freeze
|
747
|
+
end
|
748
|
+
private :bound_params
|
749
|
+
|
750
|
+
# Create new form info, initializing it from given hash or request, if anything.
|
751
|
+
def initialize( *args )
|
752
|
+
@params = bound_params
|
753
|
+
@errors = nil
|
754
|
+
for arg in args
|
755
|
+
if arg.is_a? Hash
|
756
|
+
set( arg )
|
757
|
+
else
|
758
|
+
import( arg )
|
759
|
+
end
|
760
|
+
end
|
761
|
+
end
|
762
|
+
|
763
|
+
# Initialize form clone.
|
764
|
+
def initialize_clone( other )
|
765
|
+
super
|
766
|
+
@params = bound_params
|
767
|
+
@errors &&= Hash[ @errors.map{ |k,v| [ k, v.clone ] } ]
|
768
|
+
end
|
769
|
+
|
770
|
+
# Initialize form copy.
|
771
|
+
def initialize_dup( other )
|
772
|
+
super
|
773
|
+
@params = bound_params
|
774
|
+
@errors = nil
|
775
|
+
end
|
776
|
+
|
777
|
+
# Freeze the form.
|
778
|
+
def freeze
|
779
|
+
unless frozen?
|
780
|
+
validate?
|
781
|
+
@errors.freeze.each{ |k,v| v.freeze }
|
782
|
+
end
|
783
|
+
super
|
784
|
+
end
|
785
|
+
|
786
|
+
# Import request parameter value.
|
787
|
+
def sanitize_value( value, filter = nil )
|
788
|
+
case value
|
789
|
+
when String
|
790
|
+
# Note that Rack does no encoding processing as of now,
|
791
|
+
# and even if we know content type charset, the query charset
|
792
|
+
# is not well defined and we can't fix the multi-part input here either.
|
793
|
+
# So we just hope that all clients will send the data in UTF-8 which we used in the form,
|
794
|
+
# and enforce everything to UTF-8. If it is not valid, we keep the binary string instead
|
795
|
+
# so the validation can detect it but the user can still process it himself if he wants to.
|
796
|
+
value = value.dup.force_encoding( DEFAULT_ENCODING )
|
797
|
+
if value.valid_encoding?
|
798
|
+
value = value.instance_exec( &filter ) if filter
|
799
|
+
else
|
800
|
+
value.force_encoding( Encoding::BINARY )
|
801
|
+
end
|
802
|
+
value
|
803
|
+
when Array
|
804
|
+
# Arrays are supported, but note that the validation done later only allows flat arrays.
|
805
|
+
value.map{ |x| sanitize_value( x, filter ) }
|
806
|
+
when Hash
|
807
|
+
# To reduce security issues, we prefer integer hash keys only.
|
808
|
+
# The validation done later ensures that the keys are valid, within range,
|
809
|
+
# and that only flat hashes are allowed.
|
810
|
+
Hash[ value.map{ |k, v| [ ( Integer( k, 10 ) rescue k ), sanitize_value( v, filter ) ] } ]
|
811
|
+
else
|
812
|
+
fail TypeError, "unexpected parameter type"
|
813
|
+
end
|
814
|
+
end
|
815
|
+
private :sanitize_value
|
816
|
+
|
817
|
+
# Import parameter values from given request or hash. Applies parameter input filters and transforms as well.
|
818
|
+
# Returns self for chaining.
|
819
|
+
def import( request )
|
820
|
+
for name, param in @params
|
821
|
+
if value = request[ param.code ]
|
822
|
+
value = sanitize_value( value, param.filter )
|
823
|
+
if transform = param.transform
|
824
|
+
value = value.instance_exec( &transform )
|
825
|
+
end
|
826
|
+
self[ name ] = value
|
827
|
+
end
|
828
|
+
end
|
829
|
+
self
|
830
|
+
end
|
831
|
+
|
832
|
+
# Set parameter values from given hash.
|
833
|
+
# Returns self for chaining.
|
834
|
+
def set( hash )
|
835
|
+
for name, value in hash
|
836
|
+
self[ name ] = value
|
837
|
+
end
|
838
|
+
self
|
839
|
+
end
|
840
|
+
|
841
|
+
# Clear all/given parameter values. Both names and parameters are accepted.
|
842
|
+
# Returns self for chaining.
|
843
|
+
def clear( *names )
|
844
|
+
names = names.empty? ? params_names : validate_names( names )
|
845
|
+
for name in names
|
846
|
+
self[ name ] = nil
|
847
|
+
end
|
848
|
+
self
|
849
|
+
end
|
850
|
+
|
851
|
+
# Get given parameter(s) value(s), hash style.
|
852
|
+
def []( *names )
|
853
|
+
if names.count == 1
|
854
|
+
send( names.first )
|
855
|
+
else
|
856
|
+
names.map{ |x| send( x ) }
|
857
|
+
end
|
858
|
+
end
|
859
|
+
|
860
|
+
# Set given parameter value, hash style.
|
861
|
+
# Unlike setting the attribute directly, this triggers a revalidation in the future.
|
862
|
+
def []=( name, value )
|
863
|
+
@errors = nil
|
864
|
+
send( "#{name}=", value )
|
865
|
+
end
|
866
|
+
|
867
|
+
# Return all non-empty parameters as a hash.
|
868
|
+
# See also url_params, which creates a hash suitable for url output.
|
869
|
+
def to_hash
|
870
|
+
result = {}
|
871
|
+
filled_params.each{ |x| result[ x.name ] = x.value }
|
872
|
+
result
|
873
|
+
end
|
874
|
+
alias to_h to_hash
|
875
|
+
|
876
|
+
# Convert parameters to names and fail if we encounter unknown one.
|
877
|
+
def validate_names( names )
|
878
|
+
names.flatten.map do |name|
|
879
|
+
name = name.name if name.is_a? Parameter
|
880
|
+
fail( ArgumentError, "unknown parameter #{name}" ) unless @params[ name ]
|
881
|
+
name
|
882
|
+
end
|
883
|
+
end
|
884
|
+
private :validate_names
|
885
|
+
|
886
|
+
# Create copy of itself, with given parameters unset. Both names and parameters are accepted.
|
887
|
+
def except( *names )
|
888
|
+
result = dup
|
889
|
+
for name in validate_names( names )
|
890
|
+
result[ name ] = nil
|
891
|
+
end
|
892
|
+
result
|
893
|
+
end
|
894
|
+
|
895
|
+
# Create copy of itself, with only given parameters set. Both names and parameters are accepted.
|
896
|
+
def only( *names )
|
897
|
+
# It would be easier to create new instance here and only copy selected values,
|
898
|
+
# but we want to use dup instead of new here, as the derived form can use
|
899
|
+
# different parameters in its construction.
|
900
|
+
result = dup
|
901
|
+
for name in params_names - validate_names( names )
|
902
|
+
result[ name ] = nil
|
903
|
+
end
|
904
|
+
result
|
905
|
+
end
|
906
|
+
|
907
|
+
# Parameter lists.
|
908
|
+
|
909
|
+
# Get given named parameter.
|
910
|
+
def param( name )
|
911
|
+
@params[ name ]
|
912
|
+
end
|
913
|
+
alias parameter param
|
914
|
+
|
915
|
+
# Get list of all parameters.
|
916
|
+
def params
|
917
|
+
@params.values
|
918
|
+
end
|
919
|
+
alias parameters params
|
920
|
+
|
921
|
+
# Get list of all parameter names.
|
922
|
+
def params_names
|
923
|
+
@params.keys
|
924
|
+
end
|
925
|
+
alias parameters_names params_names
|
926
|
+
|
927
|
+
# Get list of given named parameters.
|
928
|
+
# Note that nil is returned for unknown names, and duplicate parameters for duplicate names.
|
929
|
+
def named_params( *names )
|
930
|
+
@params.values_at( *names )
|
931
|
+
end
|
932
|
+
alias named_parameters named_params
|
933
|
+
|
934
|
+
# Get list of parameters with correct value types.
|
935
|
+
def correct_params
|
936
|
+
params.select{ |x| x.correct? }
|
937
|
+
end
|
938
|
+
alias correct_parameters correct_params
|
939
|
+
|
940
|
+
# Get list of parameters with incorrect value types.
|
941
|
+
def incorrect_params
|
942
|
+
params.select{ |x| x.incorrect? }
|
943
|
+
end
|
944
|
+
alias incorrect_parameters incorrect_params
|
945
|
+
|
946
|
+
# Get list of parameters with blank values.
|
947
|
+
def blank_params
|
948
|
+
params.select{ |x| x.blank? }
|
949
|
+
end
|
950
|
+
alias blank_parameters blank_params
|
951
|
+
|
952
|
+
# Get list of parameters with empty values.
|
953
|
+
def empty_params
|
954
|
+
params.select{ |x| x.empty? }
|
955
|
+
end
|
956
|
+
alias empty_parameters empty_params
|
957
|
+
|
958
|
+
# Get list of parameters with non-empty values.
|
959
|
+
def filled_params
|
960
|
+
params.select{ |x| x.filled? }
|
961
|
+
end
|
962
|
+
alias filled_parameters filled_params
|
963
|
+
|
964
|
+
# Get list of required parameters.
|
965
|
+
def required_params
|
966
|
+
params.select{ |x| x.required? }
|
967
|
+
end
|
968
|
+
alias required_parameters required_params
|
969
|
+
|
970
|
+
# Get list of optional parameters.
|
971
|
+
def optional_params
|
972
|
+
params.select{ |x| x.optional? }
|
973
|
+
end
|
974
|
+
alias optional_parameters optional_params
|
975
|
+
|
976
|
+
# Get list of disabled parameters.
|
977
|
+
def disabled_params
|
978
|
+
params.select{ |x| x.disabled? }
|
979
|
+
end
|
980
|
+
alias disabled_parameters disabled_params
|
981
|
+
|
982
|
+
# Get list of enabled parameters.
|
983
|
+
def enabled_params
|
984
|
+
params.select{ |x| x.enabled? }
|
985
|
+
end
|
986
|
+
alias enabled_parameters enabled_params
|
987
|
+
|
988
|
+
# Get list of hidden parameters.
|
989
|
+
def hidden_params
|
990
|
+
params.select{ |x| x.hidden? }
|
991
|
+
end
|
992
|
+
alias hidden_parameters hidden_params
|
993
|
+
|
994
|
+
# Get list of ignored parameters.
|
995
|
+
def ignored_params
|
996
|
+
params.select{ |x| x.ignored? }
|
997
|
+
end
|
998
|
+
alias ignored_parameters ignored_params
|
999
|
+
|
1000
|
+
# Get list of visible parameters.
|
1001
|
+
def visible_params
|
1002
|
+
params.select{ |x| x.visible? }
|
1003
|
+
end
|
1004
|
+
alias visible_parameters visible_params
|
1005
|
+
|
1006
|
+
# Get list of array parameters.
|
1007
|
+
def array_params
|
1008
|
+
params.select{ |x| x.array? }
|
1009
|
+
end
|
1010
|
+
alias array_parameters array_params
|
1011
|
+
|
1012
|
+
# Get list of hash parameters.
|
1013
|
+
def hash_params
|
1014
|
+
params.select{ |x| x.hash? }
|
1015
|
+
end
|
1016
|
+
alias hash_parameters hash_params
|
1017
|
+
|
1018
|
+
# Get list of scalar parameters.
|
1019
|
+
def scalar_params
|
1020
|
+
params.select{ |x| x.scalar? }
|
1021
|
+
end
|
1022
|
+
alias scalar_parameters scalar_params
|
1023
|
+
|
1024
|
+
# Get list of parameters tagged with given/any tags.
|
1025
|
+
def tagged_params( *tags )
|
1026
|
+
params.select{ |x| x.tagged?( *tags ) }
|
1027
|
+
end
|
1028
|
+
alias tagged_parameters tagged_params
|
1029
|
+
|
1030
|
+
# Get list of parameters not tagged with given/any tags.
|
1031
|
+
def untagged_params( *tags )
|
1032
|
+
params.select{ |x| x.untagged?( *tags ) }
|
1033
|
+
end
|
1034
|
+
alias untagged_parameters untagged_params
|
1035
|
+
|
1036
|
+
# Get list of parameters with no errors reported.
|
1037
|
+
def valid_params
|
1038
|
+
params.select{ |x| x.valid? }
|
1039
|
+
end
|
1040
|
+
alias valid_parameters valid_params
|
1041
|
+
|
1042
|
+
# Get list of parameters with some errors reported.
|
1043
|
+
def invalid_params
|
1044
|
+
params.select{ |x| x.invalid? }
|
1045
|
+
end
|
1046
|
+
alias invalid_parameters invalid_params
|
1047
|
+
|
1048
|
+
# Get all/given parameters chunked into individual rows for nicer form display.
|
1049
|
+
def chunked_params( params = self.params )
|
1050
|
+
params.chunk{ |p| p[ :row ] || :_alone }.map{ |x,a| a.count > 1 ? a : a.first }
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
# URL helpers.
|
1054
|
+
|
1055
|
+
# Return true if all parameters are empty.
|
1056
|
+
def empty?
|
1057
|
+
filled_params.empty?
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
# Get hash of all non-empty parameters for use in URL.
|
1061
|
+
def url_params
|
1062
|
+
result = {}
|
1063
|
+
filled_params.each{ |x| result[ x.code ] = x.form_value }
|
1064
|
+
result
|
1065
|
+
end
|
1066
|
+
alias url_parameters url_params
|
1067
|
+
|
1068
|
+
# Create string containing URL query from all current non-empty parameters.
|
1069
|
+
def url_query
|
1070
|
+
Rack::Utils.build_nested_query( url_params )
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
# Extend given URL with query created from all current non-empty parameters.
|
1074
|
+
def extend_url( url )
|
1075
|
+
url = url.to_s.dup
|
1076
|
+
query = url_query
|
1077
|
+
unless query.empty?
|
1078
|
+
url << ( url['?'] ? '&' : '?' ) << query
|
1079
|
+
end
|
1080
|
+
url
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
# Build URL from given URL and combination of current paramaters and provided parameters.
|
1084
|
+
def build_url( url, args = {} )
|
1085
|
+
dup.set( args ).extend_url( url )
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
# Validation.
|
1089
|
+
|
1090
|
+
# Get hash of all errors detected for each parameter.
|
1091
|
+
def errors
|
1092
|
+
validate?
|
1093
|
+
@errors.dup
|
1094
|
+
end
|
1095
|
+
|
1096
|
+
# Get list of error messages, but including only the first one reported for each parameter.
|
1097
|
+
def error_messages
|
1098
|
+
errors.values.map{ |x| x.first }
|
1099
|
+
end
|
1100
|
+
|
1101
|
+
# Remember error concerning given parameter.
|
1102
|
+
# Returns self for chaining.
|
1103
|
+
def report( name, msg )
|
1104
|
+
validate?
|
1105
|
+
( @errors[ name ] ||= [] ) << msg.to_s.dup.freeze
|
1106
|
+
self
|
1107
|
+
end
|
1108
|
+
|
1109
|
+
# Get list of errors for given parameter. Returns empty list if there were no errors.
|
1110
|
+
def errors_for( name )
|
1111
|
+
errors[ name ] || []
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
# Get first error for given parameter. Returns nil if there were no errors.
|
1115
|
+
def error_for( name )
|
1116
|
+
errors_for( name ).first
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
# Test if there were no errors (overall or for given parameters) reported.
|
1120
|
+
def valid?( *names )
|
1121
|
+
if names.empty?
|
1122
|
+
errors.empty?
|
1123
|
+
else
|
1124
|
+
validate_names( names ).all?{ |x| errors_for( x ).empty? }
|
1125
|
+
end
|
1126
|
+
end
|
1127
|
+
|
1128
|
+
# Test if there were some errors (overall or for given parameters) reported.
|
1129
|
+
def invalid?( *names )
|
1130
|
+
not valid?( *names )
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
# Return parameter(s) value(s) as long as they are all valid, nil otherwise.
|
1134
|
+
def valid( name, *names )
|
1135
|
+
self[ name, *names ] if valid?( name, *names )
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
# Validate parameter values and remember any errors detected.
|
1139
|
+
# You can override this in your class if you need more specific validation
|
1140
|
+
# and :check callback is not good enough. Just make sure to call super first.
|
1141
|
+
# Returns self for chaining.
|
1142
|
+
def validate
|
1143
|
+
@errors ||= {}
|
1144
|
+
params.each{ |x| x.validate }
|
1145
|
+
self
|
1146
|
+
end
|
1147
|
+
|
1148
|
+
# Like validate, except that it forces revalidation of all parameters.
|
1149
|
+
# Returns self for chaining.
|
1150
|
+
def validate!
|
1151
|
+
@errors = {}
|
1152
|
+
validate
|
1153
|
+
self
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
# Like validate, except that it does nothing if validation was already done.
|
1157
|
+
# Returns self for chaining.
|
1158
|
+
def validate?
|
1159
|
+
validate unless @errors
|
1160
|
+
self
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
end
|
1164
|
+
|
1165
|
+
# EOF #
|