File Coverage

blib/lib/CPAN/Mini/Inject.pm
Criterion Covered Total %
statement 260 262 99.2
branch 66 96 68.7
condition 15 32 46.8
subroutine 42 43 97.6
pod 15 15 100.0
total 398 448 88.8


line stmt bran cond sub pod time code
1             package CPAN::Mini::Inject;
2              
3 13     13   534055 use strict;
  13         44  
  13         454  
4 13     13   88 use warnings;
  13         39  
  13         1716  
5              
6 13     13   9005 use CPAN::Checksums qw( updatedir );
  13         1590775  
  13         945  
7 13     13   7593 use CPAN::Mini;
  13         671505  
  13         510  
8 13     13   6602 use CPAN::Mini::Inject::Config;
  13         91  
  13         407  
9 13     13   97 use Carp;
  13         31  
  13         647  
10 13     13   84 use Compress::Zlib;
  13         27  
  13         3629  
11 13     13   11434 use Env;
  13         29568  
  13         83  
12 13     13   7772 use File::Basename;
  13         36  
  13         946  
13 13     13   97 use File::Copy;
  13         30  
  13         714  
14 13     13   117 use File::Path qw( make_path );
  13         43  
  13         626  
15 13     13   91 use File::Spec;
  13         42  
  13         333  
16 13     13   6304 use LWP::Simple;
  13         230722  
  13         123  
17 13     13   11098 use Dist::Metadata ();
  13         391187  
  13         3256  
18              
19             =head1 NAME
20              
21             CPAN::Mini::Inject - Inject modules into a CPAN::Mini mirror.
22              
23             =head1 VERSION
24              
25             Version 0.35
26              
27             =cut
28              
29             our $VERSION = '0.35';
30             our @ISA = qw( CPAN::Mini );
31              
32             =head1 Synopsis
33              
34             If you're not going to customize the way CPAN::Mini::Inject works you
35             probably want to look at the L command instead.
36              
37             use CPAN::Mini::Inject;
38              
39             $mcpi=CPAN::Mini::Inject->new;
40             $mcpi->parsecfg('t/.mcpani/config');
41              
42             $mcpi->add( module => 'CPAN::Mini::Inject',
43             authorid => 'SSORICHE',
44             version => ' 0.01',
45             file => 'mymodules/CPAN-Mini-Inject-0.01.tar.gz' )
46              
47             $mcpi->writelist;
48             $mcpi->update_mirror;
49             $mcpi->inject;
50              
51             =head1 DESCRIPTION
52              
53             CPAN::Mini::Inject uses CPAN::Mini to build or update a local CPAN mirror
54             then adds modules from your repository to it, allowing the inclusion
55             of private modules in a minimal CPAN mirror.
56              
57             =head1 METHODS
58              
59             Each method in CPAN::Mini::Inject returns a CPAN::Mini::Inject object which
60             allows method chaining. For example:
61              
62             my $mcpi=CPAN::Mini::Inject->new;
63             $mcpi->parsecfg
64             ->update_mirror
65             ->inject;
66              
67             A C ISA L. Refer to the
68             L for that module for details of the interface
69             C inherits from it.
70              
71             =head2 C
72              
73             Create a new CPAN::Mini::Inject object.
74              
75             =cut
76              
77             sub new {
78 17     17 1 2029919 return bless
79             { config_class => 'CPAN::Mini::Inject::Config' },
80             $_[0];
81             }
82              
83             =head2 C<< config_class( [CLASS] ) >>
84              
85             Returns the name of the class handling the configuration.
86              
87             With an argument, it sets the name of the class to handle
88             the config. To use that, you'll have to call it before you
89             load the configuration.
90              
91             =cut
92              
93             sub config_class {
94 16     16 1 45 my $self = shift;
95              
96 16 50       90 if ( @_ ) { $self->{config_class} = shift }
  0         0  
97              
98 16         200 $self->{config_class};
99             }
100              
101             =head2 C<< config >>
102              
103             Returns the configuration object. This object should be from
104             the class returned by C unless you've done something
105             weird.
106              
107             =cut
108              
109             sub config {
110 146     146 1 306 my $self = shift;
111              
112 146 100       435 if ( @_ ) { $self->{config} = shift }
  6         24  
113              
114 146         753 $self->{config};
115             }
116              
117             =head2 C<< loadcfg( [FILENAME] ) >>
118              
119              
120             This is a bridge to CPAN::Mini::Inject::Config's loadconfig. It sets the
121             filename for the configuration, or uses one of the defaults.
122              
123             =cut
124              
125             sub loadcfg {
126 13     13 1 2509 my $self = shift;
127              
128 13 100       144 unless ( $self->{config} ) {
129 10         88 $self->{config} = $self->config_class->new;
130             }
131              
132 13         98 $self->{cfgfile} = $self->{config}->load_config( @_ );
133              
134 12         1287 return $self;
135             }
136              
137             =head2 C<< parsecfg() >>
138              
139             This is a bridge to CPAN::Mini::Inject::Config's parseconfig.
140              
141             =cut
142              
143             sub parsecfg {
144 15     15 1 134 my $self = shift;
145              
146 15 100       122 unless ( $self->{config} ) {
147 6         42 $self->config( $self->config_class->new );
148             }
149              
150 15         66 $self->config->parse_config( @_ );
151              
152 14         50 return $self;
153             }
154              
155             =head2 C<< site( [SITE] ) >>
156              
157             Returns the CPAN site that CPAN::Mini::Inject chose from the
158             list specified in the C directive.
159              
160             =cut
161              
162             sub site {
163 13     13   170 no warnings;
  13         36  
  13         32000  
164 8     8 1 27 my $self = shift;
165              
166 8 100       35 if ( @_ ) { $self->{site} = shift }
  3         17  
167              
168 8 100       89 $self->{site} || '';
169             }
170              
171             =head2 C
172              
173             Test each site listed in the remote parameter of the config file by performing
174             a get on each site in order for authors/01mailrc.txt.gz. The first site to
175             respond successfully is set as the instance variable site.
176              
177             print "$mcpi->{site}\n"; # ftp://ftp.cpan.org/pub/CPAN
178              
179             C accepts an optional parameter to enable verbose mode.
180              
181             =cut
182              
183             sub testremote {
184 2     2 1 9 my $self = shift;
185 2         8 my $verbose = shift;
186              
187 2 100       11 $self->site( undef ) if $self->site;
188              
189 2 50       16 $ENV{FTP_PASSIVE} = 1 if ( $self->config->get( 'passive' ) );
190              
191 2         16 for my $site ( split( /\s+/, $self->config->get( 'remote' ) ) ) {
192              
193 3 50       125320 $site .= '/' unless ( $site =~ m/\/$/ );
194              
195 3 50       17 print "Testing site: $site\n" if ( $verbose );
196              
197 3 100       39 if ( get( $site . 'authors/01mailrc.txt.gz' ) ) {
198 2         94857 $self->site( $site );
199              
200 2 50       11 print "\n$site selected.\n" if ( $verbose );
201 2         9 last;
202             }
203             }
204              
205 2 50       12 croak "Unable to connect to any remote site" unless $self->site;
206              
207 2         11 return $self;
208             }
209              
210             =head2 C
211              
212             This is a subclass of CPAN::Mini.
213              
214             =cut
215              
216             sub update_mirror {
217 1     1 1 262 my $self = shift;
218 1         10 my %options = @_;
219              
220 1 50       12 croak 'Can not write to local: ' . $self->config->get( 'local' )
221             unless ( -w $self->config->get( 'local' ) );
222              
223 1 50       29 $ENV{FTP_PASSIVE} = 1 if $self->config->get( 'passive' );
224              
225 1   33     14 $options{local} ||= $self->config->get( 'local' );
226 1   50     23 $options{trace} ||= 0;
227 1   50     19 $options{skip_perl} ||= $self->config->get( 'perl' ) || 1;
      33        
228              
229             $self->testremote( $options{trace} )
230 1 50 33     6 unless ( $self->site || $options{remote} );
231 1   33     6 $options{remote} ||= $self->site;
232              
233 1   33     13 $options{dirmode} ||= oct( $self->config->get( 'dirmode' )
      33        
234             || sprintf( '0%o', 0777 & ~umask ) );
235              
236 1         36 CPAN::Mini->update_mirror( %options );
237             }
238              
239             =head2 C
240              
241             Add a new module to the repository. The add method copies the module
242             file into the repository with the same structure as a CPAN site. For
243             example CPAN-Mini-Inject-0.01.tar.gz is copied to
244             MYCPAN/authors/id/S/SS/SSORICHE. add creates the required directory
245             structure below the repository.
246              
247             Packages found in the distribution will be added to the module list
248             (for example both C and C
249             will be added to the F file).
250              
251             Packages will be looked for in the C key of the META file if present,
252             otherwise the files in the dist will be searched.
253             See L for more information.
254              
255             =over 4
256              
257             =item * module
258              
259             The name of the module to add.
260             The distribution file will be searched for modules
261             but you can specify the main one explicitly.
262              
263             =item * authorid
264              
265             CPAN author id. This does not have to be a real author id.
266              
267             =item * version
268              
269             The modules version number.
270             Module names and versions will be determined,
271             but you can specify one explicitly.
272              
273             =item * file
274              
275             The tar.gz of the module.
276              
277             =back
278              
279             =head3 Example
280              
281             add( module => 'Module::Name',
282             authorid => 'AUTHOR',
283             version => 0.01,
284             file => './Module-Name-0.01.tar.gz' );
285              
286             =cut
287              
288             sub add {
289 15     15 1 4167 my $self = shift;
290 15         85 my %options = @_;
291              
292 15         76 my $optionchk
293             = _optionchk( \%options, qw/authorid file/ );
294              
295 15 100       71 croak "Required option not specified: $optionchk" if $optionchk;
296 14 100       53 croak "No repository configured"
297             unless ( $self->config->get( 'repository' ) );
298 13 50       49 croak "Can not write to repository: "
299             . $self->config->get( 'repository' )
300             unless ( -w $self->config->get( 'repository' ) );
301              
302             croak "Can not read module file: $options{file}"
303 13 100       218 unless -r $options{file};
304              
305             # attempt to guess module and version
306 12         119 my $distmeta = Dist::Metadata->new( file => $options{file} );
307 12         461 my $packages = $distmeta->package_versions;
308              
309             # include passed in module and version (prefer discovered version)
310 12 100       551252 if ( $options{module} ) {
311 8   66     90 $packages->{ $options{module} } ||= $options{version};
312             }
313              
314             # if no packages were found we need explicit options
315 12 100       73 if ( !keys %$packages ) {
316 1         7 $optionchk
317             = _optionchk( \%options, qw/module version/ );
318              
319 1 50       31 croak "Required option not specified and no modules were found: $optionchk"
320             if $optionchk;
321             }
322              
323 11         703 my $modulefile = basename( $options{file} );
324 11 100       89 $self->readlist unless exists( $self->{modulelist} );
325              
326 11         62 $options{authorid} = uc( $options{authorid} );
327             $self->{authdir} = $self->_authordir( $options{authorid},
328 11         76 $self->config->get( 'repository' ) );
329              
330             my $target
331             = $self->config->get( 'repository' )
332             . '/authors/id/'
333             . $self->{authdir} . '/'
334 11         47 . basename( $options{file} );
335              
336 11 50       401 copy( $options{file}, dirname( $target ) )
337             or croak "Copy failed: $!";
338              
339 11         5011 $self->_updperms( $target );
340              
341             {
342 11         100 my $mods = join('|', keys %$packages);
  11         91  
343             # remove old versions from the list
344 11         52 @{ $self->{modulelist} }
345 11         34 = grep { $_ !~ m/\A($mods)\s+/ } @{ $self->{modulelist} };
  11         275  
  11         48  
346             }
347              
348             # make data available afterwards (since method returns $self)
349 11   100     122 push @{ $self->{added_modules} ||= [] },
350 11         30 { file => $modulefile, authorid => $options{authorid}, modules => $packages };
351              
352             push(
353 11         45 @{ $self->{modulelist} },
354             map {
355 11         28 _fmtmodule(
356             $_, File::Spec::Unix->catfile( File::Spec->splitdir( $self->{authdir} ), $modulefile ),
357 13 50       276 defined($packages->{$_}) ? $packages->{$_} : 'undef'
358             )
359             } keys %$packages
360             );
361              
362 11         578 return $self;
363             }
364              
365             =head2 C
366              
367             Returns a list of hash references describing the modules added by this instance.
368             Each hashref will contain C, C, and C.
369             The C entry is a hashref of module names and versions included in the C.
370              
371             The list is cumulative.
372             There will be one entry for each time L was called.
373              
374             This functionality is mostly provided for the included L script
375             to be able to verbosely print all the modules added.
376              
377             =cut
378              
379             sub added_modules {
380 1     1 1 6124 my $self = shift;
381 1   50     4 return @{ $self->{added_modules} ||= [] };
  1         36  
382             }
383              
384             =head2 C
385              
386             Insert modules from the repository into the local CPAN::Mini mirror. inject
387             copies each module into the appropriate directory in the CPAN::Mini mirror
388             and updates the CHECKSUMS file.
389              
390             Passing a value to C enables verbose mode, which lists each module
391             as it's injected.
392              
393             =cut
394              
395             sub inject {
396 1     1 1 3 my $self = shift;
397 1         3 my $verbose = shift;
398              
399 1 50       5 my $dirmode = oct( $self->config->get( 'dirmode' ) )
400             if ( $self->config->get( 'dirmode' ) );
401              
402 1 50       5 $self->readlist unless ( exists( $self->{modulelist} ) );
403              
404 1         3 my %updatedir;
405             my %already_injected;
406 1         2 for my $modline ( @{ $self->{modulelist} } ) {
  1         4  
407 2         16 my ( $module, $version, $file ) = split( /\s+/, $modline );
408              
409 2 50       10 next if $already_injected{$file}++;
410              
411 2         6 my $target = $self->config->get( 'local' ) . '/authors/id/' . $file;
412 2         6 my $source
413             = $self->config->get( 'repository' ) . '/authors/id/' . $file;
414              
415 2         46 $updatedir{ dirname( $file ) } = 1;
416              
417 2         31 my $tdir = dirname $target;
418 2 50       11 _make_path( $tdir, defined $dirmode ? { mode => $dirmode } : {} );
419 2 50       11 copy( $source, $tdir )
420             or croak "Copy $source to $tdir failed: $!";
421              
422 2         501 $self->_updperms( $target );
423 2 50       10 print "$target ... injected $module\n" if $verbose;
424             }
425              
426 1         5 for my $dir ( keys( %updatedir ) ) {
427 2         37 my $authdir = $self->config->get( 'local' ) . "/authors/id/$dir";
428              
429 2         16 updatedir( $authdir );
430 2         67303 $self->_updperms( "$authdir/CHECKSUMS" );
431             }
432              
433 1         8 $self->updpackages;
434 1         462 $self->updauthors;
435              
436 1         308 return $self;
437             }
438              
439             =head2 C
440              
441             Update the CPAN::Mini mirror's modules/02packages.details.txt.gz with the
442             injected module information.
443              
444             =cut
445              
446             sub updpackages {
447 1     1 1 3 my $self = shift;
448              
449 1         2 my @modules = sort( @{ $self->{modulelist} } );
  1         7  
450 1         7 my $infile = $self->_readpkgs;
451 1         64 my %packages;
452              
453             # These need to be unique-per-package, with ones that come from the input
454             # file being overridden.
455 1         5 for my $line (@$infile, @modules) {
456 8         28 my ($pkg) = split(/\s+/, $line, 2);
457 8         20 $packages{$pkg} = $line;
458             };
459              
460 1         11 $self->_writepkgs( [ sort { lc $a cmp lc $b } values %packages ] );
  13         31  
461             }
462              
463             =head2 C
464              
465             Update the CPAN::Mini mirror's authors/01mailrc.txt.gz with
466             stub information should the author not actually exist on CPAN
467              
468             =cut
469              
470             sub updauthors {
471 1     1 1 4 my $self = shift;
472              
473 1         17 my $repo_authors = $self->_readauthors;
474             my %author_ids_in_repo = map {
475 1         43 my ( $id ) = $_ =~ /alias \s+ (\S+)/xms;
  4         23  
476 4         13 $id => 1;
477             } @$repo_authors;
478              
479 1         4 my @authors;
480             my %authors_added;
481             AUTHOR:
482 1         3 for my $modline ( @{ $self->{modulelist} } ) {
  1         6  
483 2         12 my ( $module, $version, $file ) = split( /\s+/, $modline );
484              
485             # extract the author from the path
486 2         26 my @dirs = File::Spec->splitdir( $file );
487 2         7 my $author = $dirs[2];
488              
489 2 50       7 next AUTHOR if defined $author_ids_in_repo{$author};
490 2 50       7 next AUTHOR if defined $authors_added{$author};
491              
492 2         14 push @$repo_authors,
493             sprintf( 'alias %-10s "Custom Non-CPAN author "',
494             $author );
495 2         8 $authors_added{$author} = 1;
496             }
497              
498 1         5 $self->_writeauthors( $repo_authors );
499              
500             }
501              
502             =head2 C
503              
504             Load the repository's modulelist.
505              
506             =cut
507              
508             sub _repo_file {
509 7     7   34 File::Spec->catfile( shift->config->get( 'repository' ), @_ );
510             }
511              
512 7     7   37 sub _modulelist { shift->_repo_file( 'modulelist' ) }
513              
514             sub readlist {
515 7     7 1 24 my $self = shift;
516              
517 7         28 $self->{modulelist} = undef;
518              
519 7         38 my $ml = $self->_modulelist;
520 7 100       257 return $self unless -e $ml;
521              
522 2 50       59 open MODLIST, '<', $ml or croak "Can not read module list: $ml ($!)";
523 2         38 while ( ) {
524 6         23 chomp;
525 6         15 push @{ $self->{modulelist} }, $_;
  6         42  
526             }
527 2         19 close MODLIST;
528              
529 2         14 return $self;
530             }
531              
532             =head2 C
533              
534             Write to the repository modulelist.
535              
536             =cut
537              
538             sub writelist {
539 2     2 1 6 my $self = shift;
540              
541             croak 'Can not write module list: '
542             . $self->config->get( 'repository' )
543             . "/modulelist ERROR: $!"
544             unless ( -w $self->{config}{repository} . '/modulelist'
545 2 50 66     49 || -w $self->{config}{repository} );
546 2 50       10 return $self unless defined( $self->{modulelist} );
547              
548 2         9 open( MODLIST,
549             '>' . $self->config->get( 'repository' ) . '/modulelist' );
550 2         9 for ( sort( @{ $self->{modulelist} } ) ) {
  2         17  
551 6         17 chomp;
552 6         38 print MODLIST "$_\n";
553             }
554 2         73 close( MODLIST );
555              
556 2         14 $self->_updperms(
557             $self->config->get( 'repository' ) . '/modulelist' );
558              
559 2         14 return $self;
560             }
561              
562             sub _updperms {
563 17     17   69 my ( $self, $file ) = @_;
564              
565 17 50       74 chmod oct( $self->config->get( 'dirmode' ) ) & 06666, $file
566             if $self->config->get( 'dirmode' );
567             }
568              
569             sub _optionchk {
570 16     16   63 my ( $options, @list ) = @_;
571 16         38 my @missing;
572              
573 16         51 for my $option ( @list ) {
574             push @missing, $option
575 32 100       116 unless defined $$options{$option};
576             }
577              
578 16         73 return join ' ', @missing;
579             }
580              
581             sub _make_path {
582 13     13   67 my $um = umask 0;
583 13         3899 make_path( @_ );
584 13         66 umask $um;
585             }
586              
587             sub _authordir {
588 11     11   50 my ( $self, $author, $dir ) = @_;
589              
590             my @author
591 11         66 = ( substr( $author, 0, 1 ), substr( $author, 0, 2 ), $author );
592              
593 11         48 my $dm = $self->config->get( 'dirmode' );
594             my @new
595 11 50       274 = _make_path( File::Spec->catdir( $dir, 'authors', 'id', @author ),
596             defined $dm ? { mode => oct $dm } : {} );
597              
598 11         131 return return File::Spec->catdir( @author );
599             }
600              
601             sub _fmtmodule {
602 15     15   841 my ( $module, $file, $version ) = @_;
603 15         45 my $fw = 38 - length $version;
604 15 100       61 $fw = length $module if $fw < length $module;
605 15         163 return sprintf "%-${fw}s %s %s", $module, $version, $file;
606             }
607              
608 0     0   0 sub _cfg { $_[0]->{config}{ $_[1] } }
609              
610             sub _readpkgs {
611 1     1   3 my $self = shift;
612              
613 1 50       5 my $gzread = gzopen(
614             $self->config->get( 'local' )
615             . '/modules/02packages.details.txt.gz', 'rb'
616             ) or croak "Cannot open local 02packages.details.txt.gz: $gzerrno";
617              
618 1         2877 my $inheader = 1;
619 1         3 my @packages;
620             my $package;
621              
622 1         6 while ( $gzread->gzreadline( $package ) ) {
623 15 100       1640 if ( $inheader ) {
624 9 100       39 $inheader = 0 unless $package =~ /\S/;
625 9         44 next;
626             }
627 6         16 chomp( $package );
628 6         19 push( @packages, $package );
629             }
630              
631 1         190 $gzread->gzclose;
632              
633 1         159 return \@packages;
634             }
635              
636             sub _writepkgs {
637 1     1   4 my $self = shift;
638 1         3 my $pkgs = shift;
639              
640 1 50       6 my $gzwrite = gzopen(
641             $self->config->get( 'local' )
642             . '/modules/02packages.details.txt.gz', 'wb'
643             )
644             or croak
645             "Can't open local 02packages.details.txt.gz for writing: $gzerrno";
646              
647 1         2578 $gzwrite->gzwrite( "File: 02packages.details.txt\n" );
648 1         168 $gzwrite->gzwrite(
649             "URL: http://www.perl.com/CPAN/modules/02packages.details.txt\n"
650             );
651 1         108 $gzwrite->gzwrite(
652             'Description: Package names found in directory $CPAN/authors/id/'
653             . "\n" );
654 1         115 $gzwrite->gzwrite( "Columns: package name, version, path\n" );
655 1         86 $gzwrite->gzwrite(
656             "Intended-For: Automated fetch routines, namespace documentation.\n"
657             );
658 1         92 $gzwrite->gzwrite( "Written-By: CPAN::Mini::Inject $VERSION\n" );
659 1         84 $gzwrite->gzwrite( "Line-Count: " . scalar( @$pkgs ) . "\n" );
660             # Last-Updated: Sat, 19 Mar 2005 19:49:10 GMT
661 1         82 $gzwrite->gzwrite( "Last-Updated: " . _fmtdate() . "\n\n" );
662              
663 1         88 $gzwrite->gzwrite( "$_\n" ) for ( @$pkgs );
664              
665 1         548 $gzwrite->gzclose;
666              
667             }
668              
669             sub _readauthors {
670 1     1   2 my $self = shift;
671 1 50       6 my $gzread
672             = gzopen( $self->config->get( 'local' ) . '/authors/01mailrc.txt.gz',
673             'rb' )
674             or croak "Cannot open "
675             . $self->config->get( 'local' )
676             . "/authors/01mailrc.txt.gz: $gzerrno";
677              
678 1         2529 my @authors;
679             my $author;
680              
681 1         7 while ( $gzread->gzreadline( $author ) ) {
682 4         507 chomp( $author );
683 4         11 push( @authors, $author );
684             }
685              
686 1         143 $gzread->gzclose;
687              
688 1         98 return \@authors;
689             }
690              
691             sub _writeauthors {
692 1     1   4 my $self = shift;
693 1         3 my $authors = shift;
694              
695 1 50       6 my $gzwrite
696             = gzopen( $self->config->get( 'local' ) . '/authors/01mailrc.txt.gz',
697             'wb' )
698             or croak
699             "Can't open local authors/01mailrc.txt.gz for writing: $gzerrno";
700              
701 1         1766 $gzwrite->gzwrite( "$_\n" ) for ( sort @$authors );
702              
703 1         508 $gzwrite->gzclose;
704              
705             }
706              
707             sub _fmtdate {
708 1     1   25 my @date = split( /\s+/, scalar( gmtime ) );
709 1         13 return "$date[0], $date[2] $date[1] $date[4] $date[3] GMT";
710             }
711              
712             =head1 See Also
713              
714             L
715              
716             =head1 Current Maintainer
717              
718             Christian Walde C<< >>
719              
720             =head1 Original Author
721              
722             Shawn Sorichetti, C<< >>
723              
724             =head1 Acknowledgements
725              
726             Special thanks to David Bartle, for bringing this module up
727             to date, and resolving the reported bugs.
728              
729             Thanks to Jozef Kutej for numerous patches.
730              
731             =head1 Bugs
732              
733             Please report any bugs or feature requests to
734             C, or through the web interface at
735             L. I will be notified, and then you'll automatically
736             be notified of progress on your bug as I make changes.
737              
738             =head1 Copyright & License
739              
740             Copyright 2008-2009 Shawn Sorichetti, Andy Armstrong, All Rights Reserved.
741              
742             This program is free software; you can redistribute it and/or modify it
743             under the same terms as Perl itself.
744              
745             =cut
746              
747             1; # End of CPAN::Mini::Inject