File Coverage

blib/lib/Sub/NamedParams.pm
Criterion Covered Total %
statement 58 59 98.3
branch 25 28 89.2
condition 2 3 66.6
subroutine 9 9 100.0
pod 1 1 100.0
total 95 100 95.0


line stmt bran cond sub pod time code
1             package Sub::NamedParams;
2              
3 1     1   10321 use 5.005;
  1         4  
  1         54  
4 1     1   7 use strict;
  1         2  
  1         48  
5 1     1   5 use Carp qw/croak/;
  1         7  
  1         82  
6              
7             require Exporter;
8 1     1   6 use vars qw/ $VERSION @ISA @EXPORT_OK /;
  1         1  
  1         404  
9             $VERSION = '1.02';
10             @ISA = qw/ Exporter/;
11             @EXPORT_OK = qw/ wrap /;
12              
13             # I don't like defining this outside the sub, but I want this lexically
14             # scoped (so programmers won't futz with it) and persistant.
15             {
16             my %wrapped_sub;
17            
18             sub wrap {
19 7     7 1 2922 my %wrapper = @_;
20 7         16 my $package = caller(0);
21            
22 7         13 foreach ( qw/ sub names / ) {
23 14 50       41 if ( ! exists $wrapper{$_} ) {
24 0         0 croak "You must supply '$_' in the argument list'";
25             }
26             }
27            
28 7 100       286 croak "'sub' value must not be a reference." unless !ref $wrapper{sub};
29            
30 6         10 my $sub = $wrapper{ sub };
31 6 50       18 if ( $sub !~ /::/ ) {
32             # if it's not fully qualified, append the package
33 6         16 $sub = "${package}::$sub";
34             }
35            
36 6         9 my $target = $sub;
37 6 100       15 if ( exists $wrapper{ target } ) {
38 2         4 $target = $wrapper{ target };
39 2 50       8 if ( $target !~ /::/ ) {
40 2         3 $target = "${package}::$target";
41             }
42             }
43 6 100       15 if (my $error = _bad_target( $target, \%wrapper )) {
44 2         287 croak $error;
45             }
46            
47 4         11 $wrapped_sub{ $target } = 1;
48            
49 1     1   6 no strict 'refs';
  1         2  
  1         323  
50 4         12 my $orig_sub = \&$sub;
51            
52 4 100       13 $wrapper{ hashref } = 1 if ! exists $wrapper{ hashref };
53 4 100       15 $wrapper{ default } = {} if ! exists $wrapper{ default };
54            
55 4         13 local $^W; # suppress warnings about redefined sub
56 4         26 *{$target} = sub {
57 5 100   5   689 my %args = $wrapper{ hashref } ? %{$_[0]} : @_;
  2         10  
58 5         6 my @orig_args;
59 5         9 foreach my $arg_name ( @{$wrapper{ names }} ) {
  5         15  
60 11 100       28 if ( exists $args{ $arg_name } ) {
    100          
61 6         16 push @orig_args, $args{ $arg_name };
62             } elsif ( exists $wrapper{ default }{ $arg_name } ) {
63 4         12 push @orig_args, $wrapper{ default }{ $arg_name };
64             } else {
65 1         166 croak( "Cannot find value or default for '$arg_name'" );
66             }
67             }
68 4         14 return $orig_sub->( @orig_args );
69             }
70 4         20 }
71            
72             sub _bad_target {
73 6     6   9 my ( $target, $wrap ) = @_;
74 6         9 my $error = '';
75 6 100 66     33 if ( exists $wrapped_sub{ $target } ) {
    100          
76 1         3 $error = "Cannot rewrap '$target'";
77             } elsif (exists $wrap->{target} and $wrap->{sub} ne $wrap->{target}) {
78 1     1   7 no strict 'refs';
  1         2  
  1         93  
79 2 100       3 if ( defined &{$target} ) {
  2         9  
80 1         3 $error = "Cannot target a pre-existing sub: '$target'";
81             }
82             }
83 6         19 return $error;
84             }
85             }
86             'Ovid';
87              
88             __END__