File Coverage

blib/lib/CGI/Application/Plugin/ViewCode.pm
Criterion Covered Total %
statement 6 15 40.0
branch 0 6 0.0
condition 0 6 0.0
subroutine 2 4 50.0
pod n/a
total 8 31 25.8


line stmt bran cond sub pod time code
1             package CGI::Application::Plugin::ViewCode;
2 3     3   4636175 use warnings;
  3         8  
  3         126  
3 3     3   16 use strict;
  3         8  
  3         7907  
4              
5             =head1 NAME
6              
7             CGI::Application::Plugin::ViewCode - View the source of the running application
8              
9             =cut
10              
11             our $VERSION = '1.02';
12              
13             # DEFAULT_STYLES taken from Apache::Syntax::Highlight::Perl by Enrico Sorcinelli
14             our %DEFAULT_STYLES = (
15             'Comment_Normal' => 'color:#006699;font-style:italic;',
16             'Comment_POD' => 'color:#001144;font-style:italic;',
17             'Directive' => 'color:#339999;font-style:italic;',
18             'Label' => 'color:#993399;font-style:italic;',
19             'Quote' => 'color:#0000aa;',
20             'String' => 'color:#0000aa;',
21             'Subroutine' => 'color:#998800;',
22             'Variable_Scalar' => 'color:#008800;',
23             'Variable_Array' => 'color:#ff7700;',
24             'Variable_Hash' => 'color:#8800ff;',
25             'Variable_Typeglob' => 'color:#ff0033;',
26             'Whitespace' => 'white-space: pre;',
27             'Character' => 'color:#880000;',
28             'Keyword' => 'color:#000000;',
29             'Builtin_Operator' => 'color:#330000;',
30             'Builtin_Function' => 'color:#000011;',
31             'Operator' => 'color:#000000;',
32             'Bareword' => 'color:#33AA33;',
33             'Package' => 'color:#990000;',
34             'Number' => 'color:#ff00ff;',
35             'Symbol' => 'color:#000000;',
36             'CodeTerm' => 'color:#000000;',
37             'DATA' => 'color:#000000;',
38             'LineNumber' => 'color:#BBBBBB;'
39             );
40              
41             our %SUBSTITUTIONS = (
42             '<' => '<',
43             '>' => '>',
44             '&' => '&',
45             );
46              
47             =head1 SYNOPSIS
48              
49             In your CGI::Application based class
50              
51             use CGI::Application::Plugin::ViewCode;
52              
53             Then you can view your module's source (or pod) as it's running by changing the url
54              
55             ?rm=view_code
56             ?rm=view_code#215
57             ?rm=view_code&pod=0&line_no=0
58             ?rm=view_code&module=CGI-Application
59              
60             ?rm=view_pod
61             ?rm=view_pod&module=CGI-Application
62              
63             =head1 INTERFACE
64              
65             This plugin works by adding extra run modes (named C and C< view_pod >) to the
66             application. By calling this run mode you can see the source or POD of the running module
67             (by default) or you can specify which module you would like to view (see L).
68              
69              
70             =head2 view_code
71              
72             This extra run mode will accept the following arguments in the query string:
73              
74             =over
75              
76             =item module
77              
78             The name of the module to view. By default it is the module currently being run. Also,
79             since colons (':') aren't simply typed into URL's, you can just substitute '-' for '::'.
80              
81             ?rm=view_code?module=My-Base-Class
82              
83             =item highlight
84              
85             Boolean indicates whether syntax highlighting (using L)
86             is C or C. By default it is C.
87              
88             =item line_no
89              
90             Boolean indicates whether the viewing of line numbers is C or C. By default it is C.
91             It C is on, you can also specify which line number you want to see by adding an anchor
92             to the link:
93              
94             ?rm=view_code#215
95              
96             This will take you immediately to line 215 of the current application module.
97              
98             =item pod
99              
100             Boolean indicates whether POD is seen or not. By default it is seen>.
101              
102             =back
103              
104              
105             =head2 view_pod
106              
107             This extra run mode will accept the following arguments in the query string:
108              
109             =over
110              
111             =item module
112              
113             The name of the module to view. By default it is the module currently being run. Also,
114             since colons (':') aren't simply typed into URL's, you can just substitute '-' for '::'.
115              
116             ?rm=view_pod?module=My-Base-Class
117              
118             =back
119              
120             =head1 AS A POPUP WINDOW
121              
122             This plugin can be used in conjunction with L. If we detect
123             that L is running and turned on, we will create a sub-report
124             that includes the highlighted source code.
125              
126              
127             So you can simply do the following:
128              
129             BEGIN { $ENV{CAP_DEVPOPUP_EXEC} = 1; } # turn it on for real
130             use CGI::Application::Plugin::DevPopup;
131             use CGI::Application::Plugin::ViewCode;
132              
133             Befault, this report will be the same thing produced by C. If you want this
134             report to include the C report, simply set the the C<$ENV{CAP_VIEWCODE_POPUP_POD}>
135             to true. You can also turn off the C report but setting
136             C<$ENV{CAP_VIEWCODE_POPUP_CODE}> to false.
137              
138             # have the POD report, but not the code in the dev popup window
139             BEGIN {
140             $ENV{CAP_DEVPOPUP_EXEC} = 1; # turn it on for real
141             $ENV{CAP_VIEWCODE_POPUP_POD} = 1; # turn on POD report
142             $ENV{CAP_VIEWCODE_POPUP_CODE} = 0; # turn off code report
143             }
144             use CGI::Application::Plugin::DevPopup;
145             use CGI::Application::Plugin::ViewCode;
146              
147             =cut
148              
149             sub import {
150 0     0     my $caller = scalar(caller);
151 0           $caller->add_callback( init => \&_add_runmode );
152              
153             # if we are running under CGI::Application::Plugin::DevPopup
154 0 0         if( $ENV{CAP_DEVPOPUP_EXEC} ) {
155             # if we wan't to add the POD report
156 0 0 0       if( exists $ENV{CAP_VIEWCODE_POPUP_POD} && $ENV{CAP_VIEWCODE_POPUP_POD} ) {
157 0           $caller->add_callback( devpopup_report => \&_view_pod );
158             }
159             # include the view_code report by default unless it's turned off
160 0 0 0       if(! (exists $ENV{CAP_VIEWCODE_POPUP_CODE} && !$ENV{CAP_VIEWCODE_POPUP_CODE}) ) {
161 0           $caller->add_callback( devpopup_report => \&_view_code );
162             }
163             }
164             }
165              
166             sub _add_runmode {
167 0     0     my $self = shift;
168 0           $self->run_modes(
169             view_code => \&_view_code,
170             view_pod => \&_view_pod
171             );
172             }
173              
174             sub _view_code {
175             my $self = shift;
176             my $query = $self->query;
177              
178             my %options;
179             foreach my $opt qw(highlight line_no pod) {
180             if( defined $query->param($opt) ) {
181             $options{$opt} = $query->param($opt);
182             } else {
183             $options{$opt} = 1;
184             }
185             }
186            
187             # get the file to be viewed
188             my $module = _module_name($query->param('module') || ref($self));
189             # change into file name
190             my $file = _module_file_name($module);
191              
192             # make sure the file exists
193             if( $file && -e $file ) {
194             my $IN;
195             open($IN, $file)
196             or return _error("Could not open $file for reading! $!");
197             my @lines= <$IN>;
198              
199             # if we aren't going to highlight then turn all colors/styles
200             # into simple black
201             my %styles = %DEFAULT_STYLES;
202             my $style_sec = '';
203             foreach my $style (keys %styles) {
204             $styles{$style} = 'color:#000000;'
205             if( !$options{highlight} );
206             $style_sec .= ".$style { $styles{$style} }\n";
207             }
208              
209             # now use Syntax::Highlight::Perl::Improved to do the work
210             require Syntax::Highlight::Perl::Improved;
211             my $formatter = Syntax::Highlight::Perl::Improved->new();
212             $formatter->define_substitution(%SUBSTITUTIONS);
213             foreach my $style (keys %styles) {
214             $formatter->set_format($style, [qq(), qq()]);
215             }
216             @lines = $formatter->format_string(@lines);
217            
218             # if we want line numbers
219             if( $options{line_no} ) {
220             my $i = 1;
221             @lines = map {
222             (qq() . $i++ . qq(: ). $_)
223             } @lines;
224             }
225              
226             # apply any other transformations necessary
227             if( $options{highlight} || !$options{pod} ) {
228             foreach my $line (@lines) {
229             # if they don't want the pod
230             if( !$options{pod} ) {
231             if( $line =~ /
232             $line = '';
233             next;
234             }
235             }
236            
237             # if they are highlighting
238             if( $options{highlight} ) {
239             if( $line =~ /([^<]*)<\/span>/ ) {
240             my $package = $1;
241             my $link = $package;
242             $link =~ s/::/-/g;
243             my $rm = $self->mode_param();
244             $rm = ref $rm ? 'rm' : $rm; # not really anything we can do if their mode_param returns a sub ref
245             $link = "?$rm=view_code&module=$package;view_code_no_popup=1";
246             $line =~ s/[^<]*<\/span>/$package<\/a>/;
247             }
248             }
249             }
250             }
251             my $code = join('', @lines);
252              
253             # if we are under CGI::Application::Plugin::DevPopup then let's create this as a report instead
254             if( $ENV{CAP_DEVPOPUP_EXEC} && !$query->param('view_code_no_popup') ) {
255             $self->devpopup->add_report(
256             title => 'View Code',
257             summary => "View code of $module",
258             report => "
$code
",
259             );
260             } else {
261             return qq(
262            
263            
264             $module - View Source
265            
266            
267            
268            
$code
269            
270            
271             );
272             }
273             } else {
274             return _error( ($file ? "File $file " : "Module $module ") . "does not exist!");
275             }
276             }
277              
278             sub _view_pod {
279             my $self = shift;
280             my $query = $self->query;
281              
282             # get the file to be viewed
283             my $module = _module_name($query->param('module') || ref($self));
284             # change into file name
285             my $file = _module_file_name($module);
286              
287             # make sure the file exists
288             if( $file && -e $file ) {
289             require Pod::Xhtml;
290             my $pod_parser = new Pod::Xhtml(
291             StringMode => 1,
292             MakeIndex => 0,
293             FragmentOnly => 1,
294             TopLinks => 0,
295             MakeMeta => 0,
296             );
297             $pod_parser->parse_from_file($file);
298             my $pod = $pod_parser->asString;
299              
300             # if we are under CGI::Application::Plugin::DevPopup then let's create this as a report instead
301             if( $ENV{CAP_DEVPOPUP_EXEC} && !$query->param('view_code_no_popup') ) {
302             $self->devpopup->add_report(
303             title => 'View POD',
304             summary => "View POD of $module",
305             report => "
$pod
",
306             );
307             } else {
308             return qq(
309            
310            
311             $module - View POD
312            
313            
314            
$pod
315            
316            
317             );
318             }
319             } else {
320             return _error( ($file ? "File $file " : "Module $module ") . "does not exist!");
321             }
322             }
323              
324              
325             sub _module_name {
326             my $name = shift;
327             $name =~ s/-/::/g;
328             return $name;
329             }
330              
331             sub _module_file_name {
332             my $module = shift;
333             # change into file name
334             $module =~ s/::/\//g;
335             $module .= '.pm';
336             return $INC{$module};
337             }
338              
339              
340             sub _error {
341             my $message = shift;
342             return qq(
343            
344            
345             View Source Error!
346            
347            
348            

Error!

349             Sorry, but there was an error in your
350             request to view the source:
351            
$message
352            
353            
354             );
355             }
356              
357             1;
358              
359             __END__