1 package Catalyst::Helper;
5 use base 'Class::Accessor::Fast';
15 use Catalyst::Exception;
16 use Path::Class qw/dir file/;
17 use File::ShareDir qw/dist_dir/;
23 Catalyst::Helper - Bootstrap a Catalyst application
27 catalyst.pl <myappname>
31 sub get_sharedir_file {
32 my ($self, @filename) = @_;
33 my $file = file( dist_dir('Catalyst-Devel'), @filename);
35 my $contents = $file->slurp;
40 my ( $self, $file ) = @_;
42 return $self->get_sharedir_file($file);
46 my ( $self, $name ) = @_;
48 # Needs to be here for PAR
51 if ( $name =~ /[^\w:]/ || $name =~ /^\d/ || $name =~ /\b:\b|:{3,}/) {
52 warn "Error: Invalid application name.\n";
55 $self->{name } = $name;
56 $self->{dir } = $name;
57 $self->{dir } =~ s/\:\:/-/g;
58 $self->{script } = File::Spec->catdir( $self->{dir}, 'script' );
59 $self->{appprefix } = Catalyst::Utils::appprefix($name);
60 $self->{appenv } = Catalyst::Utils::class2env($name);
61 $self->{startperl } = -r '/usr/bin/env'
62 ? '#!/usr/bin/env perl'
63 : "#!$Config{perlpath} -w";
64 $self->{scriptgen } = $Catalyst::Devel::CATALYST_SCRIPT_GEN || 4;
65 $self->{catalyst_version} = $Catalyst::VERSION;
66 $self->{author } = $self->{author} = $ENV{'AUTHOR'}
67 || eval { @{ [ getpwuid($<) ] }[6] }
68 || 'Catalyst developer';
70 my $gen_scripts = ( $self->{makefile} ) ? 0 : 1;
71 my $gen_makefile = ( $self->{scripts} ) ? 0 : 1;
72 my $gen_app = ( $self->{scripts} || $self->{makefile} ) ? 0 : 1;
94 $self->_mk_information;
103 $self->{author} = $self->{author} = $ENV{'AUTHOR'}
104 || eval { @{ [ getpwuid($<) ] }[6] }
106 $self->{base} ||= File::Spec->catdir( $FindBin::Bin, '..' );
107 unless ( $_[0] =~ /^(?:model|view|controller)$/i ) {
110 my $class = "Catalyst::Helper::$helper";
111 eval "require $class";
114 Catalyst::Exception->throw(
115 message => qq/Couldn't load helper "$class", "$@"/ );
118 if ( $class->can('mk_stuff') ) {
119 return 1 unless $class->mk_stuff( $self, @args );
124 my $name = shift || "Missing name for model/view/controller";
127 return 0 if $name =~ /[^\w\:]/;
129 $self->{long_type} = ucfirst $type;
130 $type = 'M' if $type =~ /model/i;
131 $type = 'V' if $type =~ /view/i;
132 $type = 'C' if $type =~ /controller/i;
133 my $appdir = File::Spec->catdir( split /\:\:/, $app );
135 File::Spec->catdir( $FindBin::Bin, '..', 'lib', $appdir, 'C' );
136 $type = $self->{long_type} unless -d $test_path;
137 $self->{type} = $type;
138 $self->{name} = $name;
139 $self->{class} = "$app\::$type\::$name";
143 File::Spec->catdir( $FindBin::Bin, '..', 'lib', $appdir, $type );
145 if ( $name =~ /\:/ ) {
146 my @path = split /\:\:/, $name;
148 $path = File::Spec->catdir( $path, @path );
150 $self->mk_dir($path);
151 $file = File::Spec->catfile( $path, "$file.pm" );
152 $self->{file} = $file;
155 $self->{test_dir} = File::Spec->catdir( $FindBin::Bin, '..', 't' );
156 $self->{test} = $self->next_test;
160 my $comp = $self->{long_type};
161 my $class = "Catalyst::Helper::$comp\::$helper";
162 eval "require $class";
165 Catalyst::Exception->throw(
166 message => qq/Couldn't load helper "$class", "$@"/ );
169 if ( $class->can('mk_compclass') ) {
170 return 1 unless $class->mk_compclass( $self, @args );
172 else { return 1 unless $self->_mk_compclass }
174 if ( $class->can('mk_comptest') ) {
175 $class->mk_comptest( $self, @args );
177 else { $self->_mk_comptest }
182 return 1 unless $self->_mk_compclass;
190 my ( $self, $dir ) = @_;
192 print qq/ exists "$dir"\n/;
195 if ( mkpath [$dir] ) {
196 print qq/created "$dir"\n/;
200 Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
204 my ( $self, $file, $content ) = @_;
206 print qq/ exists "$file"\n/;
208 unless ( $self->{'.newfiles'}
210 || $self->{makefile} );
211 if ( $self->{'.newfiles'} ) {
212 if ( my $f = IO::File->new("< $file") ) {
213 my $oldcontent = join( '', (<$f>) );
214 return 0 if $content eq $oldcontent;
219 if ( my $f = IO::File->new("> $file") ) {
222 print qq/created "$file"\n/;
226 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
230 my ( $self, $tname ) = @_;
231 if ($tname) { $tname = "$tname.t" }
233 my $name = $self->{name};
237 $tname = $prefix . '.t';
238 $self->{prefix} = $prefix;
239 $prefix = lc $prefix;
241 $self->{uri} = "/$prefix";
243 my $dir = $self->{test_dir};
244 my $type = lc $self->{type};
246 return File::Spec->catfile( $dir, "$type\_$tname" );
250 my ( $self, $file, $path, $vars ) = @_;
252 my $t = Template->new;
253 my $template = $self->get_sharedir_file( 'root', $file );
254 return 0 unless $template;
256 $t->process( \$template, { %{$self}, %$vars }, \$output )
257 || Catalyst::Exception->throw(
258 message => qq/Couldn't process "$file", / . $t->error() );
259 $self->mk_file( $path, $output );
262 sub _mk_information {
264 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
269 $self->mk_dir( $self->{dir} );
270 $self->mk_dir( $self->{script} );
271 $self->{lib} = File::Spec->catdir( $self->{dir}, 'lib' );
272 $self->mk_dir( $self->{lib} );
273 $self->{root} = File::Spec->catdir( $self->{dir}, 'root' );
274 $self->mk_dir( $self->{root} );
275 $self->{static} = File::Spec->catdir( $self->{root}, 'static' );
276 $self->mk_dir( $self->{static} );
277 $self->{images} = File::Spec->catdir( $self->{static}, 'images' );
278 $self->mk_dir( $self->{images} );
279 $self->{t} = File::Spec->catdir( $self->{dir}, 't' );
280 $self->mk_dir( $self->{t} );
282 $self->{class} = File::Spec->catdir( split( /\:\:/, $self->{name} ) );
283 $self->{mod} = File::Spec->catdir( $self->{lib}, $self->{class} );
284 $self->mk_dir( $self->{mod} );
286 if ( $self->{short} ) {
287 $self->{m} = File::Spec->catdir( $self->{mod}, 'M' );
288 $self->mk_dir( $self->{m} );
289 $self->{v} = File::Spec->catdir( $self->{mod}, 'V' );
290 $self->mk_dir( $self->{v} );
291 $self->{c} = File::Spec->catdir( $self->{mod}, 'C' );
292 $self->mk_dir( $self->{c} );
295 $self->{m} = File::Spec->catdir( $self->{mod}, 'Model' );
296 $self->mk_dir( $self->{m} );
297 $self->{v} = File::Spec->catdir( $self->{mod}, 'View' );
298 $self->mk_dir( $self->{v} );
299 $self->{c} = File::Spec->catdir( $self->{mod}, 'Controller' );
300 $self->mk_dir( $self->{c} );
302 my $name = $self->{name};
304 $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
305 $self->{base} = File::Spec->rel2abs( $self->{dir} );
310 my $mod = $self->{mod};
311 $self->render_file( 'appclass.tt', "$mod.pm" );
316 $self->render_file( 'rootclass.tt',
317 File::Spec->catfile( $self->{c}, "Root.pm" ) );
322 $self->{path} = File::Spec->catfile( 'lib', split( '::', $self->{name} ) );
323 $self->{path} .= '.pm';
324 my $dir = $self->{dir};
325 $self->render_file( 'makefile.tt', "$dir\/Makefile.PL" );
327 if ( $self->{makefile} ) {
329 # deprecate the old Build.PL file when regenerating Makefile.PL
330 $self->_deprecate_file(
331 File::Spec->catdir( $self->{dir}, 'Build.PL' ) );
337 my $dir = $self->{dir};
338 my $appprefix = $self->{appprefix};
339 $self->render_file( 'config.tt',
340 File::Spec->catfile( $dir, "$appprefix.conf" ) );
345 my $dir = $self->{dir};
346 $self->render_file( 'readme.tt', "$dir\/README" );
351 my $dir = $self->{dir};
352 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
353 $self->render_file( 'changes.tt', "$dir\/Changes", { time => $time } );
359 $self->render_file( 'apptest.tt', "$t\/01app.t" );
360 $self->render_file( 'podtest.tt', "$t\/02pod.t" );
361 $self->render_file( 'podcoveragetest.tt', "$t\/03podcoverage.t" );
366 my $script = $self->{script};
367 my $appprefix = $self->{appprefix};
368 $self->render_file( 'cgi.tt', "$script\/$appprefix\_cgi.pl" );
369 chmod 0700, "$script/$appprefix\_cgi.pl";
374 my $script = $self->{script};
375 my $appprefix = $self->{appprefix};
376 $self->render_file( 'fastcgi.tt', "$script\/$appprefix\_fastcgi.pl" );
377 chmod 0700, "$script/$appprefix\_fastcgi.pl";
382 my $script = $self->{script};
383 my $appprefix = $self->{appprefix};
384 $self->render_file( 'server.tt', "$script\/$appprefix\_server.pl" );
385 chmod 0700, "$script/$appprefix\_server.pl";
390 my $script = $self->{script};
391 my $appprefix = $self->{appprefix};
392 $self->render_file( 'test.tt', "$script/$appprefix\_test.pl" );
393 chmod 0700, "$script/$appprefix\_test.pl";
398 my $script = $self->{script};
399 my $appprefix = $self->{appprefix};
400 $self->render_file( 'create.tt', "$script\/$appprefix\_create.pl" );
401 chmod 0700, "$script/$appprefix\_create.pl";
406 my $file = $self->{file};
407 return $self->render_file( 'compclass.tt', "$file" );
412 my $test = $self->{test};
413 $self->render_file( 'comptest.tt', "$test" );
418 my $images = $self->{images};
420 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
421 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
422 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
423 for my $name (@images) {
424 my $image = $self->get_file("$name.png");
425 $self->mk_file( File::Spec->catfile( $images, "$name.png" ), $image );
431 my $root = $self->{root};
432 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico' );
433 my $dest = File::Spec->catfile( $root, "favicon.ico" );
434 $self->mk_file( $dest, $favicon );
438 sub _deprecate_file {
439 my ( $self, $file ) = @_;
442 if ( my $f = IO::File->new("< $file") ) {
443 $oldcontent = join( '', (<$f>) );
445 my $newfile = $file . '.deprecated';
446 if ( my $f = IO::File->new("> $newfile") ) {
448 print $f $oldcontent;
449 print qq/created "$newfile"\n/;
451 print qq/removed "$file"\n/;
454 Catalyst::Exception->throw(
455 message => qq/Couldn't create "$file", "$!"/ );
461 This module is used by B<catalyst.pl> to create a set of scripts for a
462 new catalyst application. The scripts each contain documentation and
463 will output help on how to use them if called incorrectly or in some
464 cases, with no arguments.
466 It also provides some useful methods for a Helper module to call when
467 creating a component. See L</METHODS>.
473 Used to create new components for a catalyst application at the
478 The catalyst test server, starts an HTTPD which outputs debugging to
483 A script for running tests from the command-line.
487 Run your application as a CGI.
491 Run the application as a fastcgi app. Either by hand, or call this
492 from FastCgiServer in your http server config.
496 The L</_create.pl> script creates application components using Helper
497 modules. The Catalyst team provides a good number of Helper modules
498 for you to use. You can also add your own.
500 Helpers are classes that provide two methods.
502 * mk_compclass - creates the Component class
503 * mk_comptest - creates the Component test
505 So when you call C<scripts/myapp_create.pl view MyView TT>, create
506 will try to execute Catalyst::Helper::View::TT->mk_compclass and
507 Catalyst::Helper::View::TT->mk_comptest.
509 See L<Catalyst::Helper::View::TT> and
510 L<Catalyst::Helper::Model::DBIC::Schema> for examples.
512 All helper classes should be under one of the following namespaces.
514 Catalyst::Helper::Model::
515 Catalyst::Helper::View::
516 Catalyst::Helper::Controller::
518 =head2 COMMON HELPERS
524 L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
528 L<Catalyst::Helper::View::TT> - Template Toolkit view
532 L<Catalyst::Helper::Model::LDAP>
536 L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
542 The helpers will read author name from /etc/passwd by default. + To override, please export the AUTHOR variable.
548 This method in your Helper module is called with C<$helper>
549 which is a L<Catalyst::Helper> object, and whichever other arguments
550 the user added to the command-line. You can use the $helper to call methods
553 If the Helper module does not contain a C<mk_compclass> method, it
554 will fall back to calling L</render_file>, with an argument of
559 This method in your Helper module is called with C<$helper>
560 which is a L<Catalyst::Helper> object, and whichever other arguments
561 the user added to the command-line. You can use the $helper to call methods
564 If the Helper module does not contain a C<mk_compclass> method, it
565 will fall back to calling L</render_file>, with an argument of
570 This method is called if the user does not supply any of the usual
571 component types C<view>, C<controller>, C<model>. It is passed the
572 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
573 arguments the user typed.
575 There is no fallback for this method.
577 =head1 INTERNAL METHODS
579 These are the methods that the Helper classes can call on the
580 <$helper> object passed to them.
582 =head2 render_file ($file, $path, $vars)
584 Render and create a file from a template in DATA using Template
585 Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
586 the path to the file and $vars is the hashref as expected by
587 L<Template Toolkit|Template>.
589 =head2 get_file ($class, $file)
591 Fetch file contents from the DATA section. This is used internally by
592 L</render_file>. $class is the name of the class to get the DATA
593 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
598 Create the main application skeleton. This is called by L<catalyst.pl>.
600 =head2 mk_component ($app)
602 This method is called by L<create.pl> to make new components
603 for your application.
605 =head3 mk_dir ($path)
607 Surprisingly, this function makes a directory.
609 =head2 mk_file ($file, $content)
611 Writes content to a file. Called by L</render_file>.
613 =head2 next_test ($test_name)
615 Calculates the name of the next numbered test file and returns it.
616 Don't give the number or the .t suffix for the test name.
620 The helpers will read author name from /etc/passwd by default.
621 To override, please export the AUTHOR variable.
625 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
626 L<Catalyst::Response>, L<Catalyst>
630 Catalyst Contributors, see Catalyst.pm
634 This library is free software. You can redistribute it and/or modify
635 it under the same terms as Perl itself.