1 package Catalyst::Helper;
13 use Catalyst::Exception;
14 use Path::Class qw/dir file/;
15 use File::ShareDir qw/dist_dir/;
16 use namespace::autoclean;
22 Catalyst::Helper - Bootstrap a Catalyst application
26 catalyst.pl <myappname>
30 sub get_sharedir_file {
31 my ($self, @filename) = @_;
33 if (-d "inc/.author" && -f "lib/Catalyst/Helper.pm"
34 ) { # Can't use sharedir if we're in a checkout
35 # this feels horrible, better ideas?
39 $dist_dir = dist_dir('Catalyst-Devel');
41 my $file = file( $dist_dir, @filename);
42 Carp::confess("Cannot find $file") unless -r $file;
43 my $contents = $file->slurp;
47 # Do not touch this method, *EVER*, it is needed for back compat.
49 my ( $self, $class, $file ) = @_;
50 unless ( $cache{$class} ) {
52 $cache{$class} = eval "package $class; <DATA>";
54 my $data = $cache{$class};
55 Carp::confess("Could not get data from __DATA__ segment for $class")
57 my @files = split /^__(.+)__\r?\n/m, $data;
60 my ( $name, $content ) = splice @files, 0, 2;
61 return $content if $name eq $file;
68 my ( $self, $name ) = @_;
70 # Needs to be here for PAR
73 if ( $name =~ /[^\w:]/ || $name =~ /^\d/ || $name =~ /\b:\b|:{3,}/) {
74 warn "Error: Invalid application name.\n";
77 $self->{name } = $name;
78 $self->{dir } = $name;
79 $self->{dir } =~ s/\:\:/-/g;
80 $self->{script } = dir( $self->{dir}, 'script' );
81 $self->{appprefix } = Catalyst::Utils::appprefix($name);
82 $self->{appenv } = Catalyst::Utils::class2env($name);
83 $self->{startperl } = -r '/usr/bin/env'
84 ? '#!/usr/bin/env perl'
85 : "#!$Config{perlpath} -w";
86 $self->{scriptgen } = $Catalyst::Devel::CATALYST_SCRIPT_GEN;
87 $self->{catalyst_version} = $Catalyst::VERSION;
88 $self->{author } = $self->{author} = $ENV{'AUTHOR'}
89 || eval { @{ [ getpwuid($<) ] }[6] }
90 || 'Catalyst developer';
92 my $gen_scripts = ( $self->{makefile} ) ? 0 : 1;
93 my $gen_makefile = ( $self->{scripts} ) ? 0 : 1;
94 my $gen_app = ( $self->{scripts} || $self->{makefile} ) ? 0 : 1;
97 for ( qw/ _mk_dirs _mk_config _mk_appclass _mk_rootclass _mk_readme
98 _mk_changes _mk_apptest _mk_images _mk_favicon/ ) {
107 for ( qw/ _mk_cgi _mk_fastcgi _mk_server
108 _mk_test _mk_create _mk_information
116 ## not much of this can really be changed, mk_compclass must be left for
122 $self->{author} = $self->{author} = $ENV{'AUTHOR'}
123 || eval { @{ [ getpwuid($<) ] }[6] }
125 $self->{base} ||= dir( $FindBin::Bin, '..' );
126 unless ( $_[0] =~ /^(?:model|view|controller)$/i ) {
129 my $class = "Catalyst::Helper::$helper";
130 eval "require $class";
133 Catalyst::Exception->throw(
134 message => qq/Couldn't load helper "$class", "$@"/ );
137 if ( $class->can('mk_stuff') ) {
138 return 1 unless $class->mk_stuff( $self, @args );
143 my $name = shift || "Missing name for model/view/controller";
146 return 0 if $name =~ /[^\w\:]/;
148 $self->{long_type} = ucfirst $type;
149 $type = 'M' if $type =~ /model/i;
150 $type = 'V' if $type =~ /view/i;
151 $type = 'C' if $type =~ /controller/i;
152 my $appdir = dir( split /\:\:/, $app );
154 dir( $self->{base}, 'lib', $appdir, 'C' );
155 $type = $self->{long_type} unless -d $test_path;
156 $self->{type} = $type;
157 $self->{name} = $name;
158 $self->{class} = "$app\::$type\::$name";
162 dir( $self->{base}, 'lib', $appdir, $type );
164 if ( $name =~ /\:/ ) {
165 my @path = split /\:\:/, $name;
167 $path = dir( $path, @path );
169 $self->mk_dir($path);
170 $file = file( $path, "$file.pm" );
171 $self->{file} = $file;
174 $self->{test_dir} = dir( $self->{base}, 't' );
175 $self->{test} = $self->next_test;
179 my $comp = $self->{long_type};
180 my $class = "Catalyst::Helper::$comp\::$helper";
181 eval "require $class";
184 Catalyst::Exception->throw(
185 message => qq/Couldn't load helper "$class", "$@"/ );
188 if ( $class->can('mk_compclass') ) {
189 return 1 unless $class->mk_compclass( $self, @args );
192 return 1 unless $self->_mk_compclass
195 if ( $class->can('mk_comptest') ) {
196 $class->mk_comptest( $self, @args );
205 return 1 unless $self->_mk_compclass;
213 my ( $self, $dir ) = @_;
215 print qq/ exists "$dir"\n/;
218 if ( mkpath [$dir] ) {
219 print qq/created "$dir"\n/;
223 Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
227 my ( $self, $file, $content ) = @_;
228 if ( -e $file && -s _ ) {
229 print qq/ exists "$file"\n/;
231 unless ( $self->{'.newfiles'}
233 || $self->{makefile} );
234 if ( $self->{'.newfiles'} ) {
235 if ( my $f = IO::File->new("< $file") ) {
236 my $oldcontent = join( '', (<$f>) );
237 return 0 if $content eq $oldcontent;
243 if ( my $f = IO::File->new("> $file") ) {
246 print qq/created "$file"\n/;
250 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
254 my ( $self, $tname ) = @_;
255 if ($tname) { $tname = "$tname.t" }
257 my $name = $self->{name};
261 $tname = $prefix . '.t';
262 $self->{prefix} = $prefix;
263 $prefix = lc $prefix;
265 $self->{uri} = "/$prefix";
267 my $dir = $self->{test_dir};
268 my $type = lc $self->{type};
270 return file( $dir, "$type\_$tname" );
273 # Do not touch this method, *EVER*, it is needed for back compat.
274 ## addendum: we had to split this method so we could have backwards
275 ## compatability. otherwise, we'd have no way to pass stuff from __DATA__
278 my ( $self, $file, $path, $vars ) = @_;
279 my $template = $self->get_file( ( caller(0) )[0], $file );
280 $self->render_file_contents($template, $path, $vars);
283 sub render_sharedir_file {
284 my ( $self, $file, $path, $vars ) = @_;
285 my $template = $self->get_sharedir_file( $file );
286 die("Cannot get template from $file for $self\n") unless $template;
287 $self->render_file_contents($template, $path, $vars);
290 sub render_file_contents {
291 my ( $self, $template, $path, $vars ) = @_;
293 my $t = Template->new;
294 return 0 unless $template;
296 $t->process( \$template, { %{$self}, %$vars }, \$output )
297 || Catalyst::Exception->throw(
298 message => qq/Couldn't process "$template", / . $t->error() );
299 $self->mk_file( $path, $output );
302 sub _mk_information {
304 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
309 $self->mk_dir( $self->{dir} );
310 $self->mk_dir( $self->{script} );
311 $self->{lib} = dir( $self->{dir}, 'lib' );
312 $self->mk_dir( $self->{lib} );
313 $self->{root} = dir( $self->{dir}, 'root' );
314 $self->mk_dir( $self->{root} );
315 $self->{static} = dir( $self->{root}, 'static' );
316 $self->mk_dir( $self->{static} );
317 $self->{images} = dir( $self->{static}, 'images' );
318 $self->mk_dir( $self->{images} );
319 $self->{t} = dir( $self->{dir}, 't' );
320 $self->mk_dir( $self->{t} );
322 $self->{class} = dir( split( /\:\:/, $self->{name} ) );
323 $self->{mod} = dir( $self->{lib}, $self->{class} );
324 $self->mk_dir( $self->{mod} );
326 if ( $self->{short} ) {
327 $self->{m} = dir( $self->{mod}, 'M' );
328 $self->mk_dir( $self->{m} );
329 $self->{v} = dir( $self->{mod}, 'V' );
330 $self->mk_dir( $self->{v} );
331 $self->{c} = dir( $self->{mod}, 'C' );
332 $self->mk_dir( $self->{c} );
335 $self->{m} = dir( $self->{mod}, 'Model' );
336 $self->mk_dir( $self->{m} );
337 $self->{v} = dir( $self->{mod}, 'View' );
338 $self->mk_dir( $self->{v} );
339 $self->{c} = dir( $self->{mod}, 'Controller' );
340 $self->mk_dir( $self->{c} );
342 my $name = $self->{name};
344 $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
345 $self->{base} = dir( $self->{dir} )->absolute;
350 my $mod = $self->{mod};
351 $self->render_sharedir_file( file('lib', 'MyApp.pm.tt'), "$mod.pm" );
356 $self->render_sharedir_file( file('lib', 'MyApp', 'Controller', 'Root.pm.tt'),
357 file( $self->{c}, "Root.pm" ) );
362 $self->{path} = dir( 'lib', split( '::', $self->{name} ) );
363 $self->{path} .= '.pm';
364 my $dir = $self->{dir};
365 $self->render_sharedir_file( 'Makefile.PL.tt', file($dir, "Makefile.PL") );
367 if ( $self->{makefile} ) {
369 # deprecate the old Build.PL file when regenerating Makefile.PL
370 $self->_deprecate_file(
371 file( $self->{dir}, 'Build.PL' ) );
377 my $dir = $self->{dir};
378 my $appprefix = $self->{appprefix};
379 $self->render_sharedir_file( 'myapp.conf.tt',
380 file( $dir, "$appprefix.conf" ) );
385 my $dir = $self->{dir};
386 $self->render_sharedir_file( 'README.tt', file($dir, "README") );
391 my $dir = $self->{dir};
392 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
393 $self->render_sharedir_file( 'Changes.tt', file($dir, "Changes"), { time => $time } );
399 $self->render_sharedir_file( file('t', '01app.t.tt'), file($t, "01app.t") );
400 $self->render_sharedir_file( file('t', '02pod.t.tt'), file($t, "02pod.t") );
401 $self->render_sharedir_file( file('t', '03podcoverage.t.tt'), file($t, "03podcoverage.t") );
406 my $script = $self->{script};
407 my $appprefix = $self->{appprefix};
408 $self->render_sharedir_file( file('script', 'myapp_cgi.pl.tt'), file($script,"$appprefix\_cgi.pl") );
409 chmod 0700, file($script,"$appprefix\_cgi.pl");
414 my $script = $self->{script};
415 my $appprefix = $self->{appprefix};
416 $self->render_sharedir_file( file('script', 'myapp_fastcgi.pl.tt'), file($script, "$appprefix\_fastcgi.pl") );
417 chmod 0700, file($script, "$appprefix\_fastcgi.pl");
422 my $script = $self->{script};
423 my $appprefix = $self->{appprefix};
424 $self->render_sharedir_file( file('script', 'myapp_server.pl.tt'), file($script, "$appprefix\_server.pl") );
425 chmod 0700, file($script, "$appprefix\_server.pl");
430 my $script = $self->{script};
431 my $appprefix = $self->{appprefix};
432 $self->render_sharedir_file( file('script', 'myapp_test.pl.tt'), file($script, "$appprefix\_test.pl") );
433 chmod 0700, file($script, "$appprefix\_test.pl");
438 my $script = $self->{script};
439 my $appprefix = $self->{appprefix};
440 $self->render_sharedir_file( file('script', 'myapp_create.pl.tt'), file($script, "$appprefix\_create.pl") );
441 chmod 0700, file($script, "$appprefix\_create.pl");
446 my $file = $self->{file};
447 return $self->render_sharedir_file( file('lib', 'Helper', 'compclass.pm.tt'), $file );
452 my $test = $self->{test};
453 $self->render_sharedir_file( file('t', 'comptest.tt'), $test ); ## wtf do i rename this to?
458 my $images = $self->{images};
460 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
461 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
462 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
463 for my $name (@images) {
464 my $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin");
465 $self->mk_file( file( $images, "$name.png" ), $image );
471 my $root = $self->{root};
472 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' );
473 my $dest = dir( $root, "favicon.ico" );
474 $self->mk_file( $dest, $favicon );
478 sub _deprecate_file {
479 my ( $self, $file ) = @_;
481 my ($f, $oldcontent);
482 if ( $f = IO::File->new("< $file") ) {
483 $oldcontent = join( '', (<$f>) );
485 my $newfile = $file . '.deprecated';
486 if ( $f = IO::File->new("> $newfile") ) {
488 print $f $oldcontent;
489 print qq/created "$newfile"\n/;
491 print qq/removed "$file"\n/;
494 Catalyst::Exception->throw(
495 message => qq/Couldn't create "$file", "$!"/ );
501 This module is used by B<catalyst.pl> to create a set of scripts for a
502 new catalyst application. The scripts each contain documentation and
503 will output help on how to use them if called incorrectly or in some
504 cases, with no arguments.
506 It also provides some useful methods for a Helper module to call when
507 creating a component. See L</METHODS>.
513 Used to create new components for a catalyst application at the
518 The catalyst test server, starts an HTTPD which outputs debugging to
523 A script for running tests from the command-line.
527 Run your application as a CGI.
531 Run the application as a fastcgi app. Either by hand, or call this
532 from FastCgiServer in your http server config.
536 The L</_create.pl> script creates application components using Helper
537 modules. The Catalyst team provides a good number of Helper modules
538 for you to use. You can also add your own.
540 Helpers are classes that provide two methods.
542 * mk_compclass - creates the Component class
543 * mk_comptest - creates the Component test
545 So when you call C<scripts/myapp_create.pl view MyView TT>, create
546 will try to execute Catalyst::Helper::View::TT->mk_compclass and
547 Catalyst::Helper::View::TT->mk_comptest.
549 See L<Catalyst::Helper::View::TT> and
550 L<Catalyst::Helper::Model::DBIC::Schema> for examples.
552 All helper classes should be under one of the following namespaces.
554 Catalyst::Helper::Model::
555 Catalyst::Helper::View::
556 Catalyst::Helper::Controller::
558 =head2 COMMON HELPERS
564 L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
568 L<Catalyst::Helper::View::TT> - Template Toolkit view
572 L<Catalyst::Helper::Model::LDAP>
576 L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
582 The helpers will read author name from /etc/passwd by default.
583 To override, please export the AUTHOR variable.
589 This method in your Helper module is called with C<$helper>
590 which is a L<Catalyst::Helper> object, and whichever other arguments
591 the user added to the command-line. You can use the $helper to call methods
594 If the Helper module does not contain a C<mk_compclass> method, it
595 will fall back to calling L</render_file>, with an argument of
600 This method in your Helper module is called with C<$helper>
601 which is a L<Catalyst::Helper> object, and whichever other arguments
602 the user added to the command-line. You can use the $helper to call methods
605 If the Helper module does not contain a C<mk_compclass> method, it
606 will fall back to calling L</render_file>, with an argument of
611 This method is called if the user does not supply any of the usual
612 component types C<view>, C<controller>, C<model>. It is passed the
613 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
614 arguments the user typed.
616 There is no fallback for this method.
618 =head1 INTERNAL METHODS
620 These are the methods that the Helper classes can call on the
621 <$helper> object passed to them.
623 =head2 render_file ($file, $path, $vars)
625 Render and create a file from a template in DATA using Template
626 Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
627 the path to the file and $vars is the hashref as expected by
628 L<Template Toolkit|Template>.
630 =head2 get_file ($class, $file)
632 Fetch file contents from the DATA section. This is used internally by
633 L</render_file>. $class is the name of the class to get the DATA
634 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
639 Create the main application skeleton. This is called by L<catalyst.pl>.
641 =head2 mk_component ($app)
643 This method is called by L<create.pl> to make new components
644 for your application.
646 =head2 mk_dir ($path)
648 Surprisingly, this function makes a directory.
650 =head2 mk_file ($file, $content)
652 Writes content to a file. Called by L</render_file>.
654 =head2 next_test ($test_name)
656 Calculates the name of the next numbered test file and returns it.
657 Don't give the number or the .t suffix for the test name.
661 =head2 get_sharedir_file
663 Method for getting a file out of share/
667 =head2 render_file_contents
669 Process a L<Template::Toolkit> template.
673 =head2 render_sharedir_file
675 Render a template/image file from our share directory
681 The helpers will read author name from /etc/passwd by default.
682 To override, please export the AUTHOR variable.
686 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
687 L<Catalyst::Response>, L<Catalyst>
691 Catalyst Contributors, see Catalyst.pm
695 This library is free software. You can redistribute it and/or modify
696 it under the same terms as Perl itself.