File Coverage

blib/lib/Class/ParamParser.pm
Criterion Covered Total %
statement 72 72 100.0
branch 40 42 95.2
condition 24 30 80.0
subroutine 9 9 100.0
pod 2 2 100.0
total 147 155 94.8


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Class::ParamParser - Provides complex parameter list parsing
4              
5             =cut
6              
7             ######################################################################
8              
9             package Class::ParamParser;
10             require 5.004;
11              
12             # Copyright (c) 1999-2003, Darren R. Duncan. All rights reserved. This module
13             # is free software; you can redistribute it and/or modify it under the same terms
14             # as Perl itself. However, I do request that this copyright information and
15             # credits remain attached to the file. If you modify this module and
16             # redistribute a changed version then please attach a note listing the
17             # modifications. This module is available "as-is" and the author can not be held
18             # accountable for any problems resulting from its use.
19              
20 1     1   630 use strict;
  1         1  
  1         28  
21 1     1   4 use warnings;
  1         1  
  1         25  
22 1     1   5 use vars qw($VERSION);
  1         4  
  1         1033  
23             $VERSION = '1.041';
24              
25             ######################################################################
26              
27             =head1 DEPENDENCIES
28              
29             =head2 Perl Version
30              
31             5.004
32              
33             =head2 Standard Modules
34              
35             I
36              
37             =head2 Nonstandard Modules
38              
39             I
40              
41             =head1 SYNOPSIS
42              
43             use Class::ParamParser;
44             @ISA = qw( Class::ParamParser );
45              
46             =head2 PARSING PARAMS INTO NAMED HASH
47              
48             sub textfield {
49             my $self = shift( @_ );
50             my $rh_params = $self->params_to_hash( \@_, 0,
51             [ 'name', 'value', 'size', 'maxlength' ],
52             { 'default' => 'value' } );
53             $rh_params->{'type'} = 'text';
54             return( $self->make_html_tag( 'input', $rh_params ) );
55             }
56              
57             sub textarea {
58             my $self = shift( @_ );
59             my $rh_params = $self->params_to_hash( \@_, 0,
60             [ 'name', 'text', 'rows', 'cols' ], { 'default' => 'text',
61             'value' => 'text', 'columns' => 'cols' }, 'text', 1 );
62             my $ra_text = delete( $rh_params->{'text'} );
63             return( $self->make_html_tag( 'textarea', $rh_params, $ra_text ) );
64             }
65              
66             sub AUTOLOAD {
67             my $self = shift( @_ );
68             my $rh_params = $self->params_to_hash( \@_, 0, 'text', {}, 'text' );
69             my $ra_text = delete( $rh_params->{'text'} );
70             $AUTOLOAD =~ m/([^:]*)$/;
71             my $tag_name = $1;
72             return( $self->make_html_tag( $tag_name, $rh_params, $ra_text ) );
73             }
74              
75             =head2 PARSING PARAMS INTO POSITIONAL ARRAY
76              
77             sub property {
78             my $self = shift( @_ );
79             my ($key,$new_value) = $self->params_to_array(\@_,1,['key','value']);
80             if( defined( $new_value ) ) {
81             $self->{$key} = $new_value;
82             }
83             return( $self->{$key} );
84             }
85              
86             sub make_html_tag {
87             my $self = shift( @_ );
88             my ($tag_name, $rh_params, $ra_text) =
89             $self->params_to_array( \@_, 1,
90             [ 'tag', 'params', 'text' ],
91             { 'name' => 'tag', 'param' => 'params' } );
92             ref($rh_params) eq 'HASH' or $rh_params = {};
93             ref($ra_text) eq 'ARRAY' or $ra_text = [$ra_text];
94             return( join( '',
95             "<$tag_name",
96             (map { " $_=\"$rh_params->{$_}\"" } keys %{$rh_params}),
97             ">",
98             @{$ra_text},
99             "",
100             ) );
101             }
102              
103             =head1 DESCRIPTION
104              
105             This Perl 5 object class implements two methods which inherited classes can use
106             to tidy up parameter lists for their own methods and functions. The two methods
107             differ in that one returns a HASH ref containing named parameters and the other
108             returns an ARRAY ref containing positional parameters.
109              
110             Both methods can process the same kind of input parameter formats:
111              
112             =over 4
113              
114             =item
115              
116             I
117              
118             =item
119              
120             value
121              
122             =item
123              
124             value1, value2, ...
125              
126             =item
127              
128             name1 => value1, name2 => value2, ...
129              
130             =item
131              
132             -name1 => value1, -NAME2 => value2, ...
133              
134             =item
135              
136             { -Name1 => value1, NAME2 => value2, ... }
137              
138             =item
139              
140             { name1 => value1, -Name2 => value2, ... }, valueR
141              
142             =item
143              
144             { name1 => value1, -Name2 => value2, ... }, valueR1, valueR2, ...
145              
146             =back
147              
148             Those examples included single or multiple positional parameters, single or
149             multiple named parameters, and a HASH ref containing named parameters (with
150             optional "remaining" values afterwards). That list of input variations is not
151             exhaustive. Named parameters can either be prefixed with "-" or left natural.
152              
153             We assume that the parameters are named when either they come as a HASH ref or
154             the first parameter begins with a "-". We assume that they are positional if
155             there is an odd number of them. Otherwise we are in doubt and rely on an
156             optional argument to the tidying method that tells us which to guess by default.
157              
158             We assume that any "value" may be an array ref (aka "multiple" values under the
159             same name) and hence we don't do anything special with them, passing them as is.
160             The only exception to this is with "remaining" values; if there is more than one
161             of them and the first isn't an array ref, then they are all put in an array ref.
162              
163             If the source and destination are both positional, then they are identical.
164              
165             =head1 SYNTAX
166              
167             This class does not export any functions or methods, so you need to call them
168             using object notation. This means using Bfunction()> for functions
169             and B<$object-Emethod()> for methods. If you are inheriting this class for
170             your own modules, then that often means something like B<$self-Emethod()>.
171             Note that this class doesn't have any properties of its own.
172              
173             =head1 FUNCTIONS AND METHODS
174              
175             =head2 params_to_hash( SOURCE, DEF, NAMES[, RENAME[, REM[, LC]]] )
176              
177             See below for argument descriptions.
178              
179             =cut
180              
181             ######################################################################
182              
183             sub params_to_hash {
184 88     88 1 728 my ($self, $ra_args, $posit_by_def, $ra_posit_names, $rh_rename,
185             $remaining_param_name, $lc) = @_;
186              
187             # Shortcut - no input means no output.
188 88 100 100     349 ref( $ra_args ) eq 'ARRAY' and @{$ra_args} or return( {} );
  87         404  
189              
190             # Put named arguments in $rh_args if there are any; put undef otherwise.
191             # When the first element of $ra_args is a hash ref, other elems go in @rem.
192 78         171 my ($rh_args, @rem) = $self->_args_are_named( $ra_args, 1, !$posit_by_def );
193              
194             # If the arguments are not named then...
195 78 100       296 ref( $rh_args ) eq 'HASH' or do {
196              
197             # Shortcut - input is positional but no named translator, so no output.
198 21 100 66     68 ref( $ra_posit_names ) eq 'ARRAY' and @{$ra_posit_names} or return( {} );
  16         53  
199              
200             # Translate positional arguments to named and return them.
201 16 50       40 ref( $ra_posit_names ) eq 'ARRAY' or $ra_posit_names = [$ra_posit_names];
202 16         34 return( $self->_posit_to_named( $ra_args, $ra_posit_names ) );
203             };
204              
205             # Normalize named argument aliases to their standard versions.
206 57 100       127 ref( $rh_rename ) eq 'HASH' or $rh_rename = {};
207 57         67 my %args_out = %{$self->_rename_named_args( $rh_args, $rh_rename, 1, $lc )};
  57         116  
208              
209             # Incorporate "remaining" arguments if desired.
210 57 100 100     244 if( @rem and $remaining_param_name ) {
211 10 100 66     55 $args_out{$remaining_param_name} =
212             (ref( $rem[0] ) eq 'ARRAY' or @rem == 1) ? $rem[0] : \@rem;
213             }
214              
215             # Return named arguments.
216 57         405 return( \%args_out );
217             }
218              
219             ######################################################################
220              
221             =head2 params_to_array( SOURCE, DEF, NAMES[, RENAME[, REM[, LC]]] )
222              
223             See below for argument descriptions.
224              
225             =cut
226              
227             ######################################################################
228              
229             sub params_to_array {
230 88     88 1 604 my ($self, $ra_args, $posit_by_def, $ra_posit_names, $rh_rename,
231             $remaining_param_name, $lc) = @_;
232              
233             # Shortcut - no input means no output.
234 88 100 100     227 ref( $ra_args ) eq 'ARRAY' and @{$ra_args} or return( [] );
  87         294  
235              
236             # Put named arguments in $rh_args if there are any; put undef otherwise.
237             # When the first element of $ra_args is a hash ref, other elems go in @rem.
238 78         166 my ($rh_args, @rem) = $self->_args_are_named( $ra_args, 1, !$posit_by_def );
239              
240             # If the arguments are not named, then return a copy of positional arguments.
241 78 100       319 ref( $rh_args ) eq 'HASH' or return( [@{$ra_args}] ); # input = output
  21         90  
242              
243             # Shortcut - input is named but no positional translator, so no output.
244 57 100 66     155 ref( $ra_posit_names ) eq 'ARRAY' and @{$ra_posit_names} or return( [] );
  48         146  
245              
246             # Normalize named argument aliases to their standard versions.
247 48 100       248 ref( $rh_rename ) eq 'HASH' or $rh_rename = {};
248 48         49 my %args_out = %{$self->_rename_named_args( $rh_args, $rh_rename, 1, $lc )};
  48         108  
249              
250             # Incorporate "remaining" arguments if desired.
251 48 100 100     602 if( @rem and $remaining_param_name ) {
252 10 100 66     61 $args_out{$remaining_param_name} =
253             (ref( $rem[0] ) eq 'ARRAY' or @rem == 1) ? $rem[0] : \@rem;
254             }
255              
256             # Translate named arguments to positional and return them.
257 48 50       115 ref( $ra_posit_names ) eq 'ARRAY' or $ra_posit_names = [$ra_posit_names];
258 48         127 return( $self->_named_to_posit( \%args_out, $ra_posit_names ) );
259             }
260              
261             ######################################################################
262              
263             =head1 ARGUMENTS
264              
265             The arguments for the above methods are the same, so they are discussed together
266             here:
267              
268             =over 4
269              
270             =item 1
271              
272             The first argument, SOURCE, is an ARRAY ref containing the original parameters
273             that were passed to the method which calls this one. It is safe to pass "\@_"
274             because we don't modify the argument at all. If SOURCE isn't a valid ARRAY ref
275             then its default value is [].
276              
277             =item 1
278              
279             The second argument, DEF, is a boolean/scalar that tells us whether, when in
280             doubt over whether SOURCE is in positional or named format, what to guess by
281             default. A value of 0, the default, means we guess named, and a value of 1 means
282             we assume positional.
283              
284             =item 1
285              
286             The third argument, NAMES, is an ARRAY ref (or SCALAR) that provides the names to
287             use when SOURCE and our return value are not in the same format (named or
288             positional). This is because positional parameters don't know what their names
289             are and named parameters (hashes) don't know what order they belong in; the NAMES
290             array provides the missing information to both. The first name in NAMES matches
291             the first value in a positional SOURCE, and so-on. Likewise, the order of
292             argument names in NAMES determines the sequence for positional output when the
293             SOURCE is named.
294              
295             =item 1
296              
297             The optional fourth argument, RENAME, is a HASH ref that allows us to interpret a
298             variety of names from a SOURCE in named format as being aliases for one enother.
299             The keys in the hash are names to look for and the values are what to rename them
300             to. Keys are matched regardless of whether the SOURCE names have "-" in front
301             of them or not. If several SOURCE names are renamed to the same hash value, then
302             all but one are lost; the SOURCE should never contain more than one alias for the
303             same parameter anyway. One way to explicitely delete a parameter is to rename it
304             with "", as parameters with that name are discarded.
305              
306             =item 1
307              
308             The optional fifth argument, REM, is only used in circumstances where the first
309             element of SOURCE is a HASH ref containing the actual named parameters that
310             SOURCE would otherwise be. If SOURCE has extra, "remaining" elements following
311             the HASH ref, then REM says what its name is. Remaining parameters with the same
312             name as normal parameters (post renaming and "-" substitution) take precedence.
313             The default value for REM is "", and it is discarded unless renamed. Note that
314             the value returned with REM can be either a single scalar value, when the
315             "remaining" is a single scalar value, or an array ref, when there are more than
316             one "remaining" or the first "remaining" is an array ref (passed as is).
317              
318             =item 1
319              
320             The optional sixth argument, LC, is a boolean/scalar that forces named parameters
321             in SOURCE to be lowercased; by default this is false, meaning that the original
322             case is preserved. Use this when you want your named parameters to have
323             case-insensitive names, for accurate matching by your own code or RENAME. If you
324             use this, you must provide lowercased keys and values in your RENAME hash, as
325             well as lowercased NAMES and REM; none of these are lowercased for you.
326              
327             =back
328              
329             =cut
330              
331             ######################################################################
332             # _args_are_named( ARGS[, USE_DASHES[, GUESS_NAMED]] )
333             # This private method will check if the incoming argument list, provided in
334             # the array ref argument ARGS, appears to be in named format or not. If it is
335             # named then this method will return a hash ref containing the raw named
336             # version (true); otherwise, it returns undef (false). By default, ARGS is
337             # known to be named if its first element is a hash ref, and assumed to be
338             # positional if the count of arguments is odd. If neither of those two
339             # conditions are true then we have an even argument count and we are in doubt
340             # of whether they are named or not. The argument GUESS_NAMED says what to do
341             # in that case; if it is true then we guess named and if it is false then we
342             # guess positional. If the argument USE_DASHES is true then we check the first
343             # element in ARGS to see if it begins with a dash, "-", and if it does then we
344             # assume that ARGS is named regardless of the count of elements.
345             # When the first element of ARGS is a hash ref, any other elements of ARGS are
346             # also returned as "remaining" values, if they exist, after the hash ref.
347             # So you can call this like "($rh_named, @rem) = _args_are_named()".
348              
349             sub _args_are_named {
350 156     156   236 my ($self, $ra_args, $use_dashes, $guess_named) = @_;
351 156 100 66     587 if( ref( $ra_args->[0] ) eq 'HASH' ) {
    100          
    100          
352 84         86 return( @{$ra_args} ); # literal hash in first return elem
  84         227  
353 54         107 } elsif( $use_dashes and substr( $ra_args->[0], 0, 1 ) eq '-' ) {
354 18         21 return( { @{$ra_args} } ); # first element starts with "-"
  18         79  
355             } elsif( @{$ra_args} % 2 ) {
356 36         71 return( undef ); # odd # elements
357             } else {
358 18 100       33 return( $guess_named ? { @{$ra_args} } : undef ); # even num elements
  12         48  
359             }
360             }
361              
362             # _posit_to_named( ARGS, POSIT_NAMES )
363             # This private method will take ARGS in positional format, as an array ref, and
364             # return a named version as a hash ref. POSIT_NAMES is an array ref that is
365             # used as a translation table between the two formats. The elements ot
366             # POSIT_NAMES are the new names for arguments at corresponding element numbers
367             # in ARGS. We are checking array lengths below to avoid warnings.
368              
369             sub _posit_to_named {
370 16     16   23 my ($self, $ra_args, $ra_pn) = @_;
371 16         17 my ($ind_to_use) = sort ($#{$ra_pn}, $#{$ra_args}); # largest common index
  16         21  
  16         50  
372 16         35 my %args_out = map { ( $ra_pn->[$_] => $ra_args->[$_] ) } (0..$ind_to_use);
  34         224  
373 16         29 delete( $args_out{''} ); # remove unwanted elements
374 16         72 return( \%args_out );
375             }
376              
377             # _named_to_posit( ARGS, POSIT_NAMES )
378             # This private method will take ARGS in named format, as an hash ref, and return
379             # a positional version as an array ref. POSIT_NAMES is an array ref that is
380             # used as a translation table between the two formats. The elements ot
381             # POSIT_NAMES are matched with keys in ARGS and the values of ARGS are output in
382             # corresponding element numbers with POSIT_NAMES.
383              
384             sub _named_to_posit {
385 48     48   67 my ($self, $rh_args, $ra_pn) = @_;
386 48         54 return( [ map { $rh_args->{$ra_pn->[$_]} } (0..$#{$ra_pn}) ] );
  144         457  
  48         97  
387             }
388              
389             # _rename_named_args( ARGS, RENAME[, USE_DASHES[, LOWERCASE]] )
390             # This private method will take a hash ref as input via ARGS and copy it into a
391             # new hash ref, which it returns. During the copy, hash keys may be renamed in
392             # several ways. If LOWERCASE is true then the key is lowercase. If USE_DASHES
393             # is true then the leading character is removed if it is a dash, "-". Finally,
394             # the keys are looked up using the hash ref RENAME, and if there are matching
395             # keys then the associated RENAME values are substituted. If any key is
396             # renamed to the empty string or undef then it is deleted.
397              
398             sub _rename_named_args {
399 105     105   211 my ($self, $rh_args, $rh_rename, $use_dashes, $lowercase) = @_;
400 105         590 my %args_out = ();
401 105         128 foreach my $key (sort keys %{$rh_args}) {
  105         329  
402 289         414 my $value = $rh_args->{$key};
403 289 100       599 $lowercase and $key = lc( $key ); # change to lowercase
404 289 100 66     1370 $use_dashes and substr( $key, 0, 1 ) eq '-' and
405             $key = substr( $key, 1 ); # remove leading "-"
406 289 100       715 exists( $rh_rename->{$key} ) and $key = $rh_rename->{$key}; # chg alias
407 289         914 $args_out{$key} = $value;
408             }
409 105         201 delete( $args_out{''} ); # remove unwanted elements
410 105         570 return( \%args_out );
411             }
412              
413             ######################################################################
414              
415             1;
416             __END__