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