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 } = File::Spec->catdir( $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;
115 $self->_mk_information;
124 $self->{author} = $self->{author} = $ENV{'AUTHOR'}
125 || eval { @{ [ getpwuid($<) ] }[6] }
127 $self->{base} ||= File::Spec->catdir( $FindBin::Bin, '..' );
128 unless ( $_[0] =~ /^(?:model|view|controller)$/i ) {
131 my $class = "Catalyst::Helper::$helper";
132 eval "require $class";
135 Catalyst::Exception->throw(
136 message => qq/Couldn't load helper "$class", "$@"/ );
139 if ( $class->can('mk_stuff') ) {
140 return 1 unless $class->mk_stuff( $self, @args );
145 my $name = shift || "Missing name for model/view/controller";
148 return 0 if $name =~ /[^\w\:]/;
150 $self->{long_type} = ucfirst $type;
151 $type = 'M' if $type =~ /model/i;
152 $type = 'V' if $type =~ /view/i;
153 $type = 'C' if $type =~ /controller/i;
154 my $appdir = File::Spec->catdir( split /\:\:/, $app );
156 File::Spec->catdir( $FindBin::Bin, '..', 'lib', $appdir, 'C' );
157 $type = $self->{long_type} unless -d $test_path;
158 $self->{type} = $type;
159 $self->{name} = $name;
160 $self->{class} = "$app\::$type\::$name";
164 File::Spec->catdir( $FindBin::Bin, '..', 'lib', $appdir, $type );
166 if ( $name =~ /\:/ ) {
167 my @path = split /\:\:/, $name;
169 $path = File::Spec->catdir( $path, @path );
171 $self->mk_dir($path);
172 $file = File::Spec->catfile( $path, "$file.pm" );
173 $self->{file} = $file;
176 $self->{test_dir} = File::Spec->catdir( $FindBin::Bin, '..', 't' );
177 $self->{test} = $self->next_test;
181 my $comp = $self->{long_type};
182 my $class = "Catalyst::Helper::$comp\::$helper";
183 eval "require $class";
186 Catalyst::Exception->throw(
187 message => qq/Couldn't load helper "$class", "$@"/ );
190 if ( $class->can('mk_compclass') ) {
191 return 1 unless $class->mk_compclass( $self, @args );
193 else { return 1 unless $self->_mk_compclass }
195 if ( $class->can('mk_comptest') ) {
196 $class->mk_comptest( $self, @args );
198 else { $self->_mk_comptest }
203 return 1 unless $self->_mk_compclass;
211 my ( $self, $dir ) = @_;
213 print qq/ exists "$dir"\n/;
216 if ( mkpath [$dir] ) {
217 print qq/created "$dir"\n/;
221 Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
225 my ( $self, $file, $content ) = @_;
226 if ( -e $file && -s _ ) {
227 print qq/ exists "$file"\n/;
229 unless ( $self->{'.newfiles'}
231 || $self->{makefile} );
232 if ( $self->{'.newfiles'} ) {
233 if ( my $f = IO::File->new("< $file") ) {
234 my $oldcontent = join( '', (<$f>) );
235 return 0 if $content eq $oldcontent;
241 if ( my $f = IO::File->new("> $file") ) {
244 print qq/created "$file"\n/;
248 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
252 my ( $self, $tname ) = @_;
253 if ($tname) { $tname = "$tname.t" }
255 my $name = $self->{name};
259 $tname = $prefix . '.t';
260 $self->{prefix} = $prefix;
261 $prefix = lc $prefix;
263 $self->{uri} = "/$prefix";
265 my $dir = $self->{test_dir};
266 my $type = lc $self->{type};
268 return File::Spec->catfile( $dir, "$type\_$tname" );
271 # Do not touch this method, *EVER*, it is needed for back compat.
272 ## addendum: we had to split this method so we could have backwards
273 ## compatability. otherwise, we'd have no way to pass stuff from __DATA__
276 my ( $self, $file, $path, $vars ) = @_;
277 my $template = $self->get_file( ( caller(0) )[0], $file );
278 $self->render_file_contents($template, $path, $vars);
281 sub render_sharedir_file {
282 my ( $self, $file, $path, $vars ) = @_;
283 my $template = $self->get_sharedir_file( $file );
284 die("Cannot get template from $file for $self\n") unless $template;
285 $self->render_file_contents($template, $path, $vars);
288 sub render_file_contents {
289 my ( $self, $template, $path, $vars ) = @_;
291 my $t = Template->new;
292 return 0 unless $template;
294 $t->process( \$template, { %{$self}, %$vars }, \$output )
295 || Catalyst::Exception->throw(
296 message => qq/Couldn't process "$template", / . $t->error() );
297 $self->mk_file( $path, $output );
300 sub _mk_information {
302 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
307 $self->mk_dir( $self->{dir} );
308 $self->mk_dir( $self->{script} );
309 $self->{lib} = File::Spec->catdir( $self->{dir}, 'lib' );
310 $self->mk_dir( $self->{lib} );
311 $self->{root} = File::Spec->catdir( $self->{dir}, 'root' );
312 $self->mk_dir( $self->{root} );
313 $self->{static} = File::Spec->catdir( $self->{root}, 'static' );
314 $self->mk_dir( $self->{static} );
315 $self->{images} = File::Spec->catdir( $self->{static}, 'images' );
316 $self->mk_dir( $self->{images} );
317 $self->{t} = File::Spec->catdir( $self->{dir}, 't' );
318 $self->mk_dir( $self->{t} );
320 $self->{class} = File::Spec->catdir( split( /\:\:/, $self->{name} ) );
321 $self->{mod} = File::Spec->catdir( $self->{lib}, $self->{class} );
322 $self->mk_dir( $self->{mod} );
324 if ( $self->{short} ) {
325 $self->{m} = File::Spec->catdir( $self->{mod}, 'M' );
326 $self->mk_dir( $self->{m} );
327 $self->{v} = File::Spec->catdir( $self->{mod}, 'V' );
328 $self->mk_dir( $self->{v} );
329 $self->{c} = File::Spec->catdir( $self->{mod}, 'C' );
330 $self->mk_dir( $self->{c} );
333 $self->{m} = File::Spec->catdir( $self->{mod}, 'Model' );
334 $self->mk_dir( $self->{m} );
335 $self->{v} = File::Spec->catdir( $self->{mod}, 'View' );
336 $self->mk_dir( $self->{v} );
337 $self->{c} = File::Spec->catdir( $self->{mod}, 'Controller' );
338 $self->mk_dir( $self->{c} );
340 my $name = $self->{name};
342 $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
343 $self->{base} = File::Spec->rel2abs( $self->{dir} );
348 my $mod = $self->{mod};
349 $self->render_sharedir_file( File::Spec->catfile('lib', 'MyApp.pm.tt'), "$mod.pm" );
354 $self->render_sharedir_file( File::Spec->catfile('lib', 'MyApp', 'Controller', 'Root.pm.tt'),
355 File::Spec->catfile( $self->{c}, "Root.pm" ) );
360 $self->{path} = File::Spec->catfile( 'lib', split( '::', $self->{name} ) );
361 $self->{path} .= '.pm';
362 my $dir = $self->{dir};
363 $self->render_sharedir_file( 'Makefile.PL.tt', "$dir\/Makefile.PL" );
365 if ( $self->{makefile} ) {
367 # deprecate the old Build.PL file when regenerating Makefile.PL
368 $self->_deprecate_file(
369 File::Spec->catdir( $self->{dir}, 'Build.PL' ) );
375 my $dir = $self->{dir};
376 my $appprefix = $self->{appprefix};
377 $self->render_sharedir_file( 'myapp.conf.tt',
378 File::Spec->catfile( $dir, "$appprefix.conf" ) );
383 my $dir = $self->{dir};
384 $self->render_sharedir_file( 'README.tt', "$dir\/README" );
389 my $dir = $self->{dir};
390 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
391 $self->render_sharedir_file( 'Changes.tt', "$dir\/Changes", { time => $time } );
397 $self->render_sharedir_file( File::Spec->catfile('t', '01app.t.tt'), "$t\/01app.t" );
398 $self->render_sharedir_file( File::Spec->catfile('t', '02pod.t.tt'), "$t\/02pod.t" );
399 $self->render_sharedir_file( File::Spec->catfile('t', '03podcoverage.t.tt'), "$t\/03podcoverage.t" );
404 my $script = $self->{script};
405 my $appprefix = $self->{appprefix};
406 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_cgi.pl.tt'), "$script\/$appprefix\_cgi.pl" );
407 chmod 0700, "$script/$appprefix\_cgi.pl";
412 my $script = $self->{script};
413 my $appprefix = $self->{appprefix};
414 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_fastcgi.pl.tt'), "$script\/$appprefix\_fastcgi.pl" );
415 chmod 0700, "$script/$appprefix\_fastcgi.pl";
420 my $script = $self->{script};
421 my $appprefix = $self->{appprefix};
422 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_server.pl.tt'), "$script\/$appprefix\_server.pl" );
423 chmod 0700, "$script/$appprefix\_server.pl";
428 my $script = $self->{script};
429 my $appprefix = $self->{appprefix};
430 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_test.pl.tt'), "$script/$appprefix\_test.pl" );
431 chmod 0700, "$script/$appprefix\_test.pl";
436 my $script = $self->{script};
437 my $appprefix = $self->{appprefix};
438 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_create.pl.tt'), "$script\/$appprefix\_create.pl" );
439 chmod 0700, "$script/$appprefix\_create.pl";
444 my $file = $self->{file};
445 return $self->render_sharedir_file( File::Spec->catfile('lib', 'Helper', 'compclass.pm.tt'), $file );
450 my $test = $self->{test};
451 $self->render_sharedir_file( File::Spec->catfile('t', 'comptest.tt'), $test ); ## wtf do i rename this to?
456 my $images = $self->{images};
458 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
459 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
460 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
461 for my $name (@images) {
462 my $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin");
463 $self->mk_file( File::Spec->catfile( $images, "$name.png" ), $image );
469 my $root = $self->{root};
470 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' );
471 my $dest = File::Spec->catfile( $root, "favicon.ico" );
472 $self->mk_file( $dest, $favicon );
476 sub _deprecate_file {
477 my ( $self, $file ) = @_;
480 if ( my $f = IO::File->new("< $file") ) {
481 $oldcontent = join( '', (<$f>) );
483 my $newfile = $file . '.deprecated';
484 if ( my $f = IO::File->new("> $newfile") ) {
486 print $f $oldcontent;
487 print qq/created "$newfile"\n/;
489 print qq/removed "$file"\n/;
492 Catalyst::Exception->throw(
493 message => qq/Couldn't create "$file", "$!"/ );
499 This module is used by B<catalyst.pl> to create a set of scripts for a
500 new catalyst application. The scripts each contain documentation and
501 will output help on how to use them if called incorrectly or in some
502 cases, with no arguments.
504 It also provides some useful methods for a Helper module to call when
505 creating a component. See L</METHODS>.
511 Used to create new components for a catalyst application at the
516 The catalyst test server, starts an HTTPD which outputs debugging to
521 A script for running tests from the command-line.
525 Run your application as a CGI.
529 Run the application as a fastcgi app. Either by hand, or call this
530 from FastCgiServer in your http server config.
534 The L</_create.pl> script creates application components using Helper
535 modules. The Catalyst team provides a good number of Helper modules
536 for you to use. You can also add your own.
538 Helpers are classes that provide two methods.
540 * mk_compclass - creates the Component class
541 * mk_comptest - creates the Component test
543 So when you call C<scripts/myapp_create.pl view MyView TT>, create
544 will try to execute Catalyst::Helper::View::TT->mk_compclass and
545 Catalyst::Helper::View::TT->mk_comptest.
547 See L<Catalyst::Helper::View::TT> and
548 L<Catalyst::Helper::Model::DBIC::Schema> for examples.
550 All helper classes should be under one of the following namespaces.
552 Catalyst::Helper::Model::
553 Catalyst::Helper::View::
554 Catalyst::Helper::Controller::
556 =head2 COMMON HELPERS
562 L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
566 L<Catalyst::Helper::View::TT> - Template Toolkit view
570 L<Catalyst::Helper::Model::LDAP>
574 L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
580 The helpers will read author name from /etc/passwd by default. + To override, please export the AUTHOR variable.
586 This method in your Helper module is called with C<$helper>
587 which is a L<Catalyst::Helper> object, and whichever other arguments
588 the user added to the command-line. You can use the $helper to call methods
591 If the Helper module does not contain a C<mk_compclass> method, it
592 will fall back to calling L</render_file>, with an argument of
597 This method in your Helper module is called with C<$helper>
598 which is a L<Catalyst::Helper> object, and whichever other arguments
599 the user added to the command-line. You can use the $helper to call methods
602 If the Helper module does not contain a C<mk_compclass> method, it
603 will fall back to calling L</render_file>, with an argument of
608 This method is called if the user does not supply any of the usual
609 component types C<view>, C<controller>, C<model>. It is passed the
610 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
611 arguments the user typed.
613 There is no fallback for this method.
615 =head1 INTERNAL METHODS
617 These are the methods that the Helper classes can call on the
618 <$helper> object passed to them.
620 =head2 render_file ($file, $path, $vars)
622 Render and create a file from a template in DATA using Template
623 Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
624 the path to the file and $vars is the hashref as expected by
625 L<Template Toolkit|Template>.
627 =head2 get_file ($class, $file)
629 Fetch file contents from the DATA section. This is used internally by
630 L</render_file>. $class is the name of the class to get the DATA
631 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
636 Create the main application skeleton. This is called by L<catalyst.pl>.
638 =head2 mk_component ($app)
640 This method is called by L<create.pl> to make new components
641 for your application.
643 =head3 mk_dir ($path)
645 Surprisingly, this function makes a directory.
647 =head2 mk_file ($file, $content)
649 Writes content to a file. Called by L</render_file>.
651 =head2 next_test ($test_name)
653 Calculates the name of the next numbered test file and returns it.
654 Don't give the number or the .t suffix for the test name.
658 =head2 get_sharedir_file
660 Method for getting a file out of share/
664 =head2 render_file_contents
666 Process a L<Template::Toolkit> template.
670 =head2 render_sharedir_file
672 Render a template/image file from our share directory
679 The helpers will read author name from /etc/passwd by default.
680 To override, please export the AUTHOR variable.
684 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
685 L<Catalyst::Response>, L<Catalyst>
689 Catalyst Contributors, see Catalyst.pm
693 This library is free software. You can redistribute it and/or modify
694 it under the same terms as Perl itself.