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 our $VERSION = '1.41';
26 sub get_sharedir_file {
27 my ($self, @filename) = @_;
29 if (exists $ENV{CATALYST_DEVEL_SHAREDIR}) {
30 $dist_dir = $ENV{CATALYST_DEVEL_SHAREDIR};
32 elsif (-d "inc/.author" && -f "lib/Catalyst/Helper.pm"
33 ) { # Can't use sharedir if we're in a checkout
34 # this feels horrible, better ideas?
38 $dist_dir = dist_dir('Catalyst-Devel');
40 my $file = file( $dist_dir, @filename);
41 Carp::confess("Cannot find $file") unless -r $file;
42 my $contents = $file->slurp(iomode => "<:raw");
46 # Do not touch this method, *EVER*, it is needed for back compat.
48 my ( $self, $class, $file ) = @_;
49 unless ( $cache{$class} ) {
51 $cache{$class} = eval "package $class; <DATA>";
53 my $data = $cache{$class};
54 Carp::confess("Could not get data from __DATA__ segment for $class")
56 my @files = split /^__(.+)__\r?\n/m, $data;
59 my ( $name, $content ) = splice @files, 0, 2;
60 return $content if $name eq $file;
67 my ( $self, $name ) = @_;
69 # Needs to be here for PAR
74 system perl => 'Makefile.PL'
75 and Catalyst::Exception->throw(message => q(
76 Failed to run "perl Makefile.PL".
80 $name = YAML::Tiny->read('META.yml')->[0]->{'name'};
85 if ( $name =~ /[^\w:]/ || $name =~ /^\d/ || $name =~ /\b:\b|:{3,}/) {
86 warn "Error: Invalid application name.\n";
91 if(!defined $self->{'dir'}) {
93 $self->{dir} =~ s/\:\:/-/g;
96 $self->{name } = $name;
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}";
103 $self->{scriptgen } = $Catalyst::Devel::CATALYST_SCRIPT_GEN;
104 $self->{catalyst_version} = $Catalyst::VERSION;
105 $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_psgi _mk_appclass _mk_rootclass
115 _mk_readme _mk_changes _mk_apptest _mk_podtest _mk_podcoveragetest
116 _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 ## compatibility. otherwise, we'd have no way to pass stuff from __DATA__
295 my ( $self, $file, $path, $vars, $perms ) = @_;
296 my $template = $self->get_file( ( caller(0) )[0], $file );
297 $self->render_file_contents($template, $path, $vars, $perms);
300 sub render_sharedir_file {
301 my ( $self, $file, $path, $vars, $perms ) = @_;
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, $perms);
307 sub render_file_contents {
308 my ( $self, $template, $path, $vars, $perms ) = @_;
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 my $file = $self->mk_file( $path, $output );
317 chmod $perms, file($file) if defined $perms;
321 sub _mk_information {
323 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
328 $self->mk_dir( $self->{dir} );
329 $self->mk_dir( $self->{script} );
330 $self->{lib} = dir( $self->{dir}, 'lib' );
331 $self->mk_dir( $self->{lib} );
332 $self->{root} = dir( $self->{dir}, 'root' );
333 $self->mk_dir( $self->{root} );
334 $self->{static} = dir( $self->{root}, 'static' );
335 $self->mk_dir( $self->{static} );
336 $self->{images} = dir( $self->{static}, 'images' );
337 $self->mk_dir( $self->{images} );
338 $self->{t} = dir( $self->{dir}, 't' );
339 $self->mk_dir( $self->{t} );
341 $self->{class} = dir( split( /\:\:/, $self->{name} ) );
342 $self->{mod} = dir( $self->{lib}, $self->{class} );
343 $self->mk_dir( $self->{mod} );
345 if ( $self->{short} ) {
346 $self->{m} = dir( $self->{mod}, 'M' );
347 $self->mk_dir( $self->{m} );
348 $self->{v} = dir( $self->{mod}, 'V' );
349 $self->mk_dir( $self->{v} );
350 $self->{c} = dir( $self->{mod}, 'C' );
351 $self->mk_dir( $self->{c} );
354 $self->{m} = dir( $self->{mod}, 'Model' );
355 $self->mk_dir( $self->{m} );
356 $self->{v} = dir( $self->{mod}, 'View' );
357 $self->mk_dir( $self->{v} );
358 $self->{c} = dir( $self->{mod}, 'Controller' );
359 $self->mk_dir( $self->{c} );
361 my $name = $self->{name};
363 $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
364 $self->{base} = dir( $self->{dir} )->absolute;
369 my $mod = $self->{mod};
370 $self->render_sharedir_file( file('lib', 'MyApp.pm.tt'), "$mod.pm" );
375 $self->render_sharedir_file( file('lib', 'MyApp', 'Controller', 'Root.pm.tt'),
376 file( $self->{c}, "Root.pm" ) );
381 $self->{path} = join('/', 'lib', split( '::', $self->{name} ) );
382 $self->{path} .= '.pm';
383 my $dir = $self->{dir};
384 $self->render_sharedir_file( 'Makefile.PL.tt', file($dir, "Makefile.PL") );
386 if ( $self->{makefile} ) {
388 # deprecate the old Build.PL file when regenerating Makefile.PL
389 $self->_deprecate_file(
390 file( $self->{dir}, 'Build.PL' ) );
396 my $dir = $self->{dir};
397 my $appprefix = $self->{appprefix};
398 $self->render_sharedir_file( 'myapp.psgi.tt',
399 file( $dir, "$appprefix.psgi" ) );
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") );
432 $self->render_sharedir_file( file('t', '02pod.t.tt'), file($t, "02pod.t") );
435 sub _mk_podcoveragetest {
438 $self->render_sharedir_file( file('t', '03podcoverage.t.tt'), file($t, "03podcoverage.t") );
443 my $script = $self->{script};
444 my $appprefix = $self->{appprefix};
445 $self->render_sharedir_file( file('script', 'myapp_cgi.pl.tt'),
446 file($script,"$appprefix\_cgi.pl"), undef, 0755 );
451 my $script = $self->{script};
452 my $appprefix = $self->{appprefix};
453 $self->render_sharedir_file( file('script', 'myapp_fastcgi.pl.tt'),
454 file($script, "$appprefix\_fastcgi.pl"), undef, 0755 );
459 my $script = $self->{script};
460 my $appprefix = $self->{appprefix};
461 $self->render_sharedir_file( file('script', 'myapp_server.pl.tt'),
462 file($script, "$appprefix\_server.pl"), undef, 0755 );
467 my $script = $self->{script};
468 my $appprefix = $self->{appprefix};
469 $self->render_sharedir_file( file('script', 'myapp_test.pl.tt'),
470 file($script, "$appprefix\_test.pl"), undef, 0755 );
475 my $script = $self->{script};
476 my $appprefix = $self->{appprefix};
477 $self->render_sharedir_file( file('script', 'myapp_create.pl.tt'),
478 file($script, "$appprefix\_create.pl"), undef, 0755 );
483 my $file = $self->{file};
484 return $self->render_sharedir_file( file('lib', 'Helper', 'compclass.pm.tt'), $file );
489 my $test = $self->{test};
490 $self->render_sharedir_file( file('t', 'comptest.tt'), $test ); ## wtf do i rename this to?
495 my $images = $self->{images};
497 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
498 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
499 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
500 for my $name (@images) {
501 my $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin");
502 $self->mk_file( file( $images, "$name.png" ), $image );
508 my $root = $self->{root};
509 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' );
510 my $dest = dir( $root, "favicon.ico" );
511 $self->mk_file( $dest, $favicon );
515 sub _deprecate_file {
516 my ( $self, $file ) = @_;
518 my ($f, $oldcontent);
519 if ( $f = IO::File->new("< $file") ) {
520 $oldcontent = join( '', (<$f>) );
522 my $newfile = $file . '.deprecated';
523 if ( $f = IO::File->new("> $newfile") ) {
525 print $f $oldcontent;
526 print qq/created "$newfile"\n/;
528 print qq/removed "$file"\n/;
531 Catalyst::Exception->throw(
532 message => qq/Couldn't create "$file", "$!"/ );
541 Catalyst::Helper - Bootstrap a Catalyst application
545 catalyst.pl <myappname>
549 This module is used by B<catalyst.pl> to create a set of scripts for a
550 new catalyst application. The scripts each contain documentation and
551 will output help on how to use them if called incorrectly or in some
552 cases, with no arguments.
554 It also provides some useful methods for a Helper module to call when
555 creating a component. See L</METHODS>.
561 Used to create new components for a catalyst application at the
566 The catalyst test server, starts an HTTPD which outputs debugging to
571 A script for running tests from the command-line.
575 Run your application as a CGI.
579 Run the application as a fastcgi app. Either by hand, or call this
580 from FastCgiServer in your http server config.
584 The L</_create.pl> script creates application components using Helper
585 modules. The Catalyst team provides a good number of Helper modules
586 for you to use. You can also add your own.
588 Helpers are classes that provide two methods.
590 * mk_compclass - creates the Component class
591 * mk_comptest - creates the Component test
593 So when you call C<scripts/myapp_create.pl view MyView TT>, create
594 will try to execute Catalyst::Helper::View::TT->mk_compclass and
595 Catalyst::Helper::View::TT->mk_comptest.
597 See L<Catalyst::Helper::View::TT> and
598 L<Catalyst::Helper::Model::DBIC::Schema> for examples.
600 All helper classes should be under one of the following namespaces.
602 Catalyst::Helper::Model::
603 Catalyst::Helper::View::
604 Catalyst::Helper::Controller::
606 =head2 COMMON HELPERS
612 L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
616 L<Catalyst::Helper::View::TT> - Template Toolkit view
620 L<Catalyst::Helper::Model::LDAP>
624 L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
630 The helpers will read author name from /etc/passwd by default.
631 To override, please export the AUTHOR variable.
637 This method in your Helper module is called with C<$helper>
638 which is a L<Catalyst::Helper> object, and whichever other arguments
639 the user added to the command-line. You can use the $helper to call methods
642 If the Helper module does not contain a C<mk_compclass> method, it
643 will fall back to calling L</render_file>, with an argument of
648 This method in your Helper module is called with C<$helper>
649 which is a L<Catalyst::Helper> object, and whichever other arguments
650 the user added to the command-line. You can use the $helper to call methods
653 If the Helper module does not contain a C<mk_compclass> method, it
654 will fall back to calling L</render_file>, with an argument of
659 This method is called if the user does not supply any of the usual
660 component types C<view>, C<controller>, C<model>. It is passed the
661 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
662 arguments the user typed.
664 There is no fallback for this method.
666 =head1 INTERNAL METHODS
668 These are the methods that the Helper classes can call on the
669 <$helper> object passed to them.
671 =head2 render_file ($file, $path, $vars, $perms)
673 Render and create a file from a template in DATA using Template
674 Toolkit. $file is the relevant chunk of the __DATA__ section, $path is
675 the path to the file, $vars is the hashref as expected by
676 L<Template Toolkit|Template> and $perms are desired permissions (or system
677 defaults if not set).
679 =head2 get_file ($class, $file)
681 Fetch file contents from the DATA section. This is used internally by
682 L</render_file>. $class is the name of the class to get the DATA
683 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
688 Create the main application skeleton. This is called by L<catalyst.pl>.
690 =head2 mk_component ($app)
692 This method is called by L<create.pl> to make new components
693 for your application.
695 =head2 mk_dir ($path)
697 Surprisingly, this function makes a directory.
699 =head2 mk_file ($file, $content)
701 Writes content to a file. Called by L</render_file>.
703 =head2 next_test ($test_name)
705 Calculates the name of the next numbered test file and returns it.
706 Don't give the number or the .t suffix for the test name.
710 =head2 get_sharedir_file
712 Method for getting a file out of share/
714 =head2 render_file_contents
716 Process a L<Template::Toolkit> template.
718 =head2 render_sharedir_file
720 Render a template/image file from our share directory
724 The helpers will read author name from /etc/passwd by default.
725 To override, please export the AUTHOR variable.
729 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
730 L<Catalyst::Response>, L<Catalyst>
734 Catalyst Contributors, see Catalyst.pm
738 This library is free software. You can redistribute it and/or modify
739 it under the same terms as Perl itself.