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/;
19 use aliased 'Path::Class::Dir';
26 Catalyst::Helper - Bootstrap a Catalyst application
30 catalyst.pl <myappname>
36 sub get_sharedir_file {
37 my ($self, @filename) = @_;
39 if (-d "inc/.author") { # Can't use sharedir if we're in a checkout
40 # this feels horrible, better ideas?
44 $dist_dir = dist_dir('Catalyst-Devel');
46 my $file = file( $dist_dir, @filename);
47 my $contents = $file->slurp;
51 # Do not touch this method, *EVER*, it is needed for back compat.
53 my ( $self, $class, $file ) = @_;
54 unless ( $cache{$class} ) {
56 $cache{$class} = eval "package $class; <DATA>";
58 my $data = $cache{$class};
59 my @files = split /^__(.+)__\r?\n/m, $data;
62 my ( $name, $content ) = splice @files, 0, 2;
63 return $content if $name eq $file;
70 my ( $self, $name ) = @_;
72 # Needs to be here for PAR
75 if ( $name =~ /[^\w:]/ || $name =~ /^\d/ || $name =~ /\b:\b|:{3,}/) {
76 warn "Error: Invalid application name.\n";
79 $self->{name } = $name;
80 $self->{dir } = $name;
81 $self->{dir } =~ s/\:\:/-/g;
82 $self->{script } = File::Spec->catdir( $self->{dir}, 'script' );
83 $self->{appprefix } = Catalyst::Utils::appprefix($name);
84 $self->{appenv } = Catalyst::Utils::class2env($name);
85 $self->{startperl } = -r '/usr/bin/env'
86 ? '#!/usr/bin/env perl'
87 : "#!$Config{perlpath} -w";
88 $self->{scriptgen } = $Catalyst::Devel::CATALYST_SCRIPT_GEN || 4;
89 $self->{catalyst_version} = $Catalyst::VERSION;
90 $self->{author } = $self->{author} = $ENV{'AUTHOR'}
91 || eval { @{ [ getpwuid($<) ] }[6] }
92 || 'Catalyst developer';
94 my $gen_scripts = ( $self->{makefile} ) ? 0 : 1;
95 my $gen_makefile = ( $self->{scripts} ) ? 0 : 1;
96 my $gen_app = ( $self->{scripts} || $self->{makefile} ) ? 0 : 1;
102 $self->_mk_rootclass;
118 $self->_mk_information;
127 $self->{author} = $self->{author} = $ENV{'AUTHOR'}
128 || eval { @{ [ getpwuid($<) ] }[6] }
130 $self->{base} ||= File::Spec->catdir( $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 = File::Spec->catdir( split /\:\:/, $app );
159 File::Spec->catdir( $FindBin::Bin, '..', '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 File::Spec->catdir( $FindBin::Bin, '..', 'lib', $appdir, $type );
169 if ( $name =~ /\:/ ) {
170 my @path = split /\:\:/, $name;
172 $path = File::Spec->catdir( $path, @path );
174 $self->mk_dir($path);
175 $file = File::Spec->catfile( $path, "$file.pm" );
176 $self->{file} = $file;
179 $self->{test_dir} = File::Spec->catdir( $FindBin::Bin, '..', '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 ) = @_;
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;
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.
276 my ( $self, $file, $path, $vars ) = @_;
278 my $t = Template->new;
279 my $template = $self->get_sharedir_file( 'root', $file );
280 return 0 unless $template;
282 $t->process( \$template, { %{$self}, %$vars }, \$output )
283 || Catalyst::Exception->throw(
284 message => qq/Couldn't process "$file", / . $t->error() );
285 $self->mk_file( $path, $output );
288 sub _mk_information {
290 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
295 $self->mk_dir( $self->{dir} );
296 $self->mk_dir( $self->{script} );
297 $self->{lib} = File::Spec->catdir( $self->{dir}, 'lib' );
298 $self->mk_dir( $self->{lib} );
299 $self->{root} = File::Spec->catdir( $self->{dir}, 'root' );
300 $self->mk_dir( $self->{root} );
301 $self->{static} = File::Spec->catdir( $self->{root}, 'static' );
302 $self->mk_dir( $self->{static} );
303 $self->{images} = File::Spec->catdir( $self->{static}, 'images' );
304 $self->mk_dir( $self->{images} );
305 $self->{t} = File::Spec->catdir( $self->{dir}, 't' );
306 $self->mk_dir( $self->{t} );
308 $self->{class} = File::Spec->catdir( split( /\:\:/, $self->{name} ) );
309 $self->{mod} = File::Spec->catdir( $self->{lib}, $self->{class} );
310 $self->mk_dir( $self->{mod} );
312 if ( $self->{short} ) {
313 $self->{m} = File::Spec->catdir( $self->{mod}, 'M' );
314 $self->mk_dir( $self->{m} );
315 $self->{v} = File::Spec->catdir( $self->{mod}, 'V' );
316 $self->mk_dir( $self->{v} );
317 $self->{c} = File::Spec->catdir( $self->{mod}, 'C' );
318 $self->mk_dir( $self->{c} );
321 $self->{m} = File::Spec->catdir( $self->{mod}, 'Model' );
322 $self->mk_dir( $self->{m} );
323 $self->{v} = File::Spec->catdir( $self->{mod}, 'View' );
324 $self->mk_dir( $self->{v} );
325 $self->{c} = File::Spec->catdir( $self->{mod}, 'Controller' );
326 $self->mk_dir( $self->{c} );
328 my $name = $self->{name};
330 $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
331 $self->{base} = File::Spec->rel2abs( $self->{dir} );
336 my $mod = $self->{mod};
337 $self->render_file( 'appclass.tt', "$mod.pm" );
342 $self->render_file( 'rootclass.tt',
343 File::Spec->catfile( $self->{c}, "Root.pm" ) );
348 $self->{path} = File::Spec->catfile( 'lib', split( '::', $self->{name} ) );
349 $self->{path} .= '.pm';
350 my $dir = $self->{dir};
351 $self->render_file( 'makefile.tt', "$dir\/Makefile.PL" );
353 if ( $self->{makefile} ) {
355 # deprecate the old Build.PL file when regenerating Makefile.PL
356 $self->_deprecate_file(
357 File::Spec->catdir( $self->{dir}, 'Build.PL' ) );
363 my $dir = $self->{dir};
364 my $appprefix = $self->{appprefix};
365 $self->render_file( 'config.tt',
366 File::Spec->catfile( $dir, "$appprefix.conf" ) );
371 my $dir = $self->{dir};
372 $self->render_file( 'readme.tt', "$dir\/README" );
377 my $dir = $self->{dir};
378 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
379 $self->render_file( 'changes.tt', "$dir\/Changes", { time => $time } );
385 $self->render_file( 'apptest.tt', "$t\/01app.t" );
386 $self->render_file( 'podtest.tt', "$t\/02pod.t" );
387 $self->render_file( 'podcoveragetest.tt', "$t\/03podcoverage.t" );
392 my $script = $self->{script};
393 my $appprefix = $self->{appprefix};
394 $self->render_file( 'cgi.tt', "$script\/$appprefix\_cgi.pl" );
395 chmod 0700, "$script/$appprefix\_cgi.pl";
400 my $script = $self->{script};
401 my $appprefix = $self->{appprefix};
402 $self->render_file( 'fastcgi.tt', "$script\/$appprefix\_fastcgi.pl" );
403 chmod 0700, "$script/$appprefix\_fastcgi.pl";
408 my $script = $self->{script};
409 my $appprefix = $self->{appprefix};
410 $self->render_file( 'server.tt', "$script\/$appprefix\_server.pl" );
411 chmod 0700, "$script/$appprefix\_server.pl";
416 my $script = $self->{script};
417 my $appprefix = $self->{appprefix};
418 $self->render_file( 'test.tt', "$script/$appprefix\_test.pl" );
419 chmod 0700, "$script/$appprefix\_test.pl";
424 my $script = $self->{script};
425 my $appprefix = $self->{appprefix};
426 $self->render_file( 'create.tt', "$script\/$appprefix\_create.pl" );
427 chmod 0700, "$script/$appprefix\_create.pl";
432 my $file = $self->{file};
433 return $self->render_file( 'compclass.tt', "$file" );
438 my $test = $self->{test};
439 $self->render_file( 'comptest.tt', "$test" );
444 my $images = $self->{images};
446 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
447 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
448 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
449 for my $name (@images) {
450 my $image = $self->get_sharedir_file("root", "$name.png");
451 $self->mk_file( File::Spec->catfile( $images, "$name.png" ), $image );
457 my $root = $self->{root};
458 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico' );
459 my $dest = File::Spec->catfile( $root, "favicon.ico" );
460 $self->mk_file( $dest, $favicon );
464 sub _deprecate_file {
465 my ( $self, $file ) = @_;
468 if ( my $f = IO::File->new("< $file") ) {
469 $oldcontent = join( '', (<$f>) );
471 my $newfile = $file . '.deprecated';
472 if ( my $f = IO::File->new("> $newfile") ) {
474 print $f $oldcontent;
475 print qq/created "$newfile"\n/;
477 print qq/removed "$file"\n/;
480 Catalyst::Exception->throw(
481 message => qq/Couldn't create "$file", "$!"/ );
486 ## this is so you don't have to do make install after every change to test
487 sub _find_share_dir {
488 my ($self, $args) = @_;
489 my $share_name = $self->name;
490 if ($share_name =~ s!^/(.*?)/!!) {
492 $args->{share_base_dir} = eval {
493 Dir->new(File::ShareDir::dist_dir($dist))
499 my $dir = Dir->new(dirname($file));
501 while ($dir->parent) {
502 if (-d $dir->subdir('share') && -d $dir->subdir('share')->subdir('root')) {
503 $share_base = $dir->subdir('share')->subdir('root');
508 confess "could not find sharebase by recursion. ended up at $dir, from $file"
510 $args->{share_base_dir} = $share_base;
513 my $base = $args->{share_base_dir}->subdir($share_name);
514 confess "No such share base directory ${base}"
516 $self->share_dir($base);
523 This module is used by B<catalyst.pl> to create a set of scripts for a
524 new catalyst application. The scripts each contain documentation and
525 will output help on how to use them if called incorrectly or in some
526 cases, with no arguments.
528 It also provides some useful methods for a Helper module to call when
529 creating a component. See L</METHODS>.
535 Used to create new components for a catalyst application at the
540 The catalyst test server, starts an HTTPD which outputs debugging to
545 A script for running tests from the command-line.
549 Run your application as a CGI.
553 Run the application as a fastcgi app. Either by hand, or call this
554 from FastCgiServer in your http server config.
558 The L</_create.pl> script creates application components using Helper
559 modules. The Catalyst team provides a good number of Helper modules
560 for you to use. You can also add your own.
562 Helpers are classes that provide two methods.
564 * mk_compclass - creates the Component class
565 * mk_comptest - creates the Component test
567 So when you call C<scripts/myapp_create.pl view MyView TT>, create
568 will try to execute Catalyst::Helper::View::TT->mk_compclass and
569 Catalyst::Helper::View::TT->mk_comptest.
571 See L<Catalyst::Helper::View::TT> and
572 L<Catalyst::Helper::Model::DBIC::Schema> for examples.
574 All helper classes should be under one of the following namespaces.
576 Catalyst::Helper::Model::
577 Catalyst::Helper::View::
578 Catalyst::Helper::Controller::
580 =head2 COMMON HELPERS
586 L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
590 L<Catalyst::Helper::View::TT> - Template Toolkit view
594 L<Catalyst::Helper::Model::LDAP>
598 L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
604 The helpers will read author name from /etc/passwd by default. + To override, please export the AUTHOR variable.
610 This method in your Helper module is called with C<$helper>
611 which is a L<Catalyst::Helper> object, and whichever other arguments
612 the user added to the command-line. You can use the $helper to call methods
615 If the Helper module does not contain a C<mk_compclass> method, it
616 will fall back to calling L</render_file>, with an argument of
621 This method in your Helper module is called with C<$helper>
622 which is a L<Catalyst::Helper> object, and whichever other arguments
623 the user added to the command-line. You can use the $helper to call methods
626 If the Helper module does not contain a C<mk_compclass> method, it
627 will fall back to calling L</render_file>, with an argument of
632 This method is called if the user does not supply any of the usual
633 component types C<view>, C<controller>, C<model>. It is passed the
634 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
635 arguments the user typed.
637 There is no fallback for this method.
639 =head1 INTERNAL METHODS
641 These are the methods that the Helper classes can call on the
642 <$helper> object passed to them.
644 =head2 render_file ($file, $path, $vars)
646 Render and create a file from a template in DATA using Template
647 Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
648 the path to the file and $vars is the hashref as expected by
649 L<Template Toolkit|Template>.
651 =head2 get_file ($class, $file)
653 Fetch file contents from the DATA section. This is used internally by
654 L</render_file>. $class is the name of the class to get the DATA
655 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
660 Create the main application skeleton. This is called by L<catalyst.pl>.
662 =head2 mk_component ($app)
664 This method is called by L<create.pl> to make new components
665 for your application.
667 =head3 mk_dir ($path)
669 Surprisingly, this function makes a directory.
671 =head2 mk_file ($file, $content)
673 Writes content to a file. Called by L</render_file>.
675 =head2 next_test ($test_name)
677 Calculates the name of the next numbered test file and returns it.
678 Don't give the number or the .t suffix for the test name.
682 The helpers will read author name from /etc/passwd by default.
683 To override, please export the AUTHOR variable.
687 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
688 L<Catalyst::Response>, L<Catalyst>
692 Catalyst Contributors, see Catalyst.pm
696 This library is free software. You can redistribute it and/or modify
697 it under the same terms as Perl itself.