Why not adopt me?
NAME
Params::Smart - use both positional and named arguments in a subroutine
SYNOPSIS
use Params::Smart 0.04;
sub my_sub {
%args = Params(qw( foo bar ?bo ?baz ))->args(@_);
...
}
my_sub( foo=> 1, bar=>2, bo=>3 ); # call with named arguments
my_sub(1, 2, 3); # same, with positional args
DESCRIPTION
This module provides "smart" parameter handling for subroutines without having to use a changed syntax or source filters. Features include:
Mixed use of named and positional parameters.
Type checking and coercion through callbacks.
Dyanmic parameters configured from callbacks.
Memoization of parameter templates.
Usage is as follows:
sub my_sub {
%vals = Params( @template )->args( @args );
...
}
The @template
specifies the names of parameters in the order that they should be given in subroutine calls, and @args
is the list of argument to be parsed: usually you just specify the void list @_
.
The keys in the returned hash %vals
are assigned to the appropriate arguments, irrespective of calling style.
Names may be called with an optional initial dash, as with Getargs::Mixed:
my_sub( -first => 1, -second => 2 );
Smart parameters can be used for method calls:
sub my_method {
my $self = shift;
%vals = Params( @template )->args( @args );
...
}
The values may also contain additional keys which begin with an underscore. These are internal/diagnostic values:
- _named
-
True if the parameters were treated as named, false if positional. See "CAVEATS" below.
To improve performance, Params
memoizes parameter templates when they are parsed, based on where the call to Params
was made.
This may be problematic if templates are changed dynamically. To override memoization, use ParamsNC function:
%vals = ParamsNC( @template )->args( @_ );
There are two styles of templates, "Simple Parameter Templates" with a Perl6-like syntax, and "Complex Parameter Templates" which allow more options to be specified using hashes.
Simple Parameter Templates
Simple parameter templates contain a list of key names in the order that they are expected for positional calls:
sub my_sub {
%vals = Params(qw( first second third ))->args(@_);
...
}
Calling the subroutine with the following
my_sub(1, 2, 3);
sets the values
%vals = (
first => 1,
second => 2,
third => 3
);
Parameters are required by default. To make a parameter optional, add a question mark before it:
%vals = Params(qw( first second ?third ))->args(@_);
Note that no required parameters may follow an optional parameter.
If one wants to "slurp" all remaining arguments into one value, add an asterisk before it:
%vals = Params(qw( first *second ))->args(@_);
So the above example call would set the values
%vals = (
first => 1,
second => [ 2, 3 ]
);
Note that the slurp argument is required unless it also includes a question-mark:
%vals = Params(qw( first *?second ))->args(@_);
You can also mark options as being allowed when called with named parameters only by adding a plus sign before them:
%vals = Params(qw( common +?obscure +?strange +?weird ))->args(@_);
This is useful when there are many options which are rarely needed (and too awkward to use in positional calling), or may have dangerous side effects if accidentally specified with a positional calling style. (The order of named-only parameters does not matter.)
You can also enforce named-only calling conventions on a subroutine by omitting question-marks from at least one parameter:
%vals = Params(qw( +first +second ))->args(@_);
As of version 0.04, default values can also be specified:
%vals = Params(qw( first=1 second=2 ))->args(@_);
Defaults can be delimited with quotes:
%vals = Params( 'first="some string"' ))->args(@_);
You can also specify aliases by separating them with a vertical bar:
%vals = Params(qw( hour|hh minute|min|mm seconds|sec|ss ))->args(@_);
All named parameter calls using aliases will be stored using the first name.
In general use of aliases are not recommended for subroutines. (This feature is a hook for implementing script-wide "getopts"-like functions.)
Complex Parameter Templates
You may use more complex templates if you need to specify additional information, such as callbacks:
%vals = Params(
{
name => "first",
required => 1,
callback => sub { ... },
comment => "first parameter",
},
{
name => "next",
slurp => 1,
comment => "second parameter",
},
)->args(@_);
Each parameter is specified by a hash reference with the following keys:
- name
-
The name of the parameter. May include aliases, separated by vertical bars.
- required
-
The parameter is required if true.
- default
-
A default value of the parameter.
- slurp
-
This parameter slurps the remaining arguments if true. The parameter will be an array reference.
- name_only
-
This parameter may be specified using named-calls only if true.
- needs
-
This parameter needs these other parameters to be specified (either as a list reference, or a string for a single required parameter).
- type
-
Not yet implemented. Use the callback to validate the value.
- callback
-
An optional callback which validates and coerces the parameter. The callback is passed the parameter-parsing object, the name of the parameter, and the value:
callback => sub { my ($self, $name, $value) = @_; ... return $value; },
The
$name
is the primary name for the parameter, and not any aliases which might have been used.It is expected to return the coerced value, or die if there is a problem:
callback => sub { my ($self, $name, $value, $hashref) = @_; die "$name must be >= 0" if ($value < 0); return $value || 1; },
Callbacks can also update the acceptable parameters:
callback => sub { my ($self, $name, $value, $hashref) = @_; if ($value eq "zip") { $self->set_param( { name => "compression_level", default => 6, } ); } return $value; },
One can use this to change or add new named parameters based on the values of existing parameters. However, one should use
ParamsNC
so that the modified template is not cached.In many cases you should use the "needs" option and avoid dynamically updating the parameters.
Note that dynamically-added parameters cannot dynamically add other parameters (at least not in this version).
The
$hashref
is a reference to the values being returned. One may not be able to rely on a specific parameter being set before the callback is executed, however.Note that the order that callbacks are called is not determined, so do not rely on one callback being called before another.
Do not call any internal methods aside from those documented here, as they do not have a defined behavior and may change in future versions.
- comment
-
An optional comment describing the field. This is currently unused but may be displayed in error messages in future versions.
Compatability with Previous Versions
Note that the formatting for simple parameter templates has changed since version 0.03, and the complex parameter templates were not implemented until version 0.04, so it is best to specify a minimum version in use statements
use Params::Smart 0.04;
CAVEATS
Because Perl5 treats hashes as lists, this module attempts to interpret the arguments as a hash of named parameters first. If some hash keys match, and some do not, then it assumes there has been an error. If no keys match, then it assumes that it the arguments are positional.
In theory one can pass positional arguments where every other argument matches a hash key, or one can pass a hash with the wrong keys (possible if one copies/pastes code from the wrong call) and so it is treated as a positional argument.
This is probably uncommon for most data, but subroutines should take extra care to check if values are within allowed ranges. There may even be security issues if users can blindly specify data that they know can cause this confusion. If the application is critical enough, then this may not be an appropriate module to use (at least not until the ability to distinguish between lists and hashes is improved).
To diagnose potential bugs, or to enforce named or positional calling one can check the "_named" parameter.
A future version might make use of Perl internals to get around this problem.
SEE ALSO
This module is superficially similar in function to Getargs::Mixed but does not require named parameters to have an initial dash ('-').
Class::NamedParams provides a framework for implementing named parameters in classes.
Sub::NamedParams will create a named-parameter wrapper around subroutines which use positional parameters.
The syntax of the parameter templates is inspired by Perl6::Subs, though not necessarily compatible. (See also Apocalypse 6 in Perl6::Bible).
Sub::Usage inspired the error-messages returned by calls to arg().
Params::Validate is useful for (additional) parameter validation beyond what this module is capable of.
Class::ParmList provides a framework for parameter validation as well.
AUTHOR
Robert Rothenberg <rrwo at cpan.org>
Suggestions and Bug Reporting
Feedback is always welcome. Please use the CPAN Request Tracker at http://rt.cpan.org to submit bug reports.
LICENSE
Copyright (c) 2005-2007 Robert Rothenberg. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.