1 package Catalyst::Helper;
3 use Moose::Util::TypeConstraints;
14 use Catalyst::Exception;
15 use Path::Class qw/dir file/;
16 use File::ShareDir qw/dist_dir/;
17 use namespace::autoclean;
23 Catalyst::Helper - Bootstrap a Catalyst application
27 catalyst.pl <myappname>
31 sub get_sharedir_file {
32 my ($self, @filename) = @_;
34 if (-d "inc/.author" && -f "lib/Catalyst/Helper.pm"
35 ) { # Can't use sharedir if we're in a checkout
36 # this feels horrible, better ideas?
40 $dist_dir = dist_dir('Catalyst-Devel');
42 my $file = file( $dist_dir, @filename);
43 Carp::confess("Cannot find $file") unless -r $file;
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;
67 my $appname = subtype 'Str',
68 where { /[^\w:]/ or /^\d/ or /\b:\b|:{3,}/ },
69 message { "Error: Invalid application name." };
71 has name => ( is => 'ro', isa => $appname, required => 1 );
73 foreach my $name (qw/ dir script appprefix appenv author /) {
74 has $name => ( is => 'ro', isa => 'Str', init_arg => undef, lazy => 1, builder => "_build_$name" );
77 sub _build_dir { my $dir = shift->name; $dir =~ s/\:\:/-/g; return $dir; }
78 sub _build_script { dir( shift->dir, 'script' ) }
79 sub _build_appprefix { Catalyst::Utils::appprefix(shift->name) }
80 sub _build_appenv { Catalyst::Utils::appenv(shift->name) }
85 # Needs to be here for PAR
88 $self->{startperl } = -r '/usr/bin/env'
89 ? '#!/usr/bin/env perl'
90 : "#!$Config{perlpath} -w";
91 $self->{scriptgen } = $Catalyst::Devel::CATALYST_SCRIPT_GEN;
92 $self->{catalyst_version} = $Catalyst::VERSION;
93 $self->{author } = $self->{author} = $ENV{'AUTHOR'}
94 || eval { @{ [ getpwuid($<) ] }[6] }
95 || 'Catalyst developer';
97 my $gen_scripts = ( $self->{makefile} ) ? 0 : 1;
98 my $gen_makefile = ( $self->{scripts} ) ? 0 : 1;
99 my $gen_app = ( $self->{scripts} || $self->{makefile} ) ? 0 : 1;
102 for ( qw/ _mk_dirs _mk_config _mk_appclass _mk_rootclass _mk_readme
103 _mk_changes _mk_apptest _mk_images _mk_favicon/ ) {
112 for ( qw/ _mk_cgi _mk_fastcgi _mk_server
113 _mk_test _mk_create _mk_information
121 ## not much of this can really be changed, mk_compclass must be left for
127 $self->{author} = $self->{author} = $ENV{'AUTHOR'}
128 || eval { @{ [ getpwuid($<) ] }[6] }
130 $self->{base} ||= dir( $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 = dir( split /\:\:/, $app );
159 dir( $self->{base}, '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 dir( $self->{base}, 'lib', $appdir, $type );
169 if ( $name =~ /\:/ ) {
170 my @path = split /\:\:/, $name;
172 $path = dir( $path, @path );
174 $self->mk_dir($path);
175 $file = file( $path, "$file.pm" );
176 $self->{file} = $file;
179 $self->{test_dir} = dir( $self->{base}, '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 );
197 return 1 unless $self->_mk_compclass
200 if ( $class->can('mk_comptest') ) {
201 $class->mk_comptest( $self, @args );
210 return 1 unless $self->_mk_compclass;
218 my ( $self, $dir ) = @_;
220 print qq/ exists "$dir"\n/;
223 if ( mkpath [$dir] ) {
224 print qq/created "$dir"\n/;
228 Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
232 my ( $self, $file, $content ) = @_;
233 if ( -e $file && -s _ ) {
234 print qq/ exists "$file"\n/;
236 unless ( $self->{'.newfiles'}
238 || $self->{makefile} );
239 if ( $self->{'.newfiles'} ) {
240 if ( my $f = IO::File->new("< $file") ) {
241 my $oldcontent = join( '', (<$f>) );
242 return 0 if $content eq $oldcontent;
248 if ( my $f = IO::File->new("> $file") ) {
251 print qq/created "$file"\n/;
255 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
259 my ( $self, $tname ) = @_;
260 if ($tname) { $tname = "$tname.t" }
262 my $name = $self->{name};
266 $tname = $prefix . '.t';
267 $self->{prefix} = $prefix;
268 $prefix = lc $prefix;
270 $self->{uri} = "/$prefix";
272 my $dir = $self->{test_dir};
273 my $type = lc $self->{type};
275 return file( $dir, "$type\_$tname" );
278 # Do not touch this method, *EVER*, it is needed for back compat.
279 ## addendum: we had to split this method so we could have backwards
280 ## compatability. otherwise, we'd have no way to pass stuff from __DATA__
283 my ( $self, $file, $path, $vars ) = @_;
284 my $template = $self->get_file( ( caller(0) )[0], $file );
285 $self->render_file_contents($template, $path, $vars);
288 sub render_sharedir_file {
289 my ( $self, $file, $path, $vars ) = @_;
290 my $template = $self->get_sharedir_file( $file );
291 die("Cannot get template from $file for $self\n") unless $template;
292 $self->render_file_contents($template, $path, $vars);
295 sub render_file_contents {
296 my ( $self, $template, $path, $vars ) = @_;
298 my $t = Template->new;
299 return 0 unless $template;
301 $t->process( \$template, { %{$self}, %$vars }, \$output )
302 || Catalyst::Exception->throw(
303 message => qq/Couldn't process "$template", / . $t->error() );
304 $self->mk_file( $path, $output );
307 sub _mk_information {
309 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
314 $self->mk_dir( $self->{dir} );
315 $self->mk_dir( $self->{script} );
316 $self->{lib} = dir( $self->{dir}, 'lib' );
317 $self->mk_dir( $self->{lib} );
318 $self->{root} = dir( $self->{dir}, 'root' );
319 $self->mk_dir( $self->{root} );
320 $self->{static} = dir( $self->{root}, 'static' );
321 $self->mk_dir( $self->{static} );
322 $self->{images} = dir( $self->{static}, 'images' );
323 $self->mk_dir( $self->{images} );
324 $self->{t} = dir( $self->{dir}, 't' );
325 $self->mk_dir( $self->{t} );
327 $self->{class} = dir( split( /\:\:/, $self->{name} ) );
328 $self->{mod} = dir( $self->{lib}, $self->{class} );
329 $self->mk_dir( $self->{mod} );
331 $self->{m} = dir( $self->{mod}, 'Model' );
332 $self->mk_dir( $self->{m} );
333 $self->{v} = dir( $self->{mod}, 'View' );
334 $self->mk_dir( $self->{v} );
335 $self->{c} = dir( $self->{mod}, 'Controller' );
336 $self->mk_dir( $self->{c} );
338 my $name = $self->{name};
339 $self->{rootname} = "$name\::Controller::Root";
340 $self->{base} = dir( $self->{dir} )->absolute;
345 my $mod = $self->{mod};
346 $self->render_sharedir_file( file('lib', 'MyApp.pm.tt'), "$mod.pm" );
351 $self->render_sharedir_file( file('lib', 'MyApp', 'Controller', 'Root.pm.tt'),
352 file( $self->{c}, "Root.pm" ) );
357 $self->{path} = dir( 'lib', split( '::', $self->{name} ) );
358 $self->{path} .= '.pm';
359 my $dir = $self->{dir};
360 $self->render_sharedir_file( 'Makefile.PL.tt', file($dir, "Makefile.PL") );
362 if ( $self->{makefile} ) {
364 # deprecate the old Build.PL file when regenerating Makefile.PL
365 $self->_deprecate_file(
366 file( $self->{dir}, 'Build.PL' ) );
372 my $dir = $self->{dir};
373 my $appprefix = $self->{appprefix};
374 $self->render_sharedir_file( 'myapp.conf.tt',
375 file( $dir, "$appprefix.conf" ) );
380 my $dir = $self->{dir};
381 $self->render_sharedir_file( 'README.tt', file($dir, "README") );
386 my $dir = $self->{dir};
387 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
388 $self->render_sharedir_file( 'Changes.tt', file($dir, "Changes"), { time => $time } );
394 $self->render_sharedir_file( file('t', '01app.t.tt'), file($t, "01app.t") );
395 $self->render_sharedir_file( file('t', '02pod.t.tt'), file($t, "02pod.t") );
396 $self->render_sharedir_file( file('t', '03podcoverage.t.tt'), file($t, "03podcoverage.t") );
401 my $script = $self->{script};
402 my $appprefix = $self->{appprefix};
403 $self->render_sharedir_file( file('script', 'myapp_cgi.pl.tt'), file($script,"$appprefix\_cgi.pl") );
404 chmod 0700, file($script,"$appprefix\_cgi.pl");
409 my $script = $self->{script};
410 my $appprefix = $self->{appprefix};
411 $self->render_sharedir_file( file('script', 'myapp_fastcgi.pl.tt'), file($script, "$appprefix\_fastcgi.pl") );
412 chmod 0700, file($script, "$appprefix\_fastcgi.pl");
417 my $script = $self->{script};
418 my $appprefix = $self->{appprefix};
419 $self->render_sharedir_file( file('script', 'myapp_server.pl.tt'), file($script, "$appprefix\_server.pl") );
420 chmod 0700, file($script, "$appprefix\_server.pl");
425 my $script = $self->{script};
426 my $appprefix = $self->{appprefix};
427 $self->render_sharedir_file( file('script', 'myapp_test.pl.tt'), file($script, "$appprefix\_test.pl") );
428 chmod 0700, file($script, "$appprefix\_test.pl");
433 my $script = $self->{script};
434 my $appprefix = $self->{appprefix};
435 $self->render_sharedir_file( file('script', 'myapp_create.pl.tt'), file($script, "$appprefix\_create.pl") );
436 chmod 0700, file($script, "$appprefix\_create.pl");
441 my $file = $self->{file};
442 return $self->render_sharedir_file( file('lib', 'Helper', 'compclass.pm.tt'), $file );
447 my $test = $self->{test};
448 $self->render_sharedir_file( file('t', 'comptest.tt'), $test ); ## wtf do i rename this to?
453 my $images = $self->{images};
455 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
456 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
457 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
458 for my $name (@images) {
459 my $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin");
460 $self->mk_file( file( $images, "$name.png" ), $image );
466 my $root = $self->{root};
467 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' );
468 my $dest = dir( $root, "favicon.ico" );
469 $self->mk_file( $dest, $favicon );
473 sub _deprecate_file {
474 my ( $self, $file ) = @_;
476 my ($f, $oldcontent);
477 if ( $f = IO::File->new("< $file") ) {
478 $oldcontent = join( '', (<$f>) );
480 my $newfile = $file . '.deprecated';
481 if ( $f = IO::File->new("> $newfile") ) {
483 print $f $oldcontent;
484 print qq/created "$newfile"\n/;
486 print qq/removed "$file"\n/;
489 Catalyst::Exception->throw(
490 message => qq/Couldn't create "$file", "$!"/ );
496 This module is used by B<catalyst.pl> to create a set of scripts for a
497 new catalyst application. The scripts each contain documentation and
498 will output help on how to use them if called incorrectly or in some
499 cases, with no arguments.
501 It also provides some useful methods for a Helper module to call when
502 creating a component. See L</METHODS>.
508 Used to create new components for a catalyst application at the
513 The catalyst test server, starts an HTTPD which outputs debugging to
518 A script for running tests from the command-line.
522 Run your application as a CGI.
526 Run the application as a fastcgi app. Either by hand, or call this
527 from FastCgiServer in your http server config.
531 The L</_create.pl> script creates application components using Helper
532 modules. The Catalyst team provides a good number of Helper modules
533 for you to use. You can also add your own.
535 Helpers are classes that provide two methods.
537 * mk_compclass - creates the Component class
538 * mk_comptest - creates the Component test
540 So when you call C<scripts/myapp_create.pl view MyView TT>, create
541 will try to execute Catalyst::Helper::View::TT->mk_compclass and
542 Catalyst::Helper::View::TT->mk_comptest.
544 See L<Catalyst::Helper::View::TT> and
545 L<Catalyst::Helper::Model::DBIC::Schema> for examples.
547 All helper classes should be under one of the following namespaces.
549 Catalyst::Helper::Model::
550 Catalyst::Helper::View::
551 Catalyst::Helper::Controller::
553 =head2 COMMON HELPERS
559 L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
563 L<Catalyst::Helper::View::TT> - Template Toolkit view
567 L<Catalyst::Helper::Model::LDAP>
571 L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
577 The helpers will read author name from /etc/passwd by default.
578 To override, please export the AUTHOR variable.
584 This method in your Helper module is called with C<$helper>
585 which is a L<Catalyst::Helper> object, and whichever other arguments
586 the user added to the command-line. You can use the $helper to call methods
589 If the Helper module does not contain a C<mk_compclass> method, it
590 will fall back to calling L</render_file>, with an argument of
595 This method in your Helper module is called with C<$helper>
596 which is a L<Catalyst::Helper> object, and whichever other arguments
597 the user added to the command-line. You can use the $helper to call methods
600 If the Helper module does not contain a C<mk_compclass> method, it
601 will fall back to calling L</render_file>, with an argument of
606 This method is called if the user does not supply any of the usual
607 component types C<view>, C<controller>, C<model>. It is passed the
608 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
609 arguments the user typed.
611 There is no fallback for this method.
613 =head1 INTERNAL METHODS
615 These are the methods that the Helper classes can call on the
616 <$helper> object passed to them.
618 =head2 render_file ($file, $path, $vars)
620 Render and create a file from a template in DATA using Template
621 Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
622 the path to the file and $vars is the hashref as expected by
623 L<Template Toolkit|Template>.
625 =head2 get_file ($class, $file)
627 Fetch file contents from the DATA section. This is used internally by
628 L</render_file>. $class is the name of the class to get the DATA
629 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
634 Create the main application skeleton. This is called by L<catalyst.pl>.
636 =head2 mk_component ($app)
638 This method is called by L<create.pl> to make new components
639 for your application.
641 =head2 mk_dir ($path)
643 Surprisingly, this function makes a directory.
645 =head2 mk_file ($file, $content)
647 Writes content to a file. Called by L</render_file>.
649 =head2 next_test ($test_name)
651 Calculates the name of the next numbered test file and returns it.
652 Don't give the number or the .t suffix for the test name.
656 =head2 get_sharedir_file
658 Method for getting a file out of share/
662 =head2 render_file_contents
664 Process a L<Template::Toolkit> template.
668 =head2 render_sharedir_file
670 Render a template/image file from our share directory
676 The helpers will read author name from /etc/passwd by default.
677 To override, please export the AUTHOR variable.
681 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
682 L<Catalyst::Response>, L<Catalyst>
686 Catalyst Contributors, see Catalyst.pm
690 This library is free software. You can redistribute it and/or modify
691 it under the same terms as Perl itself.