File Coverage

blib/lib/App/CatalystStarter/Bloated/Initializr.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             package # hide from pause
2             App::CatalystStarter::Bloated::Initializr;
3              
4 14     14   71 use warnings;
  14         33  
  14         523  
5 14     14   72 use strict;
  14         27  
  14         283  
6 14     14   68 use Carp;
  14         25  
  14         795  
7              
8 14     14   75 use version; our $VERSION = qv('0.9.3');
  14         31  
  14         120  
9              
10 14     14   2961 use File::ShareDir qw/module_file/;
  14         12465  
  14         900  
11 14     14   14710 use Archive::Zip;
  14         746029  
  14         538  
12 14     14   115 use File::Basename;
  14         26  
  14         893  
13 14     14   5521 use Mojo::DOM;
  0            
  0            
14             use Log::Log4perl qw/:easy/;
15              
16             my $az;
17             my $logger = get_logger;
18             ## nice to have this in top
19             sub l{
20             $logger
21             }
22             sub _require_az {
23             confess "az object not initialized" unless defined $az and $az->isa("Archive::Zip");
24             }
25             sub _set_logger {
26             $logger = shift;
27             }
28              
29             ## Top level functions
30             sub deploy {
31              
32             _initialize();
33              
34             _require_az;
35              
36             my $dir = shift;
37              
38             _setup_index();
39             _move_images();
40             _move_css_js_fonts();
41              
42             $az->extractTree( "initializr", $dir );
43             l->info( "HTML5: template unzipped to catalyst root" );
44              
45             }
46             sub _initialize_from_cache {
47             l->debug("Getting template from cache");
48             _set_az_from_cache();
49             }
50             sub _initalize_over_http {
51             l->debug("Getting template from initializr.com" );
52             }
53             sub _initialize {
54             _initialize_from_cache;
55             l->debug("HTML5: Template loaded");
56             }
57              
58             ## High level functions:
59              
60              
61             ## parse index.html:
62             ## 1) substitute content for [% content %]
63             ## 2) store it again with new name wrapper.tt2
64             ## - index.html should not be in zip afterwards
65             ## 3) fix any local links to img, css or js, should point to:
66             ## 4) /static/images, css and js
67             sub _setup_index {
68              
69             _require_az;
70              
71             my $dom = _index_dom();
72              
73             ## insert content template var
74             {
75             my $div = $dom->find( 'body > div[class="container"]' )->first;
76             if ( !$div ) {
77             croak "container tag not found in html template - cannot continue";
78             }
79             $div->content( "[% content %]" );
80             l->debug( "HTML5: Wrapper content template var inserted" );
81             }
82              
83             ## insert jumbotron, might aswell since the template has it
84             {
85             my $div = $dom->find
86             ( 'body > div[class="jumbotron"] > div[class="container"]' )->first;
87             if ( !$div ) {
88             croak "container tag not found in html template - cannot continue";
89             }
90              
91             my $p = $div->parent;
92              
93             $p->prepend( "\n[% IF jumbotron %]" .
94             "[% # put a h1 and one or more p in here %]\n "
95             );
96              
97             my $h1 = $div->find( 'h1' )->first;
98             $h1->content( '[% jumbotron.header %]' );
99             my $ps = $div->find( 'p' );
100              
101             my $pa = $ps->first;
102              
103             $pa->content( '[% jumbotron.body %]' );
104              
105             my $i;
106             $div->children->each
107             ( sub {
108              
109             if ( ++$i > 2 ) {
110             $_[0]->remove;
111             }
112              
113             });
114              
115              
116              
117             $p->append( "\n[% END %]\n" );
118             l->debug( "HTML5: Wrapper jumbotron template var inserted" );
119             }
120              
121             ## fix any relative links to img/ or css/ or js/ to now point to static/
122             $dom->find("*")->each(
123             sub {
124             my($element,$i) = @_;
125              
126             my %h = %$element;
127              
128             while ( my($key,$val) = each %h ) {
129              
130             # print "# '$key'='$val' ";
131              
132             if ( $val =~ m{(?:\./)?img/} ) {
133             (my $new_val = $val) =~
134             s{(?:\./)?img/(.*)}{[% c.uri_for(QUOTEHERE/static/images/$1QUOTEHERE) %]};
135             $element->attr($key => $new_val);
136             # print "=> '$new_val'";
137             }
138             elsif ( $val =~ m{(?:\./)?(css|js)/} ) {
139             my $d = $1;
140             (my $new_val = $val) =~
141             s{(?:\./)?$d/(.*)}{[% c.uri_for(QUOTEHERE/static/$d/$1QUOTEHERE) %]};
142             $element->attr($key => $new_val);
143             # print "=> '$new_val'";
144             }
145              
146             # print "\n";
147              
148             }
149              
150             });
151             l->debug("HTML5: references to img/ css/ js/ and fonts/ changed to static/*");
152              
153             (my $new_index_content = "$dom") =~ s/QUOTEHERE/"/g;
154              
155             ## this won't be handled because it's not an html element
156             ## attribute, and we're not parsing javascript (yet?)
157             $new_index_content =~ s{\Qdocument.write('<script src="js/vendor/jquery-1.10.1.min.js">}
158             {document.write('<script src="[% c.uri_for("/static/js/vendor/jquery-1.10.1.min.js") %]">};
159              
160             ## replace it into the zip
161             my $index_member = _safely_search_one_member( qr/index\.html$/ );
162             my $index_name = $index_member->fileName;
163             my($f,$d) = fileparse( $index_name );
164             $az->contents( $index_member, $new_index_content );
165              
166             $index_member->fileName( $d."wrapper.tt2" );
167             l->debug("HTML5: index.html changed to wrapper.tt2" );
168              
169             }
170             sub _move_images {
171              
172             _require_az;
173              
174             ## change dir name from img/* to static/images/*
175              
176             my @img_members = $az->membersMatching(qr(/img/));
177              
178             if (not @img_members) {
179             carp "did not find any img/ files in zip, this does not feel right";
180             return;
181             }
182              
183             for my $m (@img_members) {
184             (my $new_name = $m->fileName) =~ s|/img/|/static/images/|;
185             $m->fileName( $new_name );
186             }
187              
188             l->debug(sprintf "HTML5: %d image(s) moved from img/ to images/",
189             scalar(@img_members) );
190              
191             }
192             sub _move_css_js_fonts {
193              
194             _require_az;
195              
196             ## change dir name from img/* to static/images/*
197              
198             my @static_members = $az->membersMatching(qr(/(?:css|js|fonts)/));
199              
200             if (not @static_members) {
201             carp "did not find any js/ or css/ files in zip, that cannot be right";
202             return;
203             }
204              
205             for my $m (@static_members) {
206             (my $new_name = $m->fileName) =~ s{/(css|js|fonts)/}{/static/$1/};
207             $m->fileName( $new_name );
208             }
209              
210             l->debug(sprintf "HTML5: %d css, js or fonts files moved to static/*",
211             scalar(@static_members) );
212              
213             }
214              
215             ## Low level functions:
216             sub _az {
217             return $az;
218             }
219             sub _set_az_from_cache {
220              
221             my $zip_file = module_file( __PACKAGE__, "initializr-verekia-4.0.zip" );
222             return $az //= Archive::Zip->new( $zip_file );
223              
224             }
225             sub _safely_search_one_member {
226              
227             my ($qr,$allowed_to_live_when_doesnt_match) = @_;
228              
229             _require_az;
230              
231             my @m;
232              
233             if ( ref $qr eq "Regexp" ) {
234             @m = $az->membersMatching({ regex => $qr });
235             }
236             else {
237             @m = ($az->memberNamed( $qr ));
238             }
239              
240             if ( @m != 1 and not $allowed_to_live_when_doesnt_match or @m > 1 ) {
241             croak "Found 0 or more than one zip member match for '$qr'";
242             }
243              
244             return $m[0];
245              
246             }
247             sub _zip_content {
248              
249             my( $qr, $new_content ) = @_;
250              
251             _require_az;
252              
253             my $member = _safely_search_one_member($qr) or return;
254              
255             if ( $new_content ) {
256             return $az->contents( $member, $new_content );
257             }
258             else {
259             return $az->contents( $member );
260             }
261             }
262             sub _index_dom {
263              
264             _require_az;
265              
266             my $h = _zip_content( qr/index\.html$/ );
267             my $dom = Mojo::DOM->new( $h );
268              
269             return $dom;
270              
271             }
272              
273             1; # Magic true value required at end of module
274             __END__
275              
276             =encoding utf8
277              
278             =head1 NAME
279              
280             App::CatalystStarter::Bloated::Initializr - Setup a html5 template
281             from initializr.com in your catalyst project
282              
283              
284             =head1 VERSION
285              
286             This document describes App::CatalystStarter::Bloated::Initializr version 0.9.3
287              
288             =head1 SYNOPSIS
289              
290             # Don't use this module. catalyst-fatstart.pl uses this for magic.
291              
292             =head1 DESCRIPTION
293              
294             This module offers the following functionality:
295              
296             =over
297              
298             =item Offer a cached zip download from initializr.com with a given set of options
299              
300             =item Download a new zip file from initializr.com.
301              
302             If this fails, offer to provide the cached version bundled with this
303             module instead
304              
305             =item Process the zipped file, correct paths to images, css and js
306              
307             Adaptes it to fit to a catalyst setup with /root/static/images etc.
308              
309             =item Inserts [% content %] to make it work as a wrapper
310              
311             Inspects the HTML and locates what content should be substituted with this tag
312              
313             =item Future versions will allow custom downloads from initializr.com
314              
315             Ie choosing what content to include
316              
317             =back
318              
319             All modifications is done in the zip file which is then written to disk.
320              
321             =head1 INTERFACE
322              
323             =head2 deploy($directory)
324              
325             Processes zip and extracts it at given dir.
326              
327             =head1 DIAGNOSTICS
328              
329             =for author to fill in:
330             List every single error and warning message that the module can
331             generate (even the ones that will "never happen"), with a full
332             explanation of each problem, one or more likely causes, and any
333             suggested remedies.
334              
335             =over
336              
337             =item C<< Error message here, perhaps with %s placeholders >>
338              
339             [Description of error here]
340              
341             =item C<< Another error message here >>
342              
343             [Description of error here]
344              
345             [Et cetera, et cetera]
346              
347             =back
348              
349              
350             =head1 CONFIGURATION AND ENVIRONMENT
351              
352             =for author to fill in:
353             A full explanation of any configuration system(s) used by the
354             module, including the names and locations of any configuration
355             files, and the meaning of any environment variables or properties
356             that can be set. These descriptions must also include details of any
357             configuration language used.
358              
359             App::CatalystStarter::Bloated::Initializr requires no configuration files or environment variables.
360              
361              
362             =head1 DEPENDENCIES
363              
364             =for author to fill in:
365             A list of all the other modules that this module relies upon,
366             including any restrictions on versions, and an indication whether
367             the module is part of the standard Perl distribution, part of the
368             module's distribution, or must be installed separately. ]
369              
370             None.
371              
372              
373             =head1 INCOMPATIBILITIES
374              
375             =for author to fill in:
376             A list of any modules that this module cannot be used in conjunction
377             with. This may be due to name conflicts in the interface, or
378             competition for system or program resources, or due to internal
379             limitations of Perl (for example, many modules that use source code
380             filters are mutually incompatible).
381              
382             None reported.
383              
384              
385             =head1 BUGS AND LIMITATIONS
386              
387             =for author to fill in:
388             A list of known problems with the module, together with some
389             indication Whether they are likely to be fixed in an upcoming
390             release. Also a list of restrictions on the features the module
391             does provide: data types that cannot be handled, performance issues
392             and the circumstances in which they may arise, practical
393             limitations on the size of data sets, special cases that are not
394             (yet) handled, etc.
395              
396             No bugs have been reported.
397              
398             Please report any bugs or feature requests to
399             C<bug-bug-app-catalyststarter::bloated::initializr@rt.cpan.org>, or through the web interface at
400             L<http://rt.cpan.org>.
401              
402              
403             =head1 AUTHOR
404              
405             Torbjørn Lindahl C<< <torbjorn.lindahl@gmail.com> >>
406              
407              
408             =head1 LICENCE AND COPYRIGHT
409              
410             Copyright (c) 2014, Torbjørn Lindahl C<< <torbjorn.lindahl@gmail.com> >>. All rights reserved.
411              
412             This module is free software; you can redistribute it and/or
413             modify it under the same terms as Perl itself. See L<perlartistic>.
414              
415              
416             =head1 DISCLAIMER OF WARRANTY
417              
418             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
419             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
420             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
421             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
422             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
423             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
424             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
425             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
426             NECESSARY SERVICING, REPAIR, OR CORRECTION.
427              
428             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
429             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
430             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
431             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
432             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
433             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
434             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
435             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
436             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
437             SUCH DAMAGES.