need to move the marked lines into one method
[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
e4d44269 269# these can certainly be globbed into one method
68ccb5e5 270sub _mk_cgi {
271 my $self = shift;
272 my $script = $self->{script};
273 my $appprefix = $self->{appprefix};
c485fb09 274 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_cgi.pl.tt'), "$script\/$appprefix\_cgi.pl" );
e4d44269 275 chmod 0700, "$script/$appprefix\_cgi.pl"; # FIXME
68ccb5e5 276}
277
278sub _mk_fastcgi {
279 my $self = shift;
280 my $script = $self->{script};
281 my $appprefix = $self->{appprefix};
c485fb09 282 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_fastcgi.pl.tt'), "$script\/$appprefix\_fastcgi.pl" );
e4d44269 283 chmod 0700, "$script/$appprefix\_fastcgi.pl"; # FIXME
68ccb5e5 284}
285
286sub _mk_server {
287 my $self = shift;
288 my $script = $self->{script};
289 my $appprefix = $self->{appprefix};
c485fb09 290 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_server.pl.tt'), "$script\/$appprefix\_server.pl" );
e4d44269 291 chmod 0700, "$script/$appprefix\_server.pl"; # FIXME
68ccb5e5 292}
293
294sub _mk_test {
295 my $self = shift;
296 my $script = $self->{script};
297 my $appprefix = $self->{appprefix};
c485fb09 298 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_test.pl.tt'), "$script/$appprefix\_test.pl" );
e4d44269 299 chmod 0700, "$script/$appprefix\_test.pl"; # FIXME
68ccb5e5 300}
301
302sub _mk_create {
303 my $self = shift;
304 my $script = $self->{script};
305 my $appprefix = $self->{appprefix};
c485fb09 306 $self->render_sharedir_file( File::Spec->catfile('script', 'myapp_create.pl.tt'), "$script\/$appprefix\_create.pl" );
e4d44269 307 chmod 0700, "$script/$appprefix\_create.pl"; # FIXME
68ccb5e5 308}
309
b78e897c 310# DIE DIE DIE
68ccb5e5 311sub _mk_compclass {
312 my $self = shift;
313 my $file = $self->{file};
5517035e 314 return $self->render_sharedir_file( 'myapp_compclass.pl.tt', "$file" );
68ccb5e5 315}
316
b78e897c 317# DIE DIE DIE
68ccb5e5 318sub _mk_comptest {
319 my $self = shift;
320 my $test = $self->{test};
5517035e 321 $self->render_sharedir_file( 'comptest.tt', "$test" ); ## wtf do i rename this to?
68ccb5e5 322}
323
324sub _mk_images {
325 my $self = shift;
326 my $images = $self->{images};
327 my @images =
328 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
329 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
330 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
331 for my $name (@images) {
3f2f19ec 332 my $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin");
68ccb5e5 333 $self->mk_file( File::Spec->catfile( $images, "$name.png" ), $image );
334 }
335}
336
b78e897c 337# DIE DIE DIE
68ccb5e5 338sub _mk_favicon {
339 my $self = shift;
340 my $root = $self->{root};
f023d4a1 341 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' );
afd739f1 342 my $dest = File::Spec->catfile( $root, "favicon.ico" );
343 $self->mk_file( $dest, $favicon );
68ccb5e5 344
345}
346
347sub _deprecate_file {
348 my ( $self, $file ) = @_;
349 if ( -e $file ) {
350 my $oldcontent;
351 if ( my $f = IO::File->new("< $file") ) {
352 $oldcontent = join( '', (<$f>) );
353 }
354 my $newfile = $file . '.deprecated';
355 if ( my $f = IO::File->new("> $newfile") ) {
356 binmode $f;
357 print $f $oldcontent;
358 print qq/created "$newfile"\n/;
359 unlink $file;
360 print qq/removed "$file"\n/;
361 return 1;
362 }
363 Catalyst::Exception->throw(
364 message => qq/Couldn't create "$file", "$!"/ );
365 }
366}
367
fab70e0a 368=head1 DESCRIPTION
369
370This module is used by B<catalyst.pl> to create a set of scripts for a
371new catalyst application. The scripts each contain documentation and
372will output help on how to use them if called incorrectly or in some
373cases, with no arguments.
374
375It also provides some useful methods for a Helper module to call when
376creating a component. See L</METHODS>.
377
378=head1 SCRIPTS
379
380=head2 _create.pl
381
382Used to create new components for a catalyst application at the
383development stage.
384
385=head2 _server.pl
386
387The catalyst test server, starts an HTTPD which outputs debugging to
388the terminal.
389
390=head2 _test.pl
391
392A script for running tests from the command-line.
393
394=head2 _cgi.pl
395
396Run your application as a CGI.
397
398=head2 _fastcgi.pl
399
400Run the application as a fastcgi app. Either by hand, or call this
401from FastCgiServer in your http server config.
402
68ccb5e5 403=head1 HELPERS
404
fab70e0a 405The L</_create.pl> script creates application components using Helper
406modules. The Catalyst team provides a good number of Helper modules
407for you to use. You can also add your own.
408
68ccb5e5 409Helpers are classes that provide two methods.
410
411 * mk_compclass - creates the Component class
412 * mk_comptest - creates the Component test
413
fab70e0a 414So when you call C<scripts/myapp_create.pl view MyView TT>, create
415will try to execute Catalyst::Helper::View::TT->mk_compclass and
68ccb5e5 416Catalyst::Helper::View::TT->mk_comptest.
417
c4c50c2d 418See L<Catalyst::Helper::View::TT> and
419L<Catalyst::Helper::Model::DBIC::Schema> for examples.
68ccb5e5 420
421All helper classes should be under one of the following namespaces.
422
423 Catalyst::Helper::Model::
424 Catalyst::Helper::View::
425 Catalyst::Helper::Controller::
426
675fef06 427=head2 COMMON HELPERS
bc8d7994 428
429=over
430
431=item *
432
433L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
434
435=item *
436
437L<Catalyst::Helper::View::TT> - Template Toolkit view
438
439=item *
440
441L<Catalyst::Helper::Model::LDAP>
442
443=item *
444
445L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
446
447=back
448
449=head3 NOTE
450
675fef06 451The helpers will read author name from /etc/passwd by default. + To override, please export the AUTHOR variable.
bc8d7994 452
453=head1 METHODS
454
fab70e0a 455=head2 mk_compclass
456
457This method in your Helper module is called with C<$helper>
458which is a L<Catalyst::Helper> object, and whichever other arguments
459the user added to the command-line. You can use the $helper to call methods
460described below.
461
462If the Helper module does not contain a C<mk_compclass> method, it
463will fall back to calling L</render_file>, with an argument of
464C<compclass>.
465
466=head2 mk_comptest
467
468This method in your Helper module is called with C<$helper>
469which is a L<Catalyst::Helper> object, and whichever other arguments
470the user added to the command-line. You can use the $helper to call methods
471described below.
472
473If the Helper module does not contain a C<mk_compclass> method, it
474will fall back to calling L</render_file>, with an argument of
475C<comptest>.
476
477=head2 mk_stuff
478
479This method is called if the user does not supply any of the usual
480component types C<view>, C<controller>, C<model>. It is passed the
481C<$helper> object (an instance of L<Catalyst::Helper>), and any other
482arguments the user typed.
483
484There is no fallback for this method.
485
bc8d7994 486=head1 INTERNAL METHODS
fab70e0a 487
488These are the methods that the Helper classes can call on the
489<$helper> object passed to them.
490
28eb1300 491=head2 render_file ($file, $path, $vars)
fab70e0a 492
28eb1300 493Render and create a file from a template in DATA using Template
494Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
495the path to the file and $vars is the hashref as expected by
496L<Template Toolkit|Template>.
fab70e0a 497
28eb1300 498=head2 get_file ($class, $file)
fab70e0a 499
500Fetch file contents from the DATA section. This is used internally by
28eb1300 501L</render_file>. $class is the name of the class to get the DATA
502section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
503values for this.
fab70e0a 504
505=head2 mk_app
506
507Create the main application skeleton. This is called by L<catalyst.pl>.
508
28eb1300 509=head2 mk_component ($app)
fab70e0a 510
511This method is called by L<create.pl> to make new components
512for your application.
513
28eb1300 514=head3 mk_dir ($path)
fab70e0a 515
516Surprisingly, this function makes a directory.
517
28eb1300 518=head2 mk_file ($file, $content)
fab70e0a 519
520Writes content to a file. Called by L</render_file>.
521
28eb1300 522=head2 next_test ($test_name)
fab70e0a 523
524Calculates the name of the next numbered test file and returns it.
28eb1300 525Don't give the number or the .t suffix for the test name.
fab70e0a 526
c6dbb300 527=head2 Dir
528
529Alias for L<Path::Class::Dir>
530
531=cut
532
533=head2 get_sharedir_file
534
535Method for getting a file out of share/
536
537=cut
538
539=head2 render_file_contents
540
541Process a L<Template::Toolkit> template.
542
543=cut
544
545=head2 render_sharedir_file
546
547Render a template/image file from our share directory
548
549=cut
550
551
68ccb5e5 552=head1 NOTE
553
554The helpers will read author name from /etc/passwd by default.
555To override, please export the AUTHOR variable.
556
557=head1 SEE ALSO
558
559L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
560L<Catalyst::Response>, L<Catalyst>
561
f64c718c 562=head1 AUTHORS
68ccb5e5 563
f64c718c 564Catalyst Contributors, see Catalyst.pm
68ccb5e5 565
566=head1 LICENSE
567
7cd3b67e 568This library is free software. You can redistribute it and/or modify
68ccb5e5 569it under the same terms as Perl itself.
570
64d4433e 571=begin pod_to_ignore
572
68ccb5e5 573=cut
574
5751;
68ccb5e5 576