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;
66 my $appname = subtype 'Str',
67 where { not (/[^\w:]/ or /^\d/ or /\b:\b|:{3,}/) },
68 message { "Error: Invalid application name '$_'." };
70 has name => ( is => 'ro', isa => $appname, required => 1 );
72 my @lazy_strs = qw/ dir appprefix author rootname /;
73 foreach my $name (@lazy_strs) {
74 has $name => ( is => 'ro', isa => 'Str', init_arg => undef, lazy => 1, builder => "_build_$name" );
77 class_type 'Path::Class::Dir';
78 my $coerced_dir = subtype 'Str', where { 1 };
79 coerce $coerced_dir, from 'Path::Class::Dir', via { '' . $_ };
81 my @lazy_dirs = qw/ lib root static images t class mod m v c base script /;
82 foreach my $name (@lazy_dirs) {
83 has $name => ( is => 'ro', isa => $coerced_dir, coerce => 1, init_arg => undef, lazy => 1, builder => "_build_$name" );
88 $self->$_ for @lazy_strs, @lazy_dirs;
91 sub _build_lib { dir( shift->dir, 'lib' ) }
92 sub _build_root { dir( shift->dir, 'root' ) }
93 sub _build_static { dir( shift->root, 'static' ) }
94 sub _build_images { dir( shift->static, 'images' ) }
95 sub _build_t { dir( shift->dir, 't' ) }
96 sub _build_class { dir( split( /\:\:/, shift->name ) ) }
97 sub _build_mod { my $self = shift; dir( $self->lib, $self->class ) }
98 sub _build_m { dir( shift->mod, 'Model' ) }
99 sub _build_v { dir( shift->mod, 'View' ) }
100 sub _build_c { dir( shift->mod, 'Controller' ) }
101 sub _build_base { dir( shift->dir )->absolute }
102 sub _build_script { dir( shift->dir, 'script' ) }
104 sub _build_dir { my $dir = shift->name; $dir =~ s/\:\:/-/g; return $dir; }
105 sub _build_appprefix { Catalyst::Utils::appprefix(shift->name) }
108 || eval { @{ [ getpwuid($<) ] }[6] }
109 || 'Catalyst developer';
111 sub _build_rootname { shift->name . '::Controller::Root' }
116 # Needs to be here for PAR
119 $self->{startperl } = -r '/usr/bin/env'
120 ? '#!/usr/bin/env perl'
121 : "#!$Config{perlpath} -w";
122 $self->{scriptgen } = $Catalyst::Devel::CATALYST_SCRIPT_GEN;
123 $self->{catalyst_version} = $Catalyst::VERSION;
125 my $gen_scripts = ( $self->{makefile} ) ? 0 : 1;
126 my $gen_makefile = ( $self->{scripts} ) ? 0 : 1;
127 my $gen_app = ( $self->{scripts} || $self->{makefile} ) ? 0 : 1;
130 for ( qw/ _mk_dirs _mk_config _mk_appclass _mk_rootclass _mk_readme
131 _mk_changes _mk_apptest _mk_images _mk_favicon/ ) {
140 for ( qw/ _mk_cgi _mk_fastcgi _mk_server
141 _mk_test _mk_create _mk_information
149 ## not much of this can really be changed, mk_compclass must be left for
155 $self->{base} ||= dir( $FindBin::Bin, '..' ); # FIXME!
156 unless ( $_[0] =~ /^(?:model|view|controller)$/i ) {
159 my $class = "Catalyst::Helper::$helper";
160 eval "require $class";
163 Catalyst::Exception->throw(
164 message => qq/Couldn't load helper "$class", "$@"/ );
167 if ( $class->can('mk_stuff') ) {
168 return 1 unless $class->mk_stuff( $self, @args );
173 my $name = shift || "Missing name for model/view/controller";
176 return 0 if $name =~ /[^\w\:]/;
178 $self->{long_type} = ucfirst $type;
179 $type = 'M' if $type =~ /model/i;
180 $type = 'V' if $type =~ /view/i;
181 $type = 'C' if $type =~ /controller/i;
182 my $appdir = dir( split /\:\:/, $app );
184 dir( $self->{base}, 'lib', $appdir, 'C' );
185 $type = $self->{long_type} unless -d $test_path;
186 $self->{type} = $type;
187 $self->{name} = $name;
188 $self->{class} = "$app\::$type\::$name";
192 dir( $self->{base}, 'lib', $appdir, $type );
194 if ( $name =~ /\:/ ) {
195 my @path = split /\:\:/, $name;
197 $path = dir( $path, @path );
199 $self->mk_dir($path);
200 $file = file( $path, "$file.pm" );
201 $self->{file} = $file;
204 $self->{test_dir} = dir( $self->{base}, 't' );
205 $self->{test} = $self->next_test;
209 my $comp = $self->{long_type};
210 my $class = "Catalyst::Helper::$comp\::$helper";
211 eval "require $class";
214 Catalyst::Exception->throw(
215 message => qq/Couldn't load helper "$class", "$@"/ );
218 if ( $class->can('mk_compclass') ) {
219 return 1 unless $class->mk_compclass( $self, @args );
222 return 1 unless $self->_mk_compclass
225 if ( $class->can('mk_comptest') ) {
226 $class->mk_comptest( $self, @args );
235 return 1 unless $self->_mk_compclass;
243 my ( $self, $dir ) = @_;
245 print qq/ exists "$dir"\n/;
248 if ( mkpath [$dir] ) {
249 print qq/created "$dir"\n/;
253 Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
257 my ( $self, $file, $content ) = @_;
258 Carp::confess("No file") unless $file;
259 if ( -e $file && -s _ ) {
260 print qq/ exists "$file"\n/;
262 unless ( $self->{'.newfiles'}
264 || $self->{makefile} );
265 if ( $self->{'.newfiles'} ) {
266 if ( my $f = IO::File->new("< $file") ) {
267 my $oldcontent = join( '', (<$f>) );
268 return 0 if $content eq $oldcontent;
274 if ( my $f = IO::File->new("> $file") ) {
277 print qq/created "$file"\n/;
281 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
285 my ( $self, $tname ) = @_;
286 if ($tname) { $tname = "$tname.t" }
288 my $name = $self->{name};
292 $tname = $prefix . '.t';
293 $self->{prefix} = $prefix;
294 $prefix = lc $prefix;
296 $self->{uri} = "/$prefix";
298 my $dir = $self->{test_dir};
299 my $type = lc $self->{type};
301 return file( $dir, "$type\_$tname" );
304 # Do not touch this method, *EVER*, it is needed for back compat.
305 ## addendum: we had to split this method so we could have backwards
306 ## compatability. otherwise, we'd have no way to pass stuff from __DATA__
309 my ( $self, $file, $path, $vars ) = @_;
310 my $template = $self->get_file( ( caller(0) )[0], $file );
311 $self->render_file_contents($template, $path, $vars);
314 sub render_sharedir_file {
315 my ( $self, $file, $path, $vars ) = @_;
316 my $template = $self->get_sharedir_file( $file );
317 die("Cannot get template from $file for $self\n") unless $template;
318 $self->render_file_contents($template, $path, $vars);
321 sub render_file_contents {
322 my ( $self, $template, $path, $vars ) = @_;
324 my $t = Template->new;
325 return 0 unless $template;
327 $t->process( \$template, { %{$self}, %$vars }, \$output )
328 || Catalyst::Exception->throw(
329 message => qq/Couldn't process "$template", / . $t->error() );
330 $self->mk_file( $path, $output );
333 sub _mk_information {
335 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
340 foreach my $name ( qw/ dir script lib root static images t mod m v c /) {
341 $self->mk_dir( $self->$name() );
347 my $mod = $self->{mod};
348 $self->render_sharedir_file( file('lib', 'MyApp.pm.tt'), "$mod.pm" );
353 $self->render_sharedir_file( file('lib', 'MyApp', 'Controller', 'Root.pm.tt'),
354 file( $self->c, "Root.pm" ) );
359 $self->{path} = dir( 'lib', split( '::', $self->{name} ) );
360 $self->{path} .= '.pm';
361 $self->render_sharedir_file( 'Makefile.PL.tt', file($self->dir, "Makefile.PL") );
363 if ( $self->{makefile} ) {
365 # deprecate the old Build.PL file when regenerating Makefile.PL
366 $self->_deprecate_file(
367 file( $self->dir, 'Build.PL' ) );
373 $self->render_sharedir_file( 'myapp.conf.tt',
374 file( $self->dir, $self->appprefix . ".conf" ) );
379 $self->render_sharedir_file( 'README.tt', file($self->dir, "README") );
384 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
385 $self->render_sharedir_file( 'Changes.tt', file($self->dir, "Changes"), { time => $time } );
391 $self->render_sharedir_file( file('t', '01app.t.tt'), file($t, "01app.t") );
392 $self->render_sharedir_file( file('t', '02pod.t.tt'), file($t, "02pod.t") );
393 $self->render_sharedir_file( file('t', '03podcoverage.t.tt'), file($t, "03podcoverage.t") );
398 my $fn = file($self->script, $self->appprefix . "_cgi.pl");
399 $self->render_sharedir_file( file('script', 'myapp_cgi.pl.tt'), $fn );
405 my $fn = file($self->script, $self->appprefix . "_fastcgi.pl");
406 $self->render_sharedir_file( file('script', 'myapp_fastcgi.pl.tt'), $fn );
412 my $fn = file($self->script, $self->appprefix . "_server.pl");
413 $self->render_sharedir_file( file('script', 'myapp_server.pl.tt'), $fn );
419 my $fn = file($self->script, $self->appprefix . "_test.pl");
420 $self->render_sharedir_file( file('script', 'myapp_test.pl.tt'), $fn );
426 my $fn = file($self->script, $self->appprefix . "_create.pl");
427 $self->render_sharedir_file( file('script', 'myapp_create.pl.tt'), $fn );
433 my $file = $self->{file};
434 return $self->render_sharedir_file( file('lib', 'Helper', 'compclass.pm.tt'), $file );
439 my $test = $self->{test};
440 $self->render_sharedir_file( file('t', 'comptest.tt'), $test ); ## wtf do i rename this to?
445 my $images = $self->{images};
447 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
448 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
449 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
450 for my $name (@images) {
451 my $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin");
452 $self->mk_file( file( $images, "$name.png" ), $image );
458 my $root = $self->{root};
459 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' );
460 my $dest = dir( $root, "favicon.ico" );
461 $self->mk_file( $dest, $favicon );
465 sub _deprecate_file {
466 my ( $self, $file ) = @_;
468 my ($f, $oldcontent);
469 if ( $f = IO::File->new("< $file") ) {
470 $oldcontent = join( '', (<$f>) );
472 my $newfile = $file . '.deprecated';
473 if ( $f = IO::File->new("> $newfile") ) {
475 print $f $oldcontent;
476 print qq/created "$newfile"\n/;
478 print qq/removed "$file"\n/;
481 Catalyst::Exception->throw(
482 message => qq/Couldn't create "$file", "$!"/ );
488 This module is used by B<catalyst.pl> to create a set of scripts for a
489 new catalyst application. The scripts each contain documentation and
490 will output help on how to use them if called incorrectly or in some
491 cases, with no arguments.
493 It also provides some useful methods for a Helper module to call when
494 creating a component. See L</METHODS>.
500 Used to create new components for a catalyst application at the
505 The catalyst test server, starts an HTTPD which outputs debugging to
510 A script for running tests from the command-line.
514 Run your application as a CGI.
518 Run the application as a fastcgi app. Either by hand, or call this
519 from FastCgiServer in your http server config.
523 The L</_create.pl> script creates application components using Helper
524 modules. The Catalyst team provides a good number of Helper modules
525 for you to use. You can also add your own.
527 Helpers are classes that provide two methods.
529 * mk_compclass - creates the Component class
530 * mk_comptest - creates the Component test
532 So when you call C<scripts/myapp_create.pl view MyView TT>, create
533 will try to execute Catalyst::Helper::View::TT->mk_compclass and
534 Catalyst::Helper::View::TT->mk_comptest.
536 See L<Catalyst::Helper::View::TT> and
537 L<Catalyst::Helper::Model::DBIC::Schema> for examples.
539 All helper classes should be under one of the following namespaces.
541 Catalyst::Helper::Model::
542 Catalyst::Helper::View::
543 Catalyst::Helper::Controller::
545 =head2 COMMON HELPERS
551 L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
555 L<Catalyst::Helper::View::TT> - Template Toolkit view
559 L<Catalyst::Helper::Model::LDAP>
563 L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
569 The helpers will read author name from /etc/passwd by default.
570 To override, please export the AUTHOR variable.
576 This method in your Helper module is called with C<$helper>
577 which is a L<Catalyst::Helper> object, and whichever other arguments
578 the user added to the command-line. You can use the $helper to call methods
581 If the Helper module does not contain a C<mk_compclass> method, it
582 will fall back to calling L</render_file>, with an argument of
587 This method in your Helper module is called with C<$helper>
588 which is a L<Catalyst::Helper> object, and whichever other arguments
589 the user added to the command-line. You can use the $helper to call methods
592 If the Helper module does not contain a C<mk_compclass> method, it
593 will fall back to calling L</render_file>, with an argument of
598 This method is called if the user does not supply any of the usual
599 component types C<view>, C<controller>, C<model>. It is passed the
600 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
601 arguments the user typed.
603 There is no fallback for this method.
605 =head1 INTERNAL METHODS
607 These are the methods that the Helper classes can call on the
608 <$helper> object passed to them.
610 =head2 render_file ($file, $path, $vars)
612 Render and create a file from a template in DATA using Template
613 Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
614 the path to the file and $vars is the hashref as expected by
615 L<Template Toolkit|Template>.
617 =head2 get_file ($class, $file)
619 Fetch file contents from the DATA section. This is used internally by
620 L</render_file>. $class is the name of the class to get the DATA
621 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
626 Create the main application skeleton. This is called by L<catalyst.pl>.
628 =head2 mk_component ($app)
630 This method is called by L<create.pl> to make new components
631 for your application.
633 =head2 mk_dir ($path)
635 Surprisingly, this function makes a directory.
637 =head2 mk_file ($file, $content)
639 Writes content to a file. Called by L</render_file>.
641 =head2 next_test ($test_name)
643 Calculates the name of the next numbered test file and returns it.
644 Don't give the number or the .t suffix for the test name.
648 =head2 get_sharedir_file
650 Method for getting a file out of share/
654 =head2 render_file_contents
656 Process a L<Template::Toolkit> template.
660 =head2 render_sharedir_file
662 Render a template/image file from our share directory
668 The helpers will read author name from /etc/passwd by default.
669 To override, please export the AUTHOR variable.
673 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
674 L<Catalyst::Response>, L<Catalyst>
678 Catalyst Contributors, see Catalyst.pm
682 This library is free software. You can redistribute it and/or modify
683 it under the same terms as Perl itself.