Class: Strelka::ParamValidator
- Inherits:
-
Object
- Object
- Strelka::ParamValidator
- Extended by:
- Forwardable, Loggability, MethodUtilities
- Includes:
- DataUtilities
- Defined in:
- lib/strelka/paramvalidator.rb
Overview
A validator for user parameters.
Usage
require 'strelka/paramvalidator'
validator = Strelka::ParamValidator.new
Add validation criteria for input parameters
validator.add( :name, /^(?
Now pass in values in a hash (e.g., from an HTML form)
validator.validate( req.params )
Now if there weren't any errors, use some form values to fill out the
success page template
if validator.okay? tmpl = template :success tmpl.firstname = validator[:firstname] tmpl.lastname = validator[:lastname] tmpl.email = validator tmpl.feedback = validator return tmpl
Otherwise fill in the error template with auto-generated error messages
and return that instead.
else tmpl = template :feedback_form tmpl.errors = validator.error_messages return tmpl end
Defined Under Namespace
Classes: BuiltinConstraint, Constraint, RegexpConstraint
Constant Summary collapse
- PARAMS_HASH_RE =
Pattern for countint the number of hash levels in a parameter key
/^([^\[]+)(\[.*\])?(.)?.*$/- PARAMETER_PATTERN_STRIP_RE =
Pattern to use to strip binding operators from parameter patterns so they can be used in the middle of routing Regexps.
Regexp.union( '^', '$', '\\A', '\\z', '\\Z' )
Instance Attribute Summary collapse
-
#constraints ⇒ Object
readonly
The constraints hash.
-
#fields ⇒ Object
readonly
The Hash of raw field data (if validation has occurred).
Instance Method Summary collapse
-
#[](key) ⇒ Object
Index fetch operator; fetch the validated (and possible parsed) value for form field
key. -
#[]=(key, val) ⇒ Object
Index assignment operator; set the validated value for form field
keyto the specifiedval. -
#add(name, *args, &block) ⇒ Object
:call-seq: add( name, *flags ) add( name, constraint, *flags ) add( name, description, *flags ) add( name, constraint, description, *flags ).
-
#apply_constraint(constraint, value) ⇒ Object
Apply the specified
constraint(a Strelka::ParamValidator::Constraint object) to the givenvalue, and add the field to the appropriate field list based on the result. -
#args? ⇒ Boolean
(also: #has_args?)
Returns
trueif there were arguments given. -
#constraint_regexp_for(name) ⇒ Object
Fetch the constraint/s that apply to the parameter named
nameas a Regexp, if possible. -
#descriptions ⇒ Object
Hash of field descriptions.
-
#descriptions=(new_descs) ⇒ Object
Set field descriptions en masse to new_descs.
-
#empty? ⇒ Boolean
Returns
trueif there were no arguments given. -
#error_fields ⇒ Object
Return an array of field names which had some kind of error associated with them.
-
#error_messages(include_unknown = false) ⇒ Object
Return an error message for each missing or invalid field; if
includeUnknownistrue, also include messages for unknown fields. -
#errors? ⇒ Boolean
(also: #has_errors?)
Returns
trueif any fields are missing or contain invalid values. -
#get_description(field) ⇒ Object
Get the description for the specified
field. -
#initialize ⇒ ParamValidator
constructor
Create a new Strelka::ParamValidator object.
-
#initialize_copy(original) ⇒ Object
Copy constructor.
-
#inspect ⇒ Object
Return a human-readable representation of the validator, suitable for debugging.
-
#invalid ⇒ Object
The Hash of fields that were present, but invalid (didn't match the field's constraint).
-
#invalid_param_errors ⇒ Object
Return an Array of error messages, one for each field that was invalid from the last validation.
-
#merge(params) ⇒ Object
Return a new ParamValidator with the additional
paramsmerged into its values and re-validated. -
#merge!(params) ⇒ Object
Merge the specified
paramsinto the receiving ParamValidator and re-validate the resulting values. -
#missing ⇒ Object
The names of fields that were required, but missing from the parameter list.
-
#missing_param_errors ⇒ Object
Return an Array of error messages, one for each field missing from the last validation.
-
#okay? ⇒ Boolean
Return
trueif all required fields were present and all present fields validated correctly. -
#override(name, *args, &block) ⇒ Object
Replace the existing parameter with the specified name.
-
#param_names ⇒ Object
Return the Array of parameter names the validator knows how to validate (as Strings).
-
#reset ⇒ Object
Reset the validation state.
-
#revalidate(params = {}) ⇒ Object
Clear existing validation information, merge the specified
paramswith any existing raw fields, and re-run the validation. -
#to_s ⇒ Object
Stringified description of the validator.
-
#unknown ⇒ Object
The names of fields that were present in the parameters, but didn't have a corresponding constraint.
-
#unknown_param_errors ⇒ Object
Return an Array of error messages, one for each field present in the parameters in the last validation that didn't have a constraint associated with it.
-
#valid ⇒ Object
Returns the valid fields after expanding Rails-style 'customer[street]' variables into multi-level hashes.
-
#validate(params = nil, additional_constraints = nil) ⇒ Object
Validate the input in
params. -
#validated? ⇒ Object
Returns
trueif the paramvalidator has been given parameters to validate. -
#values_at(*selector) ⇒ Object
Returns an array containing valid parameters in the validator corresponding to the given +selector+(s).
Methods included from MethodUtilities
attr_predicate, attr_predicate_accessor, singleton_attr_accessor, singleton_attr_reader, singleton_attr_writer, singleton_method_alias, singleton_predicate_accessor, singleton_predicate_reader
Methods included from DataUtilities
autovivify, deep_copy, stringify_keys, symbolify_keys
Constructor Details
#initialize ⇒ ParamValidator
Create a new Strelka::ParamValidator object.
551 552 553 554 555 556 |
# File 'lib/strelka/paramvalidator.rb', line 551 def initialize @constraints = {} @fields = {} self.reset end |
Instance Attribute Details
#constraints ⇒ Object (readonly)
The constraints hash
573 574 575 |
# File 'lib/strelka/paramvalidator.rb', line 573 def constraints @constraints end |
#fields ⇒ Object (readonly)
The Hash of raw field data (if validation has occurred)
576 577 578 |
# File 'lib/strelka/paramvalidator.rb', line 576 def fields @fields end |
Instance Method Details
#[](key) ⇒ Object
Index fetch operator; fetch the validated (and possible parsed) value for
form field key.
809 810 811 812 |
# File 'lib/strelka/paramvalidator.rb', line 809 def []( key ) self.validate unless self.validated? return @valid[ key.to_sym ] end |
#[]=(key, val) ⇒ Object
Index assignment operator; set the validated value for form field key
to the specified val.
817 818 819 820 |
# File 'lib/strelka/paramvalidator.rb', line 817 def []=( key, val ) @parsed_params = nil @valid[ key.to_sym ] = val end |
#add(name, *args, &block) ⇒ Object
:call-seq:
add( name, *flags )
add( name, constraint, *flags )
add( name, description, *flags )
add( name, constraint, description, *flags )
Add a validation for a parameter with the specified name. The args can include
a constraint, a description, and one or more flags.
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 |
# File 'lib/strelka/paramvalidator.rb', line 604 def add( name, *args, &block ) name = name.to_sym constraint = Constraint.for( name, *args, &block ) # No-op if there's already a parameter with the same name and constraint if self.constraints.key?( name ) return if self.constraints[ name ] == constraint raise ArgumentError, "parameter %p is already defined as %s; perhaps you meant to use #override?" % [ name.to_s, self.constraints[name] ] end self.log.debug "Adding parameter %p: %p" % [ name, constraint ] self.constraints[ name ] = constraint self.validated = false end |
#apply_constraint(constraint, value) ⇒ Object
Apply the specified constraint (a Strelka::ParamValidator::Constraint object) to
the given value, and add the field to the appropriate field list based on the
result.
732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 |
# File 'lib/strelka/paramvalidator.rb', line 732 def apply_constraint( constraint, value ) if !( value.nil? || value == '' ) result = constraint.apply( value ) if !result.nil? self.log.debug " constraint for %p passed: %p" % [ constraint.name, result ] self[ constraint.name ] = result else self.log.debug " constraint for %p failed" % [ constraint.name ] @invalid[ constraint.name.to_s ] = value end elsif constraint.required? self.log.debug " missing parameter for %p" % [ constraint.name ] @missing << constraint.name.to_s end end |
#args? ⇒ Boolean Also known as: has_args?
Returns true if there were arguments given.
830 831 832 |
# File 'lib/strelka/paramvalidator.rb', line 830 def args? return !self.fields.empty? end |
#constraint_regexp_for(name) ⇒ Object
Fetch the constraint/s that apply to the parameter named name as a Regexp, if possible.
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 |
# File 'lib/strelka/paramvalidator.rb', line 760 def constraint_regexp_for( name ) self.log.debug " searching for a constraint for %p" % [ name ] # Fetch the constraint's regexp constraint = self.constraints[ name.to_sym ] or raise NameError, "no such parameter %p" % [ name ] raise ScriptError, "can't route on a parameter with a %p" % [ constraint.class ] unless constraint.respond_to?( :pattern ) re = constraint.pattern self.log.debug " bounded constraint is: %p" % [ re ] # Unbind the pattern from beginning or end of line. # :TODO: This is pretty ugly. Find a better way of modifying the regex. re_str = re.to_s. sub( %r{\(\?[\-mix]+:(.*)\)}, '\1' ). gsub( PARAMETER_PATTERN_STRIP_RE, '' ) self.log.debug " stripped constraint pattern down to: %p" % [ re_str ] return Regexp.new( "(?<#{name}>#{re_str})", re. ) end |
#descriptions ⇒ Object
Hash of field descriptions
672 673 674 675 676 |
# File 'lib/strelka/paramvalidator.rb', line 672 def descriptions return self.constraints.each_with_object({}) do |(field,constraint), hash| hash[ field ] = constraint.description end end |
#descriptions=(new_descs) ⇒ Object
Set field descriptions en masse to new_descs.
680 681 682 683 684 685 686 |
# File 'lib/strelka/paramvalidator.rb', line 680 def descriptions=( new_descs ) new_descs.each do |name, description| raise NameError, "no parameter named #{name}" unless self.constraints.key?( name.to_sym ) self.constraints[ name.to_sym ].description = description end end |
#empty? ⇒ Boolean
Returns true if there were no arguments given.
824 825 826 |
# File 'lib/strelka/paramvalidator.rb', line 824 def empty? return self.fields.empty? end |
#error_fields ⇒ Object
Return an array of field names which had some kind of error associated with them.
874 875 876 |
# File 'lib/strelka/paramvalidator.rb', line 874 def error_fields return self.missing | self.invalid.keys end |
#error_messages(include_unknown = false) ⇒ Object
Return an error message for each missing or invalid field; if
includeUnknown is true, also include messages for unknown fields.
881 882 883 884 885 886 887 888 |
# File 'lib/strelka/paramvalidator.rb', line 881 def ( include_unknown=false ) msgs = [] msgs += self.missing_param_errors + self.invalid_param_errors msgs += self.unknown_param_errors if include_unknown return msgs end |
#errors? ⇒ Boolean Also known as: has_errors?
Returns true if any fields are missing or contain invalid values.
859 860 861 |
# File 'lib/strelka/paramvalidator.rb', line 859 def errors? return !self.okay? end |
#get_description(field) ⇒ Object
Get the description for the specified field.
690 691 692 693 |
# File 'lib/strelka/paramvalidator.rb', line 690 def get_description( field ) constraint = self.constraints[ field.to_sym ] or return nil return constraint.description end |
#initialize_copy(original) ⇒ Object
Copy constructor.
560 561 562 563 564 565 |
# File 'lib/strelka/paramvalidator.rb', line 560 def initialize_copy( original ) fields = deep_copy( original.fields ) self.reset @fields = fields @constraints = deep_copy( original.constraints ) end |
#inspect ⇒ Object
Return a human-readable representation of the validator, suitable for debugging.
656 657 658 659 660 661 662 663 664 665 666 667 668 |
# File 'lib/strelka/paramvalidator.rb', line 656 def inspect required, optional = self.constraints.partition do |_, constraint| constraint.required? end return "#<%p:0x%016x %s, profile: [required: %s, optional: %s]>" % [ self.class, self.object_id / 2, self.to_s, required.empty? ? "(none)" : required.map( &:last ).map( &:name ).join(','), optional.empty? ? "(none)" : optional.map( &:last ).map( &:name ).join(','), ] end |
#invalid ⇒ Object
The Hash of fields that were present, but invalid (didn't match the field's constraint)
844 845 846 847 |
# File 'lib/strelka/paramvalidator.rb', line 844 def invalid self.validate unless self.validated? return @invalid end |
#invalid_param_errors ⇒ Object
Return an Array of error messages, one for each field that was invalid from the last validation.
903 904 905 906 907 908 909 |
# File 'lib/strelka/paramvalidator.rb', line 903 def invalid_param_errors return self.invalid.collect do |field, _| constraint = self.constraints[ field.to_sym ] or raise NameError, "no such field %p!" % [ field ] "Invalid value for '%s'" % [ constraint.description ] end end |
#merge(params) ⇒ Object
Return a new ParamValidator with the additional params merged into
its values and re-validated.
924 925 926 927 928 |
# File 'lib/strelka/paramvalidator.rb', line 924 def merge( params ) copy = self.dup copy.merge!( params ) return copy end |
#merge!(params) ⇒ Object
Merge the specified params into the receiving ParamValidator and
re-validate the resulting values.
933 934 935 936 937 |
# File 'lib/strelka/paramvalidator.rb', line 933 def merge!( params ) return if params.empty? self.log.debug "Merging parameters for revalidation: %p" % [ params ] self.revalidate( params ) end |
#missing ⇒ Object
The names of fields that were required, but missing from the parameter list.
837 838 839 840 |
# File 'lib/strelka/paramvalidator.rb', line 837 def missing self.validate unless self.validated? return @missing end |
#missing_param_errors ⇒ Object
Return an Array of error messages, one for each field missing from the last validation.
892 893 894 895 896 897 898 |
# File 'lib/strelka/paramvalidator.rb', line 892 def missing_param_errors return self.missing.collect do |field| constraint = self.constraints[ field.to_sym ] or raise NameError, "no such field %p!" % [ field ] "Missing value for '%s'" % [ constraint.description ] end end |
#okay? ⇒ Boolean
Return true if all required fields were present and all present fields validated
correctly.
867 868 869 |
# File 'lib/strelka/paramvalidator.rb', line 867 def okay? return (self.missing.empty? && self.invalid.empty?) end |
#override(name, *args, &block) ⇒ Object
Replace the existing parameter with the specified name. The args replace the existing description, constraints, and flags. See #add for details.
625 626 627 628 629 630 631 632 633 634 635 |
# File 'lib/strelka/paramvalidator.rb', line 625 def override( name, *args, &block ) name = name.to_sym raise ArgumentError, "no parameter %p defined; perhaps you meant to use #add?" % [ name.to_s ] unless self.constraints.key?( name ) self.log.debug "Overriding parameter %p" % [ name ] self.constraints[ name ] = Constraint.for( name, *args, &block ) self.validated = false end |
#param_names ⇒ Object
Return the Array of parameter names the validator knows how to validate (as Strings).
639 640 641 |
# File 'lib/strelka/paramvalidator.rb', line 639 def param_names return self.constraints.keys.map( &:to_s ).sort end |
#reset ⇒ Object
Reset the validation state.
585 586 587 588 589 590 591 592 593 |
# File 'lib/strelka/paramvalidator.rb', line 585 def reset self.log.debug "Resetting validation state." @validated = false @valid = {} @parsed_params = nil @missing = [] @unknown = [] @invalid = {} end |
#revalidate(params = {}) ⇒ Object
Clear existing validation information, merge the specified params with any existing
raw fields, and re-run the validation.
752 753 754 755 756 |
# File 'lib/strelka/paramvalidator.rb', line 752 def revalidate( params={} ) merged_fields = self.fields.merge( params ) self.reset self.validate( merged_fields ) end |
#to_s ⇒ Object
Stringified description of the validator
645 646 647 648 649 650 651 652 |
# File 'lib/strelka/paramvalidator.rb', line 645 def to_s "%d parameters (%d valid, %d invalid, %d missing)" % [ self.fields.size, self.valid.size, self.invalid.size, self.missing.size, ] end |
#unknown ⇒ Object
The names of fields that were present in the parameters, but didn't have a corresponding constraint.
852 853 854 855 |
# File 'lib/strelka/paramvalidator.rb', line 852 def unknown self.validate unless self.validated? return @unknown end |
#unknown_param_errors ⇒ Object
Return an Array of error messages, one for each field present in the parameters in the last validation that didn't have a constraint associated with it.
914 915 916 917 918 919 |
# File 'lib/strelka/paramvalidator.rb', line 914 def unknown_param_errors self.log.debug "Fetching unknown param errors for %p." % [ self.unknown ] return self.unknown.collect do |field| "Unknown parameter '%s'" % [ field.capitalize ] end end |
#valid ⇒ Object
Returns the valid fields after expanding Rails-style 'customer[street]' variables into multi-level hashes.
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 |
# File 'lib/strelka/paramvalidator.rb', line 786 def valid self.validate unless self.validated? self.log.debug "Building valid fields hash from raw data: %p" % [ @valid ] unless @parsed_params @parsed_params = {} for key, value in @valid self.log.debug " adding %s: %p" % [ key, value ] value = [ value ] if key.to_s.end_with?( '[]' ) if key.to_s.include?( '[' ) build_deep_hash( value, @parsed_params, get_levels(key.to_s) ) else @parsed_params[ key ] = value end end end return @parsed_params end |
#validate(params = nil, additional_constraints = nil) ⇒ Object
Validate the input in params. If the optional additional_constraints is
given, merge it with the validator's existing constraints before validating.
698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 |
# File 'lib/strelka/paramvalidator.rb', line 698 def validate( params=nil, additional_constraints=nil ) self.log.debug "Validating." self.reset # :TODO: Handle the additional_constraints params ||= @fields params = stringify_keys( params ) @fields = deep_copy( params ) self.log.debug "Starting validation with fields: %p" % [ @fields ] # Use the constraints list to extract all the parameters that have corresponding # constraints self.constraints.each do |field, constraint| self.log.debug " applying %s to any %p parameter/s" % [ constraint, field ] value = params.delete( field.to_s ) self.log.debug " value is: %p" % [ value ] self.apply_constraint( constraint, value ) end # Any left over are unknown params.keys.each do |field| self.log.debug " unknown field %p" % [ field ] @unknown << field end @validated = true end |
#validated? ⇒ Object
Returns true if the paramvalidator has been given parameters to validate. Adding or
overriding constraints resets this.
581 |
# File 'lib/strelka/paramvalidator.rb', line 581 attr_predicate_accessor :validated? |
#values_at(*selector) ⇒ Object
Returns an array containing valid parameters in the validator corresponding to the given +selector+(s).
942 943 944 945 |
# File 'lib/strelka/paramvalidator.rb', line 942 def values_at( *selector ) selector.map!( &:to_sym ) return self.valid.values_at( *selector ) end |