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 || 4;
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;
101 $self->_mk_rootclass;
115 $self->_mk_dbic_deploy;
118 $self->_mk_information;
127 $self->{author} = $self->{author} = $ENV{'AUTHOR'}
128 || eval { @{ [ getpwuid($<) ] }[6] }
130 $self->{base} ||= dir( $FindBin::Bin, '..' );
131 unless ( $_[0] =~ /^(?:model|view|controller)$/i ) {
134 my $class = "Catalyst::Helper::$helper";
135 eval "require $class";
138 Catalyst::Exception->throw(
139 message => qq/Couldn't load helper "$class", "$@"/ );
142 if ( $class->can('mk_stuff') ) {
143 return 1 unless $class->mk_stuff( $self, @args );
148 my $name = shift || "Missing name for model/view/controller";
151 return 0 if $name =~ /[^\w\:]/;
153 $self->{long_type} = ucfirst $type;
154 $type = 'M' if $type =~ /model/i;
155 $type = 'V' if $type =~ /view/i;
156 $type = 'C' if $type =~ /controller/i;
157 my $appdir = dir( split /\:\:/, $app );
159 dir( $self->{base}, 'lib', $appdir, 'C' );
160 $type = $self->{long_type} unless -d $test_path;
161 $self->{type} = $type;
162 $self->{name} = $name;
163 $self->{class} = "$app\::$type\::$name";
167 dir( $self->{base}, 'lib', $appdir, $type );
169 if ( $name =~ /\:/ ) {
170 my @path = split /\:\:/, $name;
172 $path = dir( $path, @path );
174 $self->mk_dir($path);
175 $file = file( $path, "$file.pm" );
176 $self->{file} = $file;
179 $self->{test_dir} = dir( $self->{base}, 't' );
180 $self->{test} = $self->next_test;
184 my $comp = $self->{long_type};
185 my $class = "Catalyst::Helper::$comp\::$helper";
186 eval "require $class";
189 Catalyst::Exception->throw(
190 message => qq/Couldn't load helper "$class", "$@"/ );
193 if ( $class->can('mk_compclass') ) {
194 return 1 unless $class->mk_compclass( $self, @args );
196 else { return 1 unless $self->_mk_compclass }
198 if ( $class->can('mk_comptest') ) {
199 $class->mk_comptest( $self, @args );
201 else { $self->_mk_comptest }
206 return 1 unless $self->_mk_compclass;
214 my ( $self, $dir ) = @_;
216 print qq/ exists "$dir"\n/;
219 if ( mkpath [$dir] ) {
220 print qq/created "$dir"\n/;
224 Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
228 my ( $self, $file, $content ) = @_;
229 if ( -e $file && -s _ ) {
230 print qq/ exists "$file"\n/;
232 unless ( $self->{'.newfiles'}
234 || $self->{makefile} );
235 if ( $self->{'.newfiles'} ) {
236 if ( my $f = IO::File->new("< $file") ) {
237 my $oldcontent = join( '', (<$f>) );
238 return 0 if $content eq $oldcontent;
244 if ( my $f = IO::File->new("> $file") ) {
247 print qq/created "$file"\n/;
251 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
255 my ( $self, $tname ) = @_;
256 if ($tname) { $tname = "$tname.t" }
258 my $name = $self->{name};
262 $tname = $prefix . '.t';
263 $self->{prefix} = $prefix;
264 $prefix = lc $prefix;
266 $self->{uri} = "/$prefix";
268 my $dir = $self->{test_dir};
269 my $type = lc $self->{type};
271 return file( $dir, "$type\_$tname" );
274 # Do not touch this method, *EVER*, it is needed for back compat.
275 ## addendum: we had to split this method so we could have backwards
276 ## compatability. otherwise, we'd have no way to pass stuff from __DATA__
279 my ( $self, $file, $path, $vars ) = @_;
280 my $template = $self->get_file( ( caller(0) )[0], $file );
281 $self->render_file_contents($template, $path, $vars);
284 sub render_sharedir_file {
285 my ( $self, $file, $path, $vars ) = @_;
286 my $template = $self->get_sharedir_file( $file );
287 die("Cannot get template from $file for $self\n") unless $template;
288 $self->render_file_contents($template, $path, $vars);
291 sub render_file_contents {
292 my ( $self, $template, $path, $vars ) = @_;
294 my $t = Template->new;
295 return 0 unless $template;
297 $t->process( \$template, { %{$self}, %$vars }, \$output )
298 || Catalyst::Exception->throw(
299 message => qq/Couldn't process "$template", / . $t->error() );
300 $self->mk_file( $path, $output );
303 sub _mk_information {
305 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
310 $self->mk_dir( $self->{dir} );
311 $self->mk_dir( $self->{script} );
312 $self->{lib} = dir( $self->{dir}, 'lib' );
313 $self->mk_dir( $self->{lib} );
314 $self->{root} = dir( $self->{dir}, 'root' );
315 $self->mk_dir( $self->{root} );
316 $self->{static} = dir( $self->{root}, 'static' );
317 $self->mk_dir( $self->{static} );
318 $self->{images} = dir( $self->{static}, 'images' );
319 $self->mk_dir( $self->{images} );
320 $self->{t} = dir( $self->{dir}, 't' );
321 $self->mk_dir( $self->{t} );
323 $self->{class} = dir( split( /\:\:/, $self->{name} ) );
324 $self->{mod} = dir( $self->{lib}, $self->{class} );
325 $self->mk_dir( $self->{mod} );
327 if ( $self->{short} ) {
328 $self->{m} = dir( $self->{mod}, 'M' );
329 $self->mk_dir( $self->{m} );
330 $self->{v} = dir( $self->{mod}, 'V' );
331 $self->mk_dir( $self->{v} );
332 $self->{c} = dir( $self->{mod}, 'C' );
333 $self->mk_dir( $self->{c} );
336 $self->{m} = dir( $self->{mod}, 'Model' );
337 $self->mk_dir( $self->{m} );
338 $self->{v} = dir( $self->{mod}, 'View' );
339 $self->mk_dir( $self->{v} );
340 $self->{c} = dir( $self->{mod}, 'Controller' );
341 $self->mk_dir( $self->{c} );
343 my $name = $self->{name};
345 $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
346 $self->{base} = dir( $self->{dir} )->absolute;
351 my $mod = $self->{mod};
352 $self->render_sharedir_file( file('lib', 'MyApp.pm.tt'), "$mod.pm" );
357 $self->render_sharedir_file( file('lib', 'MyApp', 'Controller', 'Root.pm.tt'),
358 file( $self->{c}, "Root.pm" ) );
363 $self->{path} = dir( 'lib', split( '::', $self->{name} ) );
364 $self->{path} .= '.pm';
365 my $dir = $self->{dir};
366 $self->render_sharedir_file( 'Makefile.PL.tt', file($dir, "Makefile.PL") );
368 if ( $self->{makefile} ) {
370 # deprecate the old Build.PL file when regenerating Makefile.PL
371 $self->_deprecate_file(
372 file( $self->{dir}, 'Build.PL' ) );
378 my $dir = $self->{dir};
379 my $appprefix = $self->{appprefix};
380 $self->render_sharedir_file( 'myapp.conf.tt',
381 file( $dir, "$appprefix.conf" ) );
386 my $dir = $self->{dir};
387 $self->render_sharedir_file( 'README.tt', file($dir, "README") );
392 my $dir = $self->{dir};
393 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
394 $self->render_sharedir_file( 'Changes.tt', file($dir, "Changes", { time => $time } );
400 $self->render_sharedir_file( file('t', '01app.t.tt'), file($t, "01app.t") );
401 $self->render_sharedir_file( file('t', '02pod.t.tt'), file($t, "02pod.t") );
402 $self->render_sharedir_file( file('t', '03podcoverage.t.tt'), file($t, "03podcoverage.t") );
407 my $script = $self->{script};
408 my $appprefix = $self->{appprefix};
409 $self->render_sharedir_file( file('script', 'myapp_cgi.pl.tt'), file($script,"$appprefix\_cgi.pl") );
410 chmod 0700, file($script,"$appprefix\_cgi.pl");
415 my $script = $self->{script};
416 my $appprefix = $self->{appprefix};
417 $self->render_sharedir_file( file('script', 'myapp_fastcgi.pl.tt'), file($script, "$appprefix\_fastcgi.pl") );
418 chmod 0700, file($script, "$appprefix\_fastcgi.pl");
423 my $script = $self->{script};
424 my $appprefix = $self->{appprefix};
425 $self->render_sharedir_file( file('script', 'myapp_server.pl.tt'), file($script, "$appprefix\_server.pl") );
426 chmod 0700, file($script, "$appprefix\_server.pl");
431 my $script = $self->{script};
432 my $appprefix = $self->{appprefix};
433 $self->render_sharedir_file( file('script', 'myapp_test.pl.tt'), file($script, "$appprefix\_test.pl") );
434 chmod 0700, file($script, "$appprefix\_test.pl");
439 my $script = $self->{script};
440 my $appprefix = $self->{appprefix};
441 $self->render_sharedir_file( file('script', 'myapp_create.pl.tt'), file($script, "$appprefix\_create.pl") );
442 chmod 0700, file($script, "$appprefix\_create.pl");
447 my $file = $self->{file};
448 return $self->render_sharedir_file( file('lib', 'Helper', 'compclass.pm.tt'), $file );
453 my $test = $self->{test};
454 $self->render_sharedir_file( file('t', 'comptest.tt'), $test ); ## wtf do i rename this to?
459 my $images = $self->{images};
461 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
462 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
463 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
464 for my $name (@images) {
465 my $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin");
466 $self->mk_file( file( $images, "$name.png" ), $image );
472 my $root = $self->{root};
473 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' );
474 my $dest = dir( $root, "favicon.ico" );
475 $self->mk_file( $dest, $favicon );
479 sub _mk_dbic_deploy {
481 my $script = $self->{script};
482 my $appprefix = $self->{appprefix};
483 $self->render_sharedir_file( file('script', 'myapp_deploy_schema.pl.tt'), file($script, "$appprefix\_deploy_schema.pl") );
484 chmod 0700, file($script, "$appprefix\_deploy_schema.pl");
487 sub _deprecate_file {
488 my ( $self, $file ) = @_;
491 if ( my $f = IO::File->new("< $file") ) {
492 $oldcontent = join( '', (<$f>) );
494 my $newfile = $file . '.deprecated';
495 if ( my $f = IO::File->new("> $newfile") ) {
497 print $f $oldcontent;
498 print qq/created "$newfile"\n/;
500 print qq/removed "$file"\n/;
503 Catalyst::Exception->throw(
504 message => qq/Couldn't create "$file", "$!"/ );
510 This module is used by B<catalyst.pl> to create a set of scripts for a
511 new catalyst application. The scripts each contain documentation and
512 will output help on how to use them if called incorrectly or in some
513 cases, with no arguments.
515 It also provides some useful methods for a Helper module to call when
516 creating a component. See L</METHODS>.
522 Used to create new components for a catalyst application at the
527 The catalyst test server, starts an HTTPD which outputs debugging to
530 =head2 _deploy_dbic.pl
532 Deploy a L<DBIx::Class> schema to the database of your choice.
536 A script for running tests from the command-line.
540 Run your application as a CGI.
544 Run the application as a fastcgi app. Either by hand, or call this
545 from FastCgiServer in your http server config.
549 The L</_create.pl> script creates application components using Helper
550 modules. The Catalyst team provides a good number of Helper modules
551 for you to use. You can also add your own.
553 Helpers are classes that provide two methods.
555 * mk_compclass - creates the Component class
556 * mk_comptest - creates the Component test
558 So when you call C<scripts/myapp_create.pl view MyView TT>, create
559 will try to execute Catalyst::Helper::View::TT->mk_compclass and
560 Catalyst::Helper::View::TT->mk_comptest.
562 See L<Catalyst::Helper::View::TT> and
563 L<Catalyst::Helper::Model::DBIC::Schema> for examples.
565 All helper classes should be under one of the following namespaces.
567 Catalyst::Helper::Model::
568 Catalyst::Helper::View::
569 Catalyst::Helper::Controller::
571 =head2 COMMON HELPERS
577 L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
581 L<Catalyst::Helper::View::TT> - Template Toolkit view
585 L<Catalyst::Helper::Model::LDAP>
589 L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
595 The helpers will read author name from /etc/passwd by default. + To override, please export the AUTHOR variable.
601 This method in your Helper module is called with C<$helper>
602 which is a L<Catalyst::Helper> object, and whichever other arguments
603 the user added to the command-line. You can use the $helper to call methods
606 If the Helper module does not contain a C<mk_compclass> method, it
607 will fall back to calling L</render_file>, with an argument of
612 This method in your Helper module is called with C<$helper>
613 which is a L<Catalyst::Helper> object, and whichever other arguments
614 the user added to the command-line. You can use the $helper to call methods
617 If the Helper module does not contain a C<mk_compclass> method, it
618 will fall back to calling L</render_file>, with an argument of
623 This method is called if the user does not supply any of the usual
624 component types C<view>, C<controller>, C<model>. It is passed the
625 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
626 arguments the user typed.
628 There is no fallback for this method.
630 =head1 INTERNAL METHODS
632 These are the methods that the Helper classes can call on the
633 <$helper> object passed to them.
635 =head2 render_file ($file, $path, $vars)
637 Render and create a file from a template in DATA using Template
638 Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
639 the path to the file and $vars is the hashref as expected by
640 L<Template Toolkit|Template>.
642 =head2 get_file ($class, $file)
644 Fetch file contents from the DATA section. This is used internally by
645 L</render_file>. $class is the name of the class to get the DATA
646 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
651 Create the main application skeleton. This is called by L<catalyst.pl>.
653 =head2 mk_component ($app)
655 This method is called by L<create.pl> to make new components
656 for your application.
658 =head3 mk_dir ($path)
660 Surprisingly, this function makes a directory.
662 =head2 mk_file ($file, $content)
664 Writes content to a file. Called by L</render_file>.
666 =head2 next_test ($test_name)
668 Calculates the name of the next numbered test file and returns it.
669 Don't give the number or the .t suffix for the test name.
673 =head2 get_sharedir_file
675 Method for getting a file out of share/
679 =head2 render_file_contents
681 Process a L<Template::Toolkit> template.
685 =head2 render_sharedir_file
687 Render a template/image file from our share directory
694 The helpers will read author name from /etc/passwd by default.
695 To override, please export the AUTHOR variable.
699 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
700 L<Catalyst::Response>, L<Catalyst>
704 Catalyst Contributors, see Catalyst.pm
708 This library is free software. You can redistribute it and/or modify
709 it under the same terms as Perl itself.