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.29';
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 } ||= $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_podtest _mk_podcoveragetest
126 _mk_images _mk_favicon/ ) {
135 for ( qw/ _mk_cgi _mk_fastcgi _mk_server
136 _mk_test _mk_create _mk_information
144 ## not much of this can really be changed, mk_compclass must be left for
150 $self->{author} = $self->{author} = $ENV{'AUTHOR'}
151 || eval { @{ [ getpwuid($<) ] }[6] }
153 $self->{base} ||= dir( $FindBin::Bin, '..' );
154 unless ( $_[0] =~ /^(?:model|view|controller)$/i ) {
157 my $class = "Catalyst::Helper::$helper";
158 eval "require $class";
161 Catalyst::Exception->throw(
162 message => qq/Couldn't load helper "$class", "$@"/ );
165 if ( $class->can('mk_stuff') ) {
166 return 1 unless $class->mk_stuff( $self, @args );
171 my $name = shift || "Missing name for model/view/controller";
174 return 0 if $name =~ /[^\w\:]/;
176 $self->{long_type} = ucfirst $type;
177 $type = 'M' if $type =~ /model/i;
178 $type = 'V' if $type =~ /view/i;
179 $type = 'C' if $type =~ /controller/i;
180 my $appdir = dir( split /\:\:/, $app );
182 dir( $self->{base}, 'lib', $appdir, 'C' );
183 $type = $self->{long_type} unless -d $test_path;
184 $self->{type} = $type;
185 $self->{name} = $name;
186 $self->{class} = "$app\::$type\::$name";
190 dir( $self->{base}, 'lib', $appdir, $type );
192 if ( $name =~ /\:/ ) {
193 my @path = split /\:\:/, $name;
195 $path = dir( $path, @path );
197 $self->mk_dir($path);
198 $file = file( $path, "$file.pm" );
199 $self->{file} = $file;
202 $self->{test_dir} = dir( $self->{base}, 't' );
203 $self->{test} = $self->next_test;
207 my $comp = $self->{long_type};
208 my $class = "Catalyst::Helper::$comp\::$helper";
209 eval "require $class";
212 Catalyst::Exception->throw(
213 message => qq/Couldn't load helper "$class", "$@"/ );
216 if ( $class->can('mk_compclass') ) {
217 return 1 unless $class->mk_compclass( $self, @args );
220 return 1 unless $self->_mk_compclass
223 if ( $class->can('mk_comptest') ) {
224 $class->mk_comptest( $self, @args );
233 return 1 unless $self->_mk_compclass;
241 my ( $self, $dir ) = @_;
243 print qq/ exists "$dir"\n/;
246 if ( mkpath [$dir] ) {
247 print qq/created "$dir"\n/;
251 Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
255 my ( $self, $file, $content ) = @_;
256 if ( -e $file && -s _ ) {
257 print qq/ exists "$file"\n/;
259 unless ( $self->{'.newfiles'}
261 || $self->{makefile} );
262 if ( $self->{'.newfiles'} ) {
263 if ( my $f = IO::File->new("< $file") ) {
264 my $oldcontent = join( '', (<$f>) );
265 return 0 if $content eq $oldcontent;
271 if ( my $f = IO::File->new("> $file") ) {
274 print qq/created "$file"\n/;
278 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
282 my ( $self, $tname ) = @_;
283 if ($tname) { $tname = "$tname.t" }
285 my $name = $self->{name};
289 $tname = $prefix . '.t';
290 $self->{prefix} = $prefix;
291 $prefix = lc $prefix;
293 $self->{uri} = "/$prefix";
295 my $dir = $self->{test_dir};
296 my $type = lc $self->{type};
298 return file( $dir, "$type\_$tname" );
301 # Do not touch this method, *EVER*, it is needed for back compat.
302 ## addendum: we had to split this method so we could have backwards
303 ## compatability. otherwise, we'd have no way to pass stuff from __DATA__
306 my ( $self, $file, $path, $vars ) = @_;
307 my $template = $self->get_file( ( caller(0) )[0], $file );
308 $self->render_file_contents($template, $path, $vars);
311 sub render_sharedir_file {
312 my ( $self, $file, $path, $vars ) = @_;
313 my $template = $self->get_sharedir_file( $file );
314 die("Cannot get template from $file for $self\n") unless $template;
315 $self->render_file_contents($template, $path, $vars);
318 sub render_file_contents {
319 my ( $self, $template, $path, $vars ) = @_;
321 my $t = Template->new;
322 return 0 unless $template;
324 $t->process( \$template, { %{$self}, %$vars }, \$output )
325 || Catalyst::Exception->throw(
326 message => qq/Couldn't process "$template", / . $t->error() );
327 $self->mk_file( $path, $output );
330 sub _mk_information {
332 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
337 $self->mk_dir( $self->{dir} );
338 $self->mk_dir( $self->{script} );
339 $self->{lib} = dir( $self->{dir}, 'lib' );
340 $self->mk_dir( $self->{lib} );
341 $self->{root} = dir( $self->{dir}, 'root' );
342 $self->mk_dir( $self->{root} );
343 $self->{static} = dir( $self->{root}, 'static' );
344 $self->mk_dir( $self->{static} );
345 $self->{images} = dir( $self->{static}, 'images' );
346 $self->mk_dir( $self->{images} );
347 $self->{t} = dir( $self->{dir}, 't' );
348 $self->mk_dir( $self->{t} );
350 $self->{class} = dir( split( /\:\:/, $self->{name} ) );
351 $self->{mod} = dir( $self->{lib}, $self->{class} );
352 $self->mk_dir( $self->{mod} );
354 if ( $self->{short} ) {
355 $self->{m} = dir( $self->{mod}, 'M' );
356 $self->mk_dir( $self->{m} );
357 $self->{v} = dir( $self->{mod}, 'V' );
358 $self->mk_dir( $self->{v} );
359 $self->{c} = dir( $self->{mod}, 'C' );
360 $self->mk_dir( $self->{c} );
363 $self->{m} = dir( $self->{mod}, 'Model' );
364 $self->mk_dir( $self->{m} );
365 $self->{v} = dir( $self->{mod}, 'View' );
366 $self->mk_dir( $self->{v} );
367 $self->{c} = dir( $self->{mod}, 'Controller' );
368 $self->mk_dir( $self->{c} );
370 my $name = $self->{name};
372 $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
373 $self->{base} = dir( $self->{dir} )->absolute;
378 my $mod = $self->{mod};
379 $self->render_sharedir_file( file('lib', 'MyApp.pm.tt'), "$mod.pm" );
384 $self->render_sharedir_file( file('lib', 'MyApp', 'Controller', 'Root.pm.tt'),
385 file( $self->{c}, "Root.pm" ) );
390 $self->{path} = dir( 'lib', split( '::', $self->{name} ) );
391 $self->{path} .= '.pm';
392 my $dir = $self->{dir};
393 $self->render_sharedir_file( 'Makefile.PL.tt', file($dir, "Makefile.PL") );
395 if ( $self->{makefile} ) {
397 # deprecate the old Build.PL file when regenerating Makefile.PL
398 $self->_deprecate_file(
399 file( $self->{dir}, 'Build.PL' ) );
405 my $dir = $self->{dir};
406 my $appprefix = $self->{appprefix};
407 $self->render_sharedir_file( 'myapp.conf.tt',
408 file( $dir, "$appprefix.conf" ) );
413 my $dir = $self->{dir};
414 $self->render_sharedir_file( 'README.tt', file($dir, "README") );
419 my $dir = $self->{dir};
420 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
421 $self->render_sharedir_file( 'Changes.tt', file($dir, "Changes"), { time => $time } );
427 $self->render_sharedir_file( file('t', '01app.t.tt'), file($t, "01app.t") );
433 $self->render_sharedir_file( file('t', '02pod.t.tt'), file($t, "02pod.t") );
436 sub _mk_podcoveragetest {
439 $self->render_sharedir_file( file('t', '03podcoverage.t.tt'), file($t, "03podcoverage.t") );
444 my $script = $self->{script};
445 my $appprefix = $self->{appprefix};
446 $self->render_sharedir_file( file('script', 'myapp_cgi.pl.tt'), file($script,"$appprefix\_cgi.pl") );
447 chmod 0700, file($script,"$appprefix\_cgi.pl");
452 my $script = $self->{script};
453 my $appprefix = $self->{appprefix};
454 $self->render_sharedir_file( file('script', 'myapp_fastcgi.pl.tt'), file($script, "$appprefix\_fastcgi.pl") );
455 chmod 0700, file($script, "$appprefix\_fastcgi.pl");
460 my $script = $self->{script};
461 my $appprefix = $self->{appprefix};
462 $self->render_sharedir_file( file('script', 'myapp_server.pl.tt'), file($script, "$appprefix\_server.pl") );
463 chmod 0700, file($script, "$appprefix\_server.pl");
468 my $script = $self->{script};
469 my $appprefix = $self->{appprefix};
470 $self->render_sharedir_file( file('script', 'myapp_test.pl.tt'), file($script, "$appprefix\_test.pl") );
471 chmod 0700, file($script, "$appprefix\_test.pl");
476 my $script = $self->{script};
477 my $appprefix = $self->{appprefix};
478 $self->render_sharedir_file( file('script', 'myapp_create.pl.tt'), file($script, "$appprefix\_create.pl") );
479 chmod 0700, file($script, "$appprefix\_create.pl");
484 my $file = $self->{file};
485 return $self->render_sharedir_file( file('lib', 'Helper', 'compclass.pm.tt'), $file );
490 my $test = $self->{test};
491 $self->render_sharedir_file( file('t', 'comptest.tt'), $test ); ## wtf do i rename this to?
496 my $images = $self->{images};
498 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
499 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
500 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
501 for my $name (@images) {
502 my $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin");
503 $self->mk_file( file( $images, "$name.png" ), $image );
509 my $root = $self->{root};
510 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' );
511 my $dest = dir( $root, "favicon.ico" );
512 $self->mk_file( $dest, $favicon );
516 sub _deprecate_file {
517 my ( $self, $file ) = @_;
519 my ($f, $oldcontent);
520 if ( $f = IO::File->new("< $file") ) {
521 $oldcontent = join( '', (<$f>) );
523 my $newfile = $file . '.deprecated';
524 if ( $f = IO::File->new("> $newfile") ) {
526 print $f $oldcontent;
527 print qq/created "$newfile"\n/;
529 print qq/removed "$file"\n/;
532 Catalyst::Exception->throw(
533 message => qq/Couldn't create "$file", "$!"/ );
539 This module is used by B<catalyst.pl> to create a set of scripts for a
540 new catalyst application. The scripts each contain documentation and
541 will output help on how to use them if called incorrectly or in some
542 cases, with no arguments.
544 It also provides some useful methods for a Helper module to call when
545 creating a component. See L</METHODS>.
551 Used to create new components for a catalyst application at the
556 The catalyst test server, starts an HTTPD which outputs debugging to
561 A script for running tests from the command-line.
565 Run your application as a CGI.
569 Run the application as a fastcgi app. Either by hand, or call this
570 from FastCgiServer in your http server config.
574 The L</_create.pl> script creates application components using Helper
575 modules. The Catalyst team provides a good number of Helper modules
576 for you to use. You can also add your own.
578 Helpers are classes that provide two methods.
580 * mk_compclass - creates the Component class
581 * mk_comptest - creates the Component test
583 So when you call C<scripts/myapp_create.pl view MyView TT>, create
584 will try to execute Catalyst::Helper::View::TT->mk_compclass and
585 Catalyst::Helper::View::TT->mk_comptest.
587 See L<Catalyst::Helper::View::TT> and
588 L<Catalyst::Helper::Model::DBIC::Schema> for examples.
590 All helper classes should be under one of the following namespaces.
592 Catalyst::Helper::Model::
593 Catalyst::Helper::View::
594 Catalyst::Helper::Controller::
596 =head2 COMMON HELPERS
602 L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
606 L<Catalyst::Helper::View::TT> - Template Toolkit view
610 L<Catalyst::Helper::Model::LDAP>
614 L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
620 The helpers will read author name from /etc/passwd by default.
621 To override, please export the AUTHOR variable.
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 in your Helper module is called with C<$helper>
639 which is a L<Catalyst::Helper> object, and whichever other arguments
640 the user added to the command-line. You can use the $helper to call methods
643 If the Helper module does not contain a C<mk_compclass> method, it
644 will fall back to calling L</render_file>, with an argument of
649 This method is called if the user does not supply any of the usual
650 component types C<view>, C<controller>, C<model>. It is passed the
651 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
652 arguments the user typed.
654 There is no fallback for this method.
656 =head1 INTERNAL METHODS
658 These are the methods that the Helper classes can call on the
659 <$helper> object passed to them.
661 =head2 render_file ($file, $path, $vars)
663 Render and create a file from a template in DATA using Template
664 Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
665 the path to the file and $vars is the hashref as expected by
666 L<Template Toolkit|Template>.
668 =head2 get_file ($class, $file)
670 Fetch file contents from the DATA section. This is used internally by
671 L</render_file>. $class is the name of the class to get the DATA
672 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
677 Create the main application skeleton. This is called by L<catalyst.pl>.
679 =head2 mk_component ($app)
681 This method is called by L<create.pl> to make new components
682 for your application.
684 =head2 mk_dir ($path)
686 Surprisingly, this function makes a directory.
688 =head2 mk_file ($file, $content)
690 Writes content to a file. Called by L</render_file>.
692 =head2 next_test ($test_name)
694 Calculates the name of the next numbered test file and returns it.
695 Don't give the number or the .t suffix for the test name.
699 =head2 get_sharedir_file
701 Method for getting a file out of share/
705 =head2 render_file_contents
707 Process a L<Template::Toolkit> template.
711 =head2 render_sharedir_file
713 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.