File Coverage

blib/lib/Acme/ReturnValue/MakeSite.pm
Criterion Covered Total %
statement 35 197 17.7
branch 0 28 0.0
condition 0 5 0.0
subroutine 12 25 48.0
pod 5 5 100.0
total 52 260 20.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             package Acme::ReturnValue::MakeSite;
3              
4 1     1   1761 use 5.010;
  1         6  
5 1     1   7 use strict;
  1         2  
  1         20  
6 1     1   6 use warnings;
  1         1  
  1         27  
7              
8             # ABSTRACT: generate returnvalues.plix.at
9              
10 1     1   6 use Path::Class qw();
  1         54  
  1         28  
11 1     1   530 use URI::Escape;
  1         1556  
  1         72  
12 1     1   15 use Encode qw(from_to);
  1         2  
  1         45  
13 1     1   7 use Data::Dumper;
  1         2  
  1         51  
14 1     1   7 use Acme::ReturnValue;
  1         1  
  1         31  
15 1     1   5 use Encode;
  1         2  
  1         94  
16 1     1   7 use Moose;
  1         2  
  1         9  
17 1     1   8268 use JSON;
  1         3  
  1         9  
18             with qw(MooseX::Getopt);
19 1     1   166 use MooseX::Types::Path::Class;
  1         3  
  1         16  
20              
21             has 'now' => (is=>'ro',isa=>'Str',default => sub { scalar localtime});
22             has 'quiet' => (is=>'ro',isa=>'Bool',default=>0);
23             has 'data' => (is=>'ro',isa=>'Path::Class::Dir',default=>'returnvalues',coerce=>1);
24             has 'out' => (is=>'ro',isa=>'Path::Class::Dir',default=>'htdocs',coerce=>1);
25             has 'json_decoder' => (is=>'ro',lazy_build=>1);
26             sub _build_json_decoder {
27 0     0     return JSON->new;
28             }
29              
30              
31              
32             sub run {
33 0     0 1   my $self = shift;
34              
35 0           my @interesting;
36              
37             my %cool_dists;
38 0           my %bad_dists;
39 0           my %cool_rvs;
40             #my %authors;
41              
42 0 0         if (!-d $self->out) {
43 0 0         $self->out->mkpath || die "cannot make dir ".$self->out;
44             }
45              
46 0           my $dir = $self->data;
47 0           while (my $file=$dir->next) {
48 0 0         next unless $file=~/\/(?<dist>.*)\.json$/;
49 0           my $dist=$+{dist};
50 0           $dist=~s/^\///;
51              
52 0           my $json = $file->slurp(iomode => '<:encoding(UTF-8)');
53 0           my $data = $self->json_decoder->decode($json);
54 0 0 0       next if ref($data) eq 'HASH' && $data->{is_boring};
55 0           foreach my $rreport (@$data) {
56 0           my $report = { %$rreport };
57 0 0         if (exists $report->{value}) {
58 0           $report->{value}=~s/\</&lt;/g;
59 0           $report->{value}=~s/\>/&gt;/g;
60 0 0         if(length($report->{value})>255) {
61 0           $report->{value}=substr($report->{value},0,255).'...';
62             }
63             }
64 0 0         if ($report->{bad}) {
65 0           my $bad = $report->{bad};
66 0           $bad=~s/\</&lt;/g;
67 0           $bad=~s/\>/&gt;/g;
68 0 0         if(length($bad)>255) {
69 0           $bad=substr($bad,0,255).'...';
70             }
71 0           $report->{bad}=$bad;
72             }
73              
74 0           $report->{package_br} = $report->{package};
75 0 0         if (length($report->{package_br})>40) {
76 0           my @p=split(/::/,$report->{package_br});
77 0           my @lines;
78 0           my $line = shift(@p);
79 0           foreach my $frag (@p) {
80 0           $line.='::'.$frag;
81 0 0         if (length($line)>40) {
82 0           push(@lines,$line);
83 0           $line='';
84             }
85             }
86 0 0         push (@lines,$line) if $line;
87 0           $report->{package_br}=join("<br>&nbsp;&nbsp;&nbsp;",@lines);
88             }
89 0 0         if ($report->{value}) {
90 0           push(@{$cool_dists{$dist}},$report);
  0            
91 0           push(@{$cool_rvs{$report->{value}}},$report);
  0            
92             }
93             else {
94 0           push(@{$bad_dists{$report->{PPI}}{$dist}},$report);
  0            
95             }
96             }
97             }
98            
99 0           my %by_letter;
100 0           foreach my $dist (sort keys %cool_dists) {
101 0           my $first = uc(substr($dist,0,1));
102 0           push(@{$by_letter{$first}},$dist);
  0            
103             }
104 0           my $letternav = "<ul class='menu'>";
105 0           foreach my $letter (sort keys %by_letter) {
106 0           $letternav.="<li><a href='cool_$letter.html'>$letter</a></li>";
107             }
108 0           $letternav.="</ul>";
109 0           foreach my $letter (sort keys %by_letter) {
110 0           $self->gen_cool_dists(\%cool_dists,$by_letter{$letter},$letter,$letternav);
111             }
112            
113 0           $self->gen_cool_values(\%cool_rvs);
114              
115 0           $self->gen_bad_dists(\%bad_dists);
116            
117 0           $self->gen_index;
118              
119             }
120              
121              
122             sub gen_cool_dists {
123 0     0 1   my ($self, $cool,$dists,$letter,$letternav) = @_;
124              
125 0           my $out = Path::Class::Dir->new($self->out)->file('cool_'.$letter.'.html');
126              
127 0           my $count = keys %$cool;
128 0           my @print;
129              
130 0           push(@print,$self->_html_header);
131 0           push(@print,<<EOCOOLINTRO);
132             <h3>$count Cool Distributions $letter</h3>
133             <p class="content">A list of distributions with not-boring return
134             values, sorted by name. </p>
135             EOCOOLINTRO
136            
137 0           push(@print,$letternav);
138              
139 0           push(@print,"<table>");
140 0           foreach my $dist (sort @{$dists}) {
  0            
141 0           push(@print,$self->_html_cool_dist($dist,$cool->{$dist}));
142             }
143            
144 0           push(@print,"</table>",$self->_html_footer);
145              
146 0           $out->spew(iomode => '>:encoding(UTF-8)', \@print );
147              
148             }
149              
150              
151             sub gen_cool_values {
152 0     0 1   my ($self, $dists) = @_;
153              
154 0           my $out = Path::Class::Dir->new($self->out)->file('values.html');
155 0           my @print;
156              
157 0           push(@print,$self->_html_header);
158 0           push(@print,<<EOBADINTRO);
159             <h3>Cool Return Values</h3>
160             <p class="content">
161             All cool values, sorted by number of occurence in the CPAN.
162             </p>
163              
164             <table>
165             <tr><td>Count</td><td>Return value</td><td>Packages</td></tr>
166             EOBADINTRO
167              
168 0           my $cnt=1;
169 0           foreach my $rv (
170 0           map { $_->[1] }
171 0           sort { $b->[0] <=> $a->[0] }
172 0           map { [scalar @{$dists->{$_}},$_] } keys %$dists) {
  0            
173 0           push(@print,$self->_html_cool_value($rv,$dists->{$rv},'cv_'.$cnt++));
174             }
175              
176 0           push(@print,"<table>");
177 0           push(@print,$self->_html_footer);
178 0           $out->spew(iomode => '>:encoding(UTF-8)', \@print);
179             }
180              
181              
182             sub gen_bad_dists {
183 0     0 1   my ($self, $dists) = @_;
184              
185 0           my $out = Path::Class::Dir->new($self->out)->file('bad.html');
186 0           my @print;
187              
188 0           push(@print,$self->_html_header);
189 0           push(@print,<<EOBADINTRO);
190             <h3>Bad Return Values</h3>
191              
192             <p class="content">A list of distributions that don't return a valid
193             return statement. You can consider this distributions buggy. This list
194             is further broken down into the type of <a
195             href="https://metacpan.org/pod/PPI">PPI::Statement</a> class they
196             return. To view the full bad return value, click on the
197             'show'-link.</p>
198             EOBADINTRO
199              
200 0           my @bad = sort keys %$dists;
201 0           push(@print,"<ul>");
202 0           foreach my $type (@bad) {
203 0           my $count = keys %{$dists->{$type}};
  0            
204 0           push(@print,"<li><a href='#$type'>$type ($count dists)</li>");
205             }
206 0           push(@print,"</ul>");
207            
208 0           foreach my $type (sort keys %$dists) {
209 0           push(@print,"<h3><a name='$type'>$type</a></h3>\n<table width='100%'>");
210 0           foreach my $dist (sort keys %{$dists->{$type}}) {
  0            
211 0           push(@print, $self->_html_bad_dist($dist,$dists->{$type}{$dist}));
212              
213             }
214 0           push(@print,"</table>");
215             }
216            
217 0           push(@print,"</table>");
218 0           push(@print,$self->_html_footer);
219 0           $out->spew(iomode => '>:encoding(UTF-8)', \@print);
220             }
221              
222              
223             sub gen_index {
224 0     0 1   my $self = shift;
225 0           my $out = Path::Class::Dir->new($self->out)->file('index.html');
226 0           my @print;
227 0           my $version = Acme::ReturnValue->VERSION;
228              
229 0           push(@print,$self->_html_header);
230 0           push(@print,<<EOINDEX);
231              
232             <p class="content">As you might know, all <a href="https://perl.org">Perl</a> packages are required to end with a true statement, usually '1'. But there are more interesting true values than plain old boring '1'. This site is dedicated to presenting to you those creative, funny, stupid or erroneous return values found on <a href="https://metacpan.org">CPAN</a>.</p>
233              
234             <p class="content">This site is created using <a href="https://metacpan.org/pod/Acme::ReturnValue">Acme::ReturnValue $version</a> by <a href="https://domm.plix.at">Thomas Klausner (domm)</a> on irregular intervals (but setting up a cron-job is on the TODO (for 10 years..)...). There are some <a href="https://domm.plix.at/talks/acme_returnvalue.html">slides of talks</a> available with a tiny bit more background.</p>
235              
236             <p class="content">At the moment, there are the following reports:
237             <ul class="content">
238             <li><a href="values.html">Cool values</a> - all cool values, sorted by number of occurence in the CPAN</li>
239             <li><a href="cool_A.html">Cool dists</a> - a list of distributions with not-boring return values. There still are some false positves hidden in here, which will hopefully be removed soon.</li>
240             <li><a href="bad.html">Bad return values</a> - a list of distributions that don't return a valid return statement. You can consider this distributions (or Acme::ReturnValue) buggy.</li>
241             <li>By author - not implemented yet.
242             </ul>
243             </p>
244              
245             EOINDEX
246 0           push(@print,$self->_html_footer);
247 0           $out->spew(iomode => '>:encoding(UTF-8)', \@print);
248             }
249              
250             sub _html_cool_dist {
251 0     0     my ($self, $dist,$report) = @_;
252 0           my $html;
253 0           my $count = @$report;
254              
255 0 0         if ($count>1) {
256 0           $html.="<tr><td colspan=2>".$self->_link_dist($dist)."</td></tr>";
257             }
258              
259 0           foreach my $ele (@$report) {
260 0           my $val=$ele->{'value'};
261              
262 0 0         if ($count>1) {
263 0           $html.="<tr><td class='package'>".$ele->{package_br}."</td>";
264             }
265             else {
266 0           $html.="<tr><td colspan>".$self->_link_dist($dist)."</td>";
267             }
268 0           $html.="<td>".$val."</td>";
269 0           $html.="</tr>\n";
270             }
271 0           return $html;
272             }
273              
274             sub _html_cool_value {
275 0     0     my ($self, $value, $report, $id) = @_;
276 0           my $html;
277 0           my $count = @$report;
278 0           $html = qq[<tr><td>$count</td><td>$value</td><td><a href="javascript:void(0)" onclick="] . q[$('#]. $id. q[').toggle()">show</td></td></tr>].
279             qq[<tr id='$id' style='display:none' ><td></td><td colspan=2>];
280 0           $html .= join("<br>\n",map { $self->_link_search_package($_->{package}) } sort { $a->{package} cmp $b->{package} } @$report);
  0            
  0            
281 0           $html .= "</td></tr>";
282 0           return $html;
283             }
284              
285             sub _html_bad_dist {
286 0     0     my ($self, $dist,$report) = @_;
287 0           my $html;
288              
289 0           foreach my $ele (@$report) {
290 0   0       my $val=$ele->{'bad'} || '';
291 0           my $id = $ele->{package};
292 0           $id=~s/::/_/g;
293 0           $html.="<tr><td colspan width='30%'>".$self->_link_dist($dist)."</td>";
294 0           $html.="<td width='69%'>".$ele->{package}."</a></td>".
295             q{<td width='1%'><a href="javascript:void(0)" onclick="$('#}.$id.q{').toggle()">}."show</td></tr>
296             <tr id='$id' style='display:none' ><td></td><td colspan=2>".$val."</td></tr>";
297             }
298 0           return $html;
299             }
300              
301             sub _link_dist {
302 0     0     my ($self, $dist) = @_;
303 0           my $distlink = $dist;
304 0           $distlink=~s/-[\d\.]+$//;
305 0           return "<a href='https://metacpan.org/release/$distlink'>$dist</a>";
306             }
307              
308             sub _link_search_package {
309 0     0     my ($self, $package) = @_;
310 0           return "<a href='https://metacpan.org/pod/$package'>$package</a>";
311             }
312              
313             sub _html_header {
314 0     0     my $self = shift;
315              
316 0           return <<"EOHTMLHEAD";
317             <html>
318             <head><title>Acme::ReturnValue findings</title>
319             <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
320             <script src="jquery-1.3.2.min.js" type="text/javascript"></script>
321             <link href="acme_returnvalue.css" rel="stylesheet" type="text/css">
322              
323             </head>
324              
325             <body>
326             <h1 id="top">Acme::ReturnValue</h1>
327              
328             <ul id="menubox" class="menu">
329             <li><a href="index.html">About</a></li>
330             <li><a href="values.html">Cool return values</a></li>
331             <li><a href="cool_A.html">Cool dists</a></li>
332             <li><a href="bad.html">Bad return values</a></li>
333             </ul>
334             </div>
335             EOHTMLHEAD
336             }
337              
338             sub _html_footer {
339 0     0     my $self = shift;
340 0           my $now = $self->now;
341 0           my $version = Acme::ReturnValue->VERSION;
342 0           return <<"EOHTMLFOOT";
343             <div class="footer">
344             <p>Acme::ReturnValue: <a href="https://metacpan.org/pod/Acme-ReturnValue">on CPAN</a> | <a href="https://domm.plix.at/talks/acme_returnvalue.html">talks about it</a><br>
345             Contact: domm AT cpan.org<br>
346             Generated: $now<br>
347             Version: $version<br>
348             </p>
349             </div>
350             </body></html>
351             EOHTMLFOOT
352             }
353              
354             "let's generate another stupid website";
355              
356             __END__
357              
358             =pod
359              
360             =encoding UTF-8
361              
362             =head1 NAME
363              
364             Acme::ReturnValue::MakeSite - generate returnvalues.plix.at
365              
366             =head1 VERSION
367              
368             version 1.003
369              
370             =head1 SYNOPSIS
371              
372             acme_returnvalue_makesite.pl --data path/to/dir
373              
374             =head1 DESCRIPTION
375              
376             Generate a small site based on the findings of L<Acme::ReturnValue>
377              
378             =head2 METHODS
379              
380             =head3 run
381              
382             run from the commandline (via F<acme_returnvalue_makesite.pl>
383              
384             =head3 gen_cool_dists
385              
386             Generate the list of cool dists.
387              
388             =head3 gen_cool_values
389              
390             Generate the list of cool return values.
391              
392             =head3 gen_bad_dists
393              
394             Generate the list of bad dists.
395              
396             =head3 gen_index
397              
398             Generate the start page
399              
400             =head1 AUTHOR
401              
402             Thomas Klausner <domm@cpan.org>
403              
404             =head1 COPYRIGHT AND LICENSE
405              
406             This software is copyright (c) 2013 - 2019 by Thomas Klausner.
407              
408             This is free software; you can redistribute it and/or modify it under
409             the same terms as the Perl 5 programming language system itself.
410              
411             =cut