cleaned some things up, added moar Moose to ComponentGen
[catagits/Catalyst-Devel.git] / lib / Catalyst / Helper.pm
CommitLineData
68ccb5e5 1package Catalyst::Helper;
2
143cd997 3use Moose;
68ccb5e5 4use Config;
5use File::Spec;
6use File::Path;
68ccb5e5 7use FindBin;
2415f774 8use IO::File;
9use POSIX 'strftime';
68ccb5e5 10use Template;
9ffb6b83 11use Catalyst::Devel;
68ccb5e5 12use Catalyst::Utils;
13use Catalyst::Exception;
afd739f1 14use Path::Class qw/dir file/;
15use File::ShareDir qw/dist_dir/;
143cd997 16use MooseX::Types::Moose qw/Str Bool Int/;
17
18with 'MooseX::Getopt';
239b5fc0 19
68ccb5e5 20
21my %cache;
22
23=head1 NAME
24
25Catalyst::Helper - Bootstrap a Catalyst application
26
27=head1 SYNOPSIS
28
fab70e0a 29 catalyst.pl <myappname>
68ccb5e5 30
31=cut
32
faae88e5 33
143cd997 34## this stays in Helper.pm #####################################################
afd739f1 35sub get_sharedir_file {
36 my ($self, @filename) = @_;
76b106bc 37 my $dist_dir;
143cd997 38 if (-d dir("inc", ".author") { # Can't use sharedir if we're in a checkout
76b106bc 39 # this feels horrible, better ideas?
40 $dist_dir = 'share';
41 }
42 else {
43 $dist_dir = dist_dir('Catalyst-Devel');
44 }
45 my $file = file( $dist_dir, @filename);
afd739f1 46 my $contents = $file->slurp;
47 return $contents;
48}
49
faae88e5 50# Do not touch this method, *EVER*, it is needed for back compat.
68ccb5e5 51sub get_file {
03082a71 52 my ( $self, $class, $file ) = @_;
53 unless ( $cache{$class} ) {
54 local $/;
55 $cache{$class} = eval "package $class; <DATA>";
56 }
57 my $data = $cache{$class};
0acedf59 58 Carp::confess("Could not get data from __DATA__ segment for $class")
59 unless $data;
03082a71 60 my @files = split /^__(.+)__\r?\n/m, $data;
61 shift @files;
62 while (@files) {
63 my ( $name, $content ) = splice @files, 0, 2;
64 return $content if $name eq $file;
65 }
66 return 0;
68ccb5e5 67}
68
143cd997 69################################################################################
03082a71 70
68ccb5e5 71
68ccb5e5 72sub mk_dir {
73 my ( $self, $dir ) = @_;
74 if ( -d $dir ) {
75 print qq/ exists "$dir"\n/;
76 return 0;
77 }
78 if ( mkpath [$dir] ) {
79 print qq/created "$dir"\n/;
80 return 1;
81 }
82
83 Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
84}
85
68ccb5e5 86sub mk_file {
87 my ( $self, $file, $content ) = @_;
06f62452 88 if ( -e $file && -s _ ) {
68ccb5e5 89 print qq/ exists "$file"\n/;
90 return 0
91 unless ( $self->{'.newfiles'}
92 || $self->{scripts}
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;
98 }
99 $file .= '.new';
100 }
101 }
102 if ( my $f = IO::File->new("> $file") ) {
103 binmode $f;
104 print $f $content;
105 print qq/created "$file"\n/;
106 return 1;
107 }
108
109 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
110}
111
68ccb5e5 112sub next_test {
113 my ( $self, $tname ) = @_;
114 if ($tname) { $tname = "$tname.t" }
115 else {
116 my $name = $self->{name};
117 my $prefix = $name;
118 $prefix =~ s/::/-/g;
119 $prefix = $prefix;
120 $tname = $prefix . '.t';
121 $self->{prefix} = $prefix;
122 $prefix = lc $prefix;
123 $prefix =~ s/-/\//g;
124 $self->{uri} = "/$prefix";
125 }
126 my $dir = $self->{test_dir};
127 my $type = lc $self->{type};
128 $self->mk_dir($dir);
129 return File::Spec->catfile( $dir, "$type\_$tname" );
130}
131
b78e897c 132# Do not touch this method, *EVER*, it is needed for back compat. ##############
7025ed89 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__
68ccb5e5 135sub render_file {
136 my ( $self, $file, $path, $vars ) = @_;
7025ed89 137 my $template = $self->get_file( ( caller(0) )[0], $file );
06f62452 138 $self->render_file_contents($template, $path, $vars);
7025ed89 139}
b78e897c 140################################################################################
7025ed89 141
142sub render_sharedir_file {
143 my ( $self, $file, $path, $vars ) = @_;
144 my $template = $self->get_sharedir_file( $file );
e97273a4 145 $self->render_file_contents($template, $path, $vars);
7025ed89 146}
147
148sub render_file_contents {
149 my ( $self, $template, $path, $vars ) = @_;
68ccb5e5 150 $vars ||= {};
151 my $t = Template->new;
68ccb5e5 152 return 0 unless $template;
153 my $output;
154 $t->process( \$template, { %{$self}, %$vars }, \$output )
155 || Catalyst::Exception->throw(
7025ed89 156 message => qq/Couldn't process "$template", / . $t->error() );
68ccb5e5 157 $self->mk_file( $path, $output );
158}
159
b78e897c 160# DIE DIE DIE
45d74601 161sub _mk_information {
162 my $self = shift;
163 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
164}
165
68ccb5e5 166sub _mk_dirs {
167 my $self = shift;
168 $self->mk_dir( $self->{dir} );
169 $self->mk_dir( $self->{script} );
170 $self->{lib} = File::Spec->catdir( $self->{dir}, 'lib' );
171 $self->mk_dir( $self->{lib} );
172 $self->{root} = File::Spec->catdir( $self->{dir}, 'root' );
173 $self->mk_dir( $self->{root} );
174 $self->{static} = File::Spec->catdir( $self->{root}, 'static' );
175 $self->mk_dir( $self->{static} );
176 $self->{images} = File::Spec->catdir( $self->{static}, 'images' );
177 $self->mk_dir( $self->{images} );
178 $self->{t} = File::Spec->catdir( $self->{dir}, 't' );
179 $self->mk_dir( $self->{t} );
180
181 $self->{class} = File::Spec->catdir( split( /\:\:/, $self->{name} ) );
182 $self->{mod} = File::Spec->catdir( $self->{lib}, $self->{class} );
183 $self->mk_dir( $self->{mod} );
184
185 if ( $self->{short} ) {
186 $self->{m} = File::Spec->catdir( $self->{mod}, 'M' );
187 $self->mk_dir( $self->{m} );
188 $self->{v} = File::Spec->catdir( $self->{mod}, 'V' );
189 $self->mk_dir( $self->{v} );
190 $self->{c} = File::Spec->catdir( $self->{mod}, 'C' );
191 $self->mk_dir( $self->{c} );
192 }
193 else {
194 $self->{m} = File::Spec->catdir( $self->{mod}, 'Model' );
195 $self->mk_dir( $self->{m} );
196 $self->{v} = File::Spec->catdir( $self->{mod}, 'View' );
197 $self->mk_dir( $self->{v} );
198 $self->{c} = File::Spec->catdir( $self->{mod}, 'Controller' );
199 $self->mk_dir( $self->{c} );
200 }
201 my $name = $self->{name};
202 $self->{rootname} =
203 $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
204 $self->{base} = File::Spec->rel2abs( $self->{dir} );
205}
206
b78e897c 207# DIE DIE DIE
68ccb5e5 208sub _mk_appclass {
209 my $self = shift;
210 my $mod = $self->{mod};
c485fb09 211 $self->render_sharedir_file( File::Spec->catfile('lib', 'MyApp.pm.tt'), "$mod.pm" );
68ccb5e5 212}
213
b78e897c 214# DIE DIE DIE
68ccb5e5 215sub _mk_rootclass {
216 my $self = shift;
c485fb09 217 $self->render_sharedir_file( File::Spec->catfile('lib', 'MyApp', 'Controller', 'Root.pm.tt'),
68ccb5e5 218 File::Spec->catfile( $self->{c}, "Root.pm" ) );
219}
220
b78e897c 221# DIE DIE DIE
68ccb5e5 222sub _mk_makefile {
223 my $self = shift;
224 $self->{path} = File::Spec->catfile( 'lib', split( '::', $self->{name} ) );
225 $self->{path} .= '.pm';
226 my $dir = $self->{dir};
d5ff5c0f 227 $self->render_sharedir_file( 'Makefile.PL.tt', "$dir\/Makefile.PL" );
68ccb5e5 228
229 if ( $self->{makefile} ) {
230
231 # deprecate the old Build.PL file when regenerating Makefile.PL
232 $self->_deprecate_file(
233 File::Spec->catdir( $self->{dir}, 'Build.PL' ) );
234 }
235}
236
b78e897c 237# DIE DIE DIE
68ccb5e5 238sub _mk_config {
239 my $self = shift;
240 my $dir = $self->{dir};
241 my $appprefix = $self->{appprefix};
d5ff5c0f 242 $self->render_sharedir_file( 'myapp.conf.tt',
a4e6d745 243 File::Spec->catfile( $dir, "$appprefix.conf" ) );
68ccb5e5 244}
245
b78e897c 246# DIE DIE DIE
68ccb5e5 247sub _mk_readme {
248 my $self = shift;
249 my $dir = $self->{dir};
d5ff5c0f 250 $self->render_sharedir_file( 'README.tt', "$dir\/README" );
68ccb5e5 251}
252
b78e897c 253# DIE DIE DIE
68ccb5e5 254sub _mk_changes {
255 my $self = shift;
256 my $dir = $self->{dir};
5b1ec88b 257 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
d5ff5c0f 258 $self->render_sharedir_file( 'Changes.tt', "$dir\/Changes", { time => $time } );
68ccb5e5 259}
260
261sub _mk_apptest {
262 my $self = shift;
263 my $t = $self->{t};
c485fb09 264 $self->render_sharedir_file( File::Spec->catfile('t', '01app.t.tt'), "$t\/01app.t" );
265 $self->render_sharedir_file( File::Spec->catfile('t', '02pod.t.tt'), "$t\/02pod.t" );
266 $self->render_sharedir_file( File::Spec->catfile('t', '03podcoverage.t.tt'), "$t\/03podcoverage.t" );
68ccb5e5 267}
268
269sub _mk_cgi {
270 my $self = shift;
271 my $script = $self->{script};
272 my $appprefix = $self->{appprefix};
c485fb09 273 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_cgi.pl.tt'), "$script\/$appprefix\_cgi.pl" );
68ccb5e5 274 chmod 0700, "$script/$appprefix\_cgi.pl";
275}
276
277sub _mk_fastcgi {
278 my $self = shift;
279 my $script = $self->{script};
280 my $appprefix = $self->{appprefix};
c485fb09 281 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_fastcgi.pl.tt'), "$script\/$appprefix\_fastcgi.pl" );
68ccb5e5 282 chmod 0700, "$script/$appprefix\_fastcgi.pl";
283}
284
285sub _mk_server {
286 my $self = shift;
287 my $script = $self->{script};
288 my $appprefix = $self->{appprefix};
c485fb09 289 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_server.pl.tt'), "$script\/$appprefix\_server.pl" );
68ccb5e5 290 chmod 0700, "$script/$appprefix\_server.pl";
291}
292
293sub _mk_test {
294 my $self = shift;
295 my $script = $self->{script};
296 my $appprefix = $self->{appprefix};
c485fb09 297 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_test.pl.tt'), "$script/$appprefix\_test.pl" );
68ccb5e5 298 chmod 0700, "$script/$appprefix\_test.pl";
299}
300
301sub _mk_create {
302 my $self = shift;
303 my $script = $self->{script};
304 my $appprefix = $self->{appprefix};
c485fb09 305 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_create.pl.tt'), "$script\/$appprefix\_create.pl" );
68ccb5e5 306 chmod 0700, "$script/$appprefix\_create.pl";
307}
308
b78e897c 309# DIE DIE DIE
68ccb5e5 310sub _mk_compclass {
311 my $self = shift;
312 my $file = $self->{file};
5517035e 313 return $self->render_sharedir_file( 'myapp_compclass.pl.tt', "$file" );
68ccb5e5 314}
315
b78e897c 316# DIE DIE DIE
68ccb5e5 317sub _mk_comptest {
318 my $self = shift;
319 my $test = $self->{test};
5517035e 320 $self->render_sharedir_file( 'comptest.tt', "$test" ); ## wtf do i rename this to?
68ccb5e5 321}
322
323sub _mk_images {
324 my $self = shift;
325 my $images = $self->{images};
326 my @images =
327 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
328 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
329 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
330 for my $name (@images) {
3f2f19ec 331 my $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin");
68ccb5e5 332 $self->mk_file( File::Spec->catfile( $images, "$name.png" ), $image );
333 }
334}
335
b78e897c 336# DIE DIE DIE
68ccb5e5 337sub _mk_favicon {
338 my $self = shift;
339 my $root = $self->{root};
f023d4a1 340 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' );
afd739f1 341 my $dest = File::Spec->catfile( $root, "favicon.ico" );
342 $self->mk_file( $dest, $favicon );
68ccb5e5 343
344}
345
346sub _deprecate_file {
347 my ( $self, $file ) = @_;
348 if ( -e $file ) {
349 my $oldcontent;
350 if ( my $f = IO::File->new("< $file") ) {
351 $oldcontent = join( '', (<$f>) );
352 }
353 my $newfile = $file . '.deprecated';
354 if ( my $f = IO::File->new("> $newfile") ) {
355 binmode $f;
356 print $f $oldcontent;
357 print qq/created "$newfile"\n/;
358 unlink $file;
359 print qq/removed "$file"\n/;
360 return 1;
361 }
362 Catalyst::Exception->throw(
363 message => qq/Couldn't create "$file", "$!"/ );
364 }
365}
366
fab70e0a 367=head1 DESCRIPTION
368
369This module is used by B<catalyst.pl> to create a set of scripts for a
370new catalyst application. The scripts each contain documentation and
371will output help on how to use them if called incorrectly or in some
372cases, with no arguments.
373
374It also provides some useful methods for a Helper module to call when
375creating a component. See L</METHODS>.
376
377=head1 SCRIPTS
378
379=head2 _create.pl
380
381Used to create new components for a catalyst application at the
382development stage.
383
384=head2 _server.pl
385
386The catalyst test server, starts an HTTPD which outputs debugging to
387the terminal.
388
389=head2 _test.pl
390
391A script for running tests from the command-line.
392
393=head2 _cgi.pl
394
395Run your application as a CGI.
396
397=head2 _fastcgi.pl
398
399Run the application as a fastcgi app. Either by hand, or call this
400from FastCgiServer in your http server config.
401
68ccb5e5 402=head1 HELPERS
403
fab70e0a 404The L</_create.pl> script creates application components using Helper
405modules. The Catalyst team provides a good number of Helper modules
406for you to use. You can also add your own.
407
68ccb5e5 408Helpers are classes that provide two methods.
409
410 * mk_compclass - creates the Component class
411 * mk_comptest - creates the Component test
412
fab70e0a 413So when you call C<scripts/myapp_create.pl view MyView TT>, create
414will try to execute Catalyst::Helper::View::TT->mk_compclass and
68ccb5e5 415Catalyst::Helper::View::TT->mk_comptest.
416
c4c50c2d 417See L<Catalyst::Helper::View::TT> and
418L<Catalyst::Helper::Model::DBIC::Schema> for examples.
68ccb5e5 419
420All helper classes should be under one of the following namespaces.
421
422 Catalyst::Helper::Model::
423 Catalyst::Helper::View::
424 Catalyst::Helper::Controller::
425
675fef06 426=head2 COMMON HELPERS
bc8d7994 427
428=over
429
430=item *
431
432L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
433
434=item *
435
436L<Catalyst::Helper::View::TT> - Template Toolkit view
437
438=item *
439
440L<Catalyst::Helper::Model::LDAP>
441
442=item *
443
444L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
445
446=back
447
448=head3 NOTE
449
675fef06 450The helpers will read author name from /etc/passwd by default. + To override, please export the AUTHOR variable.
bc8d7994 451
452=head1 METHODS
453
fab70e0a 454=head2 mk_compclass
455
456This method in your Helper module is called with C<$helper>
457which is a L<Catalyst::Helper> object, and whichever other arguments
458the user added to the command-line. You can use the $helper to call methods
459described below.
460
461If the Helper module does not contain a C<mk_compclass> method, it
462will fall back to calling L</render_file>, with an argument of
463C<compclass>.
464
465=head2 mk_comptest
466
467This method in your Helper module is called with C<$helper>
468which is a L<Catalyst::Helper> object, and whichever other arguments
469the user added to the command-line. You can use the $helper to call methods
470described below.
471
472If the Helper module does not contain a C<mk_compclass> method, it
473will fall back to calling L</render_file>, with an argument of
474C<comptest>.
475
476=head2 mk_stuff
477
478This method is called if the user does not supply any of the usual
479component types C<view>, C<controller>, C<model>. It is passed the
480C<$helper> object (an instance of L<Catalyst::Helper>), and any other
481arguments the user typed.
482
483There is no fallback for this method.
484
bc8d7994 485=head1 INTERNAL METHODS
fab70e0a 486
487These are the methods that the Helper classes can call on the
488<$helper> object passed to them.
489
28eb1300 490=head2 render_file ($file, $path, $vars)
fab70e0a 491
28eb1300 492Render and create a file from a template in DATA using Template
493Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
494the path to the file and $vars is the hashref as expected by
495L<Template Toolkit|Template>.
fab70e0a 496
28eb1300 497=head2 get_file ($class, $file)
fab70e0a 498
499Fetch file contents from the DATA section. This is used internally by
28eb1300 500L</render_file>. $class is the name of the class to get the DATA
501section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
502values for this.
fab70e0a 503
504=head2 mk_app
505
506Create the main application skeleton. This is called by L<catalyst.pl>.
507
28eb1300 508=head2 mk_component ($app)
fab70e0a 509
510This method is called by L<create.pl> to make new components
511for your application.
512
28eb1300 513=head3 mk_dir ($path)
fab70e0a 514
515Surprisingly, this function makes a directory.
516
28eb1300 517=head2 mk_file ($file, $content)
fab70e0a 518
519Writes content to a file. Called by L</render_file>.
520
28eb1300 521=head2 next_test ($test_name)
fab70e0a 522
523Calculates the name of the next numbered test file and returns it.
28eb1300 524Don't give the number or the .t suffix for the test name.
fab70e0a 525
c6dbb300 526=head2 Dir
527
528Alias for L<Path::Class::Dir>
529
530=cut
531
532=head2 get_sharedir_file
533
534Method for getting a file out of share/
535
536=cut
537
538=head2 render_file_contents
539
540Process a L<Template::Toolkit> template.
541
542=cut
543
544=head2 render_sharedir_file
545
546Render a template/image file from our share directory
547
548=cut
549
550
68ccb5e5 551=head1 NOTE
552
553The helpers will read author name from /etc/passwd by default.
554To override, please export the AUTHOR variable.
555
556=head1 SEE ALSO
557
558L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
559L<Catalyst::Response>, L<Catalyst>
560
f64c718c 561=head1 AUTHORS
68ccb5e5 562
f64c718c 563Catalyst Contributors, see Catalyst.pm
68ccb5e5 564
565=head1 LICENSE
566
7cd3b67e 567This library is free software. You can redistribute it and/or modify
68ccb5e5 568it under the same terms as Perl itself.
569
64d4433e 570=begin pod_to_ignore
571
68ccb5e5 572=cut
573
5741;
68ccb5e5 575