
Hi there, attached is a function I have found useful in some of the OTRS-modules I worked on recently. It is called CheckParams() and its purpose is to offer an intuitive and flexible way of checking the params-hash that has been passed into a method. Currently, many methods contain code like this: # check all needed objects foreach (qw(ConfigObject LogObject DBObject TimeObject MainObject)) { die "Got no $_" if (!$Self->{$_}); } plus more checks for additional parameters that must be passed in via the %Params hash. When using CheckParams, the code looks like this: # check all needed objects CheckParams($Self, sub { 'ConfigObject' => '!Class=Kernel::Config', 'DBObject' => '!Class=Kernel::System::DB', 'LogObject' => '!Class=Kernel::System::Log', 'MainObject' => '!Class=Kernel::System::Main', 'TimeObject' => '!Class=Kernel::System::Time', }); As you can see, you pass CheckParam() the hash-ref that shall be checked and a specification (for performance reasons this should always be an anonymous subroutine). In the specification, you simply declare the supported parameters, whether or not they are required (!) or optional (?) or if they have to be of a specific class (Class=...). CheckParams() supports checking of more complex data structures, too: CheckParams($Params, sub { 'GeneralAttrs' => { 'From' => '!', 'To' => '!', }, 'JobAttrs' => { 'Creator' => '!', 'Changes' => [ { 'Action' => 'm{^(create|change|delete|sync)$}', 'Path' => '!', 'Content' => '?', } ], } }); This code snippet makes sure that the given $Params-hash contains two elements (GeneralAttrs & JobAttrs), each of which has to follow the specified substructures. As I believe this function could be useful throughout OTRS (now that we have the 2.2-branch), I'd like to add it to the framework. I can see two ways of doing that: 1.) non-OO style: add some kind of helper module (e.g. Utils.pm) and add CheckParams() as a *function* to that. 2.) OO style: add a new module Utils/Params.pm which contains a *class-method* Check(), such that you'd have to invoke it like this Utils::Params->Check($Params, sub { ... }); So what do you think? Do you like the idea in general at all? If so, how should that functionality be integrated (OO- or non-OO-style)? Please fire any suggestions or questions to this list or directly my way. cheers, Oliver

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi Oliver, I think this is a great idea! Thanks a lot. Thomas Oliver Tappe schrieb:
Hi there,
attached is a function I have found useful in some of the OTRS-modules I worked on recently. It is called CheckParams() and its purpose is to offer an intuitive and flexible way of checking the params-hash that has been passed into a method.
Currently, many methods contain code like this:
# check all needed objects foreach (qw(ConfigObject LogObject DBObject TimeObject MainObject)) { die "Got no $_" if (!$Self->{$_}); }
plus more checks for additional parameters that must be passed in via the %Params hash.
When using CheckParams, the code looks like this:
# check all needed objects CheckParams($Self, sub { 'ConfigObject' => '!Class=Kernel::Config', 'DBObject' => '!Class=Kernel::System::DB', 'LogObject' => '!Class=Kernel::System::Log', 'MainObject' => '!Class=Kernel::System::Main', 'TimeObject' => '!Class=Kernel::System::Time', });
As you can see, you pass CheckParam() the hash-ref that shall be checked and a specification (for performance reasons this should always be an anonymous subroutine). In the specification, you simply declare the supported parameters, whether or not they are required (!) or optional (?) or if they have to be of a specific class (Class=...).
CheckParams() supports checking of more complex data structures, too:
CheckParams($Params, sub { 'GeneralAttrs' => { 'From' => '!', 'To' => '!', }, 'JobAttrs' => { 'Creator' => '!', 'Changes' => [ { 'Action' => 'm{^(create|change|delete|sync)$}', 'Path' => '!', 'Content' => '?', } ], } });
This code snippet makes sure that the given $Params-hash contains two elements (GeneralAttrs & JobAttrs), each of which has to follow the specified substructures.
As I believe this function could be useful throughout OTRS (now that we have the 2.2-branch), I'd like to add it to the framework. I can see two ways of doing that:
1.) non-OO style: add some kind of helper module (e.g. Utils.pm) and add CheckParams() as a *function* to that. 2.) OO style: add a new module Utils/Params.pm which contains a *class-method* Check(), such that you'd have to invoke it like this Utils::Params->Check($Params, sub { ... });
So what do you think? Do you like the idea in general at all? If so, how should that functionality be integrated (OO- or non-OO-style)?
Please fire any suggestions or questions to this list or directly my way.
cheers, Oliver
------------------------------------------------------------------------
=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; }
------------------------------------------------------------------------
_______________________________________________ OTRS mailing list: dev - Webpage: http://otrs.org/ Archive: http://lists.otrs.org/pipermail/dev To unsubscribe: http://lists.otrs.org/cgi-bin/listinfo/dev
- -- ((otrs)) :: OTRS GmbH :: Europaring 4 :: D - 94315 Straubing Fon: +49 (0) 9421 56818 0 :: Fax: +49 (0) 9421 56818 18 http://www.otrs.com/ :: Communication with success! Geschäftsführer: André Mindermann Handelsregister: HRB 9452 Bad Homburg Steuernummer: 003/240/97521 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.5 (GNU/Linux) Comment: Using GnuPG with SUSE - http://enigmail.mozdev.org iD8DBQFG6RNm5VEHpl41kC8RAjs/AJwJxS/7WT0DFvW2lnhzW0HfM90H4QCg53Md 7KDeKDBtOAzb6/hy7glajrc= =WCAg -----END PGP SIGNATURE-----

Hi Oliver, Oliver Tappe schrieb:
attached is a function I have found useful in some of the OTRS-modules I worked on recently. It is called CheckParams() and its purpose is to offer an intuitive and flexible way of checking the params-hash that has been passed into a method.
Currently, many methods contain code like this:
# check all needed objects foreach (qw(ConfigObject LogObject DBObject TimeObject MainObject)) { die "Got no $_" if (!$Self->{$_}); }
plus more checks for additional parameters that must be passed in via the %Params hash.
When using CheckParams, the code looks like this:
# check all needed objects CheckParams($Self, sub { 'ConfigObject' => '!Class=Kernel::Config', 'DBObject' => '!Class=Kernel::System::DB', 'LogObject' => '!Class=Kernel::System::Log', 'MainObject' => '!Class=Kernel::System::Main', 'TimeObject' => '!Class=Kernel::System::Time', });
As you can see, you pass CheckParam() the hash-ref that shall be checked and a specification (for performance reasons this should always be an anonymous subroutine). In the specification, you simply declare the supported parameters, whether or not they are required (!) or optional (?) or if they have to be of a specific class (Class=...).
CheckParams() supports checking of more complex data structures, too:
CheckParams($Params, sub { 'GeneralAttrs' => { 'From' => '!', 'To' => '!', }, 'JobAttrs' => { 'Creator' => '!', 'Changes' => [ { 'Action' => 'm{^(create|change|delete|sync)$}', 'Path' => '!', 'Content' => '?', } ], } });
This code snippet makes sure that the given $Params-hash contains two elements (GeneralAttrs & JobAttrs), each of which has to follow the specified substructures.
As I believe this function could be useful throughout OTRS (now that we have the 2.2-branch), I'd like to add it to the framework. I can see two ways of doing that:
1.) non-OO style: add some kind of helper module (e.g. Utils.pm) and add CheckParams() as a *function* to that. 2.) OO style: add a new module Utils/Params.pm which contains a *class-method* Check(), such that you'd have to invoke it like this Utils::Params->Check($Params, sub { ... });
So what do you think? Do you like the idea in general at all? If so, how should that functionality be integrated (OO- or non-OO-style)?
Please fire any suggestions or questions to this list or directly my way.
-=> Yes, 'die "Got no $_" if (!$Self->{$_});' the die() is not a option. ;) Bit I see different things to integrate it into the current OTRS framework (may be we are talking about two different things?). Anyway, here my points: o) Keep it clean: If we have a check method in the framework, we should replace any other checks from the whole framework with the new check to keep it clean. o) Check it every time?/Performance Issue: "# check all needed objects" - For normal framework, it should not be necessary to check the default objects. The framework should only deliver existing objects. You never should use e. g. $Self->{LogObject} for other perl objects. I also would see a really performance issue if we would check the object content every time. -=> So for the OTRS framework I would expect to not need to check the base objects. o) OO-Style; Only OO-Style. It would be not good to switch to other styles. o) confess(): I have looked into your source code. I never have seen confess() in the OTRS framework bevor. Whats about this? o) Filename: Utils/Params.pm is not on the current directory style. Kernel/System/Params.pm or to extend the existing Kernel/System/CheckItem.pm would sounds a little bit better to me. o) Utils::Params->Check($Params, sub { ... }); looks a little bit strange to me. I would suggest something like "$Self->{ParamsObject}" oder "$Self->{CheckObject}". And a methode like "$Self->{ParamsObject}->Check( 'ConfigObject' => '!Class=Kernel::Config', 'DBObject' => '!Class=Kernel::System::DB', 'LogObject' => '!Class=Kernel::System::Log', 'MainObject' => '!Class=Kernel::System::Main', 'TimeObject' => '!Class=Kernel::System::Time', );" o) One other thing would be to define more via attributes. E. g. 'TimeObject' => '!Class=Kernel::System::Time', Is IMO not so good because you need to use some cpu time to use a regexp for to find out what "!Class=" is. I mean something like e. g. 'TimeObject' => { Operation => '!', Type => 'Class', Match => 'Kernel::System::Time', }, IMO this would be faster and need less cpu time. o Maybe we need something like Kernel::System::Main->Die(). So back to the two different things: IMO we need only a exists-check for objects (not content related) but we could use a new Check() for param validation of e. g. strings or other content. What do you think? :-) Greetings, -Martin
------------------------------------------------------------------------
=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; }

Hi Martin,
On 2007-09-18 at 14:55:34 [+0200], Martin Edenhofer
Oliver Tappe schrieb:
[ ... ]
Please fire any suggestions or questions to this list or directly my way.
-=> Yes, 'die "Got no $_" if (!$Self->{$_});' the die() is not a option. ;)
Bit I see different things to integrate it into the current OTRS framework (may be we are talking about two different things?).
No, having read your mail I still think we are talking about the same thing ;-)
Anyway, here my points:
o) Keep it clean: If we have a check method in the framework, we should replace any other checks from the whole framework with the new check to keep it clean.
Yes, that's the aim, but it is also a lot of work to do so (many lines of code would have to be changed). But if we see the chance, we should do it all in one big refactoring step.
o) Check it every time?/Performance Issue: "# check all needed objects" - For normal framework, it should not be necessary to check the default objects. The framework should only deliver existing objects. You never should use e. g. $Self->{LogObject} for other perl objects.
Hm, I do not seem to understand you. What do you mean by default objects? And why shouldn't one use $Self->{LogObject} in other perl objects? AFAICS, that kind of use is spread all over the place. What's the problem with it?
I also would see a really performance issue if we would check the object content every time. -=> So for the OTRS framework I would expect to not need to check the base objects.
Well, CheckParams() is not meant to check object "content" at all (as you are not allowed to look into objects by definition anyway). The more complex structures are only supported for hashes (not objects). It gets a bit complicated, as objects are in fact implemented as hashes, but CheckParams() is only intended to check the parameter hash given to a function. Think of it like this: after the invocation of CheckParams(), a method knows that all required paramteres have been passed in and (optionally) that they are of the correct type.
o) OO-Style; Only OO-Style. It would be not good to switch to other styles.
Ok, you have mentioned this before, but I dare to question the validity of the argument behind it: Perl is a hybrid programming language (like C++, but unlike Java and Smalltalk for instance) and it provides a procedural and an object-oriented interface, both of which are being used by OTRS. As an example, OTRS uses the functions print(), split(), join(), open() and close() in many places, all of which are part of perl's procedural interface. I perfectly understand that most of the time, an object-oriented interface (involving a class) is more apropriate, but *not* every time. Objects usually encapsulate some kind of state or complexity, but in this case there would be no state or complexity to encapsulate. Consider this: CheckParams($Params, sub { ... }) is so much more convenient than $Checker = Kernel::System::Params->new($Params); $Checker->Check(sub { ... }); which would be the standard way of doing it in OTRS style. I personally find this rather awkward, as it makes it rather difficult to grasp the meaning of the code. However, object-oriented interfaces provide an alternative solution when it comes to implementing functions: class methods (discussion follows below): Kernel::System::Params->Check($Params, sub { ... })
o) confess(): I have looked into your source code. I never have seen confess() in the OTRS framework bevor. Whats about this?
confess() is the standard way of requesting a die together with a stack crawl. It is provided by the Carp module. One could of course use $Self->{MainObject}->Die() instead, but that would depend on having an instance of MainObject available, which may not always be the case.
o) Filename: Utils/Params.pm is not on the current directory style. Kernel/System/Params.pm or to extend the existing Kernel/System/CheckItem.pm would sounds a little bit better to me.
Yep, Kernel/System/Params.pm would be correct another suggestion would be Kernel/System/MethodArgs.pm (which at least for me expresses the actual meaning of the module best).
o) Utils::Params->Check($Params, sub { ... }); looks a little bit strange to me. I would suggest something like "$Self->{ParamsObject}" oder "$Self->{CheckObject}". And a methode like
Yes, standard OTRS style, which would require every method wanting to check its arguments to create (or already have access to) an object of the CheckParams class. As expressed above, I find that less clear (and clarity is what I like a lot ;-). The strangelooking invocation is a class method call (just like new()), i.e. "Kernel::System::Params->Check()" invokes the method Check() on the class Kernel::System::Params. Class methods are a common ocurrence in object oriented software development.
"$Self->{ParamsObject}->Check( 'ConfigObject' => '!Class=Kernel::Config', 'DBObject' => '!Class=Kernel::System::DB', 'LogObject' => '!Class=Kernel::System::Log', 'MainObject' => '!Class=Kernel::System::Main', 'TimeObject' => '!Class=Kernel::System::Time', );"
Hey, but that is using a hash instead of a hash-ref, wasting performance along the way, as that hash would have to be copied during the invocation of the method (I remember Richard Jelinek mentioning that). Using an anonymous subroutine (sub { ... }) instead of a hash-ref makes it possible to avoid the performance hit of generating the hash at all (like for the case when the parameter checking has been disabled via SysConfig).
o) One other thing would be to define more via attributes. E. g.
'TimeObject' => '!Class=Kernel::System::Time',
Is IMO not so good because you need to use some cpu time to use a regexp for to find out what "!Class=" is.
Yes, you are right, it would be good to avoid the use of regexps and that can be done easily (replacing regexps by substr()).
I mean something like e. g.
'TimeObject' => { Operation => '!', Type => 'Class', Match => 'Kernel::System::Time', },
IMO this would be faster and need less cpu time.
Yes, that certainly would be another way to avoid the regexps. I had outlined the very compact declarative interface (using just one string) such that every check could be contained on one line, making the code easier to read. But maybe it would indeed be better to switch to a more attribute-like declaration like you have suggested. I don't know, really - is there anybody else with a view on this?
o Maybe we need something like Kernel::System::Main->Die().
Yes, I'd like that. I personally would favor a *function* named Croak(), automatically exported by Kernel::System::Basics, but that is probably just me ...
So back to the two different things: IMO we need only a exists-check for objects (not content related) but we could use a new Check() for param validation of e. g. strings or other content.
What do you think? :-)
I can't really see the difference between these two. Currently, CheckParams() can offer both, it just depends on what you ask for: Kernel::System::Params->Check($Self, sub { 'ConfigObject' => '!', 'DBObject' => '!', 'LogObject' => '!', 'MainObject' => '!', 'TimeObject' => '!', # 'Filename' => '!', 'IsUTF8' => '?', }); would do just that: check only the existence of the required framework objects and check the functional parameters, too (the hashmark just separating these two for visual clarity). Notwithstanding any further discussion on this list, I think all of this would be a fine topic to talk about over dinner during our meeting next week ;-) cheers, Oliver -- ((otrs)) :: OTRS GmbH :: Europaring 4 :: D - 94315 Straubing Fon: +49 (0) 9421 56818-0 :: Fax: +49 (0) 9421 56818-18 http://www.otrs.com/ :: Communication with success!
participants (3)
-
Martin Edenhofer
-
Oliver Tappe
-
Thomas Raith