Add built local::lib
[catagits/Gitalist.git] / local-lib5 / lib / perl5 / Catalyst / Helper.pm
CommitLineData
3fea05b9 1package Catalyst::Helper;
2use Moose;
3use Config;
4use File::Spec;
5use File::Spec::Unix;
6use File::Path;
7use FindBin;
8use IO::File;
9use POSIX 'strftime';
10use Template;
11use Catalyst::Devel;
12use Catalyst::Utils;
13use Catalyst::Exception;
14use Path::Class qw/dir file/;
15use File::ShareDir qw/dist_dir/;
16use namespace::autoclean;
17
18with 'MooseX::Emulate::Class::Accessor::Fast';
19
20# Change Catalyst/Devel.pm also
21our $VERSION = '1.23';
22
23my %cache;
24
25=head1 NAME
26
27Catalyst::Helper - Bootstrap a Catalyst application
28
29=head1 SYNOPSIS
30
31 catalyst.pl <myappname>
32
33=cut
34
35sub get_sharedir_file {
36 my ($self, @filename) = @_;
37 my $dist_dir;
38 if (exists $ENV{CATALYST_DEVEL_SHAREDIR}) {
39 $dist_dir = $ENV{CATALYST_DEVEL_SHAREDIR};
40 }
41 elsif (-d "inc/.author" && -f "lib/Catalyst/Helper.pm"
42 ) { # Can't use sharedir if we're in a checkout
43 # this feels horrible, better ideas?
44 $dist_dir = 'share';
45 }
46 else {
47 $dist_dir = dist_dir('Catalyst-Devel');
48 }
49 my $file = file( $dist_dir, @filename);
50 Carp::confess("Cannot find $file") unless -r $file;
51 my $contents = $file->slurp;
52 return $contents;
53}
54
55# Do not touch this method, *EVER*, it is needed for back compat.
56sub get_file {
57 my ( $self, $class, $file ) = @_;
58 unless ( $cache{$class} ) {
59 local $/;
60 $cache{$class} = eval "package $class; <DATA>";
61 }
62 my $data = $cache{$class};
63 Carp::confess("Could not get data from __DATA__ segment for $class")
64 unless $data;
65 my @files = split /^__(.+)__\r?\n/m, $data;
66 shift @files;
67 while (@files) {
68 my ( $name, $content ) = splice @files, 0, 2;
69 return $content if $name eq $file;
70 }
71 return 0;
72}
73
74
75sub mk_app {
76 my ( $self, $name ) = @_;
77
78 # Needs to be here for PAR
79 require Catalyst;
80
81 if ( $name =~ /[^\w:]/ || $name =~ /^\d/ || $name =~ /\b:\b|:{3,}/) {
82 warn "Error: Invalid application name.\n";
83 return 0;
84 }
85 $self->{name } = $name;
86 $self->{dir } = $name;
87 $self->{dir } =~ s/\:\:/-/g;
88 $self->{script } = dir( $self->{dir}, 'script' );
89 $self->{appprefix } = Catalyst::Utils::appprefix($name);
90 $self->{appenv } = Catalyst::Utils::class2env($name);
91 $self->{startperl } = -r '/usr/bin/env'
92 ? '#!/usr/bin/env perl'
93 : "#!$Config{perlpath} -w";
94 $self->{scriptgen } = $Catalyst::Devel::CATALYST_SCRIPT_GEN;
95 $self->{catalyst_version} = $Catalyst::VERSION;
96 $self->{author } = $self->{author} = $ENV{'AUTHOR'}
97 || eval { @{ [ getpwuid($<) ] }[6] }
98 || 'Catalyst developer';
99
100 my $gen_scripts = ( $self->{makefile} ) ? 0 : 1;
101 my $gen_makefile = ( $self->{scripts} ) ? 0 : 1;
102 my $gen_app = ( $self->{scripts} || $self->{makefile} ) ? 0 : 1;
103
104 if ($gen_app) {
105 for ( qw/ _mk_dirs _mk_config _mk_appclass _mk_rootclass _mk_readme
106 _mk_changes _mk_apptest _mk_images _mk_favicon/ ) {
107
108 $self->$_;
109 }
110 }
111 if ($gen_makefile) {
112 $self->_mk_makefile;
113 }
114 if ($gen_scripts) {
115 for ( qw/ _mk_cgi _mk_fastcgi _mk_server
116 _mk_test _mk_create _mk_information
117 / ) {
118 $self->$_;
119 }
120 }
121 return $self->{dir};
122}
123
124## not much of this can really be changed, mk_compclass must be left for
125## backcompat
126sub mk_component {
127 my $self = shift;
128 my $app = shift;
129 $self->{app} = $app;
130 $self->{author} = $self->{author} = $ENV{'AUTHOR'}
131 || eval { @{ [ getpwuid($<) ] }[6] }
132 || 'A clever guy';
133 $self->{base} ||= dir( $FindBin::Bin, '..' );
134 unless ( $_[0] =~ /^(?:model|view|controller)$/i ) {
135 my $helper = shift;
136 my @args = @_;
137 my $class = "Catalyst::Helper::$helper";
138 eval "require $class";
139
140 if ($@) {
141 Catalyst::Exception->throw(
142 message => qq/Couldn't load helper "$class", "$@"/ );
143 }
144
145 if ( $class->can('mk_stuff') ) {
146 return 1 unless $class->mk_stuff( $self, @args );
147 }
148 }
149 else {
150 my $type = shift;
151 my $name = shift || "Missing name for model/view/controller";
152 my $helper = shift;
153 my @args = @_;
154 return 0 if $name =~ /[^\w\:]/;
155 $type = lc $type;
156 $self->{long_type} = ucfirst $type;
157 $type = 'M' if $type =~ /model/i;
158 $type = 'V' if $type =~ /view/i;
159 $type = 'C' if $type =~ /controller/i;
160 my $appdir = dir( split /\:\:/, $app );
161 my $test_path =
162 dir( $self->{base}, 'lib', $appdir, 'C' );
163 $type = $self->{long_type} unless -d $test_path;
164 $self->{type} = $type;
165 $self->{name} = $name;
166 $self->{class} = "$app\::$type\::$name";
167
168 # Class
169 my $path =
170 dir( $self->{base}, 'lib', $appdir, $type );
171 my $file = $name;
172 if ( $name =~ /\:/ ) {
173 my @path = split /\:\:/, $name;
174 $file = pop @path;
175 $path = dir( $path, @path );
176 }
177 $self->mk_dir($path);
178 $file = file( $path, "$file.pm" );
179 $self->{file} = $file;
180
181 # Test
182 $self->{test_dir} = dir( $self->{base}, 't' );
183 $self->{test} = $self->next_test;
184
185 # Helper
186 if ($helper) {
187 my $comp = $self->{long_type};
188 my $class = "Catalyst::Helper::$comp\::$helper";
189 eval "require $class";
190
191 if ($@) {
192 Catalyst::Exception->throw(
193 message => qq/Couldn't load helper "$class", "$@"/ );
194 }
195
196 if ( $class->can('mk_compclass') ) {
197 return 1 unless $class->mk_compclass( $self, @args );
198 }
199 else {
200 return 1 unless $self->_mk_compclass
201 }
202
203 if ( $class->can('mk_comptest') ) {
204 $class->mk_comptest( $self, @args );
205 }
206 else {
207 $self->_mk_comptest
208 }
209 }
210
211 # Fallback
212 else {
213 return 1 unless $self->_mk_compclass;
214 $self->_mk_comptest;
215 }
216 }
217 return 1;
218}
219
220sub mk_dir {
221 my ( $self, $dir ) = @_;
222 if ( -d $dir ) {
223 print qq/ exists "$dir"\n/;
224 return 0;
225 }
226 if ( mkpath [$dir] ) {
227 print qq/created "$dir"\n/;
228 return 1;
229 }
230
231 Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
232}
233
234sub mk_file {
235 my ( $self, $file, $content ) = @_;
236 if ( -e $file && -s _ ) {
237 print qq/ exists "$file"\n/;
238 return 0
239 unless ( $self->{'.newfiles'}
240 || $self->{scripts}
241 || $self->{makefile} );
242 if ( $self->{'.newfiles'} ) {
243 if ( my $f = IO::File->new("< $file") ) {
244 my $oldcontent = join( '', (<$f>) );
245 return 0 if $content eq $oldcontent;
246 }
247 $file .= '.new';
248 }
249 }
250
251 if ( my $f = IO::File->new("> $file") ) {
252 binmode $f;
253 print $f $content;
254 print qq/created "$file"\n/;
255 return 1;
256 }
257
258 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
259}
260
261sub next_test {
262 my ( $self, $tname ) = @_;
263 if ($tname) { $tname = "$tname.t" }
264 else {
265 my $name = $self->{name};
266 my $prefix = $name;
267 $prefix =~ s/::/-/g;
268 $prefix = $prefix;
269 $tname = $prefix . '.t';
270 $self->{prefix} = $prefix;
271 $prefix = lc $prefix;
272 $prefix =~ s/-/\//g;
273 $self->{uri} = "/$prefix";
274 }
275 my $dir = $self->{test_dir};
276 my $type = lc $self->{type};
277 $self->mk_dir($dir);
278 return file( $dir, "$type\_$tname" );
279}
280
281# Do not touch this method, *EVER*, it is needed for back compat.
282## addendum: we had to split this method so we could have backwards
283## compatability. otherwise, we'd have no way to pass stuff from __DATA__
284
285sub render_file {
286 my ( $self, $file, $path, $vars ) = @_;
287 my $template = $self->get_file( ( caller(0) )[0], $file );
288 $self->render_file_contents($template, $path, $vars);
289}
290
291sub render_sharedir_file {
292 my ( $self, $file, $path, $vars ) = @_;
293 my $template = $self->get_sharedir_file( $file );
294 die("Cannot get template from $file for $self\n") unless $template;
295 $self->render_file_contents($template, $path, $vars);
296}
297
298sub render_file_contents {
299 my ( $self, $template, $path, $vars ) = @_;
300 $vars ||= {};
301 my $t = Template->new;
302 return 0 unless $template;
303 my $output;
304 $t->process( \$template, { %{$self}, %$vars }, \$output )
305 || Catalyst::Exception->throw(
306 message => qq/Couldn't process "$template", / . $t->error() );
307 $self->mk_file( $path, $output );
308}
309
310sub _mk_information {
311 my $self = shift;
312 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
313}
314
315sub _mk_dirs {
316 my $self = shift;
317 $self->mk_dir( $self->{dir} );
318 $self->mk_dir( $self->{script} );
319 $self->{lib} = dir( $self->{dir}, 'lib' );
320 $self->mk_dir( $self->{lib} );
321 $self->{root} = dir( $self->{dir}, 'root' );
322 $self->mk_dir( $self->{root} );
323 $self->{static} = dir( $self->{root}, 'static' );
324 $self->mk_dir( $self->{static} );
325 $self->{images} = dir( $self->{static}, 'images' );
326 $self->mk_dir( $self->{images} );
327 $self->{t} = dir( $self->{dir}, 't' );
328 $self->mk_dir( $self->{t} );
329
330 $self->{class} = dir( split( /\:\:/, $self->{name} ) );
331 $self->{mod} = dir( $self->{lib}, $self->{class} );
332 $self->mk_dir( $self->{mod} );
333
334 if ( $self->{short} ) {
335 $self->{m} = dir( $self->{mod}, 'M' );
336 $self->mk_dir( $self->{m} );
337 $self->{v} = dir( $self->{mod}, 'V' );
338 $self->mk_dir( $self->{v} );
339 $self->{c} = dir( $self->{mod}, 'C' );
340 $self->mk_dir( $self->{c} );
341 }
342 else {
343 $self->{m} = dir( $self->{mod}, 'Model' );
344 $self->mk_dir( $self->{m} );
345 $self->{v} = dir( $self->{mod}, 'View' );
346 $self->mk_dir( $self->{v} );
347 $self->{c} = dir( $self->{mod}, 'Controller' );
348 $self->mk_dir( $self->{c} );
349 }
350 my $name = $self->{name};
351 $self->{rootname} =
352 $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
353 $self->{base} = dir( $self->{dir} )->absolute;
354}
355
356sub _mk_appclass {
357 my $self = shift;
358 my $mod = $self->{mod};
359 $self->render_sharedir_file( file('lib', 'MyApp.pm.tt'), "$mod.pm" );
360}
361
362sub _mk_rootclass {
363 my $self = shift;
364 $self->render_sharedir_file( file('lib', 'MyApp', 'Controller', 'Root.pm.tt'),
365 file( $self->{c}, "Root.pm" ) );
366}
367
368sub _mk_makefile {
369 my $self = shift;
370 $self->{path} = dir( 'lib', split( '::', $self->{name} ) );
371 $self->{path} .= '.pm';
372 my $dir = $self->{dir};
373 $self->render_sharedir_file( 'Makefile.PL.tt', file($dir, "Makefile.PL") );
374
375 if ( $self->{makefile} ) {
376
377 # deprecate the old Build.PL file when regenerating Makefile.PL
378 $self->_deprecate_file(
379 file( $self->{dir}, 'Build.PL' ) );
380 }
381}
382
383sub _mk_config {
384 my $self = shift;
385 my $dir = $self->{dir};
386 my $appprefix = $self->{appprefix};
387 $self->render_sharedir_file( 'myapp.conf.tt',
388 file( $dir, "$appprefix.conf" ) );
389}
390
391sub _mk_readme {
392 my $self = shift;
393 my $dir = $self->{dir};
394 $self->render_sharedir_file( 'README.tt', file($dir, "README") );
395}
396
397sub _mk_changes {
398 my $self = shift;
399 my $dir = $self->{dir};
400 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
401 $self->render_sharedir_file( 'Changes.tt', file($dir, "Changes"), { time => $time } );
402}
403
404sub _mk_apptest {
405 my $self = shift;
406 my $t = $self->{t};
407 $self->render_sharedir_file( file('t', '01app.t.tt'), file($t, "01app.t") );
408 $self->render_sharedir_file( file('t', '02pod.t.tt'), file($t, "02pod.t") );
409 $self->render_sharedir_file( file('t', '03podcoverage.t.tt'), file($t, "03podcoverage.t") );
410}
411
412sub _mk_cgi {
413 my $self = shift;
414 my $script = $self->{script};
415 my $appprefix = $self->{appprefix};
416 $self->render_sharedir_file( file('script', 'myapp_cgi.pl.tt'), file($script,"$appprefix\_cgi.pl") );
417 chmod 0700, file($script,"$appprefix\_cgi.pl");
418}
419
420sub _mk_fastcgi {
421 my $self = shift;
422 my $script = $self->{script};
423 my $appprefix = $self->{appprefix};
424 $self->render_sharedir_file( file('script', 'myapp_fastcgi.pl.tt'), file($script, "$appprefix\_fastcgi.pl") );
425 chmod 0700, file($script, "$appprefix\_fastcgi.pl");
426}
427
428sub _mk_server {
429 my $self = shift;
430 my $script = $self->{script};
431 my $appprefix = $self->{appprefix};
432 $self->render_sharedir_file( file('script', 'myapp_server.pl.tt'), file($script, "$appprefix\_server.pl") );
433 chmod 0700, file($script, "$appprefix\_server.pl");
434}
435
436sub _mk_test {
437 my $self = shift;
438 my $script = $self->{script};
439 my $appprefix = $self->{appprefix};
440 $self->render_sharedir_file( file('script', 'myapp_test.pl.tt'), file($script, "$appprefix\_test.pl") );
441 chmod 0700, file($script, "$appprefix\_test.pl");
442}
443
444sub _mk_create {
445 my $self = shift;
446 my $script = $self->{script};
447 my $appprefix = $self->{appprefix};
448 $self->render_sharedir_file( file('script', 'myapp_create.pl.tt'), file($script, "$appprefix\_create.pl") );
449 chmod 0700, file($script, "$appprefix\_create.pl");
450}
451
452sub _mk_compclass {
453 my $self = shift;
454 my $file = $self->{file};
455 return $self->render_sharedir_file( file('lib', 'Helper', 'compclass.pm.tt'), $file );
456}
457
458sub _mk_comptest {
459 my $self = shift;
460 my $test = $self->{test};
461 $self->render_sharedir_file( file('t', 'comptest.tt'), $test ); ## wtf do i rename this to?
462}
463
464sub _mk_images {
465 my $self = shift;
466 my $images = $self->{images};
467 my @images =
468 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
469 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
470 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
471 for my $name (@images) {
472 my $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin");
473 $self->mk_file( file( $images, "$name.png" ), $image );
474 }
475}
476
477sub _mk_favicon {
478 my $self = shift;
479 my $root = $self->{root};
480 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' );
481 my $dest = dir( $root, "favicon.ico" );
482 $self->mk_file( $dest, $favicon );
483
484}
485
486sub _deprecate_file {
487 my ( $self, $file ) = @_;
488 if ( -e $file ) {
489 my ($f, $oldcontent);
490 if ( $f = IO::File->new("< $file") ) {
491 $oldcontent = join( '', (<$f>) );
492 }
493 my $newfile = $file . '.deprecated';
494 if ( $f = IO::File->new("> $newfile") ) {
495 binmode $f;
496 print $f $oldcontent;
497 print qq/created "$newfile"\n/;
498 unlink $file;
499 print qq/removed "$file"\n/;
500 return 1;
501 }
502 Catalyst::Exception->throw(
503 message => qq/Couldn't create "$file", "$!"/ );
504 }
505}
506
507=head1 DESCRIPTION
508
509This module is used by B<catalyst.pl> to create a set of scripts for a
510new catalyst application. The scripts each contain documentation and
511will output help on how to use them if called incorrectly or in some
512cases, with no arguments.
513
514It also provides some useful methods for a Helper module to call when
515creating a component. See L</METHODS>.
516
517=head1 SCRIPTS
518
519=head2 _create.pl
520
521Used to create new components for a catalyst application at the
522development stage.
523
524=head2 _server.pl
525
526The catalyst test server, starts an HTTPD which outputs debugging to
527the terminal.
528
529=head2 _test.pl
530
531A script for running tests from the command-line.
532
533=head2 _cgi.pl
534
535Run your application as a CGI.
536
537=head2 _fastcgi.pl
538
539Run the application as a fastcgi app. Either by hand, or call this
540from FastCgiServer in your http server config.
541
542=head1 HELPERS
543
544The L</_create.pl> script creates application components using Helper
545modules. The Catalyst team provides a good number of Helper modules
546for you to use. You can also add your own.
547
548Helpers are classes that provide two methods.
549
550 * mk_compclass - creates the Component class
551 * mk_comptest - creates the Component test
552
553So when you call C<scripts/myapp_create.pl view MyView TT>, create
554will try to execute Catalyst::Helper::View::TT->mk_compclass and
555Catalyst::Helper::View::TT->mk_comptest.
556
557See L<Catalyst::Helper::View::TT> and
558L<Catalyst::Helper::Model::DBIC::Schema> for examples.
559
560All helper classes should be under one of the following namespaces.
561
562 Catalyst::Helper::Model::
563 Catalyst::Helper::View::
564 Catalyst::Helper::Controller::
565
566=head2 COMMON HELPERS
567
568=over
569
570=item *
571
572L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
573
574=item *
575
576L<Catalyst::Helper::View::TT> - Template Toolkit view
577
578=item *
579
580L<Catalyst::Helper::Model::LDAP>
581
582=item *
583
584L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
585
586=back
587
588=head3 NOTE
589
590The helpers will read author name from /etc/passwd by default.
591To override, please export the AUTHOR variable.
592
593=head1 METHODS
594
595=head2 mk_compclass
596
597This method in your Helper module is called with C<$helper>
598which is a L<Catalyst::Helper> object, and whichever other arguments
599the user added to the command-line. You can use the $helper to call methods
600described below.
601
602If the Helper module does not contain a C<mk_compclass> method, it
603will fall back to calling L</render_file>, with an argument of
604C<compclass>.
605
606=head2 mk_comptest
607
608This method in your Helper module is called with C<$helper>
609which is a L<Catalyst::Helper> object, and whichever other arguments
610the user added to the command-line. You can use the $helper to call methods
611described below.
612
613If the Helper module does not contain a C<mk_compclass> method, it
614will fall back to calling L</render_file>, with an argument of
615C<comptest>.
616
617=head2 mk_stuff
618
619This method is called if the user does not supply any of the usual
620component types C<view>, C<controller>, C<model>. It is passed the
621C<$helper> object (an instance of L<Catalyst::Helper>), and any other
622arguments the user typed.
623
624There is no fallback for this method.
625
626=head1 INTERNAL METHODS
627
628These are the methods that the Helper classes can call on the
629<$helper> object passed to them.
630
631=head2 render_file ($file, $path, $vars)
632
633Render and create a file from a template in DATA using Template
634Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
635the path to the file and $vars is the hashref as expected by
636L<Template Toolkit|Template>.
637
638=head2 get_file ($class, $file)
639
640Fetch file contents from the DATA section. This is used internally by
641L</render_file>. $class is the name of the class to get the DATA
642section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
643values for this.
644
645=head2 mk_app
646
647Create the main application skeleton. This is called by L<catalyst.pl>.
648
649=head2 mk_component ($app)
650
651This method is called by L<create.pl> to make new components
652for your application.
653
654=head2 mk_dir ($path)
655
656Surprisingly, this function makes a directory.
657
658=head2 mk_file ($file, $content)
659
660Writes content to a file. Called by L</render_file>.
661
662=head2 next_test ($test_name)
663
664Calculates the name of the next numbered test file and returns it.
665Don't give the number or the .t suffix for the test name.
666
667=cut
668
669=head2 get_sharedir_file
670
671Method for getting a file out of share/
672
673=cut
674
675=head2 render_file_contents
676
677Process a L<Template::Toolkit> template.
678
679=cut
680
681=head2 render_sharedir_file
682
683Render a template/image file from our share directory
684
685=cut
686
687=head1 NOTE
688
689The helpers will read author name from /etc/passwd by default.
690To override, please export the AUTHOR variable.
691
692=head1 SEE ALSO
693
694L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
695L<Catalyst::Response>, L<Catalyst>
696
697=head1 AUTHORS
698
699Catalyst Contributors, see Catalyst.pm
700
701=head1 LICENSE
702
703This library is free software. You can redistribute it and/or modify
704it under the same terms as Perl itself.
705
706=cut
707
7081;
709