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 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) }
82 || eval { @{ [ getpwuid($<) ] }[6] }
83 || 'Catalyst developer';
89 # Needs to be here for PAR
92 $self->{startperl } = -r '/usr/bin/env'
93 ? '#!/usr/bin/env perl'
94 : "#!$Config{perlpath} -w";
95 $self->{scriptgen } = $Catalyst::Devel::CATALYST_SCRIPT_GEN;
96 $self->{catalyst_version} = $Catalyst::VERSION;
98 my $gen_scripts = ( $self->{makefile} ) ? 0 : 1;
99 my $gen_makefile = ( $self->{scripts} ) ? 0 : 1;
100 my $gen_app = ( $self->{scripts} || $self->{makefile} ) ? 0 : 1;
103 for ( qw/ _mk_dirs _mk_config _mk_appclass _mk_rootclass _mk_readme
104 _mk_changes _mk_apptest _mk_images _mk_favicon/ ) {
113 for ( qw/ _mk_cgi _mk_fastcgi _mk_server
114 _mk_test _mk_create _mk_information
122 ## not much of this can really be changed, mk_compclass must be left for
128 $self->{base} ||= dir( $FindBin::Bin, '..' ); # FIXME!
129 unless ( $_[0] =~ /^(?:model|view|controller)$/i ) {
132 my $class = "Catalyst::Helper::$helper";
133 eval "require $class";
136 Catalyst::Exception->throw(
137 message => qq/Couldn't load helper "$class", "$@"/ );
140 if ( $class->can('mk_stuff') ) {
141 return 1 unless $class->mk_stuff( $self, @args );
146 my $name = shift || "Missing name for model/view/controller";
149 return 0 if $name =~ /[^\w\:]/;
151 $self->{long_type} = ucfirst $type;
152 $type = 'M' if $type =~ /model/i;
153 $type = 'V' if $type =~ /view/i;
154 $type = 'C' if $type =~ /controller/i;
155 my $appdir = dir( split /\:\:/, $app );
157 dir( $self->{base}, 'lib', $appdir, 'C' );
158 $type = $self->{long_type} unless -d $test_path;
159 $self->{type} = $type;
160 $self->{name} = $name;
161 $self->{class} = "$app\::$type\::$name";
165 dir( $self->{base}, 'lib', $appdir, $type );
167 if ( $name =~ /\:/ ) {
168 my @path = split /\:\:/, $name;
170 $path = dir( $path, @path );
172 $self->mk_dir($path);
173 $file = file( $path, "$file.pm" );
174 $self->{file} = $file;
177 $self->{test_dir} = dir( $self->{base}, 't' );
178 $self->{test} = $self->next_test;
182 my $comp = $self->{long_type};
183 my $class = "Catalyst::Helper::$comp\::$helper";
184 eval "require $class";
187 Catalyst::Exception->throw(
188 message => qq/Couldn't load helper "$class", "$@"/ );
191 if ( $class->can('mk_compclass') ) {
192 return 1 unless $class->mk_compclass( $self, @args );
195 return 1 unless $self->_mk_compclass
198 if ( $class->can('mk_comptest') ) {
199 $class->mk_comptest( $self, @args );
208 return 1 unless $self->_mk_compclass;
216 my ( $self, $dir ) = @_;
218 print qq/ exists "$dir"\n/;
221 if ( mkpath [$dir] ) {
222 print qq/created "$dir"\n/;
226 Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
230 my ( $self, $file, $content ) = @_;
231 if ( -e $file && -s _ ) {
232 print qq/ exists "$file"\n/;
234 unless ( $self->{'.newfiles'}
236 || $self->{makefile} );
237 if ( $self->{'.newfiles'} ) {
238 if ( my $f = IO::File->new("< $file") ) {
239 my $oldcontent = join( '', (<$f>) );
240 return 0 if $content eq $oldcontent;
246 if ( my $f = IO::File->new("> $file") ) {
249 print qq/created "$file"\n/;
253 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
257 my ( $self, $tname ) = @_;
258 if ($tname) { $tname = "$tname.t" }
260 my $name = $self->{name};
264 $tname = $prefix . '.t';
265 $self->{prefix} = $prefix;
266 $prefix = lc $prefix;
268 $self->{uri} = "/$prefix";
270 my $dir = $self->{test_dir};
271 my $type = lc $self->{type};
273 return file( $dir, "$type\_$tname" );
276 # Do not touch this method, *EVER*, it is needed for back compat.
277 ## addendum: we had to split this method so we could have backwards
278 ## compatability. otherwise, we'd have no way to pass stuff from __DATA__
281 my ( $self, $file, $path, $vars ) = @_;
282 my $template = $self->get_file( ( caller(0) )[0], $file );
283 $self->render_file_contents($template, $path, $vars);
286 sub render_sharedir_file {
287 my ( $self, $file, $path, $vars ) = @_;
288 my $template = $self->get_sharedir_file( $file );
289 die("Cannot get template from $file for $self\n") unless $template;
290 $self->render_file_contents($template, $path, $vars);
293 sub render_file_contents {
294 my ( $self, $template, $path, $vars ) = @_;
296 my $t = Template->new;
297 return 0 unless $template;
299 $t->process( \$template, { %{$self}, %$vars }, \$output )
300 || Catalyst::Exception->throw(
301 message => qq/Couldn't process "$template", / . $t->error() );
302 $self->mk_file( $path, $output );
305 sub _mk_information {
307 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
310 foreach my $name (qw/ lib root static images t class mod m v c rootname base /) {
311 has $name => ( is => 'ro', isa => 'Str', init_arg => undef, lazy => 1, builder => "_build_$name" );
314 sub _build_lib { dir( shift->dir, 'lib' ) }
315 sub _build_root { dir( shift->dir, 'lib' ) }
316 sub _build_static { dir( shift->dir, 'lib' ) }
317 sub _build_images { dir( shift->dir, 'lib' ) }
318 sub _build_t { dir( shift->dir, 'lib' ) }
319 sub _build_class { dir( split( /\:\:/, shift->name ) ) }
320 sub _build_mod { my $self = shift; dir( $self->lib, $self->class ) }
321 sub _build_m { dir( shift->mod, 'Model' ) }
322 sub _build_v { dir( shift->mod, 'View' ) }
323 sub _build_c { dir( shift->mod, 'Controller' ) }
324 sub _build_rootname { shift->name . '::Controller::Root' }
325 sub _build_base { dir( shift->dir )->absolute }
329 foreach my $name ( qw/ dir script lib root static images t mod m v c /) {
330 $self->mk_dir( $self->$name() );
336 my $mod = $self->{mod};
337 $self->render_sharedir_file( file('lib', 'MyApp.pm.tt'), "$mod.pm" );
342 $self->render_sharedir_file( file('lib', 'MyApp', 'Controller', 'Root.pm.tt'),
343 file( $self->{c}, "Root.pm" ) );
348 $self->{path} = dir( 'lib', split( '::', $self->{name} ) );
349 $self->{path} .= '.pm';
350 $self->render_sharedir_file( 'Makefile.PL.tt', file($self->dir, "Makefile.PL") );
352 if ( $self->{makefile} ) {
354 # deprecate the old Build.PL file when regenerating Makefile.PL
355 $self->_deprecate_file(
356 file( $self->dir, 'Build.PL' ) );
362 $self->render_sharedir_file( 'myapp.conf.tt',
363 file( $self->dir, $self->appprefix . ".conf" ) );
368 $self->render_sharedir_file( 'README.tt', file($self->dir, "README") );
373 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
374 $self->render_sharedir_file( 'Changes.tt', file($self->dir, "Changes"), { time => $time } );
380 $self->render_sharedir_file( file('t', '01app.t.tt'), file($t, "01app.t") );
381 $self->render_sharedir_file( file('t', '02pod.t.tt'), file($t, "02pod.t") );
382 $self->render_sharedir_file( file('t', '03podcoverage.t.tt'), file($t, "03podcoverage.t") );
387 my $fn = file($self->script, $self->appprefix . "_cgi.pl");
388 $self->render_sharedir_file( file('script', 'myapp_cgi.pl.tt') );
394 my $fn = file($self->script, $self->appprefix . "_fastcgi.pl");
395 $self->render_sharedir_file( file('script', 'myapp_fastcgi.pl.tt'), $fn );
401 my $fn = file($self->script, $self->appprefix . "_server.pl");
402 $self->render_sharedir_file( file('script', 'myapp_server.pl.tt'), $fn );
408 my $fn = file($self->script, $self->appprefix . "_test.pl");
409 $self->render_sharedir_file( file('script', 'myapp_test.pl.tt'), $fn );
415 my $fn = file($self->script, $self->appprefix . "_create.pl");
416 $self->render_sharedir_file( file('script', 'myapp_create.pl.tt'), $fn );
422 my $file = $self->{file};
423 return $self->render_sharedir_file( file('lib', 'Helper', 'compclass.pm.tt'), $file );
428 my $test = $self->{test};
429 $self->render_sharedir_file( file('t', 'comptest.tt'), $test ); ## wtf do i rename this to?
434 my $images = $self->{images};
436 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
437 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
438 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
439 for my $name (@images) {
440 my $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin");
441 $self->mk_file( file( $images, "$name.png" ), $image );
447 my $root = $self->{root};
448 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' );
449 my $dest = dir( $root, "favicon.ico" );
450 $self->mk_file( $dest, $favicon );
454 sub _deprecate_file {
455 my ( $self, $file ) = @_;
457 my ($f, $oldcontent);
458 if ( $f = IO::File->new("< $file") ) {
459 $oldcontent = join( '', (<$f>) );
461 my $newfile = $file . '.deprecated';
462 if ( $f = IO::File->new("> $newfile") ) {
464 print $f $oldcontent;
465 print qq/created "$newfile"\n/;
467 print qq/removed "$file"\n/;
470 Catalyst::Exception->throw(
471 message => qq/Couldn't create "$file", "$!"/ );
477 This module is used by B<catalyst.pl> to create a set of scripts for a
478 new catalyst application. The scripts each contain documentation and
479 will output help on how to use them if called incorrectly or in some
480 cases, with no arguments.
482 It also provides some useful methods for a Helper module to call when
483 creating a component. See L</METHODS>.
489 Used to create new components for a catalyst application at the
494 The catalyst test server, starts an HTTPD which outputs debugging to
499 A script for running tests from the command-line.
503 Run your application as a CGI.
507 Run the application as a fastcgi app. Either by hand, or call this
508 from FastCgiServer in your http server config.
512 The L</_create.pl> script creates application components using Helper
513 modules. The Catalyst team provides a good number of Helper modules
514 for you to use. You can also add your own.
516 Helpers are classes that provide two methods.
518 * mk_compclass - creates the Component class
519 * mk_comptest - creates the Component test
521 So when you call C<scripts/myapp_create.pl view MyView TT>, create
522 will try to execute Catalyst::Helper::View::TT->mk_compclass and
523 Catalyst::Helper::View::TT->mk_comptest.
525 See L<Catalyst::Helper::View::TT> and
526 L<Catalyst::Helper::Model::DBIC::Schema> for examples.
528 All helper classes should be under one of the following namespaces.
530 Catalyst::Helper::Model::
531 Catalyst::Helper::View::
532 Catalyst::Helper::Controller::
534 =head2 COMMON HELPERS
540 L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
544 L<Catalyst::Helper::View::TT> - Template Toolkit view
548 L<Catalyst::Helper::Model::LDAP>
552 L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
558 The helpers will read author name from /etc/passwd by default.
559 To override, please export the AUTHOR variable.
565 This method in your Helper module is called with C<$helper>
566 which is a L<Catalyst::Helper> object, and whichever other arguments
567 the user added to the command-line. You can use the $helper to call methods
570 If the Helper module does not contain a C<mk_compclass> method, it
571 will fall back to calling L</render_file>, with an argument of
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 is called if the user does not supply any of the usual
588 component types C<view>, C<controller>, C<model>. It is passed the
589 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
590 arguments the user typed.
592 There is no fallback for this method.
594 =head1 INTERNAL METHODS
596 These are the methods that the Helper classes can call on the
597 <$helper> object passed to them.
599 =head2 render_file ($file, $path, $vars)
601 Render and create a file from a template in DATA using Template
602 Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
603 the path to the file and $vars is the hashref as expected by
604 L<Template Toolkit|Template>.
606 =head2 get_file ($class, $file)
608 Fetch file contents from the DATA section. This is used internally by
609 L</render_file>. $class is the name of the class to get the DATA
610 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
615 Create the main application skeleton. This is called by L<catalyst.pl>.
617 =head2 mk_component ($app)
619 This method is called by L<create.pl> to make new components
620 for your application.
622 =head2 mk_dir ($path)
624 Surprisingly, this function makes a directory.
626 =head2 mk_file ($file, $content)
628 Writes content to a file. Called by L</render_file>.
630 =head2 next_test ($test_name)
632 Calculates the name of the next numbered test file and returns it.
633 Don't give the number or the .t suffix for the test name.
637 =head2 get_sharedir_file
639 Method for getting a file out of share/
643 =head2 render_file_contents
645 Process a L<Template::Toolkit> template.
649 =head2 render_sharedir_file
651 Render a template/image file from our share directory
657 The helpers will read author name from /etc/passwd by default.
658 To override, please export the AUTHOR variable.
662 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
663 L<Catalyst::Response>, L<Catalyst>
667 Catalyst Contributors, see Catalyst.pm
671 This library is free software. You can redistribute it and/or modify
672 it under the same terms as Perl itself.