=item CheckParams() Utility function that can be used by any method that accepts param-hashes to check if the given parameters actually match the expectations. Each individual parameter has a specification that describes the expectation that the calling function has towards this param. The following specifications are supported: * '!' - the parameter is required * '?' - the parameter is optional * 'm{regex}' - the parameter must match the given regex * '!Class=...' - the parameter is required and must be an object of the given class * '?Class=...' - if the parameter has been given, it must be an object of the given class The function will confess for any unknown, missing, or non-matching param. =cut sub CheckParams { my $Params = shift or confess('need to pass in params-hashref!'); my $ParamsSpec = shift or confess('need to pass in params-spec-hashref (or -sub)!'); # TODO: allow to switch off this function via configuration in production # environments, as it is rather heavy # fetch param-spec from function, if that has been given: if (ref($ParamsSpec) eq 'CODE') { $ParamsSpec = $ParamsSpec->(); } # print a warning for any unknown parameters that have been given: my @UnknownParams = grep { !exists $ParamsSpec->{$_}; } keys %$Params; if (@UnknownParams) { my $UnknownParamsStr = join ',', @UnknownParams; confess("Enocuntered unknown params: '$UnknownParamsStr'!\n"); } # check if all required params have been specified: foreach my $Param (keys %$ParamsSpec) { my $Spec = $ParamsSpec->{$Param}; if (ref($Spec) eq 'HASH') { # Handle nested specs by recursion: my $SubParams = $Params->{$Param}; if (!defined $SubParams) { confess("Required param '$Param' is missing!"); } CheckParams($SubParams, $Spec); } elsif (ref($Spec) eq 'ARRAY') { # Handle nested spec arrays by looped recursion: my $SubParams = $Params->{$Param}; if (!defined $SubParams) { confess("Required param '$Param' is missing!"); } elsif (ref($SubParams) ne 'ARRAY') { confess("Value for param '$Param' must be an array-ref!"); } foreach my $SubParam (@$SubParams) { CheckParams($SubParam, $Spec->[0]); } } elsif ($Spec eq '!') { # required parameter: if (!exists $Params->{$Param}) { confess("Required param '$Param' is missing!"); } } elsif ($Spec =~ m{^\!Class=(.+)$}i) { my $Class = $1; # required parameter ... if (!exists $Params->{$Param}) { confess("Required param '$Param' is missing!"); } # ... of specific class if (!$Params->{$Param}->isa($Class)) { confess("Param '$Param' is not a '$Class', but that is required!"); } } elsif ($Spec eq '?') { # optional parameter - nothing to do } elsif ($Spec =~ m{^\?Class=(.+)$}i) { my $Class = $1; # optional parameter ... if (exists $Params->{$Param}) { # ... of specific class if (!$Params->{$Param}->isa($Class)) { confess("Param '$Param' is not a '$Class', but that is required!"); } } } elsif ($Spec =~ m{^m{(.+)}$}) { # try to match given regex: my $Regex = $1; my $Value = $Params->{$Param}; if ($Value !~ m{$Regex}) { confess("Required param '$Param' isn't matching regex '$Regex' (given value was '$Value')!"); } } else { # complain about unknown spec: confess("Unknown param-spec '$Spec' encountered!"); } } return scalar 1; }