Catalyst-Devel-1.39/0000755000175000017500000000000012156612327013610 5ustar ilmariilmariCatalyst-Devel-1.39/lib/0000755000175000017500000000000012156612327014356 5ustar ilmariilmariCatalyst-Devel-1.39/lib/Catalyst/0000755000175000017500000000000012156612327016142 5ustar ilmariilmariCatalyst-Devel-1.39/lib/Catalyst/Restarter.pm0000644000175000017500000001165212144377672020470 0ustar ilmariilmaripackage Catalyst::Restarter; use Moose; use Cwd qw( abs_path ); use File::ChangeNotify; use File::Spec; use FindBin; use Catalyst::Utils; use namespace::clean -except => 'meta'; has start_sub => ( is => 'ro', isa => 'CodeRef', required => 1, ); has argv => ( is => 'ro', isa => 'ArrayRef', required => 1, ); has _watcher => ( is => 'rw', isa => 'File::ChangeNotify::Watcher', ); has _filter => ( is => 'rw', isa => 'RegexpRef', ); has _child => ( is => 'rw', isa => 'Int', ); sub pick_subclass { my $class = shift; my $subclass; $subclass = defined $ENV{CATALYST_RESTARTER} ? $ENV{CATALYST_RESTARTER} : $^O eq 'MSWin32' ? 'Win32' : 'Forking'; $subclass = 'Catalyst::Restarter::' . $subclass; Catalyst::Utils::ensure_class_loaded($subclass); return $subclass; } sub BUILD { my $self = shift; my $p = shift; delete $p->{start_sub}; $p->{filter} ||= qr/(?:\/|^)(?![.#_]).+(?:\.yml$|\.yaml$|\.conf|\.pm)$/; my $app_root = abs_path( File::Spec->catdir( $FindBin::Bin, '..' ) ); # Monitor application root dir $p->{directories} ||= $app_root; # exclude t/, root/ and hidden dirs $p->{exclude} ||= [ File::Spec->catdir($app_root, 't'), File::Spec->catdir($app_root, 'root'), qr(/\.[^/]*/?$), # match hidden dirs ]; # keep filter regexp to make sure we don't restart on deleted # files or directories where we can't check -d $self->_filter( $p->{filter} ); # We could make this lazily, but this lets us check that we # received valid arguments for the watcher up front. $self->_watcher( File::ChangeNotify->instantiate_watcher( %{$p} ) ); } sub run_and_watch { my $self = shift; $self->_fork_and_start; return unless $self->_child; $self->_restart_on_changes; } sub _restart_on_changes { my $self = shift; # We use this loop in order to avoid having _handle_events() call back # into this method. We used to do that, and the end result was that stack # traces became longer and longer with every restart. Using this loop, the # portion of the stack trace that covers this code does not grow. while (1) { my @events = $self->_watcher->wait_for_events(); $self->_handle_events(@events); } } sub _handle_events { my $self = shift; my @events = @_; my @files; # Filter out any events which are the creation / deletion of directories # so that creating an empty directory won't cause a restart for my $event (@events) { my $path = $event->path(); my $type = $event->type(); if ( ( ( $type ne 'delete' && -f $path ) || ( $type eq 'delete' ) ) && ( $path =~ $self->_filter ) ) { push @files, { path => $path, type => $type }; } } if (@files) { print STDERR "\n"; print STDERR "Saw changes to the following files:\n"; for my $f (@files) { my $path = $f->{path}; my $type = $f->{type}; print STDERR " - $path ($type)\n"; } print STDERR "\n"; print STDERR "Attempting to restart the server\n\n"; $self->_kill_child; $self->_fork_and_start; } } sub DEMOLISH { my $self = shift; $self->_kill_child; } __PACKAGE__->meta->make_immutable; 1; __END__ =head1 NAME Catalyst::Restarter - Uses File::ChangeNotify to check for changed files and restart the server =head1 SYNOPSIS my $class = Catalyst::Restarter->pick_subclass; my $restarter = $class->new( directories => '/path/to/MyApp', regex => '\.yml$|\.yaml$|\.conf|\.pm$', start_sub => sub { ... } ); $restarter->run_and_watch; =head1 DESCRIPTION This is the base class for all restarters, and it also provide functionality for picking an appropriate restarter subclass for a given platform. This class uses L to watch one or more directories of files and restart the Catalyst server when any of those files changes. =head1 METHODS =head2 pick_subclass Returns the name of an appropriate subclass for the given platform. =head2 new ( start_sub => sub { ... }, ... ) This method creates a new restarter object, but should be called on a subclass, not this class. The "start_sub" argument is required. This is a subroutine reference that can be used to start the Catalyst server. =head2 run_and_watch This method forks, starts the server in a child process, and then watched for changed files in the parent. When files change, it kills the child, forks again, and starts a new server. =head1 SEE ALSO L, L =head1 AUTHORS Catalyst Contributors, see Catalyst.pm =head1 COPYRIGHT This program is free software, you can redistribute it and/or modify it under the same terms as Perl itself. =cut Catalyst-Devel-1.39/lib/Catalyst/Restarter/0000755000175000017500000000000012156612327020115 5ustar ilmariilmariCatalyst-Devel-1.39/lib/Catalyst/Restarter/Forking.pm0000644000175000017500000000231612144377672022064 0ustar ilmariilmaripackage Catalyst::Restarter::Forking; use Moose; extends 'Catalyst::Restarter'; has _child => ( is => 'rw', isa => 'Int', ); sub _fork_and_start { my $self = shift; if ( my $pid = fork ) { $self->_child($pid); } else { $self->start_sub->(); } } sub _kill_child { my $self = shift; return unless $self->_child; return unless kill 0, $self->_child; die "Cannot send INT signal to ", $self->_child, ": $!" unless kill 'INT', $self->_child; # If we don't wait for the child to exit, we could attempt to # start a new server before the old one has given up the port it # was listening on. wait; } __PACKAGE__->meta->make_immutable; 1; __END__ =head1 NAME Catalyst::Restarter::Forking - Forks and restarts the child process =head1 DESCRIPTION This class forks and runs the server in a child process. When it needs to restart, it kills the child and creates a new one. =head1 SEE ALSO L, L, =head1 AUTHORS Catalyst Contributors, see Catalyst.pm =head1 COPYRIGHT This program is free software, you can redistribute it and/or modify it under the same terms as Perl itself. =cut Catalyst-Devel-1.39/lib/Catalyst/Restarter/Win32.pm0000644000175000017500000000236212144377672021370 0ustar ilmariilmaripackage Catalyst::Restarter::Win32; use Moose; use Proc::Background; extends 'Catalyst::Restarter'; has _child => ( is => 'rw', isa => 'Proc::Background', ); sub _fork_and_start { my $self = shift; # This is totally hack-tastic, and is probably much slower, but it # does seem to work. my @command = ( $^X, map("-I$_", @INC), $0, grep { ! /^\-r/ } @{ $self->argv } ); my $child = Proc::Background->new(@command); $self->_child($child); } sub _kill_child { my $self = shift; return unless $self->_child; $self->_child->die; } __PACKAGE__->meta->make_immutable; 1; __END__ =head1 NAME Catalyst::Restarter::Win32 - Uses Proc::Background to manage process restarts =head1 DESCRIPTION This class uses L, which in turn uses L. The new process is run using the same command-line as the original script, but without any restart-based options. This is a big hack, but using forks just does not work on Windows. =head1 SEE ALSO L, L, =head1 AUTHORS Catalyst Contributors, see Catalyst.pm =head1 COPYRIGHT This program is free software, you can redistribute it and/or modify it under the same terms as Perl itself. =cut Catalyst-Devel-1.39/lib/Catalyst/Helper.pm0000644000175000017500000005122212156600775017725 0ustar ilmariilmaripackage Catalyst::Helper; use Moose; use Config; use File::Spec; use File::Spec::Unix; use File::Path; use FindBin; use IO::File; use POSIX 'strftime'; use Template; use Catalyst::Devel; use Catalyst::Utils; use Catalyst::Exception; use Path::Class qw/dir file/; use File::ShareDir qw/dist_dir/; use YAML::Tiny; use namespace::autoclean; with 'MooseX::Emulate::Class::Accessor::Fast'; # Change Catalyst/Devel.pm also our $VERSION = '1.39'; my %cache; =head1 NAME Catalyst::Helper - Bootstrap a Catalyst application =head1 SYNOPSIS catalyst.pl =cut sub get_sharedir_file { my ($self, @filename) = @_; my $dist_dir; if (exists $ENV{CATALYST_DEVEL_SHAREDIR}) { $dist_dir = $ENV{CATALYST_DEVEL_SHAREDIR}; } elsif (-d "inc/.author" && -f "lib/Catalyst/Helper.pm" ) { # Can't use sharedir if we're in a checkout # this feels horrible, better ideas? $dist_dir = 'share'; } else { $dist_dir = dist_dir('Catalyst-Devel'); } my $file = file( $dist_dir, @filename); Carp::confess("Cannot find $file") unless -r $file; my $contents = $file->slurp(iomode => "<:raw"); return $contents; } # Do not touch this method, *EVER*, it is needed for back compat. sub get_file { my ( $self, $class, $file ) = @_; unless ( $cache{$class} ) { local $/; $cache{$class} = eval "package $class; "; } my $data = $cache{$class}; Carp::confess("Could not get data from __DATA__ segment for $class") unless $data; my @files = split /^__(.+)__\r?\n/m, $data; shift @files; while (@files) { my ( $name, $content ) = splice @files, 0, 2; return $content if $name eq $file; } return 0; } sub mk_app { my ( $self, $name ) = @_; # Needs to be here for PAR require Catalyst; if($name eq '.') { if(!-e 'META.yml') { system perl => 'Makefile.PL' and Catalyst::Exception->throw(message => q( Failed to run "perl Makefile.PL". )); } $name = YAML::Tiny->read('META.yml')->[0]->{'name'}; $name =~ s/-/::/g; $self->{dir} = '.'; } if ( $name =~ /[^\w:]/ || $name =~ /^\d/ || $name =~ /\b:\b|:{3,}/) { warn "Error: Invalid application name.\n"; return 0; } if(!defined $self->{'dir'}) { $self->{dir} = $name; $self->{dir} =~ s/\:\:/-/g; } $self->{name } = $name; $self->{script } = dir( $self->{dir}, 'script' ); $self->{appprefix } = Catalyst::Utils::appprefix($name); $self->{appenv } = Catalyst::Utils::class2env($name); $self->{startperl } = -r '/usr/bin/env' ? '#!/usr/bin/env perl' : "#!$Config{perlpath}"; $self->{scriptgen } = $Catalyst::Devel::CATALYST_SCRIPT_GEN; $self->{catalyst_version} = $Catalyst::VERSION; $self->{author } ||= $ENV{'AUTHOR'} || eval { @{ [ getpwuid($<) ] }[6] } || 'Catalyst developer'; my $gen_scripts = ( $self->{makefile} ) ? 0 : 1; my $gen_makefile = ( $self->{scripts} ) ? 0 : 1; my $gen_app = ( $self->{scripts} || $self->{makefile} ) ? 0 : 1; if ($gen_app) { for ( qw/ _mk_dirs _mk_config _mk_psgi _mk_appclass _mk_rootclass _mk_readme _mk_changes _mk_apptest _mk_podtest _mk_podcoveragetest _mk_images _mk_favicon/ ) { $self->$_; } } if ($gen_makefile) { $self->_mk_makefile; } if ($gen_scripts) { for ( qw/ _mk_cgi _mk_fastcgi _mk_server _mk_test _mk_create _mk_information / ) { $self->$_; } } return $self->{dir}; } ## not much of this can really be changed, mk_compclass must be left for ## backcompat sub mk_component { my $self = shift; my $app = shift; $self->{app} = $app; $self->{author} = $self->{author} = $ENV{'AUTHOR'} || eval { @{ [ getpwuid($<) ] }[6] } || 'A clever guy'; $self->{base} ||= dir( $FindBin::Bin, '..' ); unless ( $_[0] =~ /^(?:model|view|controller)$/i ) { my $helper = shift; my @args = @_; my $class = "Catalyst::Helper::$helper"; eval "require $class"; if ($@) { Catalyst::Exception->throw( message => qq/Couldn't load helper "$class", "$@"/ ); } if ( $class->can('mk_stuff') ) { return 1 unless $class->mk_stuff( $self, @args ); } } else { my $type = shift; my $name = shift || "Missing name for model/view/controller"; my $helper = shift; my @args = @_; return 0 if $name =~ /[^\w\:]/; $type = lc $type; $self->{long_type} = ucfirst $type; $type = 'M' if $type =~ /model/i; $type = 'V' if $type =~ /view/i; $type = 'C' if $type =~ /controller/i; my $appdir = dir( split /\:\:/, $app ); my $test_path = dir( $self->{base}, 'lib', $appdir, 'C' ); $type = $self->{long_type} unless -d $test_path; $self->{type} = $type; $self->{name} = $name; $self->{class} = "$app\::$type\::$name"; # Class my $path = dir( $self->{base}, 'lib', $appdir, $type ); my $file = $name; if ( $name =~ /\:/ ) { my @path = split /\:\:/, $name; $file = pop @path; $path = dir( $path, @path ); } $self->mk_dir($path); $file = file( $path, "$file.pm" ); $self->{file} = $file; # Test $self->{test_dir} = dir( $self->{base}, 't' ); $self->{test} = $self->next_test; # Helper if ($helper) { my $comp = $self->{long_type}; my $class = "Catalyst::Helper::$comp\::$helper"; eval "require $class"; if ($@) { Catalyst::Exception->throw( message => qq/Couldn't load helper "$class", "$@"/ ); } if ( $class->can('mk_compclass') ) { return 1 unless $class->mk_compclass( $self, @args ); } else { return 1 unless $self->_mk_compclass } if ( $class->can('mk_comptest') ) { $class->mk_comptest( $self, @args ); } else { $self->_mk_comptest } } # Fallback else { return 1 unless $self->_mk_compclass; $self->_mk_comptest; } } return 1; } sub mk_dir { my ( $self, $dir ) = @_; if ( -d $dir ) { print qq/ exists "$dir"\n/; return 0; } if ( mkpath [$dir] ) { print qq/created "$dir"\n/; return 1; } Catalyst::Exception->throw( message => qq/Couldn't create "$dir", "$!"/ ); } sub mk_file { my ( $self, $file, $content ) = @_; if ( -e $file && -s _ ) { print qq/ exists "$file"\n/; return 0 unless ( $self->{'.newfiles'} || $self->{scripts} || $self->{makefile} ); if ( $self->{'.newfiles'} ) { if ( my $f = IO::File->new("< $file") ) { my $oldcontent = join( '', (<$f>) ); return 0 if $content eq $oldcontent; } $file .= '.new'; } } if ( my $f = IO::File->new("> $file") ) { binmode $f; print $f $content; print qq/created "$file"\n/; return $file; } Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ ); } sub next_test { my ( $self, $tname ) = @_; if ($tname) { $tname = "$tname.t" } else { my $name = $self->{name}; my $prefix = $name; $prefix =~ s/::/-/g; $prefix = $prefix; $tname = $prefix . '.t'; $self->{prefix} = $prefix; $prefix = lc $prefix; $prefix =~ s/-/\//g; $self->{uri} = "/$prefix"; } my $dir = $self->{test_dir}; my $type = lc $self->{type}; $self->mk_dir($dir); return file( $dir, "$type\_$tname" ); } # Do not touch this method, *EVER*, it is needed for back compat. ## addendum: we had to split this method so we could have backwards ## compatability. otherwise, we'd have no way to pass stuff from __DATA__ sub render_file { my ( $self, $file, $path, $vars, $perms ) = @_; my $template = $self->get_file( ( caller(0) )[0], $file ); $self->render_file_contents($template, $path, $vars, $perms); } sub render_sharedir_file { my ( $self, $file, $path, $vars, $perms ) = @_; my $template = $self->get_sharedir_file( $file ); die("Cannot get template from $file for $self\n") unless $template; $self->render_file_contents($template, $path, $vars, $perms); } sub render_file_contents { my ( $self, $template, $path, $vars, $perms ) = @_; $vars ||= {}; my $t = Template->new; return 0 unless $template; my $output; $t->process( \$template, { %{$self}, %$vars }, \$output ) || Catalyst::Exception->throw( message => qq/Couldn't process "$template", / . $t->error() ); my $file = $self->mk_file( $path, $output ); chmod $perms, file($file) if defined $perms; return $file; } sub _mk_information { my $self = shift; print qq/Change to application directory and Run "perl Makefile.PL" to make sure your install is complete\n/; } sub _mk_dirs { my $self = shift; $self->mk_dir( $self->{dir} ); $self->mk_dir( $self->{script} ); $self->{lib} = dir( $self->{dir}, 'lib' ); $self->mk_dir( $self->{lib} ); $self->{root} = dir( $self->{dir}, 'root' ); $self->mk_dir( $self->{root} ); $self->{static} = dir( $self->{root}, 'static' ); $self->mk_dir( $self->{static} ); $self->{images} = dir( $self->{static}, 'images' ); $self->mk_dir( $self->{images} ); $self->{t} = dir( $self->{dir}, 't' ); $self->mk_dir( $self->{t} ); $self->{class} = dir( split( /\:\:/, $self->{name} ) ); $self->{mod} = dir( $self->{lib}, $self->{class} ); $self->mk_dir( $self->{mod} ); if ( $self->{short} ) { $self->{m} = dir( $self->{mod}, 'M' ); $self->mk_dir( $self->{m} ); $self->{v} = dir( $self->{mod}, 'V' ); $self->mk_dir( $self->{v} ); $self->{c} = dir( $self->{mod}, 'C' ); $self->mk_dir( $self->{c} ); } else { $self->{m} = dir( $self->{mod}, 'Model' ); $self->mk_dir( $self->{m} ); $self->{v} = dir( $self->{mod}, 'View' ); $self->mk_dir( $self->{v} ); $self->{c} = dir( $self->{mod}, 'Controller' ); $self->mk_dir( $self->{c} ); } my $name = $self->{name}; $self->{rootname} = $self->{short} ? "$name\::C::Root" : "$name\::Controller::Root"; $self->{base} = dir( $self->{dir} )->absolute; } sub _mk_appclass { my $self = shift; my $mod = $self->{mod}; $self->render_sharedir_file( file('lib', 'MyApp.pm.tt'), "$mod.pm" ); } sub _mk_rootclass { my $self = shift; $self->render_sharedir_file( file('lib', 'MyApp', 'Controller', 'Root.pm.tt'), file( $self->{c}, "Root.pm" ) ); } sub _mk_makefile { my $self = shift; $self->{path} = join('/', 'lib', split( '::', $self->{name} ) ); $self->{path} .= '.pm'; my $dir = $self->{dir}; $self->render_sharedir_file( 'Makefile.PL.tt', file($dir, "Makefile.PL") ); if ( $self->{makefile} ) { # deprecate the old Build.PL file when regenerating Makefile.PL $self->_deprecate_file( file( $self->{dir}, 'Build.PL' ) ); } } sub _mk_psgi { my $self = shift; my $dir = $self->{dir}; my $appprefix = $self->{appprefix}; $self->render_sharedir_file( 'myapp.psgi.tt', file( $dir, "$appprefix.psgi" ) ); } sub _mk_config { my $self = shift; my $dir = $self->{dir}; my $appprefix = $self->{appprefix}; $self->render_sharedir_file( 'myapp.conf.tt', file( $dir, "$appprefix.conf" ) ); } sub _mk_readme { my $self = shift; my $dir = $self->{dir}; $self->render_sharedir_file( 'README.tt', file($dir, "README") ); } sub _mk_changes { my $self = shift; my $dir = $self->{dir}; my $time = strftime('%Y-%m-%d %H:%M:%S', localtime time); $self->render_sharedir_file( 'Changes.tt', file($dir, "Changes"), { time => $time } ); } sub _mk_apptest { my $self = shift; my $t = $self->{t}; $self->render_sharedir_file( file('t', '01app.t.tt'), file($t, "01app.t") ); } sub _mk_podtest { my $self = shift; my $t = $self->{t}; $self->render_sharedir_file( file('t', '02pod.t.tt'), file($t, "02pod.t") ); } sub _mk_podcoveragetest { my $self = shift; my $t = $self->{t}; $self->render_sharedir_file( file('t', '03podcoverage.t.tt'), file($t, "03podcoverage.t") ); } sub _mk_cgi { my $self = shift; my $script = $self->{script}; my $appprefix = $self->{appprefix}; $self->render_sharedir_file( file('script', 'myapp_cgi.pl.tt'), file($script,"$appprefix\_cgi.pl"), undef, 0755 ); } sub _mk_fastcgi { my $self = shift; my $script = $self->{script}; my $appprefix = $self->{appprefix}; $self->render_sharedir_file( file('script', 'myapp_fastcgi.pl.tt'), file($script, "$appprefix\_fastcgi.pl"), undef, 0755 ); } sub _mk_server { my $self = shift; my $script = $self->{script}; my $appprefix = $self->{appprefix}; $self->render_sharedir_file( file('script', 'myapp_server.pl.tt'), file($script, "$appprefix\_server.pl"), undef, 0755 ); } sub _mk_test { my $self = shift; my $script = $self->{script}; my $appprefix = $self->{appprefix}; $self->render_sharedir_file( file('script', 'myapp_test.pl.tt'), file($script, "$appprefix\_test.pl"), undef, 0755 ); } sub _mk_create { my $self = shift; my $script = $self->{script}; my $appprefix = $self->{appprefix}; $self->render_sharedir_file( file('script', 'myapp_create.pl.tt'), file($script, "$appprefix\_create.pl"), undef, 0755 ); } sub _mk_compclass { my $self = shift; my $file = $self->{file}; return $self->render_sharedir_file( file('lib', 'Helper', 'compclass.pm.tt'), $file ); } sub _mk_comptest { my $self = shift; my $test = $self->{test}; $self->render_sharedir_file( file('t', 'comptest.tt'), $test ); ## wtf do i rename this to? } sub _mk_images { my $self = shift; my $images = $self->{images}; my @images = qw/catalyst_logo btn_120x50_built btn_120x50_built_shadow btn_120x50_powered btn_120x50_powered_shadow btn_88x31_built btn_88x31_built_shadow btn_88x31_powered btn_88x31_powered_shadow/; for my $name (@images) { my $image = $self->get_sharedir_file("root", "static", "images", "$name.png.bin"); $self->mk_file( file( $images, "$name.png" ), $image ); } } sub _mk_favicon { my $self = shift; my $root = $self->{root}; my $favicon = $self->get_sharedir_file( 'root', 'favicon.ico.bin' ); my $dest = dir( $root, "favicon.ico" ); $self->mk_file( $dest, $favicon ); } sub _deprecate_file { my ( $self, $file ) = @_; if ( -e $file ) { my ($f, $oldcontent); if ( $f = IO::File->new("< $file") ) { $oldcontent = join( '', (<$f>) ); } my $newfile = $file . '.deprecated'; if ( $f = IO::File->new("> $newfile") ) { binmode $f; print $f $oldcontent; print qq/created "$newfile"\n/; unlink $file; print qq/removed "$file"\n/; return 1; } Catalyst::Exception->throw( message => qq/Couldn't create "$file", "$!"/ ); } } =head1 DESCRIPTION This module is used by B to create a set of scripts for a new catalyst application. The scripts each contain documentation and will output help on how to use them if called incorrectly or in some cases, with no arguments. It also provides some useful methods for a Helper module to call when creating a component. See L. =head1 SCRIPTS =head2 _create.pl Used to create new components for a catalyst application at the development stage. =head2 _server.pl The catalyst test server, starts an HTTPD which outputs debugging to the terminal. =head2 _test.pl A script for running tests from the command-line. =head2 _cgi.pl Run your application as a CGI. =head2 _fastcgi.pl Run the application as a fastcgi app. Either by hand, or call this from FastCgiServer in your http server config. =head1 HELPERS The L script creates application components using Helper modules. The Catalyst team provides a good number of Helper modules for you to use. You can also add your own. Helpers are classes that provide two methods. * mk_compclass - creates the Component class * mk_comptest - creates the Component test So when you call C, create will try to execute Catalyst::Helper::View::TT->mk_compclass and Catalyst::Helper::View::TT->mk_comptest. See L and L for examples. All helper classes should be under one of the following namespaces. Catalyst::Helper::Model:: Catalyst::Helper::View:: Catalyst::Helper::Controller:: =head2 COMMON HELPERS =over =item * L - DBIx::Class models =item * L - Template Toolkit view =item * L =item * L - wrap any class into a Catalyst model =back =head3 NOTE The helpers will read author name from /etc/passwd by default. To override, please export the AUTHOR variable. =head1 METHODS =head2 mk_compclass This method in your Helper module is called with C<$helper> which is a L object, and whichever other arguments the user added to the command-line. You can use the $helper to call methods described below. If the Helper module does not contain a C method, it will fall back to calling L, with an argument of C. =head2 mk_comptest This method in your Helper module is called with C<$helper> which is a L object, and whichever other arguments the user added to the command-line. You can use the $helper to call methods described below. If the Helper module does not contain a C method, it will fall back to calling L, with an argument of C. =head2 mk_stuff This method is called if the user does not supply any of the usual component types C, C, C. It is passed the C<$helper> object (an instance of L), and any other arguments the user typed. There is no fallback for this method. =head1 INTERNAL METHODS These are the methods that the Helper classes can call on the <$helper> object passed to them. =head2 render_file ($file, $path, $vars, $perms) Render and create a file from a template in DATA using Template Toolkit. $file is the relevent chunk of the __DATA__ section, $path is the path to the file, $vars is the hashref as expected by L