File Coverage

blib/lib/Catalyst/Helper.pm
Criterion Covered Total %
statement 6 6 100.0
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 8 8 100.0


line stmt bran cond sub pod time code
1             package Catalyst::Helper;
2              
3 1     1   1285 use strict;
  1         3  
  1         47  
4 1     1   6 use base 'Class::Accessor::Fast';
  1         1  
  1         736  
5             use Config;
6             use File::Spec;
7             use File::Path;
8             use IO::File;
9             use FindBin;
10             use Template;
11             use Catalyst::Utils;
12             use Catalyst::Exception;
13              
14             our $VERSION = '1.0_01';
15             my %cache;
16              
17             =head1 NAME
18              
19             Catalyst::Helper - Bootstrap a Catalyst application
20              
21             =head1 SYNOPSIS
22              
23             See L
24              
25             =head1 DESCRIPTION
26              
27             Bootstrap a Catalyst application. Autogenerates scripts
28              
29             =head2 METHODS
30              
31             =head3 get_file
32              
33             Slurp file from DATA.
34              
35             =cut
36              
37             sub get_file {
38             my ( $self, $class, $file ) = @_;
39             unless ( $cache{$class} ) {
40             local $/;
41             $cache{$class} = eval "package $class; ";
42             }
43             my $data = $cache{$class};
44             my @files = split /^__(.+)__\r?\n/m, $data;
45             shift @files;
46             while (@files) {
47             my ( $name, $content ) = splice @files, 0, 2;
48             return $content if $name eq $file;
49             }
50             return 0;
51             }
52              
53             =head3 mk_app
54              
55             Create the main application skeleton.
56              
57             =cut
58              
59             sub mk_app {
60             my ( $self, $name ) = @_;
61              
62             # Needs to be here for PAR
63             require Catalyst;
64              
65             if ( $name =~ /[^\w\:]/ ) {
66             warn "Error: Invalid application name.\n";
67             return 0;
68             }
69             $self->{name} = $name;
70             $self->{dir} = $name;
71             $self->{dir} =~ s/\:\:/-/g;
72             $self->{script} = File::Spec->catdir( $self->{dir}, 'script' );
73             $self->{appprefix} = Catalyst::Utils::appprefix($name);
74             $self->{startperl} = "#!$Config{perlpath} -w";
75             $self->{scriptgen} = $Catalyst::CATALYST_SCRIPT_GEN || 4;
76             $self->{author} = $self->{author} = $ENV{'AUTHOR'}
77             || eval { @{ [ getpwuid($<) ] }[6] }
78             || 'Catalyst developer';
79              
80             my $gen_scripts = ( $self->{makefile} ) ? 0 : 1;
81             my $gen_makefile = ( $self->{scripts} ) ? 0 : 1;
82             my $gen_app = ( $self->{scripts} || $self->{makefile} ) ? 0 : 1;
83              
84             if ($gen_app) {
85             $self->_mk_dirs;
86             $self->_mk_config;
87             $self->_mk_appclass;
88             $self->_mk_rootclass;
89             $self->_mk_readme;
90             $self->_mk_changes;
91             $self->_mk_apptest;
92             $self->_mk_images;
93             $self->_mk_favicon;
94             }
95             if ($gen_makefile) {
96             $self->_mk_makefile;
97             }
98             if ($gen_scripts) {
99             $self->_mk_cgi;
100             $self->_mk_fastcgi;
101             $self->_mk_server;
102             $self->_mk_test;
103             $self->_mk_create;
104             }
105             return $self->{dir};
106             }
107              
108             =head3 mk_component
109              
110             This method is called by create.pl to make new components
111             for your application.
112              
113             =cut
114              
115             sub mk_component {
116             my $self = shift;
117             my $app = shift;
118             $self->{app} = $app;
119             $self->{author} = $self->{author} = $ENV{'AUTHOR'}
120             || eval { @{ [ getpwuid($<) ] }[6] }
121             || 'A clever guy';
122             $self->{base} ||= File::Spec->catdir( $FindBin::Bin, '..' );
123             unless ( $_[0] =~ /^(?:model|view|controller|action)$/i ) {
124             my $helper = shift;
125             my @args = @_;
126             my $class = "Catalyst::Helper::$helper";
127             eval "require $class";
128              
129             if ($@) {
130             Catalyst::Exception->throw(
131             message => qq/Couldn't load helper "$class", "$@"/ );
132             }
133              
134             if ( $class->can('mk_stuff') ) {
135             return 1 unless $class->mk_stuff( $self, @args );
136             }
137             }
138             else {
139             my $type = shift;
140             my $name = shift || "Missing name for model/view/controller/action";
141             my $helper = shift;
142             my @args = @_;
143             return 0 if $name =~ /[^\w\:]/;
144             $type = lc $type;
145             $self->{long_type} = ucfirst $type;
146             $type = 'M' if $type =~ /model/i;
147             $type = 'V' if $type =~ /view/i;
148             $type = 'C' if $type =~ /controller/i;
149             my $appdir = File::Spec->catdir( split /\:\:/, $app );
150             my $test_path =
151             File::Spec->catdir( $FindBin::Bin, '..', 'lib', $appdir, 'C' );
152             $type = $self->{long_type} unless -d $test_path;
153             $self->{type} = $type;
154             $self->{name} = $name;
155             $self->{class} = "$app\::$type\::$name";
156              
157             # Class
158             my $path =
159             File::Spec->catdir( $FindBin::Bin, '..', 'lib', $appdir, $type );
160             my $file = $name;
161             if ( $name =~ /\:/ ) {
162             my @path = split /\:\:/, $name;
163             $file = pop @path;
164             $path = File::Spec->catdir( $path, @path );
165             }
166             $self->mk_dir($path);
167             $file = File::Spec->catfile( $path, "$file.pm" );
168             $self->{file} = $file;
169              
170             # Test
171             $self->{test_dir} = File::Spec->catdir( $FindBin::Bin, '..', 't' );
172             $self->{test} = $self->next_test unless $type eq 'Action';
173              
174             # Helper
175             if ($helper) {
176             my $comp = $self->{long_type};
177             my $class = "Catalyst::Helper::$comp\::$helper";
178             eval "require $class";
179              
180             if ($@) {
181             Catalyst::Exception->throw(
182             message => qq/Couldn't load helper "$class", "$@"/ );
183             }
184              
185             if ( $class->can('mk_compclass') ) {
186             return 1 unless $class->mk_compclass( $self, @args );
187             }
188             else { return 1 unless $self->_mk_compclass }
189              
190             unless ( $type eq 'Action' ) {
191             if ( $class->can('mk_comptest') ) {
192             $class->mk_comptest( $self, @args );
193             }
194             else { $self->_mk_comptest }
195             }
196             }
197              
198             # Fallback
199             else {
200             return 1 unless $self->_mk_compclass;
201             $self->_mk_comptest unless $type eq 'Action';
202             }
203             }
204             return 1;
205             }
206              
207             =head3 mk_dir
208              
209             Surprisingly, this function makes a directory.
210              
211             =cut
212              
213             sub mk_dir {
214             my ( $self, $dir ) = @_;
215             if ( -d $dir ) {
216             print qq/ exists "$dir"\n/;
217             return 0;
218             }
219             if ( mkpath [$dir] ) {
220             print qq/created "$dir"\n/;
221             return 1;
222             }
223              
224             Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
225             }
226              
227             =head3 mk_file
228              
229             writes content to a file.
230              
231             =cut
232              
233             sub mk_file {
234             my ( $self, $file, $content ) = @_;
235             if ( -e $file ) {
236             print qq/ exists "$file"\n/;
237             return 0
238             unless ( $self->{'.newfiles'}
239             || $self->{scripts}
240             || $self->{makefile} );
241             if ( $self->{'.newfiles'} ) {
242             if ( my $f = IO::File->new("< $file") ) {
243             my $oldcontent = join( '', (<$f>) );
244             return 0 if $content eq $oldcontent;
245             }
246             $file .= '.new';
247             }
248             }
249             if ( my $f = IO::File->new("> $file") ) {
250             binmode $f;
251             print $f $content;
252             print qq/created "$file"\n/;
253             return 1;
254             }
255              
256             Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
257             }
258              
259             =head3 next_test
260              
261             =cut
262              
263             sub next_test {
264             my ( $self, $tname ) = @_;
265             if ($tname) { $tname = "$tname.t" }
266             else {
267             my $name = $self->{name};
268             my $prefix = $name;
269             $prefix =~ s/::/-/g;
270             $prefix = $prefix;
271             $tname = $prefix . '.t';
272             $self->{prefix} = $prefix;
273             $prefix = lc $prefix;
274             $prefix =~ s/-/\//g;
275             $self->{uri} = "/$prefix";
276             }
277             my $dir = $self->{test_dir};
278             my $type = lc $self->{type};
279             $self->mk_dir($dir);
280             return File::Spec->catfile( $dir, "$type\_$tname" );
281             }
282              
283             =head3 render_file
284              
285             Render and create a file from a template in DATA using
286             Template Toolkit.
287              
288             =cut
289              
290             sub render_file {
291             my ( $self, $file, $path, $vars ) = @_;
292             $vars ||= {};
293             my $t = Template->new;
294             my $template = $self->get_file( ( caller(0) )[0], $file );
295             return 0 unless $template;
296             my $output;
297             $t->process( \$template, { %{$self}, %$vars }, \$output )
298             || Catalyst::Exception->throw(
299             message => qq/Couldn't process "$file", / . $t->error() );
300             $self->mk_file( $path, $output );
301             }
302              
303             sub _mk_dirs {
304             my $self = shift;
305             $self->mk_dir( $self->{dir} );
306             $self->mk_dir( $self->{script} );
307             $self->{lib} = File::Spec->catdir( $self->{dir}, 'lib' );
308             $self->mk_dir( $self->{lib} );
309             $self->{root} = File::Spec->catdir( $self->{dir}, 'root' );
310             $self->mk_dir( $self->{root} );
311             $self->{static} = File::Spec->catdir( $self->{root}, 'static' );
312             $self->mk_dir( $self->{static} );
313             $self->{images} = File::Spec->catdir( $self->{static}, 'images' );
314             $self->mk_dir( $self->{images} );
315             $self->{t} = File::Spec->catdir( $self->{dir}, 't' );
316             $self->mk_dir( $self->{t} );
317              
318             $self->{class} = File::Spec->catdir( split( /\:\:/, $self->{name} ) );
319             $self->{mod} = File::Spec->catdir( $self->{lib}, $self->{class} );
320             $self->mk_dir( $self->{mod} );
321              
322             if ( $self->{short} ) {
323             $self->{m} = File::Spec->catdir( $self->{mod}, 'M' );
324             $self->mk_dir( $self->{m} );
325             $self->{v} = File::Spec->catdir( $self->{mod}, 'V' );
326             $self->mk_dir( $self->{v} );
327             $self->{c} = File::Spec->catdir( $self->{mod}, 'C' );
328             $self->mk_dir( $self->{c} );
329             }
330             else {
331             $self->{m} = File::Spec->catdir( $self->{mod}, 'Model' );
332             $self->mk_dir( $self->{m} );
333             $self->{v} = File::Spec->catdir( $self->{mod}, 'View' );
334             $self->mk_dir( $self->{v} );
335             $self->{c} = File::Spec->catdir( $self->{mod}, 'Controller' );
336             $self->mk_dir( $self->{c} );
337             }
338             my $name = $self->{name};
339             $self->{rootname} =
340             $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
341             $self->{base} = File::Spec->rel2abs( $self->{dir} );
342             }
343              
344             sub _mk_appclass {
345             my $self = shift;
346             my $mod = $self->{mod};
347             $self->render_file( 'appclass', "$mod.pm" );
348             }
349              
350             sub _mk_rootclass {
351             my $self = shift;
352             $self->render_file( 'rootclass',
353             File::Spec->catfile( $self->{c}, "Root.pm" ) );
354             }
355              
356             sub _mk_makefile {
357             my $self = shift;
358             $self->{path} = File::Spec->catfile( 'lib', split( '::', $self->{name} ) );
359             $self->{path} .= '.pm';
360             my $dir = $self->{dir};
361             $self->render_file( 'makefile', "$dir\/Makefile.PL" );
362              
363             if ( $self->{makefile} ) {
364              
365             # deprecate the old Build.PL file when regenerating Makefile.PL
366             $self->_deprecate_file(
367             File::Spec->catdir( $self->{dir}, 'Build.PL' ) );
368             }
369             }
370              
371             sub _mk_config {
372             my $self = shift;
373             my $dir = $self->{dir};
374             my $appprefix = $self->{appprefix};
375             $self->render_file( 'config',
376             File::Spec->catfile( $dir, "$appprefix.yml" ) );
377             }
378              
379             sub _mk_readme {
380             my $self = shift;
381             my $dir = $self->{dir};
382             $self->render_file( 'readme', "$dir\/README" );
383             }
384              
385             sub _mk_changes {
386             my $self = shift;
387             my $dir = $self->{dir};
388             my $time = localtime time;
389             $self->render_file( 'changes', "$dir\/Changes", { time => $time } );
390             }
391              
392             sub _mk_apptest {
393             my $self = shift;
394             my $t = $self->{t};
395             $self->render_file( 'apptest', "$t\/01app.t" );
396             $self->render_file( 'podtest', "$t\/02pod.t" );
397             $self->render_file( 'podcoveragetest', "$t\/03podcoverage.t" );
398             }
399              
400             sub _mk_cgi {
401             my $self = shift;
402             my $script = $self->{script};
403             my $appprefix = $self->{appprefix};
404             $self->render_file( 'cgi', "$script\/$appprefix\_cgi.pl" );
405             chmod 0700, "$script/$appprefix\_cgi.pl";
406             }
407              
408             sub _mk_fastcgi {
409             my $self = shift;
410             my $script = $self->{script};
411             my $appprefix = $self->{appprefix};
412             $self->render_file( 'fastcgi', "$script\/$appprefix\_fastcgi.pl" );
413             chmod 0700, "$script/$appprefix\_fastcgi.pl";
414             }
415              
416             sub _mk_server {
417             my $self = shift;
418             my $script = $self->{script};
419             my $appprefix = $self->{appprefix};
420             $self->render_file( 'server', "$script\/$appprefix\_server.pl" );
421             chmod 0700, "$script/$appprefix\_server.pl";
422             }
423              
424             sub _mk_test {
425             my $self = shift;
426             my $script = $self->{script};
427             my $appprefix = $self->{appprefix};
428             $self->render_file( 'test', "$script/$appprefix\_test.pl" );
429             chmod 0700, "$script/$appprefix\_test.pl";
430             }
431              
432             sub _mk_create {
433             my $self = shift;
434             my $script = $self->{script};
435             my $appprefix = $self->{appprefix};
436             $self->render_file( 'create', "$script\/$appprefix\_create.pl" );
437             chmod 0700, "$script/$appprefix\_create.pl";
438             }
439              
440             sub _mk_compclass {
441             my $self = shift;
442             my $file = $self->{file};
443             return $self->render_file( 'compclass', "$file" );
444             }
445              
446             sub _mk_comptest {
447             my $self = shift;
448             my $test = $self->{test};
449             $self->render_file( 'comptest', "$test" );
450             }
451              
452             sub _mk_images {
453             my $self = shift;
454             my $images = $self->{images};
455             my @images =
456             qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
457             btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
458             btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
459             for my $name (@images) {
460             my $hex = $self->get_file( ( caller(0) )[0], $name );
461             my $image = pack "H*", $hex;
462             $self->mk_file( File::Spec->catfile( $images, "$name.png" ), $image );
463             }
464             }
465              
466             sub _mk_favicon {
467             my $self = shift;
468             my $root = $self->{root};
469             my $hex = $self->get_file( ( caller(0) )[0], 'favicon' );
470             my $favicon = pack "H*", $hex;
471             $self->mk_file( File::Spec->catfile( $root, "favicon.ico" ), $favicon );
472              
473             }
474              
475             sub _deprecate_file {
476             my ( $self, $file ) = @_;
477             if ( -e $file ) {
478             my $oldcontent;
479             if ( my $f = IO::File->new("< $file") ) {
480             $oldcontent = join( '', (<$f>) );
481             }
482             my $newfile = $file . '.deprecated';
483             if ( my $f = IO::File->new("> $newfile") ) {
484             binmode $f;
485             print $f $oldcontent;
486             print qq/created "$newfile"\n/;
487             unlink $file;
488             print qq/removed "$file"\n/;
489             return 1;
490             }
491             Catalyst::Exception->throw(
492             message => qq/Couldn't create "$file", "$!"/ );
493             }
494             }
495              
496             =head1 HELPERS
497              
498             Helpers are classes that provide two methods.
499              
500             * mk_compclass - creates the Component class
501             * mk_comptest - creates the Component test
502              
503             So when you call C, create would try to execute
504             Catalyst::Helper::View::TT->mk_compclass and
505             Catalyst::Helper::View::TT->mk_comptest.
506              
507             See L and L for
508             examples.
509              
510             All helper classes should be under one of the following namespaces.
511              
512             Catalyst::Helper::Model::
513             Catalyst::Helper::View::
514             Catalyst::Helper::Controller::
515              
516             =head1 NOTE
517              
518             The helpers will read author name from /etc/passwd by default.
519             To override, please export the AUTHOR variable.
520              
521             =head1 SEE ALSO
522              
523             L, L, L,
524             L, L
525              
526             =head1 AUTHOR
527              
528             Sebastian Riedel, C
529              
530             =head1 LICENSE
531              
532             This library is free software, you can redistribute it and/or modify
533             it under the same terms as Perl itself.
534              
535             =cut
536              
537             1;
538             __DATA__