File Coverage

blib/lib/Method/Delegation.pm
Criterion Covered Total %
statement 77 88 87.5
branch 32 42 76.1
condition 3 6 50.0
subroutine 14 15 93.3
pod 1 1 100.0
total 127 152 83.5


line stmt bran cond sub pod time code
1             package Method::Delegation;
2              
3 2     2   153893 use 5.006;
  2         14  
4 2     2   9 use strict;
  2         3  
  2         34  
5 2     2   9 use warnings;
  2         4  
  2         72  
6 2     2   9 use Carp;
  2         4  
  2         101  
7 2     2   802 use Sub::Install;
  2         3149  
  2         7  
8 2     2   74 use base 'Exporter';
  2         4  
  2         926  
9             our @EXPORT = qw(delegate); ## no critic
10             our $VERSION = '0.03';
11              
12             sub delegate {
13 12     12 1 3413 my %arg_for = @_;
14              
15 12         28 my ( $package, undef, undef ) = caller;
16 12         262 my $delegate = delete $arg_for{to};
17 12         19 my $methods = delete $arg_for{methods};
18 12         17 my $args = delete $arg_for{args};
19 12         14 my $if_true = delete $arg_for{if_true};
20 12         15 my $else_return = delete $arg_for{else_return};
21 12         13 my $override = delete $arg_for{override};
22 12         17 my $maybe_to = delete $arg_for{maybe_to};
23              
24 12 50       25 if ( keys %arg_for ) {
25 0         0 my $unknown = join ', ' => sort keys %arg_for;
26 0         0 croak("Unknown keys supplied to delegate(): $unknown");
27             }
28              
29 12 100       20 if ($maybe_to) {
30 5 100       9 if ($delegate) {
31 1         7 croak(
32             "You supplied both 'maybe_to' and 'to'. I don't know which to use.");
33             }
34 4 50       6 if ($if_true) {
35 0         0 croak(
36             "You supplied both 'maybe_to' and 'if_true'. I don't know which to use."
37             );
38             }
39 4         8 ( $delegate, $if_true ) = ($maybe_to) x 2;
40             }
41 11 100       28 $delegate or croak("You must supply a 'to' argument to delegate()");
42 10 100       30 $methods or croak("You must supply a 'methods' argument to delegate()");
43 9         19 $methods = _normalize_methods( $methods, $delegate );
44              
45 8 50 66     21 if ( defined $else_return && !defined $if_true ) {
46 0         0 croak(
47             "You must supply a 'if_true' argument if 'else_return' is defined");
48             }
49              
50 8         15 _assert_valid_method_name($delegate);
51 8 100       17 _assert_valid_method_name($if_true) if defined $if_true;
52              
53 8         24 while ( my ( $method, $to ) = each %$methods ) {
54 10         125 _assert_valid_method_name($method);
55 9         15 _assert_valid_method_name($to);
56              
57 9         9 my $coderef;
58 9 100       13 if ($if_true) {
59 7 50       12 if ($args) {
60             $coderef = sub {
61 0     0   0 my $self = shift;
62 0 0       0 if ( $self->$if_true ) {
63 0         0 return $self->$delegate->$to(@_);
64             }
65 0 0       0 return defined $else_return ? $else_return : ();
66 0         0 };
67             }
68             else {
69             $coderef = sub {
70 5     5   854 my $self = shift;
71 5 100       13 if ( $self->$if_true ) {
72 2         13 return $self->$delegate->$to;
73             }
74 3 100       21 return defined $else_return ? $else_return : ();
75 7         21 };
76             }
77             }
78             else {
79 2 100       4 if ($args) {
80             $coderef = sub {
81 1     1   476 my $self = shift;
82 1         4 return $self->$delegate->$to(@_);
83 1         4 };
84             }
85             else {
86             $coderef = sub {
87 1     1   504 my $self = shift;
88 1         4 return $self->$delegate->$to;
89 1         4 };
90             }
91             }
92              
93             {
94 2     2   12 no strict 'refs'; ## no critic
  2         4  
  2         672  
  9         13  
95 9 50 33     16 if ( !$override && defined *{"${package}::$method"}{CODE} ) {
  9         47  
96 0         0 croak(
97             "Package '$package' already has a method named '$method'");
98             }
99             }
100              
101 9         14 _install_delegate( $coderef, $package, $method );
102             }
103             }
104              
105             sub _normalize_methods {
106 9     9   16 my ( $methods, $delegate ) = @_;
107              
108 9 100       26 if ( 'ARRAY' eq ref $methods ) {
    100          
    100          
109 2         5 $methods = { map { $_ => $_ } @$methods };
  4         9  
110             }
111             elsif ( !ref $methods ) {
112 3         6 $methods = { $methods => $methods };
113             }
114             elsif ( 'HASH' ne ref $methods ) {
115 1         9 croak("I don't know how to delegate to '$delegate' from '$methods'");
116             }
117              
118 8 50       48 unless ( keys %$methods ) {
119 0         0 croak("You have not provideed any methods to delegate");
120             }
121 8         15 return $methods;
122             }
123              
124             sub _install_delegate {
125 9     9   15 my ( $coderef, $package, $method ) = @_;
126              
127 9         30 Sub::Install::install_sub(
128             {
129             code => $coderef,
130             into => $package,
131             as => $method,
132             }
133             );
134             }
135              
136             sub _assert_valid_method_name {
137 32     32   41 my $name = shift;
138 32 100       81 if ( $name =~ /^[a-z_][a-z0-9_]*$/i ) {
139 31         38 return $name;
140             }
141 1         10 croak("Illegal method name: '$name'");
142             }
143              
144             1;
145              
146             __END__