File: ViewCode.pm

package info (click to toggle)
libcgi-application-plugin-viewcode-perl 1.02-6
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 156 kB
  • sloc: perl: 242; makefile: 2
file content (387 lines) | stat: -rw-r--r-- 11,968 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
package CGI::Application::Plugin::ViewCode;
use warnings;
use strict;

=head1 NAME

CGI::Application::Plugin::ViewCode - View the source of the running application

=cut

our $VERSION = '1.02';

# DEFAULT_STYLES taken from Apache::Syntax::Highlight::Perl by Enrico Sorcinelli
our %DEFAULT_STYLES = (
    'Comment_Normal'    => 'color:#006699;font-style:italic;',
    'Comment_POD'       => 'color:#001144;font-style:italic;',
    'Directive'         => 'color:#339999;font-style:italic;',
    'Label'             => 'color:#993399;font-style:italic;',
    'Quote'             => 'color:#0000aa;',
    'String'            => 'color:#0000aa;',
    'Subroutine'        => 'color:#998800;',
    'Variable_Scalar'   => 'color:#008800;',
    'Variable_Array'    => 'color:#ff7700;',
    'Variable_Hash'     => 'color:#8800ff;',
    'Variable_Typeglob' => 'color:#ff0033;',
    'Whitespace'        => 'white-space: pre;',
    'Character'         => 'color:#880000;',
    'Keyword'           => 'color:#000000;',
    'Builtin_Operator'  => 'color:#330000;',
    'Builtin_Function'  => 'color:#000011;',
    'Operator'          => 'color:#000000;',
    'Bareword'          => 'color:#33AA33;',
    'Package'           => 'color:#990000;',
    'Number'            => 'color:#ff00ff;',
    'Symbol'            => 'color:#000000;',
    'CodeTerm'          => 'color:#000000;',
    'DATA'              => 'color:#000000;',
    'LineNumber'        => 'color:#BBBBBB;'
);

our %SUBSTITUTIONS = (
    '<'     => '&lt;', 
    '>'     => '&gt;', 
    '&'     => '&amp;',
);

=head1 SYNOPSIS

In your CGI::Application based class

    use CGI::Application::Plugin::ViewCode;

Then you can view your module's source (or pod) as it's running by changing the url

    ?rm=view_code
    ?rm=view_code#215
    ?rm=view_code&pod=0&line_no=0
    ?rm=view_code&module=CGI-Application

    ?rm=view_pod
    ?rm=view_pod&module=CGI-Application

=head1 INTERFACE

This plugin works by adding extra run modes (named C<view_code> and C< view_pod >) to the
application. By calling this run mode you can see the source or POD of the running module
(by default) or you can specify which module you would like to view (see L<SECURITY>).


=head2 view_code

This extra run mode will accept the following arguments in the query string:

=over

=item module

The name of the module to view. By default it is the module currently being run. Also,
since colons (':') aren't simply typed into URL's, you can just substitute '-' for '::'.

    ?rm=view_code?module=My-Base-Class

=item highlight

Boolean indicates whether syntax highlighting (using L<Syntax::Highlight::Perl::Improved>) 
is C<on> or C<off>. By default it is C<on>.

=item line_no

Boolean indicates whether the viewing of line numbers is C<on> or C<off>. By default it is C<on>.
It C<line_no> is on, you can also specify which line number you want to see by adding an anchor
to the link:

    ?rm=view_code#215

This will take you immediately to line 215 of the current application module.

=item pod

Boolean indicates whether POD is seen or not. By default it is seen>.

=back


=head2 view_pod

This extra run mode will accept the following arguments in the query string:

=over

=item module

The name of the module to view. By default it is the module currently being run. Also,
since colons (':') aren't simply typed into URL's, you can just substitute '-' for '::'.

    ?rm=view_pod?module=My-Base-Class

=back

=head1 AS A POPUP WINDOW

This plugin can be used in conjunction with L<CGI::Application::Plugin::DevPopup>. If we detect
that L<CGI::Application::Plugin::DevPopup> is running and turned on, we will create a sub-report
that includes the highlighted source code.


So you can simply do the following:

    BEGIN { $ENV{CAP_DEVPOPUP_EXEC} = 1; } # turn it on for real
    use CGI::Application::Plugin::DevPopup;
    use CGI::Application::Plugin::ViewCode;

Befault, this report will be the same thing produced by C<view_code>. If you want this
report to include the C<view_pod> report, simply set the the C<$ENV{CAP_VIEWCODE_POPUP_POD}>
to true. You can also turn off the C<view_code> report but setting 
C<$ENV{CAP_VIEWCODE_POPUP_CODE}> to false.

    # have the POD report, but not the code in the dev popup window
    BEGIN { 
        $ENV{CAP_DEVPOPUP_EXEC} = 1;       # turn it on for real
        $ENV{CAP_VIEWCODE_POPUP_POD} = 1;  # turn on POD report
        $ENV{CAP_VIEWCODE_POPUP_CODE} = 0; # turn off code report
    }
    use CGI::Application::Plugin::DevPopup;
    use CGI::Application::Plugin::ViewCode;

=cut

sub import {
    my $caller = scalar(caller);
    $caller->add_callback( init => \&_add_runmode );

    # if we are running under CGI::Application::Plugin::DevPopup
    if( $ENV{CAP_DEVPOPUP_EXEC} ) {
        # if we wan't to add the POD report
        if( exists $ENV{CAP_VIEWCODE_POPUP_POD} && $ENV{CAP_VIEWCODE_POPUP_POD} ) {
            $caller->add_callback( devpopup_report => \&_view_pod );
        }
        # include the view_code report by default unless it's turned off
        if(! (exists $ENV{CAP_VIEWCODE_POPUP_CODE} && !$ENV{CAP_VIEWCODE_POPUP_CODE}) ) {
            $caller->add_callback( devpopup_report => \&_view_code );
        }
    }
}

sub _add_runmode {
    my $self = shift;
    $self->run_modes( 
        view_code => \&_view_code,
        view_pod  => \&_view_pod
    );
}

sub _view_code {
    my $self = shift;
    my $query = $self->query;

    my %options;
    foreach my $opt (qw(highlight line_no pod)) {
        if( defined $query->param($opt) ) {
            $options{$opt} = $query->param($opt);
        } else {
            $options{$opt} = 1;
        }
    }
        
    # get the file to be viewed
    my $module = _module_name($query->param('module') || ref($self));
    # change into file name
    my $file = _module_file_name($module);

    # make sure the file exists
    if( $file && -e $file ) {
        my $IN;
        open($IN, $file) 
            or return _error("Could not open $file for reading! $!");
        my @lines= <$IN>;

        # if we aren't going to highlight then turn all colors/styles
        # into simple black
        my %styles = %DEFAULT_STYLES;
        my $style_sec = '';
        foreach my $style (keys %styles) {
            $styles{$style} = 'color:#000000;'
                if( !$options{highlight} );
            $style_sec .= ".$style { $styles{$style} }\n";
        }

        # now use Syntax::Highlight::Perl::Improved to do the work
        require Syntax::Highlight::Perl::Improved;
        my $formatter = Syntax::Highlight::Perl::Improved->new();
        $formatter->define_substitution(%SUBSTITUTIONS);
        foreach my $style (keys %styles) {
            $formatter->set_format($style, [qq(<span class="$style">), qq(</span>)]);
        }
        @lines = $formatter->format_string(@lines);
        
        # if we want line numbers
        if( $options{line_no} ) {
            my $i = 1;
            @lines = map { 
                (qq(<span class="LineNumber"><a name="$i">) . $i++ . qq(:</a></span>&nbsp;). $_) 
            } @lines;
        }

        # apply any other transformations necessary
        if( $options{highlight} || !$options{pod} ) {
            foreach my $line (@lines) {
                # if they don't want the pod
                if( !$options{pod} ) {
                    if( $line =~ /<span class="Comment_POD"/ ) {
                        $line = '';
                        next;
                    }
                }
                
                # if they are highlighting
                if( $options{highlight} ) {
                    if( $line =~ /<span class="Package">([^<]*)<\/span>/ ) {
                        my $package = $1;
                        my $link = $package;
                        $link =~ s/::/-/g;
                        my $rm = $self->mode_param();
                        $rm = ref $rm ? 'rm' : $rm; # not really anything we can do if their mode_param returns a sub ref
                        $link = "?$rm=view_code&amp;module=$package;view_code_no_popup=1";
                        $line =~ s/<span class="Package">[^<]*<\/span>/<a class="Package" href="$link">$package<\/a>/;
                    }    
                }
            }
        }
        my $code = join('', @lines);

        # if we are under CGI::Application::Plugin::DevPopup then let's create this as a report instead
        if( $ENV{CAP_DEVPOPUP_EXEC} && !$query->param('view_code_no_popup') ) {
            $self->devpopup->add_report(
                title   => 'View Code',
                summary => "View code of $module", 
                report  => "<style>$style_sec</style><pre>$code</pre>",
            );
        } else {
            return qq(
            <html>
            <head>
                <title>$module - View Source</title>
                <style>$style_sec</style>
            </head>
            <body>
                <pre>$code</pre>
            </body>
            </html>
            );
        }
    } else {
        return _error( ($file ? "File $file " : "Module $module ") . "does not exist!");
    }
}

sub _view_pod {
    my $self = shift;
    my $query = $self->query;

    # get the file to be viewed
    my $module = _module_name($query->param('module') || ref($self));
    # change into file name
    my $file = _module_file_name($module);

    # make sure the file exists
    if( $file && -e $file ) {
        require Pod::Xhtml;
        my $pod_parser = new Pod::Xhtml(
            StringMode   => 1,
            MakeIndex    => 0,
            FragmentOnly => 1,
            TopLinks     => 0,
            MakeMeta     => 0,
        );
        $pod_parser->parse_from_file($file);
        my $pod = $pod_parser->asString;

        # if we are under CGI::Application::Plugin::DevPopup then let's create this as a report instead
        if( $ENV{CAP_DEVPOPUP_EXEC} && !$query->param('view_code_no_popup') ) {
            $self->devpopup->add_report(
                title   => 'View POD',
                summary => "View POD of $module", 
                report  => "<pre>$pod</pre>",
            );
        } else {
            return qq(
            <html>
            <head>
                <title>$module - View POD</title>
            </head>
            <body>
                <pre>$pod</pre>
            </body>
            </html>
            );
        }
    } else {
        return _error( ($file ? "File $file " : "Module $module ") . "does not exist!");
    }
}


sub _module_name {
    my $name = shift;
    $name =~ s/-/::/g;  
    return $name;
}

sub _module_file_name {
    my $module = shift;
    # change into file name
    $module =~ s/::/\//g;
    $module .= '.pm';
    return $INC{$module};
}


sub _error {
    my $message = shift;
    return qq(
    <html>
      <head>
        <title>View Source Error!</title>
      </head>
      <body>
        <h1 style="color: red">Error!</h1>
        <strong>Sorry, but there was an error in your 
        request to view the source: 
        <blockquote><em>$message</em></blockquote>
      </body>
    </html>
    );
}

1;

__END__

=head1 SECURITY

This plugin is designed to be used for development only. Please do not use it in a
production system as it will allow anyone to see the source code for any loaded module.
Consider yourself warned.

=head1 AUTHOR

Michael Peters, C<< <mpeters@plusthree.com> >>

=head1 BUGS

Please report any bugs or feature requests to
C<bug-cgi-application-plugin-viewsource@rt.cpan.org>, or through the web interface at
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=CGI-Application-Plugin-ViewCode>.
I will be notified, and then you'll automatically be notified of progress on
your bug as I make changes.

=head1 ACKNOWLEDGEMENTS

=head1 COPYRIGHT & LICENSE

Copyright 2005 Michael Peters, All Rights Reserved.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.