Params-Clean
view release on metacpan or search on metacpan
lib/Params/Clean.pm view on Meta::CPAN
our (@keywords, @KEYWORDS); # We need to declare these and then init them with BEGIN so they're ready for the "use UID"
BEGIN { our @keywords=qw/POSN NAME FLAG REST TYPE PARSE/; } # UID keywords
BEGIN { our @KEYWORDS=(@keywords, "LIST", "args"); } # all keywords (LIST handled specially)
use UID @keywords; # Set up some lexicals that won't be available anywhere else, so exporting refs to them will act as unique identifiers
our %Warn; # categories of warning levels by caller: e.g. $Warn{main}{missing_start}=fatal
BEGIN {
$Warn{undef}={ # default warning levels
invalid_opts=>"warn", # illegal warning or keyword options used
funny_arglist=>"ignore", # asked to PARSE something that's not an ARRAY, HASH, or CODE
missing_start=>"ignore", # LIST cannot find specified starting key
missing_end=>"warn", # LIST cannot find specified ending key
invalid_list=>"warn", # tried to use a FLAG or LIST, etc, as endpoint to a LIST
invalid_type=>"warn", # tried to use an illegal TYPE definition
nonint_name=>"warn", # non-integral key will be used as a name
orphaned_type=>"warn", # TYPE not followed by a definition
misplaced_rest=>"warn", # REST used before last parameter
misplaced_parse=>"die", # PARSE used after first parameter
};
}
# now create constants with all our exception-type names (handy, and helps catch typos!)
BEGIN { no strict 'refs'; for my $s (keys %{$Warn{undef}}) {*{$s}=sub {return $s, @_ if wantarray; warn "ERROR: attempt to use args after '$s' which is in scalar context (perhaps you need a comma after '$s'?)" if @_; return $s};} } # stolen from UID...
our $CaseSensitive=0; # By default, we match match names case-insensitively
our $Debug=0; # Whether to show debugging messages (0 level=none)
sub same($$); sub insame($@); sub typewriter($$); sub warning; # predeclare!
sub un {grep !$_[$_], 0..@_-1;} # pull out all the keys that work out to false (used with @used!)
sub array { map ref($_) eq "ARRAY"?@$_:$_, (@_) } # Normalise a list by expanding array-refs
sub comma { "[".join(", ", array @_)."]" } # Format array(ref) into "[a, b, c]"
sub debug
# For showing debugging messages
# Does some basic cleanup, like unpacking array-refs, or looking up our UIDs
# Pass each thing you want cleaned as a separate arg
{
return unless $Debug>=shift; # do nothing unless our debugging level is high enough
my $i; my %ID=reverse(POSN=>POSN, FLAG=>FLAG, NAME=>NAME, TYPE=>TYPE, REST=>REST); # lookup hash for our special IDs
warn join " ", map $ID{$_}?"|$ID{$_}|":ref eq"ARRAY"?"[".(join " ", map $ID{$_}?"|$ID{$_}|":$_, (@$_))."]":ref eq "HASH"?"{".(join "", map {$i++%2?"$_; ":"$_=>"} %$_)."}":"$_", (@_), "\n"
}
#===========================================================================
#
# STARTUP
#
#===========================================================================
sub import
# Handle module options: renaming exported UIDs and setting desired warnings
#
# RENAMING: pass a keyword ID followed by the new name (LIST=>"PLIST") -- setting to undef means don't export it at all
# WARNINGS: warn=>"type", or die=>"type" or fatal=>"type", or ignore=>"type"
{
my $me=shift; # our package name
my @opts, my $i; push @opts, [$_[$i++]=>$_[$i++]] while $i<@_; # pair up the options (we would use a hash, but we want to preserve order, and anyway we could have the same key repeated)
my %EXPORT=map {$_=>$_} @KEYWORDS; # keywords to be exported (normally all @KEYWORDS) in convenient hash format
my $keys=join "|", @KEYWORDS; # for regex to test for any of our keywords
my $caller=(caller)[0]; # caller's package
# Set up warning/fatal/ignoral categories
$Warn{$caller}={%{$Warn{undef}}}; # start by setting up default warning levels
for (grep $opts[$_][0]=~/^(warn|die|fatal|ignore)$/, 0..$#opts) # grep through the key-halves of each opt for exception-levels
{
my $opt=delete $opts[$_];
warning(invalid_opts qq[WARNING: Ignoring attempt to set unrecogised warning category "$opt->[1]"]) and next unless exists $Warn{$caller}{$opt->[1]}; # complain if trying to set an invalid category
$Warn{$caller}{$opt->[1]}=$opt->[0]; # set level for this caller and remove opts as we handle them
}
# Look for our keywords: pairs that start with a keyword substitute the new name instead
$EXPORT{$opts[$_][0]}=$opts[$_][1] and delete $opts[$_] for grep $opts[$_][0]=~/^($keys)$/, grep exists $opts[$_], 0..$#opts; # look for our keywords and remove opts as we deal with them
no strict 'refs'; # so we can manually "export" the subs to the caller's namespace
*{$caller."::".$EXPORT{$_}}=\&{$_} for grep defined $EXPORT{$_}, keys %EXPORT; # skipping undefs
# If there are any opts left, we don't know what to do with them
warning invalid_opts "WARNING: Ignoring unrecognised options [".join(", ", map "$opts[$_][0]=>$opts[$_][1]", grep exists $opts[$_], 0..$#opts)."]" if @opts;
}
#===========================================================================
#
# LISTs
#
#===========================================================================
# "LIST" types are objects containing the pieces we need to handle lists
# {
# spec => what kind of list this is: <abs>olute or <rel>ative,
# start => the param key(s) which begin the list,
# end => the param(s) which end an absolute list,
# pos => the list of positions to grab for a relative list,
# incl => a flag indicating whether to include the starting/ending param
# }
#
# A few operators are overloaded to provide convenient syntax for building up our LIST objects
# Since assignment isn't overloadable, we also tie our object so we can STORE it ourselves
sub LIST ($) :lvalue { tie my $list, __PACKAGE__, @_; $list } # takes a single arg and turns it into a tied List-object
sub TIESCALAR { my $class=shift; bless {spec=>"abs", start=>[array @_]}, $class } # object is a hash containing the setup; all we know upon creation is the starting-point; assume absolute [can override that later if we specify more details]
sub FETCH { shift; }; # nothing fancy here, just return the object straight
use overload '<=>',sub { @{$_[0]}{spec=>end=>incl=>}=("abs", [array $_[1]], 1); shift }; # absolute list, include end point
use overload '<=', sub { @{$_[0]}{spec=>end=>incl=>}=("abs", [array $_[1]], 0); shift }; # absolute list, don't include end point
sub STORE($) { @{$_[0]}{spec=>pos=>incl=>}=("rel", [array $_[1]], "?"); } # "overload =": relative, don't force starting point either way
use overload '&', sub { @{$_[0]}{spec=>pos=>incl=>}=("rel", [array $_[1]], "Y"); shift }; # relative list, include start point
use overload '^', sub { @{$_[0]}{spec=>pos=>incl=>}=("rel", [array $_[1]], "N"); shift }; # relative list, don't include start point
use overload q(""), sub { "{". (join ", ", map "$_=>".(join ":", array($_[0]->{$_})), (qw/spec start end pos incl/) )."}" }; #stringify for debug messages
###check for attempting to use operators more than once in a row? or to use other operators?!?
#===========================================================================
#
( run in 2.638 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )