1 package Catalyst::Helper;
13 use Catalyst::Exception;
14 use Path::Class qw/dir file/;
15 use File::ShareDir qw/dist_dir/;
17 use namespace::autoclean;
19 with 'MooseX::Emulate::Class::Accessor::Fast';
21 # Change Catalyst/Devel.pm also
22 our $VERSION = '1.28';
28 Catalyst::Helper - Bootstrap a Catalyst application
32 catalyst.pl <myappname>
36 sub get_sharedir_file {
37 my ($self, @filename) = @_;
39 if (exists $ENV{CATALYST_DEVEL_SHAREDIR}) {
40 $dist_dir = $ENV{CATALYST_DEVEL_SHAREDIR};
42 elsif (-d "inc/.author" && -f "lib/Catalyst/Helper.pm"
43 ) { # Can't use sharedir if we're in a checkout
44 # this feels horrible, better ideas?
48 $dist_dir = dist_dir('Catalyst-Devel');
50 my $file = file( $dist_dir, @filename);
51 Carp::confess("Cannot find $file") unless -r $file;
52 my $contents = $file->slurp;
56 # Do not touch this method, *EVER*, it is needed for back compat.
58 my ( $self, $class, $file ) = @_;
59 unless ( $cache{$class} ) {
61 $cache{$class} = eval "package $class; <DATA>";
63 my $data = $cache{$class};
64 Carp::confess("Could not get data from __DATA__ segment for $class")
66 my @files = split /^__(.+)__\r?\n/m, $data;
69 my ( $name, $content ) = splice @files, 0, 2;
70 return $content if $name eq $file;
77 my ( $self, $name ) = @_;
79 # Needs to be here for PAR
84 system perl => 'Makefile.PL'
85 and Catalyst::Exception->throw(message => q(
86 Failed to run "perl Makefile.PL".
90 $name = YAML::Tiny->read('META.yml')->[0]->{'name'};
95 if ( $name =~ /[^\w:]/ || $name =~ /^\d/ || $name =~ /\b:\b|:{3,}/) {
96 warn "Error: Invalid application name.\n";
101 if(!defined $self->{'dir'}) {
102 $self->{dir} = $name;
103 $self->{dir} =~ s/\:\:/-/g;
106 $self->{name } = $name;
107 $self->{script } = dir( $self->{dir}, 'script' );
108 $self->{appprefix } = Catalyst::Utils::appprefix($name);
109 $self->{appenv } = Catalyst::Utils::class2env($name);
110 $self->{startperl } = -r '/usr/bin/env'
111 ? '#!/usr/bin/env perl'
112 : "#!$Config{perlpath}";
113 $self->{scriptgen } = $Catalyst::Devel::CATALYST_SCRIPT_GEN;
114 $self->{catalyst_version} = $Catalyst::VERSION;
115 $self->{author } = $self->{author} = $ENV{'AUTHOR'}
116 || eval { @{ [ getpwuid($<) ] }[6] }
117 || 'Catalyst developer';
119 my $gen_scripts = ( $self->{makefile} ) ? 0 : 1;
120 my $gen_makefile = ( $self->{scripts} ) ? 0 : 1;
121 my $gen_app = ( $self->{scripts} || $self->{makefile} ) ? 0 : 1;
124 for ( qw/ _mk_dirs _mk_config _mk_appclass _mk_rootclass _mk_readme
125 _mk_changes _mk_apptest _mk_images _mk_favicon/ ) {
134 for ( qw/ _mk_cgi _mk_fastcgi _mk_server
135 _mk_test _mk_create _mk_information
143 ## not much of this can really be changed, mk_compclass must be left for
149 $self->{author} = $self->{author} = $ENV{'AUTHOR'}
150 || eval { @{ [ getpwuid($<) ] }[6] }
152 $self->{base} ||= dir( $FindBin::Bin, '..' );
153 unless ( $_[0] =~ /^(?:model|view|controller)$/i ) {
156 my $class = "Catalyst::Helper::$helper";
157 eval "require $class";
160 Catalyst::Exception->throw(
161 message => qq/Couldn't load helper "$class", "$@"/ );
164 if ( $class->can('mk_stuff') ) {
165 return 1 unless $class->mk_stuff( $self, @args );
170 my $name = shift || "Missing name for model/view/controller";
173 return 0 if $name =~ /[^\w\:]/;
175 $self->{long_type} = ucfirst $type;
176 $type = 'M' if $type =~ /model/i;
177 $type = 'V' if $type =~ /view/i;
178 $type = 'C' if $type =~ /controller/i;
179 my $appdir = dir( split /\:\:/, $app );
181 dir( $self->{base}, 'lib', $appdir, 'C' );
182 $type = $self->{long_type} unless -d $test_path;
183 $self->{type} = $type;
184 $self->{name} = $name;
185 $self->{class} = "$app\::$type\::$name";
189 dir( $self->{base}, 'lib', $appdir, $type );
191 if ( $name =~ /\:/ ) {
192 my @path = split /\:\:/, $name;
194 $path = dir( $path, @path );
196 $self->mk_dir($path);
197 $file = file( $path, "$file.pm" );
198 $self->{file} = $file;
201 $self->{test_dir} = dir( $self->{base}, 't' );
202 $self->{test} = $self->next_test;
206 my $comp = $self->{long_type};
207 my $class = "Catalyst::Helper::$comp\::$helper";
208 eval "require $class";
211 Catalyst::Exception->throw(
212 message => qq/Couldn't load helper "$class", "$@"/ );
215 if ( $class->can('mk_compclass') ) {
216 return 1 unless $class->mk_compclass( $self, @args );
219 return 1 unless $self->_mk_compclass
222 if ( $class->can('mk_comptest') ) {
223 $class->mk_comptest( $self, @args );
232 return 1 unless $self->_mk_compclass;
240 my ( $self, $dir ) = @_;
242 print qq/ exists "$dir"\n/;
245 if ( mkpath [$dir] ) {
246 print qq/created "$dir"\n/;
250 Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
254 my ( $self, $file, $content ) = @_;
255 if ( -e $file && -s _ ) {
256 print qq/ exists "$file"\n/;
258 unless ( $self->{'.newfiles'}
260 || $self->{makefile} );
261 if ( $self->{'.newfiles'} ) {
262 if ( my $f = IO::File->new("< $file") ) {
263 my $oldcontent = join( '', (<$f>) );
264 return 0 if $content eq $oldcontent;
270 if ( my $f = IO::File->new("> $file") ) {
273 print qq/created "$file"\n/;
277 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
281 my ( $self, $tname ) = @_;
282 if ($tname) { $tname = "$tname.t" }
284 my $name = $self->{name};
288 $tname = $prefix . '.t';
289 $self->{prefix} = $prefix;
290 $prefix = lc $prefix;
292 $self->{uri} = "/$prefix";
294 my $dir = $self->{test_dir};
295 my $type = lc $self->{type};
297 return file( $dir, "$type\_$tname" );
300 # Do not touch this method, *EVER*, it is needed for back compat.
301 ## addendum: we had to split this method so we could have backwards
302 ## compatability. otherwise, we'd have no way to pass stuff from __DATA__
305 my ( $self, $file, $path, $vars ) = @_;
306 my $template = $self->get_file( ( caller(0) )[0], $file );
307 $self->render_file_contents($template, $path, $vars);
310 sub render_sharedir_file {
311 my ( $self, $file, $path, $vars ) = @_;
312 my $template = $self->get_sharedir_file( $file );
313 die("Cannot get template from $file for $self\n") unless $template;
314 $self->render_file_contents($template, $path, $vars);
317 sub render_file_contents {
318 my ( $self, $template, $path, $vars ) = @_;
320 my $t = Template->new;
321 return 0 unless $template;
323 $t->process( \$template, { %{$self}, %$vars }, \$output )
324 || Catalyst::Exception->throw(
325 message => qq/Couldn't process "$template", / . $t->error() );
326 $self->mk_file( $path, $output );
329 sub _mk_information {
331 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
336 $self->mk_dir( $self->{dir} );
337 $self->mk_dir( $self->{script} );
338 $self->{lib} = dir( $self->{dir}, 'lib' );
339 $self->mk_dir( $self->{lib} );
340 $self->{root} = dir( $self->{dir}, 'root' );
341 $self->mk_dir( $self->{root} );
342 $self->{static} = dir( $self->{root}, 'static' );
343 $self->mk_dir( $self->{static} );
344 $self->{images} = dir( $self->{static}, 'images' );
345 $self->mk_dir( $self->{images} );
346 $self->{t} = dir( $self->{dir}, 't' );
347 $self->mk_dir( $self->{t} );
349 $self->{class} = dir( split( /\:\:/, $self->{name} ) );
350 $self->{mod} = dir( $self->{lib}, $self->{class} );
351 $self->mk_dir( $self->{mod} );
353 if ( $self->{short} ) {
354 $self->{m} = dir( $self->{mod}, 'M' );
355 $self->mk_dir( $self->{m} );
356 $self->{v} = dir( $self->{mod}, 'V' );
357 $self->mk_dir( $self->{v} );
358 $self->{c} = dir( $self->{mod}, 'C' );
359 $self->mk_dir( $self->{c} );
362 $self->{m} = dir( $self->{mod}, 'Model' );
363 $self->mk_dir( $self->{m} );
364 $self->{v} = dir( $self->{mod}, 'View' );
365 $self->mk_dir( $self->{v} );
366 $self->{c} = dir( $self->{mod}, 'Controller' );
367 $self->mk_dir( $self->{c} );
369 my $name = $self->{name};
371 $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
372 $self->{base} = dir( $self->{dir} )->absolute;
377 my $mod = $self->{mod};
378 $self->render_sharedir_file( file('lib', 'MyApp.pm.tt'), "$mod.pm" );
383 $self->render_sharedir_file( file('lib', 'MyApp', 'Controller', 'Root.pm.tt'),
384 file( $self->{c}, "Root.pm" ) );
389 $self->{path} = dir( 'lib', split( '::', $self->{name} ) );
390 $self->{path} .= '.pm';
391 my $dir = $self->{dir};
392 $self->render_sharedir_file( 'Makefile.PL.tt', file($dir, "Makefile.PL") );
394 if ( $self->{makefile} ) {
396 # deprecate the old Build.PL file when regenerating Makefile.PL
397 $self->_deprecate_file(
398 file( $self->{dir}, 'Build.PL' ) );
404 my $dir = $self->{dir};
405 my $appprefix = $self->{appprefix};
406 $self->render_sharedir_file( 'myapp.conf.tt',
407 file( $dir, "$appprefix.conf" ) );
412 my $dir = $self->{dir};
413 $self->render_sharedir_file( 'README.tt', file($dir, "README") );
418 my $dir = $self->{dir};
419 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
420 $self->render_sharedir_file( 'Changes.tt', file($dir, "Changes"), { time => $time } );
426 $self->render_sharedir_file( file('t', '01app.t.tt'), file($t, "01app.t") );
427 $self->render_sharedir_file( file('t', '02pod.t.tt'), file($t, "02pod.t") );
428 $self->render_sharedir_file( file('t', '03podcoverage.t.tt'), file($t, "03podcoverage.t") );
433 my $script = $self->{script};
434 my $appprefix = $self->{appprefix};
435 $self->render_sharedir_file( file('script', 'myapp_cgi.pl.tt'), file($script,"$appprefix\_cgi.pl") );
436 chmod 0700, file($script,"$appprefix\_cgi.pl");
441 my $script = $self->{script};
442 my $appprefix = $self->{appprefix};
443 $self->render_sharedir_file( file('script', 'myapp_fastcgi.pl.tt'), file($script, "$appprefix\_fastcgi.pl") );
444 chmod 0700, file($script, "$appprefix\_fastcgi.pl");
449 my $script = $self->{script};
450 my $appprefix = $self->{appprefix};
451 $self->render_sharedir_file( file('script', 'myapp_server.pl.tt'), file($script, "$appprefix\_server.pl") );
452 chmod 0700, file($script, "$appprefix\_server.pl");
457 my $script = $self->{script};
458 my $appprefix = $self->{appprefix};
459 $self->render_sharedir_file( file('script', 'myapp_test.pl.tt'), file($script, "$appprefix\_test.pl") );
460 chmod 0700, file($script, "$appprefix\_test.pl");
465 my $script = $self->{script};
466 my $appprefix = $self->{appprefix};
467 $self->render_sharedir_file( file('script', 'myapp_create.pl.tt'), file($script, "$appprefix\_create.pl") );
468 chmod 0700, file($script, "$appprefix\_create.pl");
473 my $file = $self->{file};
474 return $self->render_sharedir_file( file('lib', 'Helper', 'compclass.pm.tt'), $file );
479 my $test = $self->{test};
480 $self->render_sharedir_file( file('t', 'comptest.tt'), $test ); ## wtf do i rename this to?
485 my $images = $self->{images};
487 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
488 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
489 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
490 for my $name (@images) {
491 my $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin");
492 $self->mk_file( file( $images, "$name.png" ), $image );
498 my $root = $self->{root};
499 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' );
500 my $dest = dir( $root, "favicon.ico" );
501 $self->mk_file( $dest, $favicon );
505 sub _deprecate_file {
506 my ( $self, $file ) = @_;
508 my ($f, $oldcontent);
509 if ( $f = IO::File->new("< $file") ) {
510 $oldcontent = join( '', (<$f>) );
512 my $newfile = $file . '.deprecated';
513 if ( $f = IO::File->new("> $newfile") ) {
515 print $f $oldcontent;
516 print qq/created "$newfile"\n/;
518 print qq/removed "$file"\n/;
521 Catalyst::Exception->throw(
522 message => qq/Couldn't create "$file", "$!"/ );
528 This module is used by B<catalyst.pl> to create a set of scripts for a
529 new catalyst application. The scripts each contain documentation and
530 will output help on how to use them if called incorrectly or in some
531 cases, with no arguments.
533 It also provides some useful methods for a Helper module to call when
534 creating a component. See L</METHODS>.
540 Used to create new components for a catalyst application at the
545 The catalyst test server, starts an HTTPD which outputs debugging to
550 A script for running tests from the command-line.
554 Run your application as a CGI.
558 Run the application as a fastcgi app. Either by hand, or call this
559 from FastCgiServer in your http server config.
563 The L</_create.pl> script creates application components using Helper
564 modules. The Catalyst team provides a good number of Helper modules
565 for you to use. You can also add your own.
567 Helpers are classes that provide two methods.
569 * mk_compclass - creates the Component class
570 * mk_comptest - creates the Component test
572 So when you call C<scripts/myapp_create.pl view MyView TT>, create
573 will try to execute Catalyst::Helper::View::TT->mk_compclass and
574 Catalyst::Helper::View::TT->mk_comptest.
576 See L<Catalyst::Helper::View::TT> and
577 L<Catalyst::Helper::Model::DBIC::Schema> for examples.
579 All helper classes should be under one of the following namespaces.
581 Catalyst::Helper::Model::
582 Catalyst::Helper::View::
583 Catalyst::Helper::Controller::
585 =head2 COMMON HELPERS
591 L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
595 L<Catalyst::Helper::View::TT> - Template Toolkit view
599 L<Catalyst::Helper::Model::LDAP>
603 L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
609 The helpers will read author name from /etc/passwd by default.
610 To override, please export the AUTHOR variable.
616 This method in your Helper module is called with C<$helper>
617 which is a L<Catalyst::Helper> object, and whichever other arguments
618 the user added to the command-line. You can use the $helper to call methods
621 If the Helper module does not contain a C<mk_compclass> method, it
622 will fall back to calling L</render_file>, with an argument of
627 This method in your Helper module is called with C<$helper>
628 which is a L<Catalyst::Helper> object, and whichever other arguments
629 the user added to the command-line. You can use the $helper to call methods
632 If the Helper module does not contain a C<mk_compclass> method, it
633 will fall back to calling L</render_file>, with an argument of
638 This method is called if the user does not supply any of the usual
639 component types C<view>, C<controller>, C<model>. It is passed the
640 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
641 arguments the user typed.
643 There is no fallback for this method.
645 =head1 INTERNAL METHODS
647 These are the methods that the Helper classes can call on the
648 <$helper> object passed to them.
650 =head2 render_file ($file, $path, $vars)
652 Render and create a file from a template in DATA using Template
653 Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
654 the path to the file and $vars is the hashref as expected by
655 L<Template Toolkit|Template>.
657 =head2 get_file ($class, $file)
659 Fetch file contents from the DATA section. This is used internally by
660 L</render_file>. $class is the name of the class to get the DATA
661 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
666 Create the main application skeleton. This is called by L<catalyst.pl>.
668 =head2 mk_component ($app)
670 This method is called by L<create.pl> to make new components
671 for your application.
673 =head2 mk_dir ($path)
675 Surprisingly, this function makes a directory.
677 =head2 mk_file ($file, $content)
679 Writes content to a file. Called by L</render_file>.
681 =head2 next_test ($test_name)
683 Calculates the name of the next numbered test file and returns it.
684 Don't give the number or the .t suffix for the test name.
688 =head2 get_sharedir_file
690 Method for getting a file out of share/
694 =head2 render_file_contents
696 Process a L<Template::Toolkit> template.
700 =head2 render_sharedir_file
702 Render a template/image file from our share directory
708 The helpers will read author name from /etc/passwd by default.
709 To override, please export the AUTHOR variable.
713 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
714 L<Catalyst::Response>, L<Catalyst>
718 Catalyst Contributors, see Catalyst.pm
722 This library is free software. You can redistribute it and/or modify
723 it under the same terms as Perl itself.