line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Mojolicious::Plugin::DOCRenderer; |
2
|
1
|
|
|
1
|
|
934
|
use Mojo::Base 'Mojolicious::Plugin'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
8
|
|
3
|
|
|
|
|
|
|
|
4
|
1
|
|
|
1
|
|
220
|
use File::Basename 'dirname'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
71
|
|
5
|
1
|
|
|
1
|
|
17
|
use File::Spec::Functions 'catdir'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
50
|
|
6
|
1
|
|
|
1
|
|
5
|
use Mojo::Asset::File; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
14
|
|
7
|
1
|
|
|
1
|
|
23
|
use Mojo::ByteStream 'b'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
66
|
|
8
|
1
|
|
|
1
|
|
10
|
use Mojo::DOM; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
22
|
|
9
|
1
|
|
|
1
|
|
5
|
use Mojo::URL; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
9
|
|
10
|
1
|
|
|
1
|
|
34
|
use Mojo::Util qw(slurp unindent url_escape); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
74
|
|
11
|
1
|
|
|
1
|
|
1172
|
use Pod::Simple::HTML; |
|
1
|
|
|
|
|
16898
|
|
|
1
|
|
|
|
|
50
|
|
12
|
1
|
|
|
1
|
|
11904
|
use Pod::Simple::Search; |
|
1
|
|
|
|
|
14925
|
|
|
1
|
|
|
|
|
2038
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $VERSION = '4.00'; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# "Futurama - The One Bright Spot in Your Life!" |
17
|
|
|
|
|
|
|
sub register { |
18
|
1
|
|
|
1
|
1
|
72
|
my ($self, $app, $conf) = @_; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# Add "doc" handler |
21
|
1
|
|
50
|
|
|
12
|
my $preprocess = $conf->{preprocess} || 'ep'; |
22
|
|
|
|
|
|
|
$app->renderer->add_handler( |
23
|
|
|
|
|
|
|
$conf->{name} || 'doc' => sub { |
24
|
0
|
|
|
0
|
|
0
|
my ($renderer, $c, $output, $options) = @_; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# Preprocess and render |
27
|
0
|
|
|
|
|
0
|
my $handler = $renderer->handlers->{$preprocess}; |
28
|
0
|
0
|
|
|
|
0
|
return undef unless $handler->($renderer, $c, $output, $options); |
29
|
0
|
|
|
|
|
0
|
$$output = _pod_to_html($$output); |
30
|
0
|
|
|
|
|
0
|
return 1; |
31
|
|
|
|
|
|
|
} |
32
|
1
|
|
50
|
|
|
37
|
); |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
# Append "templates" and "public" directories |
35
|
1
|
|
|
|
|
148
|
push @{$app->renderer->paths}, catdir(dirname(__FILE__), 'DOCRenderer', 'templates'); |
|
1
|
|
|
|
|
25
|
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# Doc |
38
|
1
|
|
50
|
|
|
98
|
my $url = $conf->{url} || '/doc'; |
39
|
1
|
|
33
|
|
|
7
|
my $module = $conf->{module} || $ENV{MOJO_APP}; |
40
|
1
|
|
|
|
|
5
|
my $defaults = {url => $url, module => $module, format => 'html'}; |
41
|
1
|
|
|
|
|
28
|
return $app->routes->any( |
42
|
|
|
|
|
|
|
"$url/:module" => $defaults => [module => qr/[^.]+/] => \&_doc); |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub _html { |
46
|
3
|
|
|
3
|
|
8
|
my ($self, $src) = @_; |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# Rewrite links |
49
|
3
|
|
|
|
|
18
|
my $dom = Mojo::DOM->new(_pod_to_html($src)); |
50
|
3
|
|
|
|
|
8718
|
my $doc = $self->url_for( $self->param('url') . '/' ); |
51
|
3
|
|
|
|
|
2192
|
for my $e ($dom->find('a[href]')->each) { |
52
|
9
|
|
|
|
|
4998
|
my $attrs = $e->attr; |
53
|
9
|
100
|
|
|
|
376
|
$attrs->{href} =~ s!%3A%3A!/!gi |
54
|
|
|
|
|
|
|
if $attrs->{href} =~ s!^http://search\.cpan\.org/perldoc\?!$doc!; |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
# Rewrite code blocks for syntax highlighting and correct indentation |
58
|
3
|
|
|
|
|
1069
|
for my $e ($dom->find('pre')->each) { |
59
|
7
|
|
|
|
|
2380
|
$e->content(my $str = unindent $e->content); |
60
|
7
|
100
|
66
|
|
|
4763
|
next if $str =~ /^\s*(?:\$|Usage:)\s+/m || $str !~ /[\$\@\%]\w|->\w/m; |
61
|
3
|
|
|
|
|
11
|
my $attrs = $e->attr; |
62
|
3
|
|
|
|
|
118
|
my $class = $attrs->{class}; |
63
|
3
|
50
|
|
|
|
15
|
$attrs->{class} = defined $class ? "$class prettyprint" : 'prettyprint'; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
# Rewrite headers |
67
|
3
|
|
|
|
|
774
|
my $toc = Mojo::URL->new->fragment('toc'); |
68
|
3
|
|
|
|
|
117
|
my (%anchors, @parts); |
69
|
3
|
|
|
|
|
14
|
for my $e ($dom->find('h1, h2, h3')->each) { |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# Anchor and text |
72
|
16
|
|
|
|
|
24383
|
my $name = my $text = $e->all_text; |
73
|
16
|
|
|
|
|
3310
|
$name =~ s/\s+/_/g; |
74
|
16
|
|
|
|
|
35
|
$name =~ s/[^\w\-]//g; |
75
|
16
|
|
|
|
|
24
|
my $anchor = $name; |
76
|
16
|
|
|
|
|
26
|
my $i = 1; |
77
|
16
|
|
|
|
|
83
|
$anchor = $name . $i++ while $anchors{$anchor}++; |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
# Rewrite |
80
|
16
|
100
|
66
|
|
|
52
|
push @parts, [] if $e->type eq 'h1' || !@parts; |
81
|
16
|
|
|
|
|
804
|
my $link = Mojo::URL->new->fragment($anchor); |
82
|
16
|
|
|
|
|
601
|
push @{$parts[-1]}, $text, $link; |
|
16
|
|
|
|
|
48
|
|
83
|
16
|
|
|
|
|
139
|
my $permalink = $self->link_to('#' => $link, class => 'permalink'); |
84
|
16
|
|
|
|
|
11928
|
$e->content($permalink . $self->link_to($text => $toc, id => $anchor)); |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# Try to find a title |
88
|
3
|
|
|
|
|
3550
|
my $title = 'Doc'; |
89
|
3
|
|
|
3
|
|
16
|
$dom->find('h1 + p')->first(sub { $title = shift->text }); |
|
3
|
|
|
|
|
10087
|
|
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# Combine everything to a proper response |
92
|
3
|
|
|
|
|
628
|
$self->content_for(doc => "$dom"); |
93
|
3
|
|
|
|
|
5554
|
$self->render(title => $title, parts => \@parts); |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
sub _doc { |
97
|
3
|
|
|
3
|
|
194492
|
my $self = shift; |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# Find module or redirect to CPAN |
100
|
3
|
|
|
|
|
19
|
my $module = $self->param('module'); |
101
|
3
|
|
|
|
|
391
|
$module =~ s!/!::!g; |
102
|
39
|
|
|
|
|
236
|
my $path |
103
|
3
|
|
|
|
|
40
|
= Pod::Simple::Search->new->find($module, map { $_, "$_/pods" } @INC); |
104
|
3
|
50
|
33
|
|
|
1982
|
return $self->redirect_to("http://metacpan.org/module/$module") |
105
|
|
|
|
|
|
|
unless $path && -r $path; |
106
|
|
|
|
|
|
|
|
107
|
3
|
|
|
|
|
17
|
my $src = slurp $path; |
108
|
3
|
|
|
3
|
|
323
|
$self->respond_to(txt => {data => $src}, any => sub { _html($self, $src) }); |
|
3
|
|
|
|
|
2517
|
|
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub _pod_to_html { |
112
|
3
|
50
|
|
3
|
|
19
|
return '' unless defined(my $pod = ref $_[0] eq 'CODE' ? shift->() : shift); |
|
|
50
|
|
|
|
|
|
113
|
|
|
|
|
|
|
|
114
|
3
|
|
|
|
|
36
|
my $parser = Pod::Simple::HTML->new; |
115
|
3
|
|
|
|
|
1987
|
$parser->$_('') for qw(force_title html_header_before_title); |
116
|
3
|
|
|
|
|
64
|
$parser->$_('') for qw(html_header_after_title html_footer); |
117
|
3
|
|
|
|
|
72
|
$parser->output_string(\(my $output)); |
118
|
3
|
50
|
|
|
|
2823
|
return $@ unless eval { $parser->parse_string_document("$pod"); 1 }; |
|
3
|
|
|
|
|
40
|
|
|
3
|
|
|
|
|
48719
|
|
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
# Filter |
121
|
3
|
|
|
|
|
90
|
$output =~ s!\n!!g; |
122
|
3
|
|
|
|
|
99
|
$output =~ s!(.*?)!$1!sg; |
123
|
|
|
|
|
|
|
|
124
|
3
|
|
|
|
|
300
|
return $output; |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
1; |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=encoding utf8 |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=head1 NAME |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
Mojolicious::Plugin::DOCRenderer - Doc Renderer Plugin |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=head1 SYNOPSIS |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
# Mojolicious::Lite |
138
|
|
|
|
|
|
|
plugin 'DOCRenderer'; |
139
|
|
|
|
|
|
|
plugin DOCRenderer => {module => 'MyApp'}; |
140
|
|
|
|
|
|
|
plugin DOCRenderer => {name => 'foo'}; |
141
|
|
|
|
|
|
|
plugin DOCRenderer => {url => '/mydoc'}; |
142
|
|
|
|
|
|
|
plugin DOCRenderer => {preprocess => 'epl'}; |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
# Mojolicious |
145
|
|
|
|
|
|
|
$self->plugin('DOCRenderer'); |
146
|
|
|
|
|
|
|
$self->plugin(DOCRenderer => {module => 'MyApp'}); |
147
|
|
|
|
|
|
|
$self->plugin(DOCRenderer => {name => 'foo'}); |
148
|
|
|
|
|
|
|
$self->plugin(DOCRenderer => {url => '/mydoc'}); |
149
|
|
|
|
|
|
|
$self->plugin(DOCRenderer => {preprocess => 'epl'}); |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
############################# |
152
|
|
|
|
|
|
|
# Mojolicious::Lite example # |
153
|
|
|
|
|
|
|
############################# |
154
|
|
|
|
|
|
|
use Mojolicious::Lite; |
155
|
|
|
|
|
|
|
use File::Basename; |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
plugin 'DOCRenderer' => { |
158
|
|
|
|
|
|
|
# use this script base name as a default module to show for "/doc" |
159
|
|
|
|
|
|
|
module => fileparse( __FILE__, qr/\.[^.]*/ ); |
160
|
|
|
|
|
|
|
}; |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
app->start; |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
__END__ |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=head1 NAME |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
MyApp - My Mojolicious::Lite Application |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=head1 DESCRIPTION |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
This documentation will be available online, for example from L. |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=cut |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
####################### |
177
|
|
|
|
|
|
|
# Mojolicious example # |
178
|
|
|
|
|
|
|
####################### |
179
|
|
|
|
|
|
|
package MyApp; |
180
|
|
|
|
|
|
|
use Mojo::Base 'Mojolicious'; |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub development_mode { |
183
|
|
|
|
|
|
|
# Enable browsing of "/doc" only in development mode |
184
|
|
|
|
|
|
|
shift->plugin( 'DOCRenderer' ); |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub startup { |
188
|
|
|
|
|
|
|
my $self = shift; |
189
|
|
|
|
|
|
|
# some code |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
__END__ |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=head1 NAME |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
MyApp - My Mojolicious Application |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=head1 DESCRIPTION |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
This documentation will be available online, for example from L. |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=cut |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=head1 DESCRIPTION |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
L generates on-the-fly and browses online |
207
|
|
|
|
|
|
|
POD documentation directly from your Mojolicious application source codes |
208
|
|
|
|
|
|
|
and makes it available under I (customizable). |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
The plugin expects that you use POD to document your codes of course. |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
The plugin is simple modification of L. |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=head1 OPTIONS |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=head2 C |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# Mojolicious::Lite |
219
|
|
|
|
|
|
|
plugin DOCRenderer => {module => 'MyApp'}; |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
Name of the module to initially display. Default is C<$ENV{MOJO_APP}>. |
222
|
|
|
|
|
|
|
Mojolicious::Lite application may have undefined C<$ENV{MOJO_APP}>; in such |
223
|
|
|
|
|
|
|
case you should set C, see Mojolicious::Lite example. |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=head2 C |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
# Mojolicious::Lite |
228
|
|
|
|
|
|
|
plugin DOCRenderer => {name => 'foo'}; |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
Handler name. |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=head2 C |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# Mojolicious::Lite |
235
|
|
|
|
|
|
|
plugin DOCRenderer => {preprocess => 'epl'}; |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
Handler name of preprocessor. |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
=head2 C |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
# Mojolicious::Lite |
242
|
|
|
|
|
|
|
plugin DOCRenderer => {url => '/mydoc'}; |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
URL from which the documentation of your project is available. Default is I. |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=head1 METHODS |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
L inherits all methods from |
249
|
|
|
|
|
|
|
L and implements the following new ones. |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
=head2 C |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
my $route = $plugin->register(Mojolicious->new); |
254
|
|
|
|
|
|
|
my $route = $plugin->register(Mojolicious->new, {name => 'foo'}); |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
Register renderer in L application. |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=head1 SEE ALSO |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
L, L, L, L. |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=cut |