1 package Catalyst::Helper;
13 use Catalyst::Exception;
14 use Path::Class qw/dir file/;
15 use File::ShareDir qw/dist_dir/;
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 / ) {
114 ## not much of this can really be changed, mk_compclass must be left for
120 $self->{author} = $self->{author} = $ENV{'AUTHOR'}
121 || eval { @{ [ getpwuid($<) ] }[6] }
123 $self->{base} ||= dir( $FindBin::Bin, '..' );
124 unless ( $_[0] =~ /^(?:model|view|controller)$/i ) {
127 my $class = "Catalyst::Helper::$helper";
128 eval "require $class";
131 Catalyst::Exception->throw(
132 message => qq/Couldn't load helper "$class", "$@"/ );
135 if ( $class->can('mk_stuff') ) {
136 return 1 unless $class->mk_stuff( $self, @args );
141 my $name = shift || "Missing name for model/view/controller";
144 return 0 if $name =~ /[^\w\:]/;
146 $self->{long_type} = ucfirst $type;
147 $type = 'M' if $type =~ /model/i;
148 $type = 'V' if $type =~ /view/i;
149 $type = 'C' if $type =~ /controller/i;
150 my $appdir = dir( split /\:\:/, $app );
152 dir( $self->{base}, 'lib', $appdir, 'C' );
153 $type = $self->{long_type} unless -d $test_path;
154 $self->{type} = $type;
155 $self->{name} = $name;
156 $self->{class} = "$app\::$type\::$name";
160 dir( $self->{base}, 'lib', $appdir, $type );
162 if ( $name =~ /\:/ ) {
163 my @path = split /\:\:/, $name;
165 $path = dir( $path, @path );
167 $self->mk_dir($path);
168 $file = file( $path, "$file.pm" );
169 $self->{file} = $file;
172 $self->{test_dir} = dir( $self->{base}, 't' );
173 $self->{test} = $self->next_test;
177 my $comp = $self->{long_type};
178 my $class = "Catalyst::Helper::$comp\::$helper";
179 eval "require $class";
182 Catalyst::Exception->throw(
183 message => qq/Couldn't load helper "$class", "$@"/ );
186 ## must be left for back compat! ###################################
187 if ( $class->can('mk_compclass') ) {
188 return 1 unless $class->mk_compclass( $self, @args );
190 else { return 1 unless $self->_mk_compclass }
192 if ( $class->can('mk_comptest') ) {
193 $class->mk_comptest( $self, @args );
195 else { $self->_mk_comptest }
196 ####################################################################
201 return 1 unless $self->_mk_compclass;
209 my ( $self, $dir ) = @_;
211 print qq/ exists "$dir"\n/;
214 if ( mkpath [$dir] ) {
215 print qq/created "$dir"\n/;
219 Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
223 my ( $self, $file, $content ) = @_;
224 if ( -e $file && -s _ ) {
225 print qq/ exists "$file"\n/;
227 unless ( $self->{'.newfiles'}
229 || $self->{makefile} );
230 if ( $self->{'.newfiles'} ) {
231 if ( my $f = IO::File->new("< $file") ) {
232 my $oldcontent = join( '', (<$f>) );
233 return 0 if $content eq $oldcontent;
239 if ( my $f = IO::File->new("> $file") ) {
242 print qq/created "$file"\n/;
246 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
250 my ( $self, $tname ) = @_;
251 if ($tname) { $tname = "$tname.t" }
253 my $name = $self->{name};
257 $tname = $prefix . '.t';
258 $self->{prefix} = $prefix;
259 $prefix = lc $prefix;
261 $self->{uri} = "/$prefix";
263 my $dir = $self->{test_dir};
264 my $type = lc $self->{type};
266 return file( $dir, "$type\_$tname" );
269 # Do not touch this method, *EVER*, it is needed for back compat.
270 ## addendum: we had to split this method so we could have backwards
271 ## compatability. otherwise, we'd have no way to pass stuff from __DATA__
274 my ( $self, $file, $path, $vars ) = @_;
275 my $template = $self->get_file( ( caller(0) )[0], $file );
276 $self->render_file_contents($template, $path, $vars);
279 sub render_sharedir_file {
280 my ( $self, $file, $path, $vars ) = @_;
281 my $template = $self->get_sharedir_file( $file );
282 die("Cannot get template from $file for $self\n") unless $template;
283 $self->render_file_contents($template, $path, $vars);
286 sub render_file_contents {
287 my ( $self, $template, $path, $vars ) = @_;
289 my $t = Template->new;
290 return 0 unless $template;
292 $t->process( \$template, { %{$self}, %$vars }, \$output )
293 || Catalyst::Exception->throw(
294 message => qq/Couldn't process "$template", / . $t->error() );
295 $self->mk_file( $path, $output );
298 sub _mk_information {
300 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
305 $self->mk_dir( $self->{dir} );
306 $self->mk_dir( $self->{script} );
307 $self->{lib} = dir( $self->{dir}, 'lib' );
308 $self->mk_dir( $self->{lib} );
309 $self->{root} = dir( $self->{dir}, 'root' );
310 $self->mk_dir( $self->{root} );
311 $self->{static} = dir( $self->{root}, 'static' );
312 $self->mk_dir( $self->{static} );
313 $self->{images} = dir( $self->{static}, 'images' );
314 $self->mk_dir( $self->{images} );
315 $self->{t} = dir( $self->{dir}, 't' );
316 $self->mk_dir( $self->{t} );
318 $self->{class} = dir( split( /\:\:/, $self->{name} ) );
319 $self->{mod} = dir( $self->{lib}, $self->{class} );
320 $self->mk_dir( $self->{mod} );
322 if ( $self->{short} ) {
323 $self->{m} = dir( $self->{mod}, 'M' );
324 $self->mk_dir( $self->{m} );
325 $self->{v} = dir( $self->{mod}, 'V' );
326 $self->mk_dir( $self->{v} );
327 $self->{c} = dir( $self->{mod}, 'C' );
328 $self->mk_dir( $self->{c} );
331 $self->{m} = dir( $self->{mod}, 'Model' );
332 $self->mk_dir( $self->{m} );
333 $self->{v} = dir( $self->{mod}, 'View' );
334 $self->mk_dir( $self->{v} );
335 $self->{c} = dir( $self->{mod}, 'Controller' );
336 $self->mk_dir( $self->{c} );
338 my $name = $self->{name};
340 $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
341 $self->{base} = dir( $self->{dir} )->absolute;
346 my $mod = $self->{mod};
347 $self->render_sharedir_file( file('lib', 'MyApp.pm.tt'), "$mod.pm" );
352 $self->render_sharedir_file( file('lib', 'MyApp', 'Controller', 'Root.pm.tt'),
353 file( $self->{c}, "Root.pm" ) );
358 $self->{path} = dir( 'lib', split( '::', $self->{name} ) );
359 $self->{path} .= '.pm';
360 my $dir = $self->{dir};
361 $self->render_sharedir_file( 'Makefile.PL.tt', file($dir, "Makefile.PL") );
363 if ( $self->{makefile} ) {
365 # deprecate the old Build.PL file when regenerating Makefile.PL
366 $self->_deprecate_file(
367 file( $self->{dir}, 'Build.PL' ) );
373 my $dir = $self->{dir};
374 my $appprefix = $self->{appprefix};
375 $self->render_sharedir_file( 'myapp.conf.tt',
376 file( $dir, "$appprefix.conf" ) );
381 my $dir = $self->{dir};
382 $self->render_sharedir_file( 'README.tt', file($dir, "README") );
387 my $dir = $self->{dir};
388 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
389 $self->render_sharedir_file( 'Changes.tt', file($dir, "Changes", { time => $time } );
395 $self->render_sharedir_file( file('t', '01app.t.tt'), file($t, "01app.t") );
396 $self->render_sharedir_file( file('t', '02pod.t.tt'), file($t, "02pod.t") );
397 $self->render_sharedir_file( file('t', '03podcoverage.t.tt'), file($t, "03podcoverage.t") );
402 my $script = $self->{script};
403 my $appprefix = $self->{appprefix};
404 $self->render_sharedir_file( file('script', 'myapp_cgi.pl.tt'), file($script,"$appprefix\_cgi.pl") );
405 chmod 0700, file($script,"$appprefix\_cgi.pl");
410 my $script = $self->{script};
411 my $appprefix = $self->{appprefix};
412 $self->render_sharedir_file( file('script', 'myapp_fastcgi.pl.tt'), file($script, "$appprefix\_fastcgi.pl") );
413 chmod 0700, file($script, "$appprefix\_fastcgi.pl");
418 my $script = $self->{script};
419 my $appprefix = $self->{appprefix};
420 $self->render_sharedir_file( file('script', 'myapp_server.pl.tt'), file($script, "$appprefix\_server.pl") );
421 chmod 0700, file($script, "$appprefix\_server.pl");
426 my $script = $self->{script};
427 my $appprefix = $self->{appprefix};
428 $self->render_sharedir_file( file('script', 'myapp_test.pl.tt'), file($script, "$appprefix\_test.pl") );
429 chmod 0700, file($script, "$appprefix\_test.pl");
434 my $script = $self->{script};
435 my $appprefix = $self->{appprefix};
436 $self->render_sharedir_file( file('script', 'myapp_create.pl.tt'), file($script, "$appprefix\_create.pl") );
437 chmod 0700, file($script, "$appprefix\_create.pl");
442 my $file = $self->{file};
443 return $self->render_sharedir_file( file('lib', 'Helper', 'compclass.pm.tt'), $file );
448 my $test = $self->{test};
449 $self->render_sharedir_file( file('t', 'comptest.tt'), $test ); ## wtf do i rename this to?
454 my $images = $self->{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 $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin");
461 $self->mk_file( file( $images, "$name.png" ), $image );
467 my $root = $self->{root};
468 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' );
469 my $dest = dir( $root, "favicon.ico" );
470 $self->mk_file( $dest, $favicon );
474 sub _mk_dbic_deploy {
476 my $script = $self->{script};
477 my $appprefix = $self->{appprefix};
478 $self->render_sharedir_file( file('script', 'myapp_deploy_schema.pl.tt'), file($script, "$appprefix\_deploy_schema.pl") );
479 chmod 0700, file($script, "$appprefix\_deploy_schema.pl");
482 sub _deprecate_file {
483 my ( $self, $file ) = @_;
486 if ( my $f = IO::File->new("< $file") ) {
487 $oldcontent = join( '', (<$f>) );
489 my $newfile = $file . '.deprecated';
490 if ( my $f = IO::File->new("> $newfile") ) {
492 print $f $oldcontent;
493 print qq/created "$newfile"\n/;
495 print qq/removed "$file"\n/;
498 Catalyst::Exception->throw(
499 message => qq/Couldn't create "$file", "$!"/ );
505 This module is used by B<catalyst.pl> to create a set of scripts for a
506 new catalyst application. The scripts each contain documentation and
507 will output help on how to use them if called incorrectly or in some
508 cases, with no arguments.
510 It also provides some useful methods for a Helper module to call when
511 creating a component. See L</METHODS>.
517 Used to create new components for a catalyst application at the
522 The catalyst test server, starts an HTTPD which outputs debugging to
527 A script for running tests from the command-line.
531 Run your application as a CGI.
535 Run the application as a fastcgi app. Either by hand, or call this
536 from FastCgiServer in your http server config.
540 The L</_create.pl> script creates application components using Helper
541 modules. The Catalyst team provides a good number of Helper modules
542 for you to use. You can also add your own.
544 Helpers are classes that provide two methods.
546 * mk_compclass - creates the Component class
547 * mk_comptest - creates the Component test
549 So when you call C<scripts/myapp_create.pl view MyView TT>, create
550 will try to execute Catalyst::Helper::View::TT->mk_compclass and
551 Catalyst::Helper::View::TT->mk_comptest.
553 See L<Catalyst::Helper::View::TT> and
554 L<Catalyst::Helper::Model::DBIC::Schema> for examples.
556 All helper classes should be under one of the following namespaces.
558 Catalyst::Helper::Model::
559 Catalyst::Helper::View::
560 Catalyst::Helper::Controller::
562 =head2 COMMON HELPERS
568 L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
572 L<Catalyst::Helper::View::TT> - Template Toolkit view
576 L<Catalyst::Helper::Model::LDAP>
580 L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
586 The helpers will read author name from /etc/passwd by default.
587 To override, please export the AUTHOR variable.
593 This method in your Helper module is called with C<$helper>
594 which is a L<Catalyst::Helper> object, and whichever other arguments
595 the user added to the command-line. You can use the $helper to call methods
598 If the Helper module does not contain a C<mk_compclass> method, it
599 will fall back to calling L</render_file>, with an argument of
604 This method in your Helper module is called with C<$helper>
605 which is a L<Catalyst::Helper> object, and whichever other arguments
606 the user added to the command-line. You can use the $helper to call methods
609 If the Helper module does not contain a C<mk_compclass> method, it
610 will fall back to calling L</render_file>, with an argument of
615 This method is called if the user does not supply any of the usual
616 component types C<view>, C<controller>, C<model>. It is passed the
617 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
618 arguments the user typed.
620 There is no fallback for this method.
622 =head1 INTERNAL METHODS
624 These are the methods that the Helper classes can call on the
625 <$helper> object passed to them.
627 =head2 render_file ($file, $path, $vars)
629 Render and create a file from a template in DATA using Template
630 Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
631 the path to the file and $vars is the hashref as expected by
632 L<Template Toolkit|Template>.
634 =head2 get_file ($class, $file)
636 Fetch file contents from the DATA section. This is used internally by
637 L</render_file>. $class is the name of the class to get the DATA
638 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
643 Create the main application skeleton. This is called by L<catalyst.pl>.
645 =head2 mk_component ($app)
647 This method is called by L<create.pl> to make new components
648 for your application.
650 =head3 mk_dir ($path)
652 Surprisingly, this function makes a directory.
654 =head2 mk_file ($file, $content)
656 Writes content to a file. Called by L</render_file>.
658 =head2 next_test ($test_name)
660 Calculates the name of the next numbered test file and returns it.
661 Don't give the number or the .t suffix for the test name.
665 =head2 get_sharedir_file
667 Method for getting a file out of share/
671 =head2 render_file_contents
673 Process a L<Template::Toolkit> template.
677 =head2 render_sharedir_file
679 Render a template/image file from our share directory
685 The helpers will read author name from /etc/passwd by default.
686 To override, please export the AUTHOR variable.
690 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
691 L<Catalyst::Response>, L<Catalyst>
695 Catalyst Contributors, see Catalyst.pm
699 This library is free software. You can redistribute it and/or modify
700 it under the same terms as Perl itself.