1 package Catalyst::Helper;
13 use Catalyst::Exception;
14 use Path::Class qw/dir file/;
15 use File::ShareDir qw/dist_dir/;
16 use namespace::autoclean;
18 with 'MooseX::Emulate::Class::Accessor::Fast';
20 # Change Catalyst/Devel.pm also
21 our $VERSION = '1.23';
27 Catalyst::Helper - Bootstrap a Catalyst application
31 catalyst.pl <myappname>
35 sub get_sharedir_file {
36 my ($self, @filename) = @_;
39 if (exists $self->{base}) {
40 push @try_dirs, $self->{base};
42 if (exists $ENV{CATALYST_DEVEL_SHAREDIR}) {
43 push @try_dirs, $ENV{CATALYST_DEVEL_SHAREDIR}
45 if (-d "inc/.author" && -f "lib/Catalyst/Helper.pm"
46 ) { # Can't use sharedir if we're in a checkout
47 # this feels horrible, better ideas?
48 push @try_dirs, 'share';
51 push @try_dirs, dist_dir('Catalyst-Devel');
55 foreach my $dist_dir (@try_dirs) {
56 $file = file( $dist_dir, @filename);
59 Carp::confess("Cannot find $file") unless -r $file;
60 my $contents = $file->slurp;
64 # Do not touch this method, *EVER*, it is needed for back compat.
66 my ( $self, $class, $file ) = @_;
67 unless ( $cache{$class} ) {
69 $cache{$class} = eval "package $class; <DATA>";
71 my $data = $cache{$class};
72 Carp::confess("Could not get data from __DATA__ segment for $class")
74 my @files = split /^__(.+)__\r?\n/m, $data;
77 my ( $name, $content ) = splice @files, 0, 2;
78 return $content if $name eq $file;
85 my ( $self, $name ) = @_;
87 # Needs to be here for PAR
90 if ( $name =~ /[^\w:]/ || $name =~ /^\d/ || $name =~ /\b:\b|:{3,}/) {
91 warn "Error: Invalid application name.\n";
94 $self->{name } = $name;
95 $self->{dir } = $name;
96 $self->{dir } =~ s/\:\:/-/g;
97 $self->{script } = dir( $self->{dir}, 'script' );
98 $self->{appprefix } = Catalyst::Utils::appprefix($name);
99 $self->{appenv } = Catalyst::Utils::class2env($name);
100 $self->{startperl } = -r '/usr/bin/env'
101 ? '#!/usr/bin/env perl'
102 : "#!$Config{perlpath} -w";
103 $self->{scriptgen } = $Catalyst::Devel::CATALYST_SCRIPT_GEN;
104 $self->{catalyst_version} = $Catalyst::VERSION;
105 $self->{author } = $self->{author} = $ENV{'AUTHOR'}
106 || eval { @{ [ getpwuid($<) ] }[6] }
107 || 'Catalyst developer';
109 my $gen_scripts = ( $self->{makefile} ) ? 0 : 1;
110 my $gen_makefile = ( $self->{scripts} ) ? 0 : 1;
111 my $gen_app = ( $self->{scripts} || $self->{makefile} ) ? 0 : 1;
114 for ( qw/ _mk_dirs _mk_config _mk_appclass _mk_rootclass _mk_readme
115 _mk_changes _mk_apptest _mk_images _mk_favicon/ ) {
124 for ( qw/ _mk_cgi _mk_fastcgi _mk_server
125 _mk_test _mk_create _mk_information
133 ## not much of this can really be changed, mk_compclass must be left for
139 $self->{author} = $self->{author} = $ENV{'AUTHOR'}
140 || eval { @{ [ getpwuid($<) ] }[6] }
142 $self->{base} ||= dir( $FindBin::Bin, '..' );
143 unless ( $_[0] =~ /^(?:model|view|controller)$/i ) {
146 my $class = "Catalyst::Helper::$helper";
147 eval "require $class";
150 Catalyst::Exception->throw(
151 message => qq/Couldn't load helper "$class", "$@"/ );
154 if ( $class->can('mk_stuff') ) {
155 return 1 unless $class->mk_stuff( $self, @args );
160 my $name = shift || "Missing name for model/view/controller";
163 return 0 if $name =~ /[^\w\:]/;
165 $self->{long_type} = ucfirst $type;
166 $type = 'M' if $type =~ /model/i;
167 $type = 'V' if $type =~ /view/i;
168 $type = 'C' if $type =~ /controller/i;
169 my $appdir = dir( split /\:\:/, $app );
171 dir( $self->{base}, 'lib', $appdir, 'C' );
172 $type = $self->{long_type} unless -d $test_path;
173 $self->{type} = $type;
174 $self->{name} = $name;
175 $self->{class} = "$app\::$type\::$name";
179 dir( $self->{base}, 'lib', $appdir, $type );
181 if ( $name =~ /\:/ ) {
182 my @path = split /\:\:/, $name;
184 $path = dir( $path, @path );
186 $self->mk_dir($path);
187 $file = file( $path, "$file.pm" );
188 $self->{file} = $file;
191 $self->{test_dir} = dir( $self->{base}, 't' );
192 $self->{test} = $self->next_test;
196 my $comp = $self->{long_type};
197 my $class = "Catalyst::Helper::$comp\::$helper";
198 eval "require $class";
201 Catalyst::Exception->throw(
202 message => qq/Couldn't load helper "$class", "$@"/ );
205 if ( $class->can('mk_compclass') ) {
206 return 1 unless $class->mk_compclass( $self, @args );
209 return 1 unless $self->_mk_compclass
212 if ( $class->can('mk_comptest') ) {
213 $class->mk_comptest( $self, @args );
222 return 1 unless $self->_mk_compclass;
230 my ( $self, $dir ) = @_;
232 print qq/ exists "$dir"\n/;
235 if ( mkpath [$dir] ) {
236 print qq/created "$dir"\n/;
240 Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
244 my ( $self, $file, $content ) = @_;
245 if ( -e $file && -s _ ) {
246 print qq/ exists "$file"\n/;
248 unless ( $self->{'.newfiles'}
250 || $self->{makefile} );
251 if ( $self->{'.newfiles'} ) {
252 if ( my $f = IO::File->new("< $file") ) {
253 my $oldcontent = join( '', (<$f>) );
254 return 0 if $content eq $oldcontent;
260 if ( my $f = IO::File->new("> $file") ) {
263 print qq/created "$file"\n/;
267 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
271 my ( $self, $tname ) = @_;
272 if ($tname) { $tname = "$tname.t" }
274 my $name = $self->{name};
278 $tname = $prefix . '.t';
279 $self->{prefix} = $prefix;
280 $prefix = lc $prefix;
282 $self->{uri} = "/$prefix";
284 my $dir = $self->{test_dir};
285 my $type = lc $self->{type};
287 return file( $dir, "$type\_$tname" );
290 # Do not touch this method, *EVER*, it is needed for back compat.
291 ## addendum: we had to split this method so we could have backwards
292 ## compatability. otherwise, we'd have no way to pass stuff from __DATA__
295 my ( $self, $file, $path, $vars ) = @_;
296 my $template = $self->get_file( ( caller(0) )[0], $file );
297 $self->render_file_contents($template, $path, $vars);
300 sub render_sharedir_file {
301 my ( $self, $file, $path, $vars ) = @_;
302 my $template = $self->get_sharedir_file( $file );
303 die("Cannot get template from $file for $self\n") unless $template;
304 $self->render_file_contents($template, $path, $vars);
307 sub render_file_contents {
308 my ( $self, $template, $path, $vars ) = @_;
310 my $t = Template->new;
311 return 0 unless $template;
313 $t->process( \$template, { %{$self}, %$vars }, \$output )
314 || Catalyst::Exception->throw(
315 message => qq/Couldn't process "$template", / . $t->error() );
316 $self->mk_file( $path, $output );
319 sub _mk_information {
321 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
326 $self->mk_dir( $self->{dir} );
327 $self->mk_dir( $self->{script} );
328 $self->{lib} = dir( $self->{dir}, 'lib' );
329 $self->mk_dir( $self->{lib} );
330 $self->{root} = dir( $self->{dir}, 'root' );
331 $self->mk_dir( $self->{root} );
332 $self->{static} = dir( $self->{root}, 'static' );
333 $self->mk_dir( $self->{static} );
334 $self->{images} = dir( $self->{static}, 'images' );
335 $self->mk_dir( $self->{images} );
336 $self->{t} = dir( $self->{dir}, 't' );
337 $self->mk_dir( $self->{t} );
339 $self->{class} = dir( split( /\:\:/, $self->{name} ) );
340 $self->{mod} = dir( $self->{lib}, $self->{class} );
341 $self->mk_dir( $self->{mod} );
343 if ( $self->{short} ) {
344 $self->{m} = dir( $self->{mod}, 'M' );
345 $self->mk_dir( $self->{m} );
346 $self->{v} = dir( $self->{mod}, 'V' );
347 $self->mk_dir( $self->{v} );
348 $self->{c} = dir( $self->{mod}, 'C' );
349 $self->mk_dir( $self->{c} );
352 $self->{m} = dir( $self->{mod}, 'Model' );
353 $self->mk_dir( $self->{m} );
354 $self->{v} = dir( $self->{mod}, 'View' );
355 $self->mk_dir( $self->{v} );
356 $self->{c} = dir( $self->{mod}, 'Controller' );
357 $self->mk_dir( $self->{c} );
359 my $name = $self->{name};
361 $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
362 $self->{base} = dir( $self->{dir} )->absolute;
367 my $mod = $self->{mod};
368 $self->render_sharedir_file( file('lib', 'MyApp.pm.tt'), "$mod.pm" );
373 $self->render_sharedir_file( file('lib', 'MyApp', 'Controller', 'Root.pm.tt'),
374 file( $self->{c}, "Root.pm" ) );
379 $self->{path} = dir( 'lib', split( '::', $self->{name} ) );
380 $self->{path} .= '.pm';
381 my $dir = $self->{dir};
382 $self->render_sharedir_file( 'Makefile.PL.tt', file($dir, "Makefile.PL") );
384 if ( $self->{makefile} ) {
386 # deprecate the old Build.PL file when regenerating Makefile.PL
387 $self->_deprecate_file(
388 file( $self->{dir}, 'Build.PL' ) );
394 my $dir = $self->{dir};
395 my $appprefix = $self->{appprefix};
396 $self->render_sharedir_file( 'myapp.conf.tt',
397 file( $dir, "$appprefix.conf" ) );
402 my $dir = $self->{dir};
403 $self->render_sharedir_file( 'README.tt', file($dir, "README") );
408 my $dir = $self->{dir};
409 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
410 $self->render_sharedir_file( 'Changes.tt', file($dir, "Changes"), { time => $time } );
416 $self->render_sharedir_file( file('t', '01app.t.tt'), file($t, "01app.t") );
417 $self->render_sharedir_file( file('t', '02pod.t.tt'), file($t, "02pod.t") );
418 $self->render_sharedir_file( file('t', '03podcoverage.t.tt'), file($t, "03podcoverage.t") );
423 my $script = $self->{script};
424 my $appprefix = $self->{appprefix};
425 $self->render_sharedir_file( file('script', 'myapp_cgi.pl.tt'), file($script,"$appprefix\_cgi.pl") );
426 chmod 0700, file($script,"$appprefix\_cgi.pl");
431 my $script = $self->{script};
432 my $appprefix = $self->{appprefix};
433 $self->render_sharedir_file( file('script', 'myapp_fastcgi.pl.tt'), file($script, "$appprefix\_fastcgi.pl") );
434 chmod 0700, file($script, "$appprefix\_fastcgi.pl");
439 my $script = $self->{script};
440 my $appprefix = $self->{appprefix};
441 $self->render_sharedir_file( file('script', 'myapp_server.pl.tt'), file($script, "$appprefix\_server.pl") );
442 chmod 0700, file($script, "$appprefix\_server.pl");
447 my $script = $self->{script};
448 my $appprefix = $self->{appprefix};
449 $self->render_sharedir_file( file('script', 'myapp_test.pl.tt'), file($script, "$appprefix\_test.pl") );
450 chmod 0700, file($script, "$appprefix\_test.pl");
455 my $script = $self->{script};
456 my $appprefix = $self->{appprefix};
457 $self->render_sharedir_file( file('script', 'myapp_create.pl.tt'), file($script, "$appprefix\_create.pl") );
458 chmod 0700, file($script, "$appprefix\_create.pl");
463 my $file = $self->{file};
464 return $self->render_sharedir_file( file('lib', 'Helper', 'compclass.pm.tt'), $file );
469 my $test = $self->{test};
470 $self->render_sharedir_file( file('t', 'comptest.tt'), $test ); ## wtf do i rename this to?
475 my $images = $self->{images};
477 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
478 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
479 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
480 for my $name (@images) {
481 my $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin");
482 $self->mk_file( file( $images, "$name.png" ), $image );
488 my $root = $self->{root};
489 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' );
490 my $dest = dir( $root, "favicon.ico" );
491 $self->mk_file( $dest, $favicon );
495 sub _deprecate_file {
496 my ( $self, $file ) = @_;
498 my ($f, $oldcontent);
499 if ( $f = IO::File->new("< $file") ) {
500 $oldcontent = join( '', (<$f>) );
502 my $newfile = $file . '.deprecated';
503 if ( $f = IO::File->new("> $newfile") ) {
505 print $f $oldcontent;
506 print qq/created "$newfile"\n/;
508 print qq/removed "$file"\n/;
511 Catalyst::Exception->throw(
512 message => qq/Couldn't create "$file", "$!"/ );
518 This module is used by B<catalyst.pl> to create a set of scripts for a
519 new catalyst application. The scripts each contain documentation and
520 will output help on how to use them if called incorrectly or in some
521 cases, with no arguments.
523 It also provides some useful methods for a Helper module to call when
524 creating a component. See L</METHODS>.
530 Used to create new components for a catalyst application at the
535 The catalyst test server, starts an HTTPD which outputs debugging to
540 A script for running tests from the command-line.
544 Run your application as a CGI.
548 Run the application as a fastcgi app. Either by hand, or call this
549 from FastCgiServer in your http server config.
553 The L</_create.pl> script creates application components using Helper
554 modules. The Catalyst team provides a good number of Helper modules
555 for you to use. You can also add your own.
557 Helpers are classes that provide two methods.
559 * mk_compclass - creates the Component class
560 * mk_comptest - creates the Component test
562 So when you call C<scripts/myapp_create.pl view MyView TT>, create
563 will try to execute Catalyst::Helper::View::TT->mk_compclass and
564 Catalyst::Helper::View::TT->mk_comptest.
566 See L<Catalyst::Helper::View::TT> and
567 L<Catalyst::Helper::Model::DBIC::Schema> for examples.
569 All helper classes should be under one of the following namespaces.
571 Catalyst::Helper::Model::
572 Catalyst::Helper::View::
573 Catalyst::Helper::Controller::
575 =head2 COMMON HELPERS
581 L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
585 L<Catalyst::Helper::View::TT> - Template Toolkit view
589 L<Catalyst::Helper::Model::LDAP>
593 L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
599 The helpers will read author name from /etc/passwd by default.
600 To override, please export the AUTHOR variable.
606 This method in your Helper module is called with C<$helper>
607 which is a L<Catalyst::Helper> object, and whichever other arguments
608 the user added to the command-line. You can use the $helper to call methods
611 If the Helper module does not contain a C<mk_compclass> method, it
612 will fall back to calling L</render_file>, with an argument of
617 This method in your Helper module is called with C<$helper>
618 which is a L<Catalyst::Helper> object, and whichever other arguments
619 the user added to the command-line. You can use the $helper to call methods
622 If the Helper module does not contain a C<mk_compclass> method, it
623 will fall back to calling L</render_file>, with an argument of
628 This method is called if the user does not supply any of the usual
629 component types C<view>, C<controller>, C<model>. It is passed the
630 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
631 arguments the user typed.
633 There is no fallback for this method.
635 =head1 INTERNAL METHODS
637 These are the methods that the Helper classes can call on the
638 <$helper> object passed to them.
640 =head2 render_file ($file, $path, $vars)
642 Render and create a file from a template in DATA using Template
643 Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
644 the path to the file and $vars is the hashref as expected by
645 L<Template Toolkit|Template>.
647 =head2 get_file ($class, $file)
649 Fetch file contents from the DATA section. This is used internally by
650 L</render_file>. $class is the name of the class to get the DATA
651 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
656 Create the main application skeleton. This is called by L<catalyst.pl>.
658 =head2 mk_component ($app)
660 This method is called by L<create.pl> to make new components
661 for your application.
663 =head2 mk_dir ($path)
665 Surprisingly, this function makes a directory.
667 =head2 mk_file ($file, $content)
669 Writes content to a file. Called by L</render_file>.
671 =head2 next_test ($test_name)
673 Calculates the name of the next numbered test file and returns it.
674 Don't give the number or the .t suffix for the test name.
678 =head2 get_sharedir_file
680 Method for getting a file out of share/
684 =head2 render_file_contents
686 Process a L<Template::Toolkit> template.
690 =head2 render_sharedir_file
692 Render a template/image file from our share directory
698 The helpers will read author name from /etc/passwd by default.
699 To override, please export the AUTHOR variable.
703 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
704 L<Catalyst::Response>, L<Catalyst>
708 Catalyst Contributors, see Catalyst.pm
712 This library is free software. You can redistribute it and/or modify
713 it under the same terms as Perl itself.