Subject: | Refactor Module::Starter::App and Module::Starter::Simple to allow easier subclassing |
The Module::Starter::App process puts a lot of obstacles in the way of a subclass. The ::App
class calls a class method (create_distro) in the implementing class, and it can't very well
extend it unless it wants to let Module::Starter::Simple::create_distro call new(). Anyone could
override it, but that's a lot of work to do if you want only a small tweak. The current setup
doesn't give a reasonable subclass the chance to interact with the object until it's already in
create_distro, and that's too late.
Instead of all of that, I've modified the process to give subclasses more hooks into the
process. To the end user, nothing should look or act differently. Current subclasses should
still work. New subclasses can do much more using the hooks before and after ::App calls
create_distro.
With these fixes, which introduces a post_create_distro hook, create_distro only has to create
the files it has to create. Anything later, such as creating MANIFEST, META.yml, or any other
sort of post-processing, is a different step. Plugins don't have to override large chunks of
code to do something extra. This would be a first step in solving my previous bug report
about MANIFEST creation.
Although the patch is from git, it's against the stuff I cloned from Google Code.
Subject: | 0001--Refactor-Module-Starter-App-Module-Starter-Si.patch |
From 26e4d06c8249a95d49d02e71290607fc1f88ac51 Mon Sep 17 00:00:00 2001
From: brian d foy <brian.d.foy@gmail.com>
Date: Sat, 9 Jan 2010 16:05:30 +0100
Subject: [PATCH] * Refactor Module::Starter::App, Module::Starter::Simple for better access
There shouldn't be any feature changes in this commit. I only
refactored the stuff in Module::Starter::App and adjust the
process to handle the refactoring. This allows much better control
of the process for any subclasses.
---
.gitignore | 4 ++
lib/Module/Starter.pm | 4 +-
lib/Module/Starter/App.pm | 81 ++++++++++++++++++++++-----------
lib/Module/Starter/BuilderSet.pm | 4 +-
lib/Module/Starter/Plugin/Template.pm | 4 +-
lib/Module/Starter/Simple.pm | 71 +++++++++++++++++++++-------
t/starter-new.t | 22 +++++++++
7 files changed, 139 insertions(+), 51 deletions(-)
create mode 100644 .gitignore
create mode 100644 t/starter-new.t
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e2c0318
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.DS_Store
+Makefile
+blib
+pm_to_blib
diff --git a/lib/Module/Starter.pm b/lib/Module/Starter.pm
index 516dcd4..9fa3446 100644
--- a/lib/Module/Starter.pm
+++ b/lib/Module/Starter.pm
@@ -10,11 +10,11 @@ Module::Starter - a simple starter kit for any module
=head1 VERSION
-version 1.54
+version 1.54_01
=cut
-our $VERSION = '1.54';
+our $VERSION = '1.54_01';
=head1 SYNOPSIS
diff --git a/lib/Module/Starter/App.pm b/lib/Module/Starter/App.pm
index f2e711d..cfb04fe 100644
--- a/lib/Module/Starter/App.pm
+++ b/lib/Module/Starter/App.pm
@@ -9,15 +9,29 @@ Module::Starter::App - the code behind the command line program
use warnings;
use strict;
-our $VERSION = '1.54';
+our $VERSION = '1.54_01';
+use File::Spec::Functions;
use Getopt::Long;
use Pod::Usage;
use Carp qw( croak );
+sub _config_file {
+ my( $class ) = @_;
+
+ my $configdir = $ENV{MODULE_STARTER_DIR} || '';
+ if ( !$configdir && $ENV{HOME} ) {
+ $configdir = catfile( $ENV{HOME}, '.module-starter' );
+ }
+
+
+ return catfile( $configdir, 'config' );
+}
+
sub _config_read {
- my $filename = shift;
+ my( $class ) = @_;
+ my $filename = $class->_config_file;
return unless -e $filename;
open( my $config_file, '<', $filename )
@@ -29,30 +43,9 @@ sub _config_read {
next if /\A\s*\Z/sm;
if (/\A(\w+):\s*(.+)\Z/sm) { $config{$1} = $2; }
}
- return %config;
-}
-
-=head2 run
-
- Module::Starter::App->run;
-
-This is equivalent to runnint F<module-starter>. Its behavior is still subject
-to change.
-
-=cut
-
-sub run {
-
- my $configdir = $ENV{MODULE_STARTER_DIR} || '';
- if ( !$configdir && $ENV{HOME} ) {
- $configdir = "$ENV{HOME}/.module-starter";
- }
-
- my %config = _config_read( "$configdir/config" );
# The options that accept multiple arguments must be set to an
# arrayref
-
$config{plugins} = [ split /(?:\s*,\s*|\s+)/, $config{plugins} ] if $config{plugins};
$config{builder} = [ split /(?:\s*,\s*|\s+)/, $config{builder} ] if $config{builder};
@@ -60,6 +53,14 @@ sub run {
$config{$key} = [] unless exists $config{$key};
}
+ return %config;
+}
+
+sub _process_command_line {
+ my( $self, %config ) = @_;
+
+ $config{argv} = [ @ARGV ];
+
pod2usage(2) unless @ARGV;
GetOptions(
@@ -96,14 +97,40 @@ sub run {
$config{class} ||= 'Module::Starter';
$config{builder} = ['ExtUtils::MakeMaker'] unless @{$config{builder}};
+
+ return %config;
+}
+
+=head2 run
+
+ Module::Starter::App->run;
+
+This is equivalent to running F<module-starter>. Its behavior is still subject
+to change.
+
+=cut
+
+sub run {
+ my $class = shift;
+
+ my %config = $class->_config_read;
+
+ %config = $class->_process_command_line( %config );
eval "require $config{class};";
- croak "invalid starter class $config{class}: $@" if $@;
- $config{class}->import(@{$config{plugins}});
+ print "Class is $config{class}\n";
+ croak "Could not load starter class $config{class}: $@" if $@;
+
+ $config{class}->import( @{$config{plugins}} );
- $config{class}->create_distro( %config );
+ my $starter = $config{class}->new( %config );
+ $starter->postprocess_config;
+ $starter->pre_create_distro;
+ $starter->create_distro;
+ $starter->post_create_distro;
+ $starter->pre_exit;
- print "Created starter directories and files\n";
+ return 1;
}
1;
diff --git a/lib/Module/Starter/BuilderSet.pm b/lib/Module/Starter/BuilderSet.pm
index 488898d..32df6f0 100644
--- a/lib/Module/Starter/BuilderSet.pm
+++ b/lib/Module/Starter/BuilderSet.pm
@@ -11,11 +11,11 @@ Module::Starter::BuilderSet - determine builder metadata
=head1 VERSION
-Version 1.54
+Version 1.54_01
=cut
-our $VERSION = '1.54';
+our $VERSION = '1.54_01';
=head1 SYNOPSIS
diff --git a/lib/Module/Starter/Plugin/Template.pm b/lib/Module/Starter/Plugin/Template.pm
index 791b814..a5b86aa 100644
--- a/lib/Module/Starter/Plugin/Template.pm
+++ b/lib/Module/Starter/Plugin/Template.pm
@@ -10,11 +10,11 @@ Module::Starter::Plugin::Template - module starter with templates
=head1 VERSION
-Version 1.54
+Version 1.54_01
=cut
-our $VERSION = '1.54';
+our $VERSION = '1.54_01';
=head1 SYNOPSIS
diff --git a/lib/Module/Starter/Simple.pm b/lib/Module/Starter/Simple.pm
index bb942cc..be4ed44 100644
--- a/lib/Module/Starter/Simple.pm
+++ b/lib/Module/Starter/Simple.pm
@@ -15,11 +15,11 @@ Module::Starter::Simple - a simple, comprehensive Module::Starter plugin
=head1 VERSION
-Version 1.54
+Version 1.54_01
=cut
-our $VERSION = '1.54';
+our $VERSION = '1.54_01';
=head1 SYNOPSIS
@@ -36,6 +36,42 @@ the directories with the required files.
=head1 CLASS METHODS
+=head2 C<< new(%args) >>
+
+This method is called to construct and initialize a new Module::Starter object.
+It is never called by the end user, only internally by C<create_distro>, which
+creates ephemeral Module::Starter objects. It's documented only to call it to
+the attention of subclass authors.
+
+=cut
+
+sub new {
+ my $class = shift;
+ return bless { @_ } => $class;
+}
+
+=head1 OBJECT METHODS
+
+All the methods documented below are object methods, meant to be called
+internally by the ephemperal objects created during the execution of the class
+method C<create_distro> above.
+
+=head2 postprocess_config
+
+A hook to do any work after the configuration is initially processed.
+
+=cut
+
+sub postprocess_config { 1 };
+
+=head2 pre_create_distro
+
+A hook to do any work right before the distro is created.
+
+=cut
+
+sub pre_create_distro { 1 };
+
=head2 C<< create_distro(%args) >>
This method works as advertised in L<Module::Starter>.
@@ -43,10 +79,11 @@ This method works as advertised in L<Module::Starter>.
=cut
sub create_distro {
- my $class = shift;
-
- my $self = $class->new( @_ );
+ my $either = shift;
+ $either = $either->new( @_ ) unless( ref $either );
+ my $self = $either;
+
my $modules = $self->{modules} || [];
my @modules = map { split /,/ } @{$modules};
croak "No modules specified.\n" unless @modules;
@@ -86,25 +123,23 @@ sub create_distro {
return;
}
-=head2 C<< new(%args) >>
+=head2 post_create_distro
-This method is called to construct and initialize a new Module::Starter object.
-It is never called by the end user, only internally by C<create_distro>, which
-creates ephemeral Module::Starter objects. It's documented only to call it to
-the attention of subclass authors.
+A hook to do any work after creating the distribution.
=cut
-sub new {
- my $class = shift;
- return bless { @_ } => $class;
-}
+sub post_create_distro { 1 };
-=head1 OBJECT METHODS
+=head2 pre_exit
-All the methods documented below are object methods, meant to be called
-internally by the ephemperal objects created during the execution of the class
-method C<create_distro> above.
+A hook to do any work right before exit time.
+
+=cut
+
+sub pre_exit {
+ print "Created starter directories and files\n";
+}
=head2 create_basedir
diff --git a/t/starter-new.t b/t/starter-new.t
new file mode 100644
index 0000000..8f65502
--- /dev/null
+++ b/t/starter-new.t
@@ -0,0 +1,22 @@
+#!perl
+
+use strict;
+use warnings;
+
+use Test::More 'no_plan';
+
+my $class = 'Module::Starter';
+my $method = 'new';
+
+use_ok( $class );
+ok( grep /Module::Starter::Simple/, get_isa( $class ) );
+can_ok( $class, $method );
+
+my $object = $class->$method();
+isa_ok( $object, $class );
+
+sub get_isa {
+ my $class = shift;
+ no strict 'refs';
+ @{ "${class}::ISA" };
+}
--
1.6.2.5