File Coverage

blib/lib/Attribute/Signature.pm
Criterion Covered Total %
statement 123 134 91.7
branch 53 76 69.7
condition 13 21 61.9
subroutine 19 19 100.0
pod 5 7 71.4
total 213 257 82.8


line stmt bran cond sub pod time code
1             package Attribute::Signature;
2            
3 1     1   385 use 5.006;
  1         2  
  1         32  
4 1     1   3 use strict;
  1         1  
  1         25  
5             #use warnings::register;
6            
7 1     1   3 use Carp;
  1         8  
  1         76  
8 1     1   7 use Scalar::Util qw ( blessed );
  1         1  
  1         124  
9            
10             #use Data::Dumper;
11 1     1   932 use Attribute::Handlers;
  1         4794  
  1         6  
12 1     1   34 use attributes ();
  1         1  
  1         68  
13             #local $^W=0;
14            
15             our $VERSION = '1.11';
16             my $SIGNATURES = {};
17            
18             sub UNIVERSAL::with : ATTR(CODE,INIT) {
19 28     28 0 15054 my ($package, $symbol, $referent, $attr, $data) = @_;
20            
21 28         28 my $large = *{$symbol}{NAME};
  28         37  
22 28         68 my $subname = substr($large, rindex($large, ':') + 1);
23            
24 1     1   4 no warnings qw( redefine );
  1         1  
  1         423  
25            
26             ## make sure we have an array ref, so its easier
27 28 50       57 if (!ref($data)) {
28 0         0 $data = [ $data ];
29             }
30            
31             ## save this for later use
32 28         55 $SIGNATURES->{$package}->{with}->{$subname} = $data;
33            
34 28         65 my $attributes = { map { ($_, 1) } attributes::get( $referent ) };
  1         25  
35            
36 28 100       374 if ($attributes->{method}) {
37 1 50       3 print "Signature on sub $subname is for a method\n" if $::AS_DEBUG;
38 1         2 unshift @$data, $package; ## put a placeholder in the front
39             }
40            
41 28         73 *{$symbol} = sub {
42 117     117   127 my $i = 0;
43 117         101 my $count = scalar(@_);
44            
45 117 100       212 if ($attributes->{method}) {
46 3         3 $i = 1;
47             }
48            
49 117 100       175 if ($count != scalar(@$data)) {
50 20 100       31 if ($attributes->{method}) {
51 1         83 croak("invalid number of arguments passed to method $subname");
52             } else {
53 19         1565 croak("invalid number of arguments passed to subroutine $subname ($count passed, ".scalar(@$data)." required");
54             }
55             }
56            
57 97         89 my $m = 0;
58 97 50       124 print "Comparisons\n" if $::AS_DEBUG;
59 97 50       114 print "\tSignature\tValue\n" if $::AS_DEBUG;
60 97         71 my @failed;
61 97         142 while($i <= $count) {
62 201 50       250 print "\t$data->[$i]\t\t$_[$i]\n" if $::AS_DEBUG;
63 201 100       292 last unless $data->[$i];
64 104         118 my $ok=0;
65 104 100 33     412 if (lc($data->[$i]) eq $data->[$i]) {
    50 66        
    100          
66             ## here we are checking for little types
67 72         64 my $type = $data->[$i];
68 72 50       319 if (Attribute::Signature->can( $type )) {
69 72 100       133 if (Attribute::Signature->$type( $_[$i] )) {
70 49         49 $ok++;
71             }
72             }
73             } elsif ((blessed($_[$i])) && $_[$i]->isa( $data->[$i]) ) {
74             # || string($_[$i])
75 0         0 $ok++;
76             } elsif (!blessed($_[$i]) && ref($_[$i]) eq $data->[$i]) {
77 8         7 $ok++;
78             }
79 104 100       147 if ($ok) {
80 57         44 $m++ ;
81             } else {
82 47         52 push @failed,$i;
83             }
84 104         151 $i++;
85             }
86            
87 97 100       143 if ($attributes->{method}) { $m++; }
  2         3  
88            
89 97 0 33     132 print "Out of band:\n\tCount\tMatched\n\t$count\t$m\n" if defined $::AS_DEBUG && $::AS_DEBUG;
90            
91 97 100       115 if ($m != $count) {
92 47         4024 croak("call to $subname does not match signature (failed args:".join(',',@failed).")");
93             } else {
94             #$referent->( @_ );
95 50         133 goto &$referent;
96             }
97 28         123 };
98 1     1   5 }
  1         2  
  1         5  
99            
100             sub UNIVERSAL::returns : ATTR(CODE,INIT) {
101 28     28 0 4125 my ($package, $symbol, $referent, $attr, $data) = @_;
102            
103 28         29 my $large = *{$symbol}{NAME};
  28         38  
104 28         67 my $subname = substr($large, rindex($large, ':') + 1);
105            
106 1     1   301 no warnings qw( redefine );
  1         2  
  1         457  
107            
108             ## make sure we have an array ref, so its easier
109 28 50       56 if (!ref($data)) {
110 0         0 $data = [ $data ];
111             }
112            
113             ## save this for later use
114 28         52 $SIGNATURES->{$package}->{returns}->{$subname} = $data;
115            
116 28         57 my $attributes = { map { ($_, 1) } attributes::get( $referent ) };
  0         0  
117            
118 28 50       371 if ($attributes->{method}) {
119 0 0       0 print "Signature on sub $subname is for a method\n" if $::AS_DEBUG;
120 0         0 unshift @$data, $package; ## put a placeholder in the front
121             }
122            
123 28         71 *{$symbol} = sub {
124            
125 96     96   20759 my @return = $referent->( @_ );
126            
127 29         126 my $i = 0;
128 29         28 my $count = scalar(@return);
129            
130 29 50       57 if ($count != scalar(@$data)) {
131 0 0       0 if ($attributes->{method}) {
132 0         0 croak("invalid number of arguments returned from method $subname");
133             } else {
134 0         0 croak("invalid number of arguments returned from subroutine $subname");
135             }
136             }
137            
138 29         30 my $m = 0;
139 29 50       36 print "ReturnComparisons\n" if $::AS_DEBUG;
140 29 50       62 print "\tSignature\tValue\n" if $::AS_DEBUG;
141 29         48 while($i <= $count) {
142 60 50       69 print "\t$data->[$i]\t\t$return[$i]\n" if $::AS_DEBUG;
143 60 100       84 last unless $data->[$i];
144 31 100 100     120 if (lc($data->[$i]) eq $data->[$i]) {
    100 33        
    50 66        
    100          
145             ## here we are checking for little types
146 23         24 my $type = $data->[$i];
147 23 50       78 if (Attribute::Signature->can( $type )) {
148 23 100       38 if (Attribute::Signature->$type( $return[$i] )) {
149 19         18 $m++;
150             }
151             }
152             } elsif ($data->[$i] eq 'REF' && ref($return[$i])) {
153 1         1 $m++;
154             # } elsif ((blessed($return[$i]) || string($return[$i])) && $return[$i]->isa( $data->[$i]) ) {
155             } elsif (blessed($return[$i]) && $return[$i]->isa( $data->[$i]) ) {
156 0         0 $m++;
157             } elsif (!blessed($return[$i]) && ref($return[$i]) eq $data->[$i]) {
158 3         2 $m++;
159             } else {
160             # no match
161             }
162 31         46 $i++;
163             }
164            
165 29 50       46 if ($attributes->{method}) { $m++; }
  0         0  
166            
167 29 50       42 print "ReturnOut of band:\n\tCount\tMatched\n\t$count\t$m\n" if $::AS_DEBUG;
168            
169 29 100       45 if ($m != $count) {
170 8         615 croak("Arguments returned from $subname do not match signature $m != $count");
171             } else {
172 21         31 $referent->( @_ );
173             }
174 28         133 };
175 1     1   6 }
  1         1  
  1         3  
176            
177             sub getSignature {
178 2     2 1 406 my $class = shift;
179 2         3 my $fqsn = shift;
180            
181             ## this is my sub && package
182 2         8 my $subname = substr($fqsn, rindex($fqsn, ':') + 1);
183 2         5 my $package = substr($fqsn, 0, rindex($fqsn, '::'));
184            
185 2 100       6 if (wantarray) {
186 1         6 return($SIGNATURES->{$package}->{with}->{$subname}, $SIGNATURES->{$package}->{returns}->{$subname});
187             } else {
188 1         4 return $SIGNATURES->{$package}->{with}->{$subname};
189             }
190             }
191            
192             sub string {
193 45     45 1 39 my $class = shift;
194 45         109 return not ref $_[0];
195             }
196            
197             sub number {
198 20     20 1 19 my $class = shift;
199 20   100     39 return $class->float($_[0]) || $class->integer($_[0]);
200             }
201            
202             sub float {
203 37     37 1 34 my $class = shift;
204 37         193 return $_[0] =~ /^\d*\.\d*$/;
205             }
206            
207             sub integer {
208 29     29 1 22 my $class = shift;
209 29         158 return $_[0] =~ /^\d+$/;
210             }
211            
212             1;
213            
214             __END__