File Coverage

inc/CGI/Application/Plugin/AutoRunmode.pm
Criterion Covered Total %
statement 40 126 31.7
branch 5 64 7.8
condition 1 12 8.3
subroutine 12 27 44.4
pod 1 9 11.1
total 59 238 24.7


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