1 package Catalyst::Helper;
12 use Catalyst::Exception;
13 use Path::Class qw/dir file/;
14 use File::ShareDir qw/dist_dir/;
15 use namespace::autoclean;
21 Catalyst::Helper - Bootstrap a Catalyst application
25 catalyst.pl <myappname>
29 sub get_sharedir_file {
30 my ($self, @filename) = @_;
32 if (-d "inc/.author" && -f "lib/Catalyst/Helper.pm"
33 ) { # Can't use sharedir if we're in a checkout
34 # this feels horrible, better ideas?
38 $dist_dir = dist_dir('Catalyst-Devel');
40 my $file = file( $dist_dir, @filename);
41 Carp::confess("Cannot find $file") unless -r $file;
42 my $contents = $file->slurp;
46 # Do not touch this method, *EVER*, it is needed for back compat.
48 my ( $self, $class, $file ) = @_;
49 unless ( $cache{$class} ) {
51 $cache{$class} = eval "package $class; <DATA>";
53 my $data = $cache{$class};
54 Carp::confess("Could not get data from __DATA__ segment for $class")
56 my @files = split /^__(.+)__\r?\n/m, $data;
59 my ( $name, $content ) = splice @files, 0, 2;
60 return $content if $name eq $file;
67 my ( $self, $name ) = @_;
69 # Needs to be here for PAR
72 if ( $name =~ /[^\w:]/ || $name =~ /^\d/ || $name =~ /\b:\b|:{3,}/) {
73 warn "Error: Invalid application name.\n";
76 $self->{name } = $name;
77 $self->{dir } = $name;
78 $self->{dir } =~ s/\:\:/-/g;
79 $self->{script } = dir( $self->{dir}, 'script' );
80 $self->{appprefix } = Catalyst::Utils::appprefix($name);
81 $self->{appenv } = Catalyst::Utils::class2env($name);
82 $self->{startperl } = -r '/usr/bin/env'
83 ? '#!/usr/bin/env perl'
84 : "#!$Config{perlpath} -w";
85 $self->{scriptgen } = $Catalyst::Devel::CATALYST_SCRIPT_GEN || 34;
86 $self->{catalyst_version} = $Catalyst::VERSION;
87 $self->{author } = $self->{author} = $ENV{'AUTHOR'}
88 || eval { @{ [ getpwuid($<) ] }[6] }
89 || 'Catalyst developer';
91 my $gen_scripts = ( $self->{makefile} ) ? 0 : 1;
92 my $gen_makefile = ( $self->{scripts} ) ? 0 : 1;
93 my $gen_app = ( $self->{scripts} || $self->{makefile} ) ? 0 : 1;
96 for ( qw/ _mk_dirs _mk_config _mk_appclass _mk_rootclass _mk_readme
97 _mk_changes _mk_apptest _mk_images _mk_favicon/ ) {
106 for ( qw/ _mk_cgi _mk_fastcgi _mk_server
107 _mk_test _mk_create _mk_information
115 ## not much of this can really be changed, mk_compclass must be left for
121 $self->{author} = $self->{author} = $ENV{'AUTHOR'}
122 || eval { @{ [ getpwuid($<) ] }[6] }
124 $self->{base} ||= dir( $FindBin::Bin, '..' );
125 unless ( $_[0] =~ /^(?:model|view|controller)$/i ) {
128 my $class = "Catalyst::Helper::$helper";
129 eval "require $class";
132 Catalyst::Exception->throw(
133 message => qq/Couldn't load helper "$class", "$@"/ );
136 if ( $class->can('mk_stuff') ) {
137 return 1 unless $class->mk_stuff( $self, @args );
142 my $name = shift || "Missing name for model/view/controller";
145 return 0 if $name =~ /[^\w\:]/;
147 $self->{long_type} = ucfirst $type;
148 $type = 'M' if $type =~ /model/i;
149 $type = 'V' if $type =~ /view/i;
150 $type = 'C' if $type =~ /controller/i;
151 my $appdir = dir( split /\:\:/, $app );
153 dir( $self->{base}, 'lib', $appdir, 'C' );
154 $type = $self->{long_type} unless -d $test_path;
155 $self->{type} = $type;
156 $self->{name} = $name;
157 $self->{class} = "$app\::$type\::$name";
161 dir( $self->{base}, 'lib', $appdir, $type );
163 if ( $name =~ /\:/ ) {
164 my @path = split /\:\:/, $name;
166 $path = dir( $path, @path );
168 $self->mk_dir($path);
169 $file = file( $path, "$file.pm" );
170 $self->{file} = $file;
173 $self->{test_dir} = dir( $self->{base}, 't' );
174 $self->{test} = $self->next_test;
178 my $comp = $self->{long_type};
179 my $class = "Catalyst::Helper::$comp\::$helper";
180 eval "require $class";
183 Catalyst::Exception->throw(
184 message => qq/Couldn't load helper "$class", "$@"/ );
187 ## must be left for back compat! ###################################
188 if ( $class->can('mk_compclass') ) {
189 return 1 unless $class->mk_compclass( $self, @args );
191 else { return 1 unless $self->_mk_compclass }
193 if ( $class->can('mk_comptest') ) {
194 $class->mk_comptest( $self, @args );
196 else { $self->_mk_comptest }
197 ####################################################################
202 return 1 unless $self->_mk_compclass;
210 my ( $self, $dir ) = @_;
212 print qq/ exists "$dir"\n/;
215 if ( mkpath [$dir] ) {
216 print qq/created "$dir"\n/;
220 Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
224 my ( $self, $file, $content ) = @_;
225 if ( -e $file && -s _ ) {
226 print qq/ exists "$file"\n/;
228 unless ( $self->{'.newfiles'}
230 || $self->{makefile} );
231 if ( $self->{'.newfiles'} ) {
232 if ( my $f = IO::File->new("< $file") ) {
233 my $oldcontent = join( '', (<$f>) );
234 return 0 if $content eq $oldcontent;
240 if ( my $f = IO::File->new("> $file") ) {
243 print qq/created "$file"\n/;
247 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
251 my ( $self, $tname ) = @_;
252 if ($tname) { $tname = "$tname.t" }
254 my $name = $self->{name};
258 $tname = $prefix . '.t';
259 $self->{prefix} = $prefix;
260 $prefix = lc $prefix;
262 $self->{uri} = "/$prefix";
264 my $dir = $self->{test_dir};
265 my $type = lc $self->{type};
267 return file( $dir, "$type\_$tname" );
270 # Do not touch this method, *EVER*, it is needed for back compat.
271 ## addendum: we had to split this method so we could have backwards
272 ## compatability. otherwise, we'd have no way to pass stuff from __DATA__
275 my ( $self, $file, $path, $vars ) = @_;
276 my $template = $self->get_file( ( caller(0) )[0], $file );
277 $self->render_file_contents($template, $path, $vars);
280 sub render_sharedir_file {
281 my ( $self, $file, $path, $vars ) = @_;
282 my $template = $self->get_sharedir_file( $file );
283 die("Cannot get template from $file for $self\n") unless $template;
284 $self->render_file_contents($template, $path, $vars);
287 sub render_file_contents {
288 my ( $self, $template, $path, $vars ) = @_;
290 my $t = Template->new;
291 return 0 unless $template;
293 $t->process( \$template, { %{$self}, %$vars }, \$output )
294 || Catalyst::Exception->throw(
295 message => qq/Couldn't process "$template", / . $t->error() );
296 $self->mk_file( $path, $output );
299 sub _mk_information {
301 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
306 $self->mk_dir( $self->{dir} );
307 $self->mk_dir( $self->{script} );
308 $self->{lib} = dir( $self->{dir}, 'lib' );
309 $self->mk_dir( $self->{lib} );
310 $self->{root} = dir( $self->{dir}, 'root' );
311 $self->mk_dir( $self->{root} );
312 $self->{static} = dir( $self->{root}, 'static' );
313 $self->mk_dir( $self->{static} );
314 $self->{images} = dir( $self->{static}, 'images' );
315 $self->mk_dir( $self->{images} );
316 $self->{t} = dir( $self->{dir}, 't' );
317 $self->mk_dir( $self->{t} );
319 $self->{class} = dir( split( /\:\:/, $self->{name} ) );
320 $self->{mod} = dir( $self->{lib}, $self->{class} );
321 $self->mk_dir( $self->{mod} );
323 if ( $self->{short} ) {
324 $self->{m} = dir( $self->{mod}, 'M' );
325 $self->mk_dir( $self->{m} );
326 $self->{v} = dir( $self->{mod}, 'V' );
327 $self->mk_dir( $self->{v} );
328 $self->{c} = dir( $self->{mod}, 'C' );
329 $self->mk_dir( $self->{c} );
332 $self->{m} = dir( $self->{mod}, 'Model' );
333 $self->mk_dir( $self->{m} );
334 $self->{v} = dir( $self->{mod}, 'View' );
335 $self->mk_dir( $self->{v} );
336 $self->{c} = dir( $self->{mod}, 'Controller' );
337 $self->mk_dir( $self->{c} );
339 my $name = $self->{name};
341 $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
342 $self->{base} = dir( $self->{dir} )->absolute;
347 my $mod = $self->{mod};
348 $self->render_sharedir_file( file('lib', 'MyApp.pm.tt'), "$mod.pm" );
353 $self->render_sharedir_file( file('lib', 'MyApp', 'Controller', 'Root.pm.tt'),
354 file( $self->{c}, "Root.pm" ) );
359 $self->{path} = dir( 'lib', split( '::', $self->{name} ) );
360 $self->{path} .= '.pm';
361 my $dir = $self->{dir};
362 $self->render_sharedir_file( 'Makefile.PL.tt', file($dir, "Makefile.PL") );
364 if ( $self->{makefile} ) {
366 # deprecate the old Build.PL file when regenerating Makefile.PL
367 $self->_deprecate_file(
368 file( $self->{dir}, 'Build.PL' ) );
374 my $dir = $self->{dir};
375 my $appprefix = $self->{appprefix};
376 $self->render_sharedir_file( 'myapp.conf.tt',
377 file( $dir, "$appprefix.conf" ) );
382 my $dir = $self->{dir};
383 $self->render_sharedir_file( 'README.tt', file($dir, "README") );
388 my $dir = $self->{dir};
389 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
390 $self->render_sharedir_file( 'Changes.tt', file($dir, "Changes"), { time => $time } );
396 $self->render_sharedir_file( file('t', '01app.t.tt'), file($t, "01app.t") );
397 $self->render_sharedir_file( file('t', '02pod.t.tt'), file($t, "02pod.t") );
398 $self->render_sharedir_file( file('t', '03podcoverage.t.tt'), file($t, "03podcoverage.t") );
403 my $script = $self->{script};
404 my $appprefix = $self->{appprefix};
405 $self->render_sharedir_file( file('script', 'myapp_cgi.pl.tt'), file($script,"$appprefix\_cgi.pl") );
406 chmod 0700, file($script,"$appprefix\_cgi.pl");
411 my $script = $self->{script};
412 my $appprefix = $self->{appprefix};
413 $self->render_sharedir_file( file('script', 'myapp_fastcgi.pl.tt'), file($script, "$appprefix\_fastcgi.pl") );
414 chmod 0700, file($script, "$appprefix\_fastcgi.pl");
419 my $script = $self->{script};
420 my $appprefix = $self->{appprefix};
421 $self->render_sharedir_file( file('script', 'myapp_server.pl.tt'), file($script, "$appprefix\_server.pl") );
422 chmod 0700, file($script, "$appprefix\_server.pl");
427 my $script = $self->{script};
428 my $appprefix = $self->{appprefix};
429 $self->render_sharedir_file( file('script', 'myapp_test.pl.tt'), file($script, "$appprefix\_test.pl") );
430 chmod 0700, file($script, "$appprefix\_test.pl");
435 my $script = $self->{script};
436 my $appprefix = $self->{appprefix};
437 $self->render_sharedir_file( file('script', 'myapp_create.pl.tt'), file($script, "$appprefix\_create.pl") );
438 chmod 0700, file($script, "$appprefix\_create.pl");
443 my $file = $self->{file};
444 return $self->render_sharedir_file( file('lib', 'Helper', 'compclass.pm.tt'), $file );
449 my $test = $self->{test};
450 $self->render_sharedir_file( file('t', 'comptest.tt'), $test ); ## wtf do i rename this to?
455 my $images = $self->{images};
457 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
458 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
459 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
460 for my $name (@images) {
461 my $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin");
462 $self->mk_file( file( $images, "$name.png" ), $image );
468 my $root = $self->{root};
469 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' );
470 my $dest = dir( $root, "favicon.ico" );
471 $self->mk_file( $dest, $favicon );
475 sub _deprecate_file {
476 my ( $self, $file ) = @_;
478 my ($f, $oldcontent);
479 if ( $f = IO::File->new("< $file") ) {
480 $oldcontent = join( '', (<$f>) );
482 my $newfile = $file . '.deprecated';
483 if ( $f = IO::File->new("> $newfile") ) {
485 print $f $oldcontent;
486 print qq/created "$newfile"\n/;
488 print qq/removed "$file"\n/;
491 Catalyst::Exception->throw(
492 message => qq/Couldn't create "$file", "$!"/ );
498 This module is used by B<catalyst.pl> to create a set of scripts for a
499 new catalyst application. The scripts each contain documentation and
500 will output help on how to use them if called incorrectly or in some
501 cases, with no arguments.
503 It also provides some useful methods for a Helper module to call when
504 creating a component. See L</METHODS>.
510 Used to create new components for a catalyst application at the
515 The catalyst test server, starts an HTTPD which outputs debugging to
520 A script for running tests from the command-line.
524 Run your application as a CGI.
528 Run the application as a fastcgi app. Either by hand, or call this
529 from FastCgiServer in your http server config.
533 The L</_create.pl> script creates application components using Helper
534 modules. The Catalyst team provides a good number of Helper modules
535 for you to use. You can also add your own.
537 Helpers are classes that provide two methods.
539 * mk_compclass - creates the Component class
540 * mk_comptest - creates the Component test
542 So when you call C<scripts/myapp_create.pl view MyView TT>, create
543 will try to execute Catalyst::Helper::View::TT->mk_compclass and
544 Catalyst::Helper::View::TT->mk_comptest.
546 See L<Catalyst::Helper::View::TT> and
547 L<Catalyst::Helper::Model::DBIC::Schema> for examples.
549 All helper classes should be under one of the following namespaces.
551 Catalyst::Helper::Model::
552 Catalyst::Helper::View::
553 Catalyst::Helper::Controller::
555 =head2 COMMON HELPERS
561 L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
565 L<Catalyst::Helper::View::TT> - Template Toolkit view
569 L<Catalyst::Helper::Model::LDAP>
573 L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
579 The helpers will read author name from /etc/passwd by default.
580 To override, please export the AUTHOR variable.
586 This method in your Helper module is called with C<$helper>
587 which is a L<Catalyst::Helper> object, and whichever other arguments
588 the user added to the command-line. You can use the $helper to call methods
591 If the Helper module does not contain a C<mk_compclass> method, it
592 will fall back to calling L</render_file>, with an argument of
597 This method in your Helper module is called with C<$helper>
598 which is a L<Catalyst::Helper> object, and whichever other arguments
599 the user added to the command-line. You can use the $helper to call methods
602 If the Helper module does not contain a C<mk_compclass> method, it
603 will fall back to calling L</render_file>, with an argument of
608 This method is called if the user does not supply any of the usual
609 component types C<view>, C<controller>, C<model>. It is passed the
610 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
611 arguments the user typed.
613 There is no fallback for this method.
615 =head1 INTERNAL METHODS
617 These are the methods that the Helper classes can call on the
618 <$helper> object passed to them.
620 =head2 render_file ($file, $path, $vars)
622 Render and create a file from a template in DATA using Template
623 Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
624 the path to the file and $vars is the hashref as expected by
625 L<Template Toolkit|Template>.
627 =head2 get_file ($class, $file)
629 Fetch file contents from the DATA section. This is used internally by
630 L</render_file>. $class is the name of the class to get the DATA
631 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
636 Create the main application skeleton. This is called by L<catalyst.pl>.
638 =head2 mk_component ($app)
640 This method is called by L<create.pl> to make new components
641 for your application.
643 =head3 mk_dir ($path)
645 Surprisingly, this function makes a directory.
647 =head2 mk_file ($file, $content)
649 Writes content to a file. Called by L</render_file>.
651 =head2 next_test ($test_name)
653 Calculates the name of the next numbered test file and returns it.
654 Don't give the number or the .t suffix for the test name.
658 =head2 get_sharedir_file
660 Method for getting a file out of share/
664 =head2 render_file_contents
666 Process a L<Template::Toolkit> template.
670 =head2 render_sharedir_file
672 Render a template/image file from our share directory
678 The helpers will read author name from /etc/passwd by default.
679 To override, please export the AUTHOR variable.
683 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
684 L<Catalyst::Response>, L<Catalyst>
688 Catalyst Contributors, see Catalyst.pm
692 This library is free software. You can redistribute it and/or modify
693 it under the same terms as Perl itself.