1 package Catalyst::Helper;
12 use Catalyst::Exception;
13 use Path::Class qw/dir file/;
14 use File::ShareDir qw/dist_dir/;
15 use aliased 'Path::Class::Dir';
16 use namespace::autoclean;
22 Catalyst::Helper - Bootstrap a Catalyst application
26 catalyst.pl <myappname>
32 sub get_sharedir_file {
33 my ($self, @filename) = @_;
35 if (-d "inc/.author" && -f "lib/Catalyst/Helper.pm"
36 ) { # Can't use sharedir if we're in a checkout
37 # this feels horrible, better ideas?
41 $dist_dir = dist_dir('Catalyst-Devel');
43 my $file = file( $dist_dir, @filename);
44 my $contents = $file->slurp;
48 # Do not touch this method, *EVER*, it is needed for back compat.
50 my ( $self, $class, $file ) = @_;
51 unless ( $cache{$class} ) {
53 $cache{$class} = eval "package $class; <DATA>";
55 my $data = $cache{$class};
56 Carp::confess("Could not get data from __DATA__ segment for $class")
58 my @files = split /^__(.+)__\r?\n/m, $data;
61 my ( $name, $content ) = splice @files, 0, 2;
62 return $content if $name eq $file;
69 my ( $self, $name ) = @_;
71 # Needs to be here for PAR
74 if ( $name =~ /[^\w:]/ || $name =~ /^\d/ || $name =~ /\b:\b|:{3,}/) {
75 warn "Error: Invalid application name.\n";
78 $self->{name } = $name;
79 $self->{dir } = $name;
80 $self->{dir } =~ s/\:\:/-/g;
81 $self->{script } = File::Spec->catdir( $self->{dir}, 'script' );
82 $self->{appprefix } = Catalyst::Utils::appprefix($name);
83 $self->{appenv } = Catalyst::Utils::class2env($name);
84 $self->{startperl } = -r '/usr/bin/env'
85 ? '#!/usr/bin/env perl'
86 : "#!$Config{perlpath} -w";
87 $self->{scriptgen } = $Catalyst::Devel::CATALYST_SCRIPT_GEN || 4;
88 $self->{catalyst_version} = $Catalyst::VERSION;
89 $self->{author } = $self->{author} = $ENV{'AUTHOR'}
90 || eval { @{ [ getpwuid($<) ] }[6] }
91 || 'Catalyst developer';
93 my $gen_scripts = ( $self->{makefile} ) ? 0 : 1;
94 my $gen_makefile = ( $self->{scripts} ) ? 0 : 1;
95 my $gen_app = ( $self->{scripts} || $self->{makefile} ) ? 0 : 1;
101 $self->_mk_rootclass;
117 $self->_mk_information;
126 $self->{author} = $self->{author} = $ENV{'AUTHOR'}
127 || eval { @{ [ getpwuid($<) ] }[6] }
129 $self->{base} ||= File::Spec->catdir( $FindBin::Bin, '..' );
130 unless ( $_[0] =~ /^(?:model|view|controller)$/i ) {
133 my $class = "Catalyst::Helper::$helper";
134 eval "require $class";
137 Catalyst::Exception->throw(
138 message => qq/Couldn't load helper "$class", "$@"/ );
141 if ( $class->can('mk_stuff') ) {
142 return 1 unless $class->mk_stuff( $self, @args );
147 my $name = shift || "Missing name for model/view/controller";
150 return 0 if $name =~ /[^\w\:]/;
152 $self->{long_type} = ucfirst $type;
153 $type = 'M' if $type =~ /model/i;
154 $type = 'V' if $type =~ /view/i;
155 $type = 'C' if $type =~ /controller/i;
156 my $appdir = File::Spec->catdir( split /\:\:/, $app );
158 File::Spec->catdir( $FindBin::Bin, '..', 'lib', $appdir, 'C' );
159 $type = $self->{long_type} unless -d $test_path;
160 $self->{type} = $type;
161 $self->{name} = $name;
162 $self->{class} = "$app\::$type\::$name";
166 File::Spec->catdir( $FindBin::Bin, '..', 'lib', $appdir, $type );
168 if ( $name =~ /\:/ ) {
169 my @path = split /\:\:/, $name;
171 $path = File::Spec->catdir( $path, @path );
173 $self->mk_dir($path);
174 $file = File::Spec->catfile( $path, "$file.pm" );
175 $self->{file} = $file;
178 $self->{test_dir} = File::Spec->catdir( $FindBin::Bin, '..', 't' );
179 $self->{test} = $self->next_test;
183 my $comp = $self->{long_type};
184 my $class = "Catalyst::Helper::$comp\::$helper";
185 eval "require $class";
188 Catalyst::Exception->throw(
189 message => qq/Couldn't load helper "$class", "$@"/ );
192 if ( $class->can('mk_compclass') ) {
193 return 1 unless $class->mk_compclass( $self, @args );
195 else { return 1 unless $self->_mk_compclass }
197 if ( $class->can('mk_comptest') ) {
198 $class->mk_comptest( $self, @args );
200 else { $self->_mk_comptest }
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::Spec->catfile( $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 $self->render_file_contents($template, $path, $vars);
289 sub render_file_contents {
290 my ( $self, $template, $path, $vars ) = @_;
292 my $t = Template->new;
293 return 0 unless $template;
295 $t->process( \$template, { %{$self}, %$vars }, \$output )
296 || Catalyst::Exception->throw(
297 message => qq/Couldn't process "$template", / . $t->error() );
298 $self->mk_file( $path, $output );
301 sub _mk_information {
303 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
308 $self->mk_dir( $self->{dir} );
309 $self->mk_dir( $self->{script} );
310 $self->{lib} = File::Spec->catdir( $self->{dir}, 'lib' );
311 $self->mk_dir( $self->{lib} );
312 $self->{root} = File::Spec->catdir( $self->{dir}, 'root' );
313 $self->mk_dir( $self->{root} );
314 $self->{static} = File::Spec->catdir( $self->{root}, 'static' );
315 $self->mk_dir( $self->{static} );
316 $self->{images} = File::Spec->catdir( $self->{static}, 'images' );
317 $self->mk_dir( $self->{images} );
318 $self->{t} = File::Spec->catdir( $self->{dir}, 't' );
319 $self->mk_dir( $self->{t} );
321 $self->{class} = File::Spec->catdir( split( /\:\:/, $self->{name} ) );
322 $self->{mod} = File::Spec->catdir( $self->{lib}, $self->{class} );
323 $self->mk_dir( $self->{mod} );
325 if ( $self->{short} ) {
326 $self->{m} = File::Spec->catdir( $self->{mod}, 'M' );
327 $self->mk_dir( $self->{m} );
328 $self->{v} = File::Spec->catdir( $self->{mod}, 'V' );
329 $self->mk_dir( $self->{v} );
330 $self->{c} = File::Spec->catdir( $self->{mod}, 'C' );
331 $self->mk_dir( $self->{c} );
334 $self->{m} = File::Spec->catdir( $self->{mod}, 'Model' );
335 $self->mk_dir( $self->{m} );
336 $self->{v} = File::Spec->catdir( $self->{mod}, 'View' );
337 $self->mk_dir( $self->{v} );
338 $self->{c} = File::Spec->catdir( $self->{mod}, 'Controller' );
339 $self->mk_dir( $self->{c} );
341 my $name = $self->{name};
343 $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
344 $self->{base} = File::Spec->rel2abs( $self->{dir} );
349 my $mod = $self->{mod};
350 $self->render_sharedir_file( File::Spec->catfile('lib', 'MyApp.pm.tt'), "$mod.pm" );
355 $self->render_sharedir_file( File::Spec->catfile('lib', 'MyApp', 'Controller', 'Root.pm.tt'),
356 File::Spec->catfile( $self->{c}, "Root.pm" ) );
361 $self->{path} = File::Spec->catfile( 'lib', split( '::', $self->{name} ) );
362 $self->{path} .= '.pm';
363 my $dir = $self->{dir};
364 $self->render_sharedir_file( 'Makefile.PL.tt', "$dir\/Makefile.PL" );
366 if ( $self->{makefile} ) {
368 # deprecate the old Build.PL file when regenerating Makefile.PL
369 $self->_deprecate_file(
370 File::Spec->catdir( $self->{dir}, 'Build.PL' ) );
376 my $dir = $self->{dir};
377 my $appprefix = $self->{appprefix};
378 $self->render_sharedir_file( 'myapp.conf.tt',
379 File::Spec->catfile( $dir, "$appprefix.conf" ) );
384 my $dir = $self->{dir};
385 $self->render_sharedir_file( 'README.tt', "$dir\/README" );
390 my $dir = $self->{dir};
391 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
392 $self->render_sharedir_file( 'Changes.tt', "$dir\/Changes", { time => $time } );
398 $self->render_sharedir_file( File::Spec->catfile('t', '01app.t.tt'), "$t\/01app.t" );
399 $self->render_sharedir_file( File::Spec->catfile('t', '02pod.t.tt'), "$t\/02pod.t" );
400 $self->render_sharedir_file( File::Spec->catfile('t', '03podcoverage.t.tt'), "$t\/03podcoverage.t" );
405 my $script = $self->{script};
406 my $appprefix = $self->{appprefix};
407 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_cgi.pl.tt'), "$script\/$appprefix\_cgi.pl" );
408 chmod 0700, "$script/$appprefix\_cgi.pl";
413 my $script = $self->{script};
414 my $appprefix = $self->{appprefix};
415 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_fastcgi.pl.tt'), "$script\/$appprefix\_fastcgi.pl" );
416 chmod 0700, "$script/$appprefix\_fastcgi.pl";
421 my $script = $self->{script};
422 my $appprefix = $self->{appprefix};
423 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_server.pl.tt'), "$script\/$appprefix\_server.pl" );
424 chmod 0700, "$script/$appprefix\_server.pl";
429 my $script = $self->{script};
430 my $appprefix = $self->{appprefix};
431 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_test.pl.tt'), "$script/$appprefix\_test.pl" );
432 chmod 0700, "$script/$appprefix\_test.pl";
437 my $script = $self->{script};
438 my $appprefix = $self->{appprefix};
439 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_create.pl.tt'), "$script\/$appprefix\_create.pl" );
440 chmod 0700, "$script/$appprefix\_create.pl";
445 my $file = $self->{file};
446 return $self->render_sharedir_file( 'lib', 'Helper', 'compclass.tt', "$file" );
451 my $test = $self->{test};
452 $self->render_sharedir_file( 't', 'comptest.tt', "$test" ); ## wtf do i rename this to?
457 my $images = $self->{images};
459 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
460 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
461 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
462 for my $name (@images) {
463 my $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin");
464 $self->mk_file( File::Spec->catfile( $images, "$name.png" ), $image );
470 my $root = $self->{root};
471 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' );
472 my $dest = File::Spec->catfile( $root, "favicon.ico" );
473 $self->mk_file( $dest, $favicon );
477 sub _deprecate_file {
478 my ( $self, $file ) = @_;
481 if ( my $f = IO::File->new("< $file") ) {
482 $oldcontent = join( '', (<$f>) );
484 my $newfile = $file . '.deprecated';
485 if ( my $f = IO::File->new("> $newfile") ) {
487 print $f $oldcontent;
488 print qq/created "$newfile"\n/;
490 print qq/removed "$file"\n/;
493 Catalyst::Exception->throw(
494 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. + To override, please export the AUTHOR variable.
588 This method in your Helper module is called with C<$helper>
589 which is a L<Catalyst::Helper> object, and whichever other arguments
590 the user added to the command-line. You can use the $helper to call methods
593 If the Helper module does not contain a C<mk_compclass> method, it
594 will fall back to calling L</render_file>, with an argument of
599 This method in your Helper module is called with C<$helper>
600 which is a L<Catalyst::Helper> object, and whichever other arguments
601 the user added to the command-line. You can use the $helper to call methods
604 If the Helper module does not contain a C<mk_compclass> method, it
605 will fall back to calling L</render_file>, with an argument of
610 This method is called if the user does not supply any of the usual
611 component types C<view>, C<controller>, C<model>. It is passed the
612 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
613 arguments the user typed.
615 There is no fallback for this method.
617 =head1 INTERNAL METHODS
619 These are the methods that the Helper classes can call on the
620 <$helper> object passed to them.
622 =head2 render_file ($file, $path, $vars)
624 Render and create a file from a template in DATA using Template
625 Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
626 the path to the file and $vars is the hashref as expected by
627 L<Template Toolkit|Template>.
629 =head2 get_file ($class, $file)
631 Fetch file contents from the DATA section. This is used internally by
632 L</render_file>. $class is the name of the class to get the DATA
633 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
638 Create the main application skeleton. This is called by L<catalyst.pl>.
640 =head2 mk_component ($app)
642 This method is called by L<create.pl> to make new components
643 for your application.
645 =head3 mk_dir ($path)
647 Surprisingly, this function makes a directory.
649 =head2 mk_file ($file, $content)
651 Writes content to a file. Called by L</render_file>.
653 =head2 next_test ($test_name)
655 Calculates the name of the next numbered test file and returns it.
656 Don't give the number or the .t suffix for the test name.
660 Alias for L<Path::Class::Dir>
664 =head2 get_sharedir_file
666 Method for getting a file out of share/
670 =head2 render_file_contents
672 Process a L<Template::Toolkit> template.
676 =head2 render_sharedir_file
678 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.