File Coverage

blib/lib/CGI/Application/Plugin/AutoRunmode.pm
Criterion Covered Total %
statement 133 135 98.5
branch 62 68 91.1
condition 13 18 72.2
subroutine 29 29 100.0
pod 1 9 11.1
total 238 259 91.8


line stmt bran cond sub pod time code
1             package CGI::Application::Plugin::AutoRunmode;
2              
3 9     9   318313 use strict;
  9         24  
  9         508  
4             require Exporter;
5             require CGI::Application;
6 9     9   55 use Carp;
  9         20  
  9         5469  
7              
8             our $VERSION = '0.18';
9              
10              
11             our %RUNMODES = ();
12              
13             # two different versions of this module,
14             # depending on whether Attribute::Handlers is
15             # available
16              
17             my $has_ah;
18              
19             BEGIN{
20 9     9   681 eval 'use Attribute::Handlers; $has_ah=1;';
  9     9   9041  
  9         46308  
  9         109  
21              
22 9 50       52 if ($has_ah){
23 9 100 66 9 0 830 $has_ah = eval <<'WITH_AH';
  9 50   9 0 67  
  9 100   9 0 21  
  9 100   9   1434  
  9 100   7   54  
  9 100   26   20  
  9     20   139  
  20     9   40933  
  20     26   87  
  20         249  
  10         16  
  10         27  
  10         37  
  10         195  
  26         38527  
  9         4312  
  9         19  
  9         52  
  9         1184  
  9         40  
  9         3718  
  9         20  
  9         51  
  7         1081  
  7         27  
  26         8864  
  26         66  
  28         268  
  10         18  
  10         30  
  18         60  
  9         16  
  9         28  
  9         32  
  7         13  
  7         22  
  26         186  
24              
25             # run this handler twice:
26             # in CHECK when we have the name, and also in BEGIN
27             # (because CHECK does not seem to work in mod_perl)
28              
29             sub CGI::Application::Runmode :ATTR(CODE,BEGIN,CHECK) {
30             my ( $pkg, $glob, $ref, $attr, $data, $phase ) = @_;
31             no strict 'refs';
32             $RUNMODES{"$ref"} = 1;
33             if ($CGI::Application::VERSION >= 4 && $phase eq 'CHECK'){
34             # also install the init-hook to register
35             # named runmodes
36             my $name = *{$glob}{NAME};
37             if ($name ne 'ANON'){
38             $pkg->add_callback('init', sub{
39             $_[0]->run_modes( $name => $ref )
40             if ($_[0]->can($name)) eq $ref
41             } )
42             }
43             }
44             }
45             sub CGI::Application::StartRunmode :ATTR(CODE,BEGIN) {
46             my ( $pkg, $glob, $ref, $attr, $data, $phase ) = @_;
47             install_start_mode($pkg, $ref);
48             }
49             sub CGI::Application::ErrorRunmode :ATTR(CODE,BEGIN) {
50             my ( $pkg, $glob, $ref, $attr, $data, $phase ) = @_;
51             install_error_mode($pkg, $ref);
52             }
53              
54             # the Attribute::Handler version still exports a MODIFY_CODE_ATTRIBUTES
55             # but only to provide backwards compatibility (case-independent attribute
56             # names )
57              
58             sub MODIFY_CODE_ATTRIBUTES{
59             my ($pkg, $ref, @attr) = @_;
60             foreach (@attr){
61             if (uc $_ eq 'RUNMODE'){
62             $_ = 'Runmode';
63             next;
64             }
65             if (uc $_ eq 'STARTRUNMODE'){
66             $_ = 'StartRunmode';
67             next;
68             }
69             if (uc $_ eq 'ERRORRUNMODE'){
70             $_ = 'ErrorRunmode';
71             next;
72             }
73             }
74             return $pkg->SUPER::MODIFY_CODE_ATTRIBUTES($ref, @attr);
75             }
76              
77             1;
78             WITH_AH
79 9 50       56 warn "failed to load Attribute::Handlers version of CAP:AutoRunmode $@" if $@;
80             }
81              
82              
83              
84 9 50       7602 unless ($has_ah){
85 0 0       0 eval <<'WITHOUT_AH' or die $@;
86             sub MODIFY_CODE_ATTRIBUTES{
87             my ($pkg, $ref, @attr) = @_;
88            
89             my @unknown;
90             foreach (@attr){
91             my $u = uc $_;
92             $CGI::Application::Plugin::AutoRunmode::RUNMODES{"$ref"} = 1, next
93             if $u eq 'RUNMODE';
94             if ($u eq 'STARTRUNMODE'){
95             install_start_mode($pkg, $ref);
96             next;
97             }
98             if ($u eq 'ERRORRUNMODE'){
99             install_error_mode($pkg, $ref);
100             next;
101             }
102             push @unknown, $_;
103             }
104             return @unknown;
105             }
106             1;
107             WITHOUT_AH
108             }
109              
110             }
111              
112              
113             our @ISA = qw(Exporter);
114              
115             # always export the attribute handlers
116             sub import{
117 20     20   44999 __PACKAGE__->export_to_level(1, @_, 'MODIFY_CODE_ATTRIBUTES');
118            
119             # if CGI::App > 4 install the hook
120             # (unless cgiapp_prerun requested)
121 20 100 66     937 if ( @_ < 2 and $CGI::Application::VERSION >= 4 ){
122 9         29 my $caller = scalar(caller);
123 9 100       166 if (UNIVERSAL::isa($caller, 'CGI::Application')){
124 3         40 $caller->add_callback('prerun', \&cgiapp_prerun);
125             }
126             }
127            
128            
129            
130             };
131              
132             our @EXPORT_OK = qw[
133             cgiapp_prerun
134             MODIFY_CODE_ATTRIBUTES
135             ];
136              
137              
138              
139             our %__illegal_names = qw[
140             can can
141             isa isa
142             VERSION VERSION
143             AUTOLOAD AUTOLOAD
144             new new
145             DESTROY DESTROY
146             ];
147              
148             sub cgiapp_prerun{
149 42     42 1 31381 my ($self, $rm) = @_;
150 42         194 my %rmodes = ($self->run_modes());
151             # If prerun_mode has been set, use it!
152 42         767 my $prerun_mode = $self->prerun_mode();
153 42 100       560 if (length($prerun_mode)) {
154 1         3 $rm = $prerun_mode;
155             }
156 42 100       174 return unless defined $rm;
157            
158 41 100       169 unless (exists $rmodes{$rm}){
159             # security check / untaint : disallow non-word characters
160 28 100       216 if ($rm =~ /^(\w+)$/){
161 24         122 $rm = $1;
162             # check :Runmodes
163 24 100       87 $self->run_modes( $rm => $rm), return
164             if is_attribute_auto_runmode($self, $rm);
165            
166             # check delegate
167 16         53 my $sub = is_delegate_auto_runmode($self, $rm);
168 16 100       88 $self->run_modes( $rm => $sub) if $sub;
169            
170             }
171             }
172             }
173              
174              
175              
176             sub install_start_mode{
177 9     9 0 18 my ($pkg, $ref) = @_;
178            
179 9     9   69 no strict 'refs';
  9         26  
  9         1104  
180 9 100       14 if (defined *{"${pkg}::start_mode"}){
  9         66  
181 2 100 66     22 if ($ENV{MOD_PERL} && exists $INC{'Apache2/Reload.pm'}){
182             # be lenient with Apache2::Reload
183             # see https://rt.cpan.org/Ticket/Display.html?id=35987
184             }else{
185 1         32 die "StartRunmode for package $pkg is already installed\n";
186             }
187             }
188            
189 8         20 my $memory;
190            
191             #if (ref $ref eq 'GLOB') {
192             # $memory = *{$ref}{NAME};
193             # $ref = *{$ref}{CODE};
194             #}
195            
196 8         30 $RUNMODES{"$ref"} = 1;
197 9     9   51 no warnings 'redefine';
  9         36  
  9         1343  
198 8         81 *{"${pkg}::start_mode"} = sub{
199 20 100   20   65625 return if @_ > 1;
200 7 100       41 return $memory if $memory;
201 3         17 return $memory = _find_name_of_sub_in_pkg($ref, $pkg);
202 8         43 };
203            
204            
205             }
206              
207              
208             sub install_error_mode{
209 7     7 0 15 my ($pkg, $ref) = @_;
210            
211 9     9   48 no strict 'refs';
  9         23  
  9         961  
212 7 100       11 if ( defined *{"${pkg}::error_mode"}){
  7         75  
213 2 100 66     20 if ($ENV{MOD_PERL} && exists $INC{'Apache2/Reload.pm'}){
214             # be lenient with Apache2::Reload
215             # see https://rt.cpan.org/Ticket/Display.html?id=35987
216             }else{
217 1         29 die "ErrorRunmode for package $pkg is already installed\n";
218             }
219             }
220            
221 6         10 my $memory;
222            
223             #if (ref $ref eq 'GLOB') {
224             # $memory = *{$ref}{NAME};
225             # $ref = *{$ref}{CODE};
226             #}
227 9     9   61 no warnings 'redefine';
  9         17  
  9         1205  
228 6         46 *{"${pkg}::error_mode"} = sub{
229 4 100   4   691 return if @_ > 1;
230 3 100       13 return $memory if $memory;
231 2         7 return $memory = _find_name_of_sub_in_pkg($ref, $pkg);
232 6         48 };
233            
234            
235             }
236              
237              
238              
239              
240              
241             # code for this inspired by Devel::Symdump
242             sub _find_name_of_sub_in_pkg{
243 5     5   17 my ($ref, $pkg) = @_;
244 9     9   54 no strict 'refs';
  9         26  
  9         4093  
245             #return *{$ref}{NAME} if ref $ref eq 'GLOB';
246 5         10 while (my ($key,$val) = each(%{*{"$pkg\::"}})) {
  59         71  
  59         727  
247 59         100 local(*ENTRY) = $val;
248 59 100 66     768 if (defined $val && defined *ENTRY{CODE}) {
249 10 100       105 next unless *ENTRY{CODE} eq $ref;
250             # rewind "each"
251 5         36 my $a = scalar keys %{*{"$pkg\::"}};
  5         8  
  5         26  
252 5         40 return $key;
253             }
254             }
255              
256 0         0 die "failed to find name for StartRunmode code ref $ref in package $pkg\n";
257             }
258              
259             sub is_attribute_auto_runmode{
260 42     42 0 91 my($app, $rm) = @_;
261 42         242 my $sub = $app->can($rm);
262 42 100       218 return unless $sub;
263 23 100       288 return $sub if $RUNMODES{"$sub"};
264             # also check the GLOB
265             #if ($has_ah){
266             # no strict 'refs';
267             # my $pkg = ref $app;
268             # warn "${pkg}::${rm}";
269             # use Data::Dumper;
270             # warn Dumper \%RUNMODES;
271             # return $sub if $RUNMODES{*{"${pkg}::${rm}"}};
272             #}
273 9         51 return undef;
274             }
275              
276             sub is_delegate_auto_runmode{
277 28     28 0 48 my($app, $rm) = @_;
278 28         142 my $delegate = $app->param('::Plugin::AutoRunmode::delegate');
279 28 100       566 return unless $delegate;
280 18 100       63 return if exists $__illegal_names{$rm};
281            
282            
283 17 100       65 my @delegates = ref($delegate) eq 'ARRAY' ? @$delegate
284             : ($delegate);
285              
286 17         32 foreach my $delegate (@delegates) {
287 18         123 my $sub = $delegate->can($rm);
288 18 100       82 next unless $sub;
289            
290             # construct a closure, as we need a second
291             # parameter (the delegate)
292 13     11   56 my $closure = sub { $sub->($_[0], $delegate); };
  11         853  
293 13         59 return $closure;
294             }
295            
296             }
297              
298             sub is_auto_runmode{
299 18   100 18 0 4835 return is_attribute_auto_runmode(@_) || is_delegate_auto_runmode(@_);
300             }
301              
302              
303              
304             1;
305             __END__