Class: Arrow::FormValidator
- Inherits:
-
FormValidator
- Object
- FormValidator
- Arrow::FormValidator
- Extends:
- Forwardable
- Includes:
- Loggable
- Defined in:
- lib/arrow/formvalidator.rb
Overview
A FormValidator variant that adds some convenience methods and additional validations.
Usage
require 'arrow/formvalidator'
# Profile specifies validation criteria for input profile = {
:required => :name, :optional => [:email, :description], :filters => [:strip, :squeeze], :untaint_all_constraints => true, :descriptions => { :email => "Customer Email", :description => "Issue Description", :name => "Customer Name", }, :constraints => { :email => :email, :name => /^[\x20-\x7f]+$/, :description => /^[\x20-\x7f]+$/, },
}
# Create a validator object and pass in a hash of request parameters and the # profile hash.
validator = Arrow::FormValidator.new
validator.validate( req_params, profile )
# Now if there weren’t any errors, send the success page if validator.okay?
return success_template
# Otherwise fill in the error template with auto-generated error messages # and return that instead. else
failure_template.errors( validator. ) return failure_template
end
VCS Id
$Id: formvalidator.rb,v 1b8226c06192 2010/08/09 17:50:38 ged $
Authors
Michael Granger
Please see the file LICENSE in the top-level directory for licensing details.
Portions of this file are from Ruby on Rails’ CGIMethods class from the action_controller:
Copyright (c) 2004 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
—
Please see the file COPYRIGHT in the ‘docs’ directory for licensing details.
Constant Summary
- Defaults =
{ :descriptions => {}, }
- RFC822_EMAIL_ADDRESS =
RFC822 Email Address Regex
Originally written by Cal Henderson c.f. iamcal.com/publish/articles/php/parsing_email/
Translated to Ruby by Tim Fletcher, with changes suggested by Dan Kubb.
Licensed under a Creative Commons Attribution-ShareAlike 2.5 License creativecommons.org/licenses/by-sa/2.5/
begin qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]' dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]' atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-' + '\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+' quoted_pair = '\\x5c[\\x00-\\x7f]' domain_literal = "\\x5b(?:#{dtext}|#{quoted_pair})*\\x5d" quoted_string = "\\x22(?:#{qtext}|#{quoted_pair})*\\x22" domain_ref = atom sub_domain = "(?:#{domain_ref}|#{domain_literal})" word = "(?:#{atom}|#{quoted_string})" domain = "#{sub_domain}(?:\\x2e#{sub_domain})*" local_part = "#{word}(?:\\x2e#{word})*" addr_spec = "#{local_part}\\x40#{domain}" /\A#{addr_spec}\z/ end
- RFC1738Hostname =
begin alphadigit = /[a-z0-9]/i # toplabel = alpha | alpha *[ alphadigit | "-" ] alphadigit toplabel = /[a-z]((#{alphadigit}|-)*#{alphadigit})?/i # domainlabel = alphadigit | alphadigit *[ alphadigit | "-" ] alphadigit domainlabel = /#{alphadigit}((#{alphadigit}|-)*#{alphadigit})?/i # hostname = *[ domainlabel "." ] toplabel hostname = /\A(#{domainlabel}\.)*#{toplabel}\z/ end
- PARAMS_HASH_RE =
Get the number of hash levels in the specified key Stolen from the CGIMethods class in Rails’ action_controller.
/^([^\[]+)(\[.*\])?(.)?.*$/
Instance Attribute Summary
- - (Object) raw_form readonly
Instance Method Summary
-
- (Object) [](key)
Index operator; fetch the validated value for form field key.
-
- (Object) []=(key, val)
Index assignment operator; set the validated value for form field key to the specified val.
-
- (Object) apply_hash_constraint(key, constraint)
Apply a constraint given as a Hash to the value/s corresponding to the specified key:.
-
- (Object) apply_proc_constraint(key, constraint, *params)
Apply a constraint that was specified as a Proc to the value for the given key.
-
- (Object) apply_regexp_constraint(key, constraint)
Applies regexp constraint to form[key].
-
- (Object) apply_string_constraint(key, constraint)
Applies a builtin constraint to form[key].
-
- (Boolean) args?
Returns true if there were arguments given.
-
- (Object) check_profile_syntax(profile)
Overridden to remove the check for extra keys.
-
- (Object) descriptions
Hash of field descriptions.
-
- (Object) descriptions(new_descs)
Set hash of field descriptions.
-
- (Object) do_constraint(key, constraints)
Apply one or more constraints to the field value/s corresponding to key.
-
- (Boolean) empty?
Returns true if there were no arguments given.
-
- (Object) error_fields
Return an array of field names which had some kind of error associated with them.
-
- (Object) error_messages(include_unknown = false)
Return an error message for each missing or invalid field; if includeUnknown is true, also include messages for unknown fields.
-
- (Boolean) errors?
(also: #has_errors?)
Returns true if any fields are missing or contain invalid values.
-
- (Object) filters
Formvalidator hack: The formvalidator filters method has a bug where he assumes an array.
-
- (Object) get_description(field)
Get the description for the specified field.
-
- (FormValidator) initialize(profile, params = nil)
constructor
Create a new Arrow::FormValidator object.
-
- (Object) match_alpha(val)
Constrain a value to alpha characters (a-z, case-insensitive).
-
- (Object) match_alphanumeric(val)
Constrain a value to alpha characters (a-z, case-insensitive and 0-9).
-
- (Object) match_boolean(val)
Constrain a value to true (or yes) and false (or no).
-
- (Object) match_date(val)
Constrain a value to a parseable Date.
-
- (Object) match_email(val)
Override the parent class’s definition to (not-sloppily) match email addresses.
-
- (Object) match_float(val)
Contrain a value to a Float.
-
- (Object) match_hostname(val)
Match valid hostnames according to the rules of the URL RFC.
-
- (Object) match_integer(val)
Constrain a value to an integer.
-
- (Object) match_printable(val)
Constrain a value to any printable characters.
-
- (Object) match_uri(val)
Match valid URIs.
-
- (Object) missing
Returns a distinct list of missing fields.
-
- (Boolean) okay?
Return true if all required fields were present and validated correctly.
-
- (Object) set_form_value(key, value, constraint)
Set the form value for the given key.
-
- (Object) to_s
Stringified description of the validator.
-
- (Object) unknown
Returns a distinct list of unknown fields.
-
- (Boolean) untaint?(field)
Returns true if the given field is one that should be untainted.
-
- (Object) valid
Returns the valid fields after expanding Rails-style ‘customer[address][street]’ variables into multi-level hashes.
-
- (Object) validate(params, additional_profile = nil)
Validate the input in params.
Methods included from Loggable
Constructor Details
- (FormValidator) initialize(profile, params = nil)
Create a new Arrow::FormValidator object.
104 105 106 107 |
# File 'lib/arrow/formvalidator.rb', line 104 def initialize( profile, params=nil ) @profile = Defaults.merge( profile ) validate( params ) if params end |
Instance Attribute Details
- (Object) raw_form (readonly)
114 115 116 |
# File 'lib/arrow/formvalidator.rb', line 114 def raw_form @raw_form end |
Instance Method Details
- (Object) [](key)
Index operator; fetch the validated value for form field key.
160 161 162 |
# File 'lib/arrow/formvalidator.rb', line 160 def []( key ) @form[ key.to_s ] end |
- (Object) []=(key, val)
Index assignment operator; set the validated value for form field key to the specified val.
167 168 169 |
# File 'lib/arrow/formvalidator.rb', line 167 def []=( key, val ) @form[ key.to_s ] = val end |
- (Object) apply_hash_constraint(key, constraint)
Apply a constraint given as a Hash to the value/s corresponding to the specified key:
constraint | A builtin constraint (as a Symbol; e.g., :email), a Regexp, or a Proc. |
name | A description of the constraint should it fail and be listed in #invalid. |
params | If constraint is a Proc, this field should contain a list of other fields to send to the Proc. |
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 |
# File 'lib/arrow/formvalidator.rb', line 462 def apply_hash_constraint( key, constraint ) action = constraint["constraint"] rval = case action when String self.apply_string_constraint( key, action ) when Regexp self.apply_regexp_constraint( key, action ) when Proc if args = constraint["params"] args.collect! {|field| @form[field] } self.apply_proc_constraint( key, action, *args ) else self.apply_proc_constraint( key, action ) end end # If the validation failed, and there's a name for this constraint, replace # the name in @invalid_fields with the name if !rval && constraint["name"] @invalid_fields[key] = constraint["name"] end return rval end |
- (Object) apply_proc_constraint(key, constraint, *params)
Apply a constraint that was specified as a Proc to the value for the given key
491 492 493 494 495 496 497 498 499 500 501 |
# File 'lib/arrow/formvalidator.rb', line 491 def apply_proc_constraint( key, constraint, *params ) value = nil unless params.empty? value = constraint.call( *params ) else value = constraint.call( @form[key] ) end self.set_form_value( key, value, constraint ) end |
- (Object) apply_regexp_constraint(key, constraint)
Applies regexp constraint to form[key]
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 |
# File 'lib/arrow/formvalidator.rb', line 505 def apply_regexp_constraint( key, constraint ) self.log.debug "Validating '%p' via regexp %p" % [@form[key], constraint] if match = constraint.match( @form[key].to_s ) self.log.debug " matched %p" % [match[0]] if match.captures.empty? self.log.debug " no captures, using whole match: %p" % [match[0]] self.set_form_value( key, match[0], constraint ) elsif match.captures.length == 1 self.log.debug " extracting one capture: %p" % [match.captures.first] self.set_form_value( key, match.captures.first, constraint ) else self.log.debug " extracting multiple captures: %p" % [match.captures] self.set_form_value( key, match.captures, constraint ) end else self.set_form_value( key, nil, constraint ) end end |
- (Object) apply_string_constraint(key, constraint)
Applies a builtin constraint to form[key].
443 444 445 446 447 448 449 |
# File 'lib/arrow/formvalidator.rb', line 443 def apply_string_constraint( key, constraint ) # FIXME: multiple elements rval = self.__send__( "match_#{constraint}", @form[key].to_s ) self.log.debug "Tried a string constraint: %p: %p" % [ @form[key].to_s, rval ] self.set_form_value( key, rval, constraint ) end |
- (Boolean) args?
Returns true if there were arguments given.
179 180 181 |
# File 'lib/arrow/formvalidator.rb', line 179 def args? return !@form.empty? end |
- (Object) check_profile_syntax(profile)
Overridden to remove the check for extra keys.
155 156 |
# File 'lib/arrow/formvalidator.rb', line 155 def check_profile_syntax( profile ) end |
- (Object) descriptions
Hash of field descriptions
128 129 130 |
# File 'lib/arrow/formvalidator.rb', line 128 def descriptions @profile[:descriptions] end |
- (Object) descriptions=(new_descs)
Set hash of field descriptions
134 135 136 |
# File 'lib/arrow/formvalidator.rb', line 134 def descriptions=( new_descs ) @profile[:descriptions] = new_descs end |
- (Object) do_constraint(key, constraints)
Apply one or more constraints to the field value/s corresponding to key.
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 |
# File 'lib/arrow/formvalidator.rb', line 424 def do_constraint( key, constraints ) constraints.each do |constraint| case constraint when String apply_string_constraint( key, constraint ) when Hash apply_hash_constraint( key, constraint ) when Proc apply_proc_constraint( key, constraint ) when Regexp apply_regexp_constraint( key, constraint ) else raise "unknown constraint type %p" % [constraint] end end end |
- (Boolean) empty?
Returns true if there were no arguments given.
173 174 175 |
# File 'lib/arrow/formvalidator.rb', line 173 def empty? return @form.empty? end |
- (Object) error_fields
Return an array of field names which had some kind of error associated with them.
215 216 217 |
# File 'lib/arrow/formvalidator.rb', line 215 def error_fields return self.missing | self.invalid.keys end |
- (Object) error_messages(include_unknown = false)
Return an error message for each missing or invalid field; if includeUnknown is true, also include messages for unknown fields.
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/arrow/formvalidator.rb', line 235 def ( include_unknown=false ) self.log.debug "Building error messages from descriptions: %p" % [ @profile[:descriptions] ] msgs = [] self.missing.each do |field| msgs << "Missing value for '%s'" % self.get_description( field ) end self.invalid.each do |field, constraint| msgs << "Invalid value for '%s'" % self.get_description( field ) end if include_unknown self.unknown.each do |field| msgs << "Unknown parameter '%s'" % self.get_description( field ) end end return msgs end |
- (Boolean) errors? Also known as: has_errors?
Returns true if any fields are missing or contain invalid values.
185 186 187 |
# File 'lib/arrow/formvalidator.rb', line 185 def errors? return !self.okay? end |
- (Object) filters
Formvalidator hack: The formvalidator filters method has a bug where he assumes an array
when it is in fact a string for multiple values (ie anytime you have a text-area with newlines in it).
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 |
# File 'lib/arrow/formvalidator.rb', line 559 def filters @filters_array = Array(@profile[:filters]) unless(@filters_array) @filters_array.each do |filter| if respond_to?( "filter_#{filter}" ) @form.keys.each do |field| # If a key has multiple elements, apply filter to each element @field_array = Array( @form[field] ) if @field_array.length > 1 @field_array.each_index do |i| elem = @field_array[i] @field_array[i] = self.send("filter_#{filter}", elem) end else if not @form[field].to_s.empty? @form[field] = self.send("filter_#{filter}", @form[field].to_s) end end end end end @form end |
- (Object) get_description(field)
Get the description for the specified field.
221 222 223 224 225 226 227 228 229 230 |
# File 'lib/arrow/formvalidator.rb', line 221 def get_description( field ) return @profile[:descriptions][ field.to_s ] if @profile[:descriptions].key?( field.to_s ) desc = field.to_s. gsub( /.*\[(\w+)\]/, "\\1" ). gsub( /_(.)/ ) {|m| " " + m[1,1].upcase }. gsub( /^(.)/ ) {|m| m.upcase } return desc end |
- (Object) match_alpha(val)
Constrain a value to alpha characters (a-z, case-insensitive)
324 325 326 327 328 329 330 |
# File 'lib/arrow/formvalidator.rb', line 324 def match_alpha( val ) if val =~ /^([a-z]+)$/i return $1 else return nil end end |
- (Object) match_alphanumeric(val)
Constrain a value to alpha characters (a-z, case-insensitive and 0-9)
334 335 336 337 338 339 340 |
# File 'lib/arrow/formvalidator.rb', line 334 def match_alphanumeric( val ) if val =~ /^([a-z0-9]+)$/i return $1 else return nil end end |
- (Object) match_boolean(val)
Constrain a value to true (or yes) and false (or no).
293 294 295 296 297 298 299 300 301 302 |
# File 'lib/arrow/formvalidator.rb', line 293 def match_boolean( val ) rval = nil if ( val =~ /^(t(?:rue)?|y(?:es)?)|1$/i ) rval = true elsif ( val =~ /^(no?|f(?:alse)?)|0$/i ) rval = false end return rval end |
- (Object) match_date(val)
Constrain a value to a parseable Date
318 319 320 |
# File 'lib/arrow/formvalidator.rb', line 318 def match_date( val ) return Date.parse( val ) rescue nil end |
- (Object) match_email(val)
Override the parent class’s definition to (not-sloppily) match email addresses.
385 386 387 388 389 390 |
# File 'lib/arrow/formvalidator.rb', line 385 def match_email( val ) match = RFC822_EMAIL_ADDRESS.match( val ) self.log.debug "Validating an email address %p: %p" % [ val, match ] return match ? match[0] : nil end |
- (Object) match_float(val)
Contrain a value to a Float
312 313 314 |
# File 'lib/arrow/formvalidator.rb', line 312 def match_float( val ) return Float( val ) rescue nil end |
- (Object) match_hostname(val)
Match valid hostnames according to the rules of the URL RFC.
404 405 406 407 |
# File 'lib/arrow/formvalidator.rb', line 404 def match_hostname( val ) match = RFC1738Hostname.match( val ) return match ? match[0] : nil end |
- (Object) match_integer(val)
Constrain a value to an integer
306 307 308 |
# File 'lib/arrow/formvalidator.rb', line 306 def match_integer( val ) return Integer( val ) rescue nil end |
- (Object) match_printable(val)
Constrain a value to any printable characters
344 345 346 347 348 349 350 |
# File 'lib/arrow/formvalidator.rb', line 344 def match_printable( val ) if val =~ /^([[:print:][:space:]]{0,255})$/ return val else return nil end end |
- (Object) match_uri(val)
Match valid URIs
411 412 413 414 415 416 417 418 419 |
# File 'lib/arrow/formvalidator.rb', line 411 def match_uri( val ) return URI.parse( val ) rescue URI::InvalidURIError => err self.log.error "Error trying to parse URI %p: %s" % [ val, err. ] return nil rescue NoMethodError self.log.debug "Ignoring bug in URI#parse" return nil end |
- (Object) missing
Returns a distinct list of missing fields. Overridden to eliminate the “undefined method `<=>’ for :foo:Symbol” error.
259 260 261 |
# File 'lib/arrow/formvalidator.rb', line 259 def missing @missing_fields.uniq.sort_by {|f| f.to_s} end |
- (Boolean) okay?
Return true if all required fields were present and validated correctly.
193 194 195 |
# File 'lib/arrow/formvalidator.rb', line 193 def okay? self.missing.empty? && self.invalid.empty? end |
- (Object) set_form_value(key, value, constraint)
Set the form value for the given key. If value is false, add it to the list of invalid fields with a description derived from the specified constraint.
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 |
# File 'lib/arrow/formvalidator.rb', line 530 def set_form_value( key, value, constraint ) key.untaint if !value.nil? self.log.debug "Setting form value for %p to %p (constraint was %p)" % [ key, value, constraint ] @form[key] = value @form[key].untaint if self.untaint?( key ) return true else self.log.debug "Clearing form value for %p (constraint was %p)" % [ key, constraint ] @form.delete( key ) @invalid_fields ||= {} @invalid_fields[ key ] ||= [] unless @invalid_fields[ key ].include?( constraint ) @invalid_fields[ key ].push( constraint ) end return false end end |
- (Object) to_s
Stringified description of the validator
123 124 125 |
# File 'lib/arrow/formvalidator.rb', line 123 def to_s "" end |
- (Object) unknown
Returns a distinct list of unknown fields.
264 265 266 |
# File 'lib/arrow/formvalidator.rb', line 264 def unknown (@unknown_fields - @invalid_fields.keys).uniq.sort_by {|f| f.to_s} end |
- (Boolean) untaint?(field)
Returns true if the given field is one that should be untainted.
199 200 201 202 203 204 205 206 207 208 209 |
# File 'lib/arrow/formvalidator.rb', line 199 def untaint?( field ) self.log.debug "Checking to see if %p should be untainted." % [field] rval = ( @untaint_all || @untaint_fields.include?(field) ) if rval self.log.debug " ...yep it should." else self.log.debug " ...nope." end return rval end |
- (Object) valid
Returns the valid fields after expanding Rails-style ‘customer[address][street]’ variables into multi-level hashes.
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/arrow/formvalidator.rb', line 271 def valid if @parsed_params.nil? @parsed_params = {} valid = super() for key, value in valid value = [value] if key =~ /.*\[\]$/ unless key.include?( '[' ) @parsed_params[ key ] = value else build_deep_hash( value, @parsed_params, get_levels(key) ) end end end return @parsed_params end |
- (Object) validate(params, additional_profile = nil)
Validate the input in params. If the optional additional_profile is given, merge it with the validator’s default profile before validating.
141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/arrow/formvalidator.rb', line 141 def validate( params, additional_profile=nil ) @raw_form = params.dup profile = @profile if additional_profile self.log.debug "Merging additional profile %p" % [additional_profile] profile = @profile.merge( additional_profile ) end super( params, profile ) end |