File Coverage

blib/lib/Sub/Stubber.pm
Criterion Covered Total %
statement 90 98 91.8
branch 27 40 67.5
condition 6 9 66.6
subroutine 17 17 100.0
pod 3 4 75.0
total 143 168 85.1


line stmt bran cond sub pod time code
1             package Sub::Stubber::Stubs;
2 1     1   32084 use strict;
  1         2  
  1         34  
3 1     1   5 use warnings;
  1         2  
  1         25  
4 1     1   1184 use Class::Struct;
  1         2982  
  1         7  
5              
6             struct __PACKAGE__,
7             ['names' => '%',
8             'import_triggers' => '@',
9             'env_triggers' => '@',
10             'triggered' => '$',
11             'imported_into' => '@',
12             ];
13            
14             sub add_trigger {
15 3     3   6 my ($self,$type,$name) = @_;
16 3 100       10 if($type eq 'env') {
    50          
17 1         1 push @{$self->env_triggers}, $name;
  1         23  
18             } elsif ($type eq 'import') {
19 2         3 push @{$self->import_triggers}, $name;
  2         42  
20             } else {
21 0         0 die("No such trigger type '$type'");
22             }
23             }
24              
25             sub add_specs {
26 8     8   383 my ($self,@specs) = @_;
27 8         15 foreach my $spec (@specs) {
28 6 100       22 if(!ref $spec) {
    50          
29 3         66 $self->names->{$spec} = undef;
30             } elsif (ref $spec eq 'HASH') {
31 3         13 while ( my ($name,$val) = each %$spec ) {
32 3         70 $self->names->{$name} = $val;
33             }
34             } else {
35 0         0 die ("Bad specifier '$spec'");
36             }
37             }
38             }
39              
40             package Sub::Stubber;
41 1     1   411 use strict;
  1         2  
  1         32  
42              
43              
44 1     1   5 no strict 'refs';
  1         1  
  1         25  
45 1     1   6 no warnings 'redefine';
  1         9  
  1         1098  
46              
47             our %PkgCache;
48             our $VERSION = 0.04;
49              
50              
51             sub get_object {
52 8     8 0 10 my $cls = shift;
53 8 50       15 my $cpkg = $cls eq __PACKAGE__ ? caller : $cls;
54            
55 8   66     123 return $PkgCache{$cpkg} ||= Sub::Stubber::Stubs->new(triggered => 0);
56             }
57              
58              
59             sub _mk_sub_real {
60 5     5   7 my ($cpkg,$subname,$value) = @_;
61 5 50       13 if($subname !~ /::/) {
62 5         11 $subname = $cpkg . '::' . $subname;
63             }
64            
65 5         11 my $old_proto = prototype $subname;
66              
67             #Hope the prototype is right!
68 5 100 100     21 if(defined $value && ref $value eq 'CODE') {
    100          
69 1         2 *{$subname} = $value;
  1         7  
70             }
71             elsif(defined $old_proto) {
72 1     1   94 eval "*$subname = sub ($old_proto) { \$value };";
  1         56  
73             }
74             else {
75 3     4   11 *{$subname} = sub { $value };
  3         22  
  4         2838  
76             }
77             }
78              
79             #The next two provide some sugary API at the expense of a bleh-like
80             #implementation. the functions are too simple to warrant an
81             #elaborate dispatch scheme, unfortunately
82              
83             sub regstubs {
84 5     5 1 367 my $cls = shift;
85 5 50       15 my $cpkg = $cls eq __PACKAGE__ ? caller : $cls;
86 5         10 get_object($cpkg)->add_specs(@_);
87             }
88              
89             sub add_trigger {
90 3     3 1 40 my $cls = shift;
91 3 50       10 my $cpkg = $cls eq __PACKAGE__ ? caller : $cls;
92 3         14 get_object($cpkg)->add_trigger(@_);
93             }
94              
95             sub mkstubs {
96              
97 3     3 1 22 my $cls = shift;
98 3 100       11 my $cpkg = $cls eq __PACKAGE__ ? caller : $cls;
99 3 50 33     12 if(!exists $PkgCache{$cpkg} && @_ == 0) {
100 0         0 die("No functions registered and non provided to mkstubs()");
101             }
102 3         5 my $obj = $PkgCache{$cpkg};
103 3 50       68 return if $obj->triggered();
104            
105 3         24 $obj->add_specs(@_);
106 3         3 while (my ($subname,$subval) = each %{$obj->names}) {
  8         171  
107 5         114 _mk_sub_real($cpkg,$subname,$subval);
108             }
109              
110 3         79 $obj->triggered(1);
111             }
112              
113             sub _import_as_base {
114 3     3   5 my ($cls,@options) = @_;
115            
116 3         7 my $user_pkg = caller();
117            
118 3         7 my $obj = $PkgCache{$cls};
119            
120 3 50       8 if(!$obj) {
121 0         0 warn("$cls has inherited from " . __PACKAGE__ . " but has not defined " .
122             "any functions for stubbing");
123 0         0 print Dumper(\%PkgCache);
124 0         0 goto GT_EXPORTER;
125             }
126            
127 3 50       74 if($obj->triggered) {
128             #No need to re-generate stubs
129 0         0 goto GT_EXPORTER;
130             }
131            
132 3         19 foreach my $env (@{$obj->env_triggers}) {
  3         97  
133 1 50       10 if($ENV{$env}) {
134 1         4 mkstubs($cls);
135 1         11 goto GT_EXPORTER;
136             }
137             }
138            
139 2         16 my $found_import_trigger = 0;
140 2         4 foreach my $import (@{$obj->import_triggers}) {
  2         53  
141 2         12 my $i = 1;
142 2         9 while ($i <= $#_) {
143 1 50       5 if($_[$i] eq $import) {
144 1         1 $found_import_trigger = 1;
145 1         4 splice(@_, $i, 1);
146             }
147 1         5 $i++;
148             }
149             }
150            
151 2 100       8 if($found_import_trigger) {
152 1         3 mkstubs($cls);
153             }
154            
155            
156             GT_EXPORTER:
157 3         10 push @{$obj->imported_into}, $user_pkg;
  3         61  
158            
159 3 50       46 if($cls->isa('Exporter')) {
160 3         2435 goto &Exporter::import;
161             }
162 0         0 1;
163             }
164              
165             sub import {
166 7     7   3035 my ($cls,@options) = @_;
167            
168 7 100       21 if($cls ne __PACKAGE__) {
169 3         11 goto &_import_as_base;
170             }
171 4         238 1;
172             }
173             1;
174              
175             __END__