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;
242 if ( my $f = IO::File->new("> $file") ) {
245 print qq/created "$file"\n/;
249 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
253 my ( $self, $tname ) = @_;
254 if ($tname) { $tname = "$tname.t" }
256 my $name = $self->{name};
260 $tname = $prefix . '.t';
261 $self->{prefix} = $prefix;
262 $prefix = lc $prefix;
264 $self->{uri} = "/$prefix";
266 my $dir = $self->{test_dir};
267 my $type = lc $self->{type};
269 return File::Spec->catfile( $dir, "$type\_$tname" );
272 # Do not touch this method, *EVER*, it is needed for back compat.
273 ## addendum: we had to split this method so we could have backwards
274 ## compatability. otherwise, we'd have no way to pass stuff from __DATA__
277 my ( $self, $file, $path, $vars ) = @_;
278 my $template = $self->get_file( ( caller(0) )[0], $file );
279 $self->render_file_contents($template, $path, $vars);
282 sub render_sharedir_file {
283 my ( $self, $file, $path, $vars ) = @_;
284 my $template = $self->get_sharedir_file( $file );
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( 'lib', 'Helper', 'compclass.pl.tt', "$file" );
450 my $test = $self->{test};
451 $self->render_sharedir_file( '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", "$!"/ );
498 ## this is so you don't have to do make install after every change to test
499 sub _find_share_dir {
500 my ($self, $args) = @_;
501 my $share_name = $self->name;
502 if ($share_name =~ s!^/(.*?)/!!) {
504 $args->{share_base_dir} = eval {
505 Dir->new(File::ShareDir::dist_dir($dist))
511 my $dir = Dir->new(dirname($file));
513 while ($dir->parent) {
514 if (-d $dir->subdir('share') && -d $dir->subdir('share')->subdir('root')) {
515 $share_base = $dir->subdir('share')->subdir('root');
520 confess "could not find sharebase by recursion. ended up at $dir, from $file"
522 $args->{share_base_dir} = $share_base;
525 my $base = $args->{share_base_dir}->subdir($share_name);
526 confess "No such share base directory ${base}"
528 $self->share_dir($base);
535 This module is used by B<catalyst.pl> to create a set of scripts for a
536 new catalyst application. The scripts each contain documentation and
537 will output help on how to use them if called incorrectly or in some
538 cases, with no arguments.
540 It also provides some useful methods for a Helper module to call when
541 creating a component. See L</METHODS>.
547 Used to create new components for a catalyst application at the
552 The catalyst test server, starts an HTTPD which outputs debugging to
557 A script for running tests from the command-line.
561 Run your application as a CGI.
565 Run the application as a fastcgi app. Either by hand, or call this
566 from FastCgiServer in your http server config.
570 The L</_create.pl> script creates application components using Helper
571 modules. The Catalyst team provides a good number of Helper modules
572 for you to use. You can also add your own.
574 Helpers are classes that provide two methods.
576 * mk_compclass - creates the Component class
577 * mk_comptest - creates the Component test
579 So when you call C<scripts/myapp_create.pl view MyView TT>, create
580 will try to execute Catalyst::Helper::View::TT->mk_compclass and
581 Catalyst::Helper::View::TT->mk_comptest.
583 See L<Catalyst::Helper::View::TT> and
584 L<Catalyst::Helper::Model::DBIC::Schema> for examples.
586 All helper classes should be under one of the following namespaces.
588 Catalyst::Helper::Model::
589 Catalyst::Helper::View::
590 Catalyst::Helper::Controller::
592 =head2 COMMON HELPERS
598 L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
602 L<Catalyst::Helper::View::TT> - Template Toolkit view
606 L<Catalyst::Helper::Model::LDAP>
610 L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
616 The helpers will read author name from /etc/passwd by default. + To override, please export the AUTHOR variable.
622 This method in your Helper module is called with C<$helper>
623 which is a L<Catalyst::Helper> object, and whichever other arguments
624 the user added to the command-line. You can use the $helper to call methods
627 If the Helper module does not contain a C<mk_compclass> method, it
628 will fall back to calling L</render_file>, with an argument of
633 This method in your Helper module is called with C<$helper>
634 which is a L<Catalyst::Helper> object, and whichever other arguments
635 the user added to the command-line. You can use the $helper to call methods
638 If the Helper module does not contain a C<mk_compclass> method, it
639 will fall back to calling L</render_file>, with an argument of
644 This method is called if the user does not supply any of the usual
645 component types C<view>, C<controller>, C<model>. It is passed the
646 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
647 arguments the user typed.
649 There is no fallback for this method.
651 =head1 INTERNAL METHODS
653 These are the methods that the Helper classes can call on the
654 <$helper> object passed to them.
656 =head2 render_file ($file, $path, $vars)
658 Render and create a file from a template in DATA using Template
659 Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
660 the path to the file and $vars is the hashref as expected by
661 L<Template Toolkit|Template>.
663 =head2 get_file ($class, $file)
665 Fetch file contents from the DATA section. This is used internally by
666 L</render_file>. $class is the name of the class to get the DATA
667 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
672 Create the main application skeleton. This is called by L<catalyst.pl>.
674 =head2 mk_component ($app)
676 This method is called by L<create.pl> to make new components
677 for your application.
679 =head3 mk_dir ($path)
681 Surprisingly, this function makes a directory.
683 =head2 mk_file ($file, $content)
685 Writes content to a file. Called by L</render_file>.
687 =head2 next_test ($test_name)
689 Calculates the name of the next numbered test file and returns it.
690 Don't give the number or the .t suffix for the test name.
694 Alias for L<Path::Class::Dir>
698 =head2 get_sharedir_file
700 Method for getting a file out of share/
704 =head2 render_file_contents
706 Process a L<Template::Toolkit> template.
710 =head2 render_sharedir_file
712 Render a template/image file from our share directory
719 The helpers will read author name from /etc/passwd by default.
720 To override, please export the AUTHOR variable.
724 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
725 L<Catalyst::Response>, L<Catalyst>
729 Catalyst Contributors, see Catalyst.pm
733 This library is free software. You can redistribute it and/or modify
734 it under the same terms as Perl itself.