Basic test for get_sharedir_file
[catagits/Catalyst-Devel.git] / lib / Catalyst / Helper.pm
CommitLineData
68ccb5e5 1package Catalyst::Helper;
2
3use strict;
c93c671b 4use warnings;
b8dea4e7 5use base 'Class::Accessor::Fast';
68ccb5e5 6use Config;
7use File::Spec;
8use File::Path;
68ccb5e5 9use FindBin;
2415f774 10use IO::File;
11use POSIX 'strftime';
68ccb5e5 12use Template;
9ffb6b83 13use Catalyst::Devel;
68ccb5e5 14use Catalyst::Utils;
15use Catalyst::Exception;
afd739f1 16use Path::Class qw/dir file/;
17use File::ShareDir qw/dist_dir/;
239b5fc0 18use Moose;
19use aliased 'Path::Class::Dir';
20
68ccb5e5 21
22my %cache;
23
24=head1 NAME
25
26Catalyst::Helper - Bootstrap a Catalyst application
27
28=head1 SYNOPSIS
29
fab70e0a 30 catalyst.pl <myappname>
68ccb5e5 31
32=cut
33
faae88e5 34
35
afd739f1 36sub get_sharedir_file {
37 my ($self, @filename) = @_;
76b106bc 38 my $dist_dir;
39 if (-d "inc/.author") { # Can't use sharedir if we're in a checkout
40 # this feels horrible, better ideas?
41 $dist_dir = 'share';
42 }
43 else {
44 $dist_dir = dist_dir('Catalyst-Devel');
45 }
46 my $file = file( $dist_dir, @filename);
afd739f1 47 my $contents = $file->slurp;
48 return $contents;
49}
50
faae88e5 51# Do not touch this method, *EVER*, it is needed for back compat.
68ccb5e5 52sub get_file {
03082a71 53 my ( $self, $class, $file ) = @_;
54 unless ( $cache{$class} ) {
55 local $/;
56 $cache{$class} = eval "package $class; <DATA>";
57 }
58 my $data = $cache{$class};
59 my @files = split /^__(.+)__\r?\n/m, $data;
60 shift @files;
61 while (@files) {
62 my ( $name, $content ) = splice @files, 0, 2;
63 return $content if $name eq $file;
64 }
65 return 0;
68ccb5e5 66}
67
03082a71 68
68ccb5e5 69sub mk_app {
70 my ( $self, $name ) = @_;
71
72 # Needs to be here for PAR
73 require Catalyst;
74
555cab18 75 if ( $name =~ /[^\w:]/ || $name =~ /^\d/ || $name =~ /\b:\b|:{3,}/) {
68ccb5e5 76 warn "Error: Invalid application name.\n";
77 return 0;
78 }
902763f2 79 $self->{name } = $name;
80 $self->{dir } = $name;
81 $self->{dir } =~ s/\:\:/-/g;
82 $self->{script } = File::Spec->catdir( $self->{dir}, 'script' );
83 $self->{appprefix } = Catalyst::Utils::appprefix($name);
e9345225 84 $self->{appenv } = Catalyst::Utils::class2env($name);
675fef06 85 $self->{startperl } = -r '/usr/bin/env'
86 ? '#!/usr/bin/env perl'
9bc8b354 87 : "#!$Config{perlpath} -w";
9ffb6b83 88 $self->{scriptgen } = $Catalyst::Devel::CATALYST_SCRIPT_GEN || 4;
902763f2 89 $self->{catalyst_version} = $Catalyst::VERSION;
90 $self->{author } = $self->{author} = $ENV{'AUTHOR'}
68ccb5e5 91 || eval { @{ [ getpwuid($<) ] }[6] }
92 || 'Catalyst developer';
93
94 my $gen_scripts = ( $self->{makefile} ) ? 0 : 1;
95 my $gen_makefile = ( $self->{scripts} ) ? 0 : 1;
96 my $gen_app = ( $self->{scripts} || $self->{makefile} ) ? 0 : 1;
97
98 if ($gen_app) {
99 $self->_mk_dirs;
100 $self->_mk_config;
101 $self->_mk_appclass;
102 $self->_mk_rootclass;
103 $self->_mk_readme;
104 $self->_mk_changes;
105 $self->_mk_apptest;
106 $self->_mk_images;
107 $self->_mk_favicon;
108 }
109 if ($gen_makefile) {
110 $self->_mk_makefile;
111 }
112 if ($gen_scripts) {
113 $self->_mk_cgi;
114 $self->_mk_fastcgi;
115 $self->_mk_server;
116 $self->_mk_test;
117 $self->_mk_create;
45d74601 118 $self->_mk_information;
68ccb5e5 119 }
120 return $self->{dir};
121}
122
68ccb5e5 123sub mk_component {
124 my $self = shift;
125 my $app = shift;
126 $self->{app} = $app;
127 $self->{author} = $self->{author} = $ENV{'AUTHOR'}
128 || eval { @{ [ getpwuid($<) ] }[6] }
129 || 'A clever guy';
130 $self->{base} ||= File::Spec->catdir( $FindBin::Bin, '..' );
131 unless ( $_[0] =~ /^(?:model|view|controller)$/i ) {
132 my $helper = shift;
133 my @args = @_;
134 my $class = "Catalyst::Helper::$helper";
135 eval "require $class";
136
137 if ($@) {
138 Catalyst::Exception->throw(
139 message => qq/Couldn't load helper "$class", "$@"/ );
140 }
141
142 if ( $class->can('mk_stuff') ) {
143 return 1 unless $class->mk_stuff( $self, @args );
144 }
145 }
146 else {
147 my $type = shift;
148 my $name = shift || "Missing name for model/view/controller";
149 my $helper = shift;
150 my @args = @_;
fab70e0a 151 return 0 if $name =~ /[^\w\:]/;
68ccb5e5 152 $type = lc $type;
153 $self->{long_type} = ucfirst $type;
154 $type = 'M' if $type =~ /model/i;
155 $type = 'V' if $type =~ /view/i;
156 $type = 'C' if $type =~ /controller/i;
157 my $appdir = File::Spec->catdir( split /\:\:/, $app );
158 my $test_path =
159 File::Spec->catdir( $FindBin::Bin, '..', 'lib', $appdir, 'C' );
160 $type = $self->{long_type} unless -d $test_path;
161 $self->{type} = $type;
162 $self->{name} = $name;
163 $self->{class} = "$app\::$type\::$name";
164
165 # Class
166 my $path =
167 File::Spec->catdir( $FindBin::Bin, '..', 'lib', $appdir, $type );
168 my $file = $name;
169 if ( $name =~ /\:/ ) {
170 my @path = split /\:\:/, $name;
171 $file = pop @path;
172 $path = File::Spec->catdir( $path, @path );
173 }
174 $self->mk_dir($path);
175 $file = File::Spec->catfile( $path, "$file.pm" );
176 $self->{file} = $file;
177
178 # Test
179 $self->{test_dir} = File::Spec->catdir( $FindBin::Bin, '..', 't' );
180 $self->{test} = $self->next_test;
181
182 # Helper
183 if ($helper) {
184 my $comp = $self->{long_type};
185 my $class = "Catalyst::Helper::$comp\::$helper";
186 eval "require $class";
187
188 if ($@) {
189 Catalyst::Exception->throw(
190 message => qq/Couldn't load helper "$class", "$@"/ );
191 }
192
193 if ( $class->can('mk_compclass') ) {
194 return 1 unless $class->mk_compclass( $self, @args );
195 }
196 else { return 1 unless $self->_mk_compclass }
197
198 if ( $class->can('mk_comptest') ) {
199 $class->mk_comptest( $self, @args );
200 }
201 else { $self->_mk_comptest }
202 }
203
204 # Fallback
205 else {
206 return 1 unless $self->_mk_compclass;
207 $self->_mk_comptest;
208 }
209 }
210 return 1;
211}
212
68ccb5e5 213sub mk_dir {
214 my ( $self, $dir ) = @_;
215 if ( -d $dir ) {
216 print qq/ exists "$dir"\n/;
217 return 0;
218 }
219 if ( mkpath [$dir] ) {
220 print qq/created "$dir"\n/;
221 return 1;
222 }
223
224 Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ );
225}
226
68ccb5e5 227sub mk_file {
228 my ( $self, $file, $content ) = @_;
06f62452 229 if ( -e $file && -s _ ) {
68ccb5e5 230 print qq/ exists "$file"\n/;
231 return 0
232 unless ( $self->{'.newfiles'}
233 || $self->{scripts}
234 || $self->{makefile} );
235 if ( $self->{'.newfiles'} ) {
236 if ( my $f = IO::File->new("< $file") ) {
237 my $oldcontent = join( '', (<$f>) );
238 return 0 if $content eq $oldcontent;
239 }
240 $file .= '.new';
241 }
242 }
243 if ( my $f = IO::File->new("> $file") ) {
244 binmode $f;
245 print $f $content;
246 print qq/created "$file"\n/;
247 return 1;
248 }
249
250 Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ );
251}
252
68ccb5e5 253sub next_test {
254 my ( $self, $tname ) = @_;
255 if ($tname) { $tname = "$tname.t" }
256 else {
257 my $name = $self->{name};
258 my $prefix = $name;
259 $prefix =~ s/::/-/g;
260 $prefix = $prefix;
261 $tname = $prefix . '.t';
262 $self->{prefix} = $prefix;
263 $prefix = lc $prefix;
264 $prefix =~ s/-/\//g;
265 $self->{uri} = "/$prefix";
266 }
267 my $dir = $self->{test_dir};
268 my $type = lc $self->{type};
269 $self->mk_dir($dir);
270 return File::Spec->catfile( $dir, "$type\_$tname" );
271}
272
faae88e5 273# Do not touch this method, *EVER*, it is needed for back compat.
7025ed89 274## addendum: we had to split this method so we could have backwards
275## compatability. otherwise, we'd have no way to pass stuff from __DATA__
faae88e5 276
68ccb5e5 277sub render_file {
278 my ( $self, $file, $path, $vars ) = @_;
7025ed89 279 my $template = $self->get_file( ( caller(0) )[0], $file );
06f62452 280 $self->render_file_contents($template, $path, $vars);
7025ed89 281}
282
283sub render_sharedir_file {
284 my ( $self, $file, $path, $vars ) = @_;
285 my $template = $self->get_sharedir_file( $file );
e97273a4 286 $self->render_file_contents($template, $path, $vars);
7025ed89 287}
288
289sub render_file_contents {
290 my ( $self, $template, $path, $vars ) = @_;
68ccb5e5 291 $vars ||= {};
292 my $t = Template->new;
68ccb5e5 293 return 0 unless $template;
294 my $output;
295 $t->process( \$template, { %{$self}, %$vars }, \$output )
296 || Catalyst::Exception->throw(
7025ed89 297 message => qq/Couldn't process "$template", / . $t->error() );
68ccb5e5 298 $self->mk_file( $path, $output );
299}
300
45d74601 301sub _mk_information {
302 my $self = shift;
303 print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/;
304}
305
68ccb5e5 306sub _mk_dirs {
307 my $self = shift;
308 $self->mk_dir( $self->{dir} );
309 $self->mk_dir( $self->{script} );
310 $self->{lib} = File::Spec->catdir( $self->{dir}, 'lib' );
311 $self->mk_dir( $self->{lib} );
312 $self->{root} = File::Spec->catdir( $self->{dir}, 'root' );
313 $self->mk_dir( $self->{root} );
314 $self->{static} = File::Spec->catdir( $self->{root}, 'static' );
315 $self->mk_dir( $self->{static} );
316 $self->{images} = File::Spec->catdir( $self->{static}, 'images' );
317 $self->mk_dir( $self->{images} );
318 $self->{t} = File::Spec->catdir( $self->{dir}, 't' );
319 $self->mk_dir( $self->{t} );
320
321 $self->{class} = File::Spec->catdir( split( /\:\:/, $self->{name} ) );
322 $self->{mod} = File::Spec->catdir( $self->{lib}, $self->{class} );
323 $self->mk_dir( $self->{mod} );
324
325 if ( $self->{short} ) {
326 $self->{m} = File::Spec->catdir( $self->{mod}, 'M' );
327 $self->mk_dir( $self->{m} );
328 $self->{v} = File::Spec->catdir( $self->{mod}, 'V' );
329 $self->mk_dir( $self->{v} );
330 $self->{c} = File::Spec->catdir( $self->{mod}, 'C' );
331 $self->mk_dir( $self->{c} );
332 }
333 else {
334 $self->{m} = File::Spec->catdir( $self->{mod}, 'Model' );
335 $self->mk_dir( $self->{m} );
336 $self->{v} = File::Spec->catdir( $self->{mod}, 'View' );
337 $self->mk_dir( $self->{v} );
338 $self->{c} = File::Spec->catdir( $self->{mod}, 'Controller' );
339 $self->mk_dir( $self->{c} );
340 }
341 my $name = $self->{name};
342 $self->{rootname} =
343 $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root";
344 $self->{base} = File::Spec->rel2abs( $self->{dir} );
345}
346
347sub _mk_appclass {
348 my $self = shift;
349 my $mod = $self->{mod};
3c24e97e 350 $self->render_file( 'appclass.tt', "$mod.pm" );
68ccb5e5 351}
352
353sub _mk_rootclass {
354 my $self = shift;
3c24e97e 355 $self->render_file( 'rootclass.tt',
68ccb5e5 356 File::Spec->catfile( $self->{c}, "Root.pm" ) );
357}
358
359sub _mk_makefile {
360 my $self = shift;
361 $self->{path} = File::Spec->catfile( 'lib', split( '::', $self->{name} ) );
362 $self->{path} .= '.pm';
363 my $dir = $self->{dir};
3c24e97e 364 $self->render_file( 'makefile.tt', "$dir\/Makefile.PL" );
68ccb5e5 365
366 if ( $self->{makefile} ) {
367
368 # deprecate the old Build.PL file when regenerating Makefile.PL
369 $self->_deprecate_file(
370 File::Spec->catdir( $self->{dir}, 'Build.PL' ) );
371 }
372}
373
374sub _mk_config {
375 my $self = shift;
376 my $dir = $self->{dir};
377 my $appprefix = $self->{appprefix};
3c24e97e 378 $self->render_file( 'config.tt',
a4e6d745 379 File::Spec->catfile( $dir, "$appprefix.conf" ) );
68ccb5e5 380}
381
382sub _mk_readme {
383 my $self = shift;
384 my $dir = $self->{dir};
3c24e97e 385 $self->render_file( 'readme.tt', "$dir\/README" );
68ccb5e5 386}
387
388sub _mk_changes {
389 my $self = shift;
390 my $dir = $self->{dir};
5b1ec88b 391 my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time);
3c24e97e 392 $self->render_file( 'changes.tt', "$dir\/Changes", { time => $time } );
68ccb5e5 393}
394
395sub _mk_apptest {
396 my $self = shift;
397 my $t = $self->{t};
3c24e97e 398 $self->render_file( 'apptest.tt', "$t\/01app.t" );
399 $self->render_file( 'podtest.tt', "$t\/02pod.t" );
400 $self->render_file( 'podcoveragetest.tt', "$t\/03podcoverage.t" );
68ccb5e5 401}
402
403sub _mk_cgi {
404 my $self = shift;
405 my $script = $self->{script};
406 my $appprefix = $self->{appprefix};
3c24e97e 407 $self->render_file( 'cgi.tt', "$script\/$appprefix\_cgi.pl" );
68ccb5e5 408 chmod 0700, "$script/$appprefix\_cgi.pl";
409}
410
411sub _mk_fastcgi {
412 my $self = shift;
413 my $script = $self->{script};
414 my $appprefix = $self->{appprefix};
3c24e97e 415 $self->render_file( 'fastcgi.tt', "$script\/$appprefix\_fastcgi.pl" );
68ccb5e5 416 chmod 0700, "$script/$appprefix\_fastcgi.pl";
417}
418
419sub _mk_server {
420 my $self = shift;
421 my $script = $self->{script};
422 my $appprefix = $self->{appprefix};
3c24e97e 423 $self->render_file( 'server.tt', "$script\/$appprefix\_server.pl" );
68ccb5e5 424 chmod 0700, "$script/$appprefix\_server.pl";
425}
426
427sub _mk_test {
428 my $self = shift;
429 my $script = $self->{script};
430 my $appprefix = $self->{appprefix};
3c24e97e 431 $self->render_file( 'test.tt', "$script/$appprefix\_test.pl" );
68ccb5e5 432 chmod 0700, "$script/$appprefix\_test.pl";
433}
434
435sub _mk_create {
436 my $self = shift;
437 my $script = $self->{script};
438 my $appprefix = $self->{appprefix};
3c24e97e 439 $self->render_file( 'create.tt', "$script\/$appprefix\_create.pl" );
68ccb5e5 440 chmod 0700, "$script/$appprefix\_create.pl";
441}
442
443sub _mk_compclass {
444 my $self = shift;
445 my $file = $self->{file};
3c24e97e 446 return $self->render_file( 'compclass.tt', "$file" );
68ccb5e5 447}
448
449sub _mk_comptest {
450 my $self = shift;
451 my $test = $self->{test};
3c24e97e 452 $self->render_file( 'comptest.tt', "$test" );
68ccb5e5 453}
454
455sub _mk_images {
456 my $self = shift;
457 my $images = $self->{images};
458 my @images =
459 qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow
460 btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built
461 btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/;
462 for my $name (@images) {
9e5643df 463 my $image = $self->get_sharedir_file("root", "$name.png");
68ccb5e5 464 $self->mk_file( File::Spec->catfile( $images, "$name.png" ), $image );
465 }
466}
467
468sub _mk_favicon {
469 my $self = shift;
470 my $root = $self->{root};
afd739f1 471 my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico' );
472 my $dest = File::Spec->catfile( $root, "favicon.ico" );
473 $self->mk_file( $dest, $favicon );
68ccb5e5 474
475}
476
477sub _deprecate_file {
478 my ( $self, $file ) = @_;
479 if ( -e $file ) {
480 my $oldcontent;
481 if ( my $f = IO::File->new("< $file") ) {
482 $oldcontent = join( '', (<$f>) );
483 }
484 my $newfile = $file . '.deprecated';
485 if ( my $f = IO::File->new("> $newfile") ) {
486 binmode $f;
487 print $f $oldcontent;
488 print qq/created "$newfile"\n/;
489 unlink $file;
490 print qq/removed "$file"\n/;
491 return 1;
492 }
493 Catalyst::Exception->throw(
494 message => qq/Couldn't create "$file", "$!"/ );
495 }
496}
497
239b5fc0 498
499## this is so you don't have to do make install after every change to test
500sub _find_share_dir {
501 my ($self, $args) = @_;
502 my $share_name = $self->name;
503 if ($share_name =~ s!^/(.*?)/!!) {
504 my $dist = $1;
505 $args->{share_base_dir} = eval {
506 Dir->new(File::ShareDir::dist_dir($dist))
507 ->subdir('share');
508 };
509 if ($@) {
510 # not installed
511 my $file = __FILE__;
512 my $dir = Dir->new(dirname($file));
513 my $share_base;
514 while ($dir->parent) {
515 if (-d $dir->subdir('share') && -d $dir->subdir('share')->subdir('root')) {
516 $share_base = $dir->subdir('share')->subdir('root');
517 last;
518 }
519 $dir = $dir->parent;
520 }
521 confess "could not find sharebase by recursion. ended up at $dir, from $file"
522 unless $share_base;
523 $args->{share_base_dir} = $share_base;
524 }
525 }
526 my $base = $args->{share_base_dir}->subdir($share_name);
527 confess "No such share base directory ${base}"
528 unless -d $base;
529 $self->share_dir($base);
530};
531
532
533
fab70e0a 534=head1 DESCRIPTION
535
536This module is used by B<catalyst.pl> to create a set of scripts for a
537new catalyst application. The scripts each contain documentation and
538will output help on how to use them if called incorrectly or in some
539cases, with no arguments.
540
541It also provides some useful methods for a Helper module to call when
542creating a component. See L</METHODS>.
543
544=head1 SCRIPTS
545
546=head2 _create.pl
547
548Used to create new components for a catalyst application at the
549development stage.
550
551=head2 _server.pl
552
553The catalyst test server, starts an HTTPD which outputs debugging to
554the terminal.
555
556=head2 _test.pl
557
558A script for running tests from the command-line.
559
560=head2 _cgi.pl
561
562Run your application as a CGI.
563
564=head2 _fastcgi.pl
565
566Run the application as a fastcgi app. Either by hand, or call this
567from FastCgiServer in your http server config.
568
68ccb5e5 569=head1 HELPERS
570
fab70e0a 571The L</_create.pl> script creates application components using Helper
572modules. The Catalyst team provides a good number of Helper modules
573for you to use. You can also add your own.
574
68ccb5e5 575Helpers are classes that provide two methods.
576
577 * mk_compclass - creates the Component class
578 * mk_comptest - creates the Component test
579
fab70e0a 580So when you call C<scripts/myapp_create.pl view MyView TT>, create
581will try to execute Catalyst::Helper::View::TT->mk_compclass and
68ccb5e5 582Catalyst::Helper::View::TT->mk_comptest.
583
c4c50c2d 584See L<Catalyst::Helper::View::TT> and
585L<Catalyst::Helper::Model::DBIC::Schema> for examples.
68ccb5e5 586
587All helper classes should be under one of the following namespaces.
588
589 Catalyst::Helper::Model::
590 Catalyst::Helper::View::
591 Catalyst::Helper::Controller::
592
675fef06 593=head2 COMMON HELPERS
bc8d7994 594
595=over
596
597=item *
598
599L<Catalyst::Helper::Model::DBIC::Schema> - DBIx::Class models
600
601=item *
602
603L<Catalyst::Helper::View::TT> - Template Toolkit view
604
605=item *
606
607L<Catalyst::Helper::Model::LDAP>
608
609=item *
610
611L<Catalyst::Helper::Model::Adaptor> - wrap any class into a Catalyst model
612
613=back
614
615=head3 NOTE
616
675fef06 617The helpers will read author name from /etc/passwd by default. + To override, please export the AUTHOR variable.
bc8d7994 618
619=head1 METHODS
620
fab70e0a 621=head2 mk_compclass
622
623This method in your Helper module is called with C<$helper>
624which is a L<Catalyst::Helper> object, and whichever other arguments
625the user added to the command-line. You can use the $helper to call methods
626described below.
627
628If the Helper module does not contain a C<mk_compclass> method, it
629will fall back to calling L</render_file>, with an argument of
630C<compclass>.
631
632=head2 mk_comptest
633
634This method in your Helper module is called with C<$helper>
635which is a L<Catalyst::Helper> object, and whichever other arguments
636the user added to the command-line. You can use the $helper to call methods
637described below.
638
639If the Helper module does not contain a C<mk_compclass> method, it
640will fall back to calling L</render_file>, with an argument of
641C<comptest>.
642
643=head2 mk_stuff
644
645This method is called if the user does not supply any of the usual
646component types C<view>, C<controller>, C<model>. It is passed the
647C<$helper> object (an instance of L<Catalyst::Helper>), and any other
648arguments the user typed.
649
650There is no fallback for this method.
651
bc8d7994 652=head1 INTERNAL METHODS
fab70e0a 653
654These are the methods that the Helper classes can call on the
655<$helper> object passed to them.
656
28eb1300 657=head2 render_file ($file, $path, $vars)
fab70e0a 658
28eb1300 659Render and create a file from a template in DATA using Template
660Toolkit. $file is the relevent chunk of the __DATA__ section, $path is
661the path to the file and $vars is the hashref as expected by
662L<Template Toolkit|Template>.
fab70e0a 663
28eb1300 664=head2 get_file ($class, $file)
fab70e0a 665
666Fetch file contents from the DATA section. This is used internally by
28eb1300 667L</render_file>. $class is the name of the class to get the DATA
668section from. __PACKAGE__ or ( caller(0) )[0] might be sensible
669values for this.
fab70e0a 670
671=head2 mk_app
672
673Create the main application skeleton. This is called by L<catalyst.pl>.
674
28eb1300 675=head2 mk_component ($app)
fab70e0a 676
677This method is called by L<create.pl> to make new components
678for your application.
679
28eb1300 680=head3 mk_dir ($path)
fab70e0a 681
682Surprisingly, this function makes a directory.
683
28eb1300 684=head2 mk_file ($file, $content)
fab70e0a 685
686Writes content to a file. Called by L</render_file>.
687
28eb1300 688=head2 next_test ($test_name)
fab70e0a 689
690Calculates the name of the next numbered test file and returns it.
28eb1300 691Don't give the number or the .t suffix for the test name.
fab70e0a 692
68ccb5e5 693=head1 NOTE
694
695The helpers will read author name from /etc/passwd by default.
696To override, please export the AUTHOR variable.
697
698=head1 SEE ALSO
699
700L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
701L<Catalyst::Response>, L<Catalyst>
702
f64c718c 703=head1 AUTHORS
68ccb5e5 704
f64c718c 705Catalyst Contributors, see Catalyst.pm
68ccb5e5 706
707=head1 LICENSE
708
7cd3b67e 709This library is free software. You can redistribute it and/or modify
68ccb5e5 710it under the same terms as Perl itself.
711
64d4433e 712=begin pod_to_ignore
713
68ccb5e5 714=cut
715
7161;
68ccb5e5 717