File Coverage

blib/lib/Acme/ReturnValue/MakeSite.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             package Acme::ReturnValue::MakeSite;
3              
4 1     1   1952 use 5.010;
  1         5  
  1         66  
5 1     1   9 use strict;
  1         3  
  1         45  
6 1     1   6 use warnings;
  1         1  
  1         39  
7              
8             # ABSTRACT: generate returnvalues.useperl.at
9              
10 1     1   1229 use Path::Class qw();
  1         76741  
  1         21  
11 1     1   2203 use URI::Escape;
  1         1741  
  1         92  
12 1     1   12033 use Encode qw(from_to);
  1         21117  
  1         98  
13 1     1   3137 use Data::Dumper;
  1         15790  
  1         92  
14 1     1   80 use Acme::ReturnValue;
  0            
  0            
15             use Encode;
16             use Moose;
17             use JSON;
18             with qw(MooseX::Getopt);
19             use MooseX::Types::Path::Class;
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             return JSON->new;
28             }
29              
30              
31              
32             sub run {
33             my $self = shift;
34              
35             my @interesting;
36              
37             my %cool_dists;
38             my %bad_dists;
39             my %cool_rvs;
40             #my %authors;
41              
42             if (!-d $self->out) {
43             $self->out->mkpath || die "cannot make dir ".$self->out;
44             }
45              
46             my $dir = $self->data;
47             while (my $file=$dir->next) {
48             next unless $file=~/\/(?<dist>.*)\.json$/;
49             my $dist=$+{dist};
50             $dist=~s/^\///;
51              
52             my $json = $file->slurp(iomode => '<:encoding(UTF-8)');
53             my $data = $self->json_decoder->decode($json);
54             next if ref($data) eq 'HASH' && $data->{is_boring};
55             foreach my $rreport (@$data) {
56             my $report = { %$rreport };
57             if (exists $report->{value}) {
58             $report->{value}=~s/\</&lt;/g;
59             $report->{value}=~s/\>/&gt;/g;
60             if(length($report->{value})>255) {
61             $report->{value}=substr($report->{value},0,255).'...';
62             }
63             }
64             if ($report->{bad}) {
65             my $bad = $report->{bad};
66             $bad=~s/\</&lt;/g;
67             $bad=~s/\>/&gt;/g;
68             if(length($bad)>255) {
69             $bad=substr($bad,0,255).'...';
70             }
71             $report->{bad}=$bad;
72             }
73              
74             $report->{package_br} = $report->{package};
75             if (length($report->{package_br})>40) {
76             my @p=split(/::/,$report->{package_br});
77             my @lines;
78             my $line = shift(@p);
79             foreach my $frag (@p) {
80             $line.='::'.$frag;
81             if (length($line)>40) {
82             push(@lines,$line);
83             $line='';
84             }
85             }
86             push (@lines,$line) if $line;
87             $report->{package_br}=join("<br>&nbsp;&nbsp;&nbsp;",@lines);
88             }
89             if ($report->{value}) {
90             push(@{$cool_dists{$dist}},$report);
91             push(@{$cool_rvs{$report->{value}}},$report);
92             }
93             else {
94             push(@{$bad_dists{$report->{PPI}}{$dist}},$report);
95             }
96             }
97             }
98            
99             my %by_letter;
100             foreach my $dist (sort keys %cool_dists) {
101             my $first = uc(substr($dist,0,1));
102             push(@{$by_letter{$first}},$dist);
103             }
104             my $letternav = "<ul class='menu'>";
105             foreach my $letter (sort keys %by_letter) {
106             $letternav.="<li><a href='cool_$letter.html'>$letter</a></li>";
107             }
108             $letternav.="</ul>";
109             foreach my $letter (sort keys %by_letter) {
110             $self->gen_cool_dists(\%cool_dists,$by_letter{$letter},$letter,$letternav);
111             }
112            
113             $self->gen_cool_values(\%cool_rvs);
114              
115             $self->gen_bad_dists(\%bad_dists);
116            
117             $self->gen_index;
118              
119             }
120              
121              
122             sub gen_cool_dists {
123             my ($self, $cool,$dists,$letter,$letternav) = @_;
124              
125             my $out = Path::Class::Dir->new($self->out)->file('cool_'.$letter.'.html');
126              
127             my $count = keys %$cool;
128             my @print;
129              
130             push(@print,$self->_html_header);
131             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             push(@print,$letternav);
138              
139             push(@print,"<table>");
140             foreach my $dist (sort @{$dists}) {
141             push(@print,$self->_html_cool_dist($dist,$cool->{$dist}));
142             }
143            
144             push(@print,"</table>",$self->_html_footer);
145              
146             $out->spew(iomode => '>:encoding(UTF-8)', [map { decode_utf8($_) } @print]);
147              
148             }
149              
150              
151             sub gen_cool_values {
152             my ($self, $dists) = @_;
153              
154             my $out = Path::Class::Dir->new($self->out)->file('values.html');
155             my @print;
156              
157             push(@print,$self->_html_header);
158             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             my $cnt=1;
169             foreach my $rv (
170             map { $_->[1] }
171             sort { $b->[0] <=> $a->[0] }
172             map { [scalar @{$dists->{$_}},$_] } keys %$dists) {
173             push(@print,$self->_html_cool_value($rv,$dists->{$rv},'cv_'.$cnt++));
174             }
175              
176             push(@print,"<table>");
177             push(@print,$self->_html_footer);
178             $out->spew(iomode => '>:encoding(UTF-8)', [map { decode_utf8($_) } @print]);
179             }
180              
181              
182             sub gen_bad_dists {
183             my ($self, $dists) = @_;
184              
185             my $out = Path::Class::Dir->new($self->out)->file('bad.html');
186             my @print;
187              
188             push(@print,$self->_html_header);
189             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="http://search.cpan.org/dist/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             my @bad = sort keys %$dists;
201             push(@print,"<ul>");
202             foreach my $type (@bad) {
203             my $count = keys %{$dists->{$type}};
204             push(@print,"<li><a href='#$type'>$type ($count dists)</li>");
205             }
206             push(@print,"</ul>");
207            
208             foreach my $type (sort keys %$dists) {
209             push(@print,"<h3><a name='$type'>$type</a></h3>\n<table width='100%'>");
210             foreach my $dist (sort keys %{$dists->{$type}}) {
211             push(@print, $self->_html_bad_dist($dist,$dists->{$type}{$dist}));
212              
213             }
214             push(@print,"</table>");
215             }
216            
217             push(@print,"</table>");
218             push(@print,$self->_html_footer);
219             $out->spew(iomode => '>:encoding(UTF-8)', [map { decode_utf8($_) } @print]);
220             }
221              
222              
223             sub gen_index {
224             my $self = shift;
225             my $out = Path::Class::Dir->new($self->out)->file('index.html');
226             my @print;
227             my $version = Acme::ReturnValue->VERSION;
228              
229             push(@print,$self->_html_header);
230             push(@print,<<EOINDEX);
231              
232             <p class="content">As you might know, all <a href="http://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="http://search.cpan.org">CPAN</a>.</p>
233              
234             <p class="content">This site is created using <a href="http://search.cpan.org/dist/Acme-ReturnValue">Acme::ReturnValue $version</a> by <a href="http://domm.plix.at">Thomas Klausner</a> on irregular intervals (but setting up a cron-job is on the TODO...). There are some <a href="http://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             push(@print,$self->_html_footer);
247             $out->spew(iomode => '>:encoding(UTF-8)', [map { decode_utf8($_) } @print]);
248              
249             }
250              
251             sub _html_cool_dist {
252             my ($self, $dist,$report) = @_;
253             my $html;
254             my $count = @$report;
255              
256             if ($count>1) {
257             $html.="<tr><td colspan=2>".$self->_link_dist($dist)."</td></tr>";
258             }
259              
260             foreach my $ele (@$report) {
261             my $val=$ele->{'value'};
262              
263             if ($count>1) {
264             $html.="<tr><td class='package'>".$ele->{package_br}."</td>";
265             }
266             else {
267             $html.="<tr><td colspan>".$self->_link_dist($dist)."</td>";
268             }
269             $html.="<td>".$val."</td>";
270             $html.="</tr>\n";
271             }
272             return $html;
273             }
274              
275             sub _html_cool_value {
276             my ($self, $value, $report, $id) = @_;
277             my $html;
278             my $count = @$report;
279             $html = qq[<tr><td>$count</td><td>$value</td><td><a href="javascript:void(0)" onclick="] . q[$('#]. $id. q[').toggle()">show</td></td></tr>].
280             qq[<tr id='$id' style='display:none' ><td></td><td colspan=2>];
281             $html .= join("<br>\n",map { $self->_link_search_package($_->{package}) } sort { $a->{package} cmp $b->{package} } @$report);
282             $html .= "</td></tr>";
283             return $html;
284             }
285              
286             sub _html_bad_dist {
287             my ($self, $dist,$report) = @_;
288             my $html;
289              
290             foreach my $ele (@$report) {
291             my $val=$ele->{'bad'} || '';
292             my $id = $ele->{package};
293             $id=~s/::/_/g;
294             $html.="<tr><td colspan width='30%'>".$self->_link_dist($dist)."</td>";
295             $html.="<td width='69%'>".$ele->{package}."</a></td>".
296             q{<td width='1%'><a href="javascript:void(0)" onclick="$('#}.$id.q{').toggle()">}."show</td></tr>
297             <tr id='$id' style='display:none' ><td></td><td colspan=2>".$val."</td></tr>";
298             }
299             return $html;
300             }
301              
302             sub _link_dist {
303             my ($self, $dist) = @_;
304             return "<a href='http://search.cpan.org/dist/$dist'>$dist</a>";
305             }
306              
307             sub _link_search_package {
308             my ($self, $package) = @_;
309             return "<a href='http://search.cpan.org/search?query=$package&mode=module'>$package</a>";
310             }
311              
312             sub _html_header {
313             my $self = shift;
314              
315             return <<"EOHTMLHEAD";
316             <html>
317             <head><title>Acme::ReturnValue findings</title>
318             <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
319             <script src="jquery-1.3.2.min.js" type="text/javascript"></script>
320             <link href="acme_returnvalue.css" rel="stylesheet" type="text/css">
321              
322             </head>
323              
324             <body>
325             <h1 id="top">Acme::ReturnValue</h1>
326              
327             <ul id="menubox" class="menu">
328             <li><a href="index.html">About</a></li>
329             <li><a href="values.html">Cool return values</a></li>
330             <li><a href="cool_A.html">Cool dists</a></li>
331             <li><a href="bad.html">Bad return values</a></li>
332             </ul>
333             </div>
334             EOHTMLHEAD
335             }
336              
337             sub _html_footer {
338             my $self = shift;
339             my $now = $self->now;
340             my $version = Acme::ReturnValue->VERSION;
341             return <<"EOHTMLFOOT";
342             <div class="comments">
343             <h3>Comments</h3>
344             <div id="disqus_thread"></div>
345             <script type="text/javascript">
346             var disqus_shortname = 'acmereturnvalues';
347             var disqus_identifier='same_comments_everywhere';
348             (function() {
349             var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
350             dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
351             (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
352             })();
353             </script>
354             <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
355              
356             </div>
357             <div class="footer">
358             <p>Acme::ReturnValue: <a href="http://search.cpan.org/dist/Acme-ReturnValue">on CPAN</a> | <a href="http://domm.plix.at/talks/acme_returnvalue.html">talks about it</a><br>
359             Contact: domm AT cpan.org<br>
360             Generated: $now<br>
361             Version: $version<br>
362             </p>
363             </div>
364             </body></html>
365             EOHTMLFOOT
366             }
367              
368             "let's generate another stupid website";
369              
370             __END__
371              
372             =pod
373              
374             =head1 NAME
375              
376             Acme::ReturnValue::MakeSite - generate returnvalues.useperl.at
377              
378             =head1 VERSION
379              
380             version 1.001
381              
382             =head1 SYNOPSIS
383              
384             acme_returnvalue_makesite.pl --data path/to/dir
385              
386             =head1 DESCRIPTION
387              
388             Generate a small site based on the findings of L<Acme::ReturnValue>
389              
390             =head2 METHODS
391              
392             =head3 run
393              
394             run from the commandline (via F<acme_returnvalue_makesite.pl>
395              
396             =head3 gen_cool_dists
397              
398             Generate the list of cool dists.
399              
400             =head3 gen_cool_values
401              
402             Generate the list of cool return values.
403              
404             =head3 gen_bad_dists
405              
406             Generate the list of bad dists.
407              
408             =head3 gen_index
409              
410             Generate the start page
411              
412             =head1 AUTHOR
413              
414             Thomas Klausner <domm@cpan.org>
415              
416             =head1 COPYRIGHT AND LICENSE
417              
418             This software is copyright (c) 2013 by Thomas Klausner.
419              
420             This is free software; you can redistribute it and/or modify it under
421             the same terms as the Perl 5 programming language system itself.
422              
423             =cut