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, $class, $file ) = @_;
41 unless ( $cache{$class} ) {
43 $cache{$class} = eval "package $class; <DATA>";
45 my $data = $cache{$class};
46 my @files = split /^__(.+)__\r?\n/m, $data;
49 my ( $name, $content ) = splice @files, 0, 2;
50 return $content if $name eq $file;
57 my ( $self, $name ) = @_;
59 # Needs to be here for PAR
62 if ( $name =~ /[^\w:]/ || $name =~ /^\d/ || $name =~ /\b:\b|:{3,}/) {
63 warn "Error: Invalid application name.\n";
66 $self->{name } = $name;
67 $self->{dir } = $name;
68 $self->{dir } =~ s/\:\:/-/g;
69 $self->{script } = File::Spec->catdir( $self->{dir}, 'script' );
70 $self->{appprefix } = Catalyst::Utils::appprefix($name);
71 $self->{appenv } = Catalyst::Utils::class2env($name);
72 $self->{startperl } = -r '/usr/bin/env'
73 ? '#!/usr/bin/env perl'
74 : "#!$Config{perlpath} -w";
75 $self->{scriptgen } = $Catalyst::Devel::CATALYST_SCRIPT_GEN || 4;
76 $self->{catalyst_version} = $Catalyst::VERSION;
77 $self->{author } = $self->{author} = $ENV{'AUTHOR'}
78 || eval { @{ [ getpwuid($<) ] }[6] }
79 || 'Catalyst developer';
81 my $gen_scripts = ( $self->{makefile} ) ? 0 : 1;
82 my $gen_makefile = ( $self->{scripts} ) ? 0 : 1;
83 my $gen_app = ( $self->{scripts} || $self->{makefile} ) ? 0 : 1;
105 $self->_mk_information;
114 $self->{author} = $self->{author} = $ENV{'AUTHOR'}
115 || eval { @{ [ getpwuid($<) ] }[6] }
117 $self->{base} ||= File::Spec->catdir( $FindBin::Bin, '..' );
118 unless ( $_[0] =~ /^(?:model|view|controller)$/i ) {
121 my $class = "Catalyst::Helper::$helper";
122 eval "require $class";
125 Catalyst::Exception->throw(
126 message => qq/Couldn't load helper "$class", "$@"/ );
129 if ( $class->can('mk_stuff') ) {
130 return 1 unless $class->mk_stuff( $self, @args );
135 my $name = shift || "Missing name for model/view/controller";
138 return 0 if $name =~ /[^\w\:]/;
140 $self->{long_type} = ucfirst $type;
141 $type = 'M' if $type =~ /model/i;
142 $type = 'V' if $type =~ /view/i;
143 $type = 'C' if $type =~ /controller/i;
144 my $appdir = File::Spec->catdir( split /\:\:/, $app );
146 File::Spec->catdir( $FindBin::Bin, '..', 'lib', $appdir, 'C' );
147 $type = $self->{long_type} unless -d $test_path;
148 $self->{type} = $type;
149 $self->{name} = $name;
150 $self->{class} = "$app\::$type\::$name";
154 File::Spec->catdir( $FindBin::Bin, '..', 'lib', $appdir, $type );
156 if ( $name =~ /\:/ ) {
157 my @path = split /\:\:/, $name;
159 $path = File::Spec->catdir( $path, @path );
161 $self->mk_dir($path);
162 $file = File::Spec->catfile( $path, "$file.pm" );
163 $self->{file} = $file;
166 $self->{test_dir} = File::Spec->catdir( $FindBin::Bin, '..', 't' );
167 $self->{test} = $self->next_test;
171 my $comp = $self->{long_type};
172 my $class = "Catalyst::Helper::$comp\::$helper";
173 eval "require $class";
176 Catalyst::Exception->throw(
177 message => qq/Couldn't load helper "$class", "$@"/ );
180 if ( $class->can('mk_compclass') ) {
181 return 1 unless $class->mk_compclass( $self, @args );
183 else { return 1 unless $self->_mk_compclass }
185 if ( $class->can('mk_comptest') ) {
186 $class->mk_comptest( $self, @args );
188 else { $self->_mk_comptest }
193 return 1 unless $self->_mk_compclass;
201 my ( $self, $dir ) = @_;
203 print qq/ exists "$dir"\n/;
206 if ( mkpath [$dir] ) {
207 print qq/created "$dir"\n/;
211 Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
215 my ( $self, $file, $content ) = @_;
217 print qq/ exists "$file"\n/;
219 unless ( $self->{'.newfiles'}
221 || $self->{makefile} );
222 if ( $self->{'.newfiles'} ) {
223 if ( my $f = IO::File->new("< $file") ) {
224 my $oldcontent = join( '', (<$f>) );
225 return 0 if $content eq $oldcontent;
230 if ( my $f = IO::File->new("> $file") ) {
233 print qq/created "$file"\n/;
237 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
241 my ( $self, $tname ) = @_;
242 if ($tname) { $tname = "$tname.t" }
244 my $name = $self->{name};
248 $tname = $prefix . '.t';
249 $self->{prefix} = $prefix;
250 $prefix = lc $prefix;
252 $self->{uri} = "/$prefix";
254 my $dir = $self->{test_dir};
255 my $type = lc $self->{type};
257 return File::Spec->catfile( $dir, "$type\_$tname" );
261 my ( $self, $file, $path, $vars ) = @_;
263 my $t = Template->new;
264 my $template = $self->get_sharedir_file( 'root', $file );
265 return 0 unless $template;
267 $t->process( \$template, { %{$self}, %$vars }, \$output )
268 || Catalyst::Exception->throw(
269 message => qq/Couldn't process "$file", / . $t->error() );
270 $self->mk_file( $path, $output );
273 sub _mk_information {
275 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
280 $self->mk_dir( $self->{dir} );
281 $self->mk_dir( $self->{script} );
282 $self->{lib} = File::Spec->catdir( $self->{dir}, 'lib' );
283 $self->mk_dir( $self->{lib} );
284 $self->{root} = File::Spec->catdir( $self->{dir}, 'root' );
285 $self->mk_dir( $self->{root} );
286 $self->{static} = File::Spec->catdir( $self->{root}, 'static' );
287 $self->mk_dir( $self->{static} );
288 $self->{images} = File::Spec->catdir( $self->{static}, 'images' );
289 $self->mk_dir( $self->{images} );
290 $self->{t} = File::Spec->catdir( $self->{dir}, 't' );
291 $self->mk_dir( $self->{t} );
293 $self->{class} = File::Spec->catdir( split( /\:\:/, $self->{name} ) );
294 $self->{mod} = File::Spec->catdir( $self->{lib}, $self->{class} );
295 $self->mk_dir( $self->{mod} );
297 if ( $self->{short} ) {
298 $self->{m} = File::Spec->catdir( $self->{mod}, 'M' );
299 $self->mk_dir( $self->{m} );
300 $self->{v} = File::Spec->catdir( $self->{mod}, 'V' );
301 $self->mk_dir( $self->{v} );
302 $self->{c} = File::Spec->catdir( $self->{mod}, 'C' );
303 $self->mk_dir( $self->{c} );
306 $self->{m} = File::Spec->catdir( $self->{mod}, 'Model' );
307 $self->mk_dir( $self->{m} );
308 $self->{v} = File::Spec->catdir( $self->{mod}, 'View' );
309 $self->mk_dir( $self->{v} );
310 $self->{c} = File::Spec->catdir( $self->{mod}, 'Controller' );
311 $self->mk_dir( $self->{c} );
313 my $name = $self->{name};
315 $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
316 $self->{base} = File::Spec->rel2abs( $self->{dir} );
321 my $mod = $self->{mod};
322 $self->render_file( 'appclass.tt', "$mod.pm" );
327 $self->render_file( 'rootclass.tt',
328 File::Spec->catfile( $self->{c}, "Root.pm" ) );
333 $self->{path} = File::Spec->catfile( 'lib', split( '::', $self->{name} ) );
334 $self->{path} .= '.pm';
335 my $dir = $self->{dir};
336 $self->render_file( 'makefile.tt', "$dir\/Makefile.PL" );
338 if ( $self->{makefile} ) {
340 # deprecate the old Build.PL file when regenerating Makefile.PL
341 $self->_deprecate_file(
342 File::Spec->catdir( $self->{dir}, 'Build.PL' ) );
348 my $dir = $self->{dir};
349 my $appprefix = $self->{appprefix};
350 $self->render_file( 'config.tt',
351 File::Spec->catfile( $dir, "$appprefix.conf" ) );
356 my $dir = $self->{dir};
357 $self->render_file( 'readme.tt', "$dir\/README" );
362 my $dir = $self->{dir};
363 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
364 $self->render_file( 'changes.tt', "$dir\/Changes", { time => $time } );
370 $self->render_file( 'apptest.tt', "$t\/01app.t" );
371 $self->render_file( 'podtest.tt', "$t\/02pod.t" );
372 $self->render_file( 'podcoveragetest.tt', "$t\/03podcoverage.t" );
377 my $script = $self->{script};
378 my $appprefix = $self->{appprefix};
379 $self->render_file( 'cgi.tt', "$script\/$appprefix\_cgi.pl" );
380 chmod 0700, "$script/$appprefix\_cgi.pl";
385 my $script = $self->{script};
386 my $appprefix = $self->{appprefix};
387 $self->render_file( 'fastcgi.tt', "$script\/$appprefix\_fastcgi.pl" );
388 chmod 0700, "$script/$appprefix\_fastcgi.pl";
393 my $script = $self->{script};
394 my $appprefix = $self->{appprefix};
395 $self->render_file( 'server.tt', "$script\/$appprefix\_server.pl" );
396 chmod 0700, "$script/$appprefix\_server.pl";
401 my $script = $self->{script};
402 my $appprefix = $self->{appprefix};
403 $self->render_file( 'test.tt', "$script/$appprefix\_test.pl" );
404 chmod 0700, "$script/$appprefix\_test.pl";
409 my $script = $self->{script};
410 my $appprefix = $self->{appprefix};
411 $self->render_file( 'create.tt', "$script\/$appprefix\_create.pl" );
412 chmod 0700, "$script/$appprefix\_create.pl";
417 my $file = $self->{file};
418 return $self->render_file( 'compclass.tt', "$file" );
423 my $test = $self->{test};
424 $self->render_file( 'comptest.tt', "$test" );
429 my $images = $self->{images};
431 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
432 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
433 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
434 for my $name (@images) {
435 my $image = $self->get_file("$name.png");
436 $self->mk_file( File::Spec->catfile( $images, "$name.png" ), $image );
442 my $root = $self->{root};
443 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico' );
444 my $dest = File::Spec->catfile( $root, "favicon.ico" );
445 $self->mk_file( $dest, $favicon );
449 sub _deprecate_file {
450 my ( $self, $file ) = @_;
453 if ( my $f = IO::File->new("< $file") ) {
454 $oldcontent = join( '', (<$f>) );
456 my $newfile = $file . '.deprecated';
457 if ( my $f = IO::File->new("> $newfile") ) {
459 print $f $oldcontent;
460 print qq/created "$newfile"\n/;
462 print qq/removed "$file"\n/;
465 Catalyst::Exception->throw(
466 message => qq/Couldn't create "$file", "$!"/ );
472 This module is used by B<catalyst.pl> to create a set of scripts for a
473 new catalyst application. The scripts each contain documentation and
474 will output help on how to use them if called incorrectly or in some
475 cases, with no arguments.
477 It also provides some useful methods for a Helper module to call when
478 creating a component. See L</METHODS>.
484 Used to create new components for a catalyst application at the
489 The catalyst test server, starts an HTTPD which outputs debugging to
494 A script for running tests from the command-line.
498 Run your application as a CGI.
502 Run the application as a fastcgi app. Either by hand, or call this
503 from FastCgiServer in your http server config.
507 The L</_create.pl> script creates application components using Helper
508 modules. The Catalyst team provides a good number of Helper modules
509 for you to use. You can also add your own.
511 Helpers are classes that provide two methods.
513 * mk_compclass - creates the Component class
514 * mk_comptest - creates the Component test
516 So when you call C<scripts/myapp_create.pl view MyView TT>, create
517 will try to execute Catalyst::Helper::View::TT->mk_compclass and
518 Catalyst::Helper::View::TT->mk_comptest.
520 See L<Catalyst::Helper::View::TT> and
521 L<Catalyst::Helper::Model::DBIC::Schema> for examples.
523 All helper classes should be under one of the following namespaces.
525 Catalyst::Helper::Model::
526 Catalyst::Helper::View::
527 Catalyst::Helper::Controller::
529 =head2 COMMON HELPERS
535 L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
539 L<Catalyst::Helper::View::TT> - Template Toolkit view
543 L<Catalyst::Helper::Model::LDAP>
547 L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
553 The helpers will read author name from /etc/passwd by default. + To override, please export the AUTHOR variable.
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 in your Helper module is called with C<$helper>
571 which is a L<Catalyst::Helper> object, and whichever other arguments
572 the user added to the command-line. You can use the $helper to call methods
575 If the Helper module does not contain a C<mk_compclass> method, it
576 will fall back to calling L</render_file>, with an argument of
581 This method is called if the user does not supply any of the usual
582 component types C<view>, C<controller>, C<model>. It is passed the
583 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
584 arguments the user typed.
586 There is no fallback for this method.
588 =head1 INTERNAL METHODS
590 These are the methods that the Helper classes can call on the
591 <$helper> object passed to them.
593 =head2 render_file ($file, $path, $vars)
595 Render and create a file from a template in DATA using Template
596 Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
597 the path to the file and $vars is the hashref as expected by
598 L<Template Toolkit|Template>.
600 =head2 get_file ($class, $file)
602 Fetch file contents from the DATA section. This is used internally by
603 L</render_file>. $class is the name of the class to get the DATA
604 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
609 Create the main application skeleton. This is called by L<catalyst.pl>.
611 =head2 mk_component ($app)
613 This method is called by L<create.pl> to make new components
614 for your application.
616 =head3 mk_dir ($path)
618 Surprisingly, this function makes a directory.
620 =head2 mk_file ($file, $content)
622 Writes content to a file. Called by L</render_file>.
624 =head2 next_test ($test_name)
626 Calculates the name of the next numbered test file and returns it.
627 Don't give the number or the .t suffix for the test name.
631 The helpers will read author name from /etc/passwd by default.
632 To override, please export the AUTHOR variable.
636 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
637 L<Catalyst::Response>, L<Catalyst>
641 Catalyst Contributors, see Catalyst.pm
645 This library is free software. You can redistribute it and/or modify
646 it under the same terms as Perl itself.