1 package Catalyst::Helper;
13 use Catalyst::Exception;
14 use Path::Class qw/dir file/;
15 use File::ShareDir qw/dist_dir/;
16 use MooseX::Types::Moose qw/Str Bool Int/;
18 with 'MooseX::Getopt';
25 Catalyst::Helper - Bootstrap a Catalyst application
29 catalyst.pl <myappname>
34 ## this stays in Helper.pm #####################################################
35 sub get_sharedir_file {
36 my ($self, @filename) = @_;
38 if (-d dir("inc", ".author") { # Can't use sharedir if we're in a checkout
39 # this feels horrible, better ideas?
43 $dist_dir = dist_dir('Catalyst-Devel');
45 my $file = file( $dist_dir, @filename);
46 my $contents = $file->slurp;
50 # Do not touch this method, *EVER*, it is needed for back compat.
52 my ( $self, $class, $file ) = @_;
53 unless ( $cache{$class} ) {
55 $cache{$class} = eval "package $class; <DATA>";
57 my $data = $cache{$class};
58 Carp::confess("Could not get data from __DATA__ segment for $class")
60 my @files = split /^__(.+)__\r?\n/m, $data;
63 my ( $name, $content ) = splice @files, 0, 2;
64 return $content if $name eq $file;
69 ################################################################################
73 my ( $self, $dir ) = @_;
75 print qq/ exists "$dir"\n/;
78 if ( mkpath [$dir] ) {
79 print qq/created "$dir"\n/;
83 Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
87 my ( $self, $file, $content ) = @_;
88 if ( -e $file && -s _ ) {
89 print qq/ exists "$file"\n/;
91 unless ( $self->{'.newfiles'}
93 || $self->{makefile} );
94 if ( $self->{'.newfiles'} ) {
95 if ( my $f = IO::File->new("< $file") ) {
96 my $oldcontent = join( '', (<$f>) );
97 return 0 if $content eq $oldcontent;
102 if ( my $f = IO::File->new("> $file") ) {
105 print qq/created "$file"\n/;
109 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
113 my ( $self, $tname ) = @_;
114 if ($tname) { $tname = "$tname.t" }
116 my $name = $self->{name};
120 $tname = $prefix . '.t';
121 $self->{prefix} = $prefix;
122 $prefix = lc $prefix;
124 $self->{uri} = "/$prefix";
126 my $dir = $self->{test_dir};
127 my $type = lc $self->{type};
129 return File::Spec->catfile( $dir, "$type\_$tname" );
132 # Do not touch this method, *EVER*, it is needed for back compat.
133 ## addendum: we had to split this method so we could have backwards
134 ## compatability. otherwise, we'd have no way to pass stuff from __DATA__
137 my ( $self, $file, $path, $vars ) = @_;
138 my $template = $self->get_file( ( caller(0) )[0], $file );
139 $self->render_file_contents($template, $path, $vars);
142 sub render_sharedir_file {
143 my ( $self, $file, $path, $vars ) = @_;
144 my $template = $self->get_sharedir_file( $file );
145 $self->render_file_contents($template, $path, $vars);
148 sub render_file_contents {
149 my ( $self, $template, $path, $vars ) = @_;
151 my $t = Template->new;
152 return 0 unless $template;
154 $t->process( \$template, { %{$self}, %$vars }, \$output )
155 || Catalyst::Exception->throw(
156 message => qq/Couldn't process "$template", / . $t->error() );
157 $self->mk_file( $path, $output );
160 sub _mk_information {
162 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
167 $self->mk_dir( $self->{dir} );
168 $self->mk_dir( $self->{script} );
169 $self->{lib} = File::Spec->catdir( $self->{dir}, 'lib' );
170 $self->mk_dir( $self->{lib} );
171 $self->{root} = File::Spec->catdir( $self->{dir}, 'root' );
172 $self->mk_dir( $self->{root} );
173 $self->{static} = File::Spec->catdir( $self->{root}, 'static' );
174 $self->mk_dir( $self->{static} );
175 $self->{images} = File::Spec->catdir( $self->{static}, 'images' );
176 $self->mk_dir( $self->{images} );
177 $self->{t} = File::Spec->catdir( $self->{dir}, 't' );
178 $self->mk_dir( $self->{t} );
180 $self->{class} = File::Spec->catdir( split( /\:\:/, $self->{name} ) );
181 $self->{mod} = File::Spec->catdir( $self->{lib}, $self->{class} );
182 $self->mk_dir( $self->{mod} );
184 if ( $self->{short} ) {
185 $self->{m} = File::Spec->catdir( $self->{mod}, 'M' );
186 $self->mk_dir( $self->{m} );
187 $self->{v} = File::Spec->catdir( $self->{mod}, 'V' );
188 $self->mk_dir( $self->{v} );
189 $self->{c} = File::Spec->catdir( $self->{mod}, 'C' );
190 $self->mk_dir( $self->{c} );
193 $self->{m} = File::Spec->catdir( $self->{mod}, 'Model' );
194 $self->mk_dir( $self->{m} );
195 $self->{v} = File::Spec->catdir( $self->{mod}, 'View' );
196 $self->mk_dir( $self->{v} );
197 $self->{c} = File::Spec->catdir( $self->{mod}, 'Controller' );
198 $self->mk_dir( $self->{c} );
200 my $name = $self->{name};
202 $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
203 $self->{base} = File::Spec->rel2abs( $self->{dir} );
208 my $mod = $self->{mod};
209 $self->render_sharedir_file( File::Spec->catfile('lib', 'MyApp.pm.tt'), "$mod.pm" );
214 $self->render_sharedir_file( File::Spec->catfile('lib', 'MyApp', 'Controller', 'Root.pm.tt'),
215 File::Spec->catfile( $self->{c}, "Root.pm" ) );
220 $self->{path} = File::Spec->catfile( 'lib', split( '::', $self->{name} ) );
221 $self->{path} .= '.pm';
222 my $dir = $self->{dir};
223 $self->render_sharedir_file( 'Makefile.PL.tt', "$dir\/Makefile.PL" );
225 if ( $self->{makefile} ) {
227 # deprecate the old Build.PL file when regenerating Makefile.PL
228 $self->_deprecate_file(
229 File::Spec->catdir( $self->{dir}, 'Build.PL' ) );
235 my $dir = $self->{dir};
236 my $appprefix = $self->{appprefix};
237 $self->render_sharedir_file( 'myapp.conf.tt',
238 File::Spec->catfile( $dir, "$appprefix.conf" ) );
243 my $dir = $self->{dir};
244 $self->render_sharedir_file( 'README.tt', "$dir\/README" );
249 my $dir = $self->{dir};
250 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
251 $self->render_sharedir_file( 'Changes.tt', "$dir\/Changes", { time => $time } );
257 $self->render_sharedir_file( File::Spec->catfile('t', '01app.t.tt'), "$t\/01app.t" );
258 $self->render_sharedir_file( File::Spec->catfile('t', '02pod.t.tt'), "$t\/02pod.t" );
259 $self->render_sharedir_file( File::Spec->catfile('t', '03podcoverage.t.tt'), "$t\/03podcoverage.t" );
264 my $script = $self->{script};
265 my $appprefix = $self->{appprefix};
266 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_cgi.pl.tt'), "$script\/$appprefix\_cgi.pl" );
267 chmod 0700, "$script/$appprefix\_cgi.pl";
272 my $script = $self->{script};
273 my $appprefix = $self->{appprefix};
274 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_fastcgi.pl.tt'), "$script\/$appprefix\_fastcgi.pl" );
275 chmod 0700, "$script/$appprefix\_fastcgi.pl";
280 my $script = $self->{script};
281 my $appprefix = $self->{appprefix};
282 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_server.pl.tt'), "$script\/$appprefix\_server.pl" );
283 chmod 0700, "$script/$appprefix\_server.pl";
288 my $script = $self->{script};
289 my $appprefix = $self->{appprefix};
290 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_test.pl.tt'), "$script/$appprefix\_test.pl" );
291 chmod 0700, "$script/$appprefix\_test.pl";
296 my $script = $self->{script};
297 my $appprefix = $self->{appprefix};
298 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_create.pl.tt'), "$script\/$appprefix\_create.pl" );
299 chmod 0700, "$script/$appprefix\_create.pl";
304 my $file = $self->{file};
305 return $self->render_sharedir_file( 'myapp_compclass.pl.tt', "$file" );
310 my $test = $self->{test};
311 $self->render_sharedir_file( 'comptest.tt', "$test" ); ## wtf do i rename this to?
316 my $images = $self->{images};
318 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
319 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
320 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
321 for my $name (@images) {
322 my $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin");
323 $self->mk_file( File::Spec->catfile( $images, "$name.png" ), $image );
329 my $root = $self->{root};
330 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' );
331 my $dest = File::Spec->catfile( $root, "favicon.ico" );
332 $self->mk_file( $dest, $favicon );
336 sub _deprecate_file {
337 my ( $self, $file ) = @_;
340 if ( my $f = IO::File->new("< $file") ) {
341 $oldcontent = join( '', (<$f>) );
343 my $newfile = $file . '.deprecated';
344 if ( my $f = IO::File->new("> $newfile") ) {
346 print $f $oldcontent;
347 print qq/created "$newfile"\n/;
349 print qq/removed "$file"\n/;
352 Catalyst::Exception->throw(
353 message => qq/Couldn't create "$file", "$!"/ );
358 ## this is so you don't have to do make install after every change to test
359 sub _find_share_dir {
360 my ($self, $args) = @_;
361 my $share_name = $self->name;
362 if ($share_name =~ s!^/(.*?)/!!) {
364 $args->{share_base_dir} = eval {
365 Dir->new(File::ShareDir::dist_dir($dist))
371 my $dir = Dir->new(dirname($file));
373 while ($dir->parent) {
374 if (-d $dir->subdir('share') && -d $dir->subdir('share')->subdir('root')) {
375 $share_base = $dir->subdir('share')->subdir('root');
380 confess "could not find sharebase by recursion. ended up at $dir, from $file"
382 $args->{share_base_dir} = $share_base;
385 my $base = $args->{share_base_dir}->subdir($share_name);
386 confess "No such share base directory ${base}"
388 $self->share_dir($base);
395 This module is used by B<catalyst.pl> to create a set of scripts for a
396 new catalyst application. The scripts each contain documentation and
397 will output help on how to use them if called incorrectly or in some
398 cases, with no arguments.
400 It also provides some useful methods for a Helper module to call when
401 creating a component. See L</METHODS>.
407 Used to create new components for a catalyst application at the
412 The catalyst test server, starts an HTTPD which outputs debugging to
417 A script for running tests from the command-line.
421 Run your application as a CGI.
425 Run the application as a fastcgi app. Either by hand, or call this
426 from FastCgiServer in your http server config.
430 The L</_create.pl> script creates application components using Helper
431 modules. The Catalyst team provides a good number of Helper modules
432 for you to use. You can also add your own.
434 Helpers are classes that provide two methods.
436 * mk_compclass - creates the Component class
437 * mk_comptest - creates the Component test
439 So when you call C<scripts/myapp_create.pl view MyView TT>, create
440 will try to execute Catalyst::Helper::View::TT->mk_compclass and
441 Catalyst::Helper::View::TT->mk_comptest.
443 See L<Catalyst::Helper::View::TT> and
444 L<Catalyst::Helper::Model::DBIC::Schema> for examples.
446 All helper classes should be under one of the following namespaces.
448 Catalyst::Helper::Model::
449 Catalyst::Helper::View::
450 Catalyst::Helper::Controller::
452 =head2 COMMON HELPERS
458 L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
462 L<Catalyst::Helper::View::TT> - Template Toolkit view
466 L<Catalyst::Helper::Model::LDAP>
470 L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
476 The helpers will read author name from /etc/passwd by default. + To override, please export the AUTHOR variable.
482 This method in your Helper module is called with C<$helper>
483 which is a L<Catalyst::Helper> object, and whichever other arguments
484 the user added to the command-line. You can use the $helper to call methods
487 If the Helper module does not contain a C<mk_compclass> method, it
488 will fall back to calling L</render_file>, with an argument of
493 This method in your Helper module is called with C<$helper>
494 which is a L<Catalyst::Helper> object, and whichever other arguments
495 the user added to the command-line. You can use the $helper to call methods
498 If the Helper module does not contain a C<mk_compclass> method, it
499 will fall back to calling L</render_file>, with an argument of
504 This method is called if the user does not supply any of the usual
505 component types C<view>, C<controller>, C<model>. It is passed the
506 C<$helper> object (an instance of L<Catalyst::Helper>), and any other
507 arguments the user typed.
509 There is no fallback for this method.
511 =head1 INTERNAL METHODS
513 These are the methods that the Helper classes can call on the
514 <$helper> object passed to them.
516 =head2 render_file ($file, $path, $vars)
518 Render and create a file from a template in DATA using Template
519 Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
520 the path to the file and $vars is the hashref as expected by
521 L<Template Toolkit|Template>.
523 =head2 get_file ($class, $file)
525 Fetch file contents from the DATA section. This is used internally by
526 L</render_file>. $class is the name of the class to get the DATA
527 section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
532 Create the main application skeleton. This is called by L<catalyst.pl>.
534 =head2 mk_component ($app)
536 This method is called by L<create.pl> to make new components
537 for your application.
539 =head3 mk_dir ($path)
541 Surprisingly, this function makes a directory.
543 =head2 mk_file ($file, $content)
545 Writes content to a file. Called by L</render_file>.
547 =head2 next_test ($test_name)
549 Calculates the name of the next numbered test file and returns it.
550 Don't give the number or the .t suffix for the test name.
554 Alias for L<Path::Class::Dir>
558 =head2 get_sharedir_file
560 Method for getting a file out of share/
564 =head2 render_file_contents
566 Process a L<Template::Toolkit> template.
570 =head2 render_sharedir_file
572 Render a template/image file from our share directory
579 The helpers will read author name from /etc/passwd by default.
580 To override, please export the AUTHOR variable.
584 L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
585 L<Catalyst::Response>, L<Catalyst>
589 Catalyst Contributors, see Catalyst.pm
593 This library is free software. You can redistribute it and/or modify
594 it under the same terms as Perl itself.