File Coverage

blib/lib/CPAN/Mini/Inject.pm
Criterion Covered Total %
statement 261 263 99.2
branch 66 96 68.7
condition 15 32 46.8
subroutine 42 43 97.6
pod 15 15 100.0
total 399 449 88.8


line stmt bran cond sub pod time code
1             package CPAN::Mini::Inject;
2              
3 13     13   783923 use strict;
  13         111  
  13         332  
4 13     13   56 use warnings;
  13         28  
  13         762  
5              
6 13     13   5623 use CPAN::Checksums 2.13 qw( updatedir );
  13         1248952  
  13         758  
7 13     13   6276 use CPAN::Mini;
  13         594266  
  13         506  
8 13     13   5533 use CPAN::Mini::Inject::Config;
  13         37  
  13         334  
9 13     13   74 use Carp;
  13         25  
  13         614  
10 13     13   68 use Compress::Zlib;
  13         21  
  13         3716  
11 13     13   9831 use Env;
  13         28877  
  13         75  
12 13     13   7254 use File::Basename;
  13         27  
  13         950  
13 13     13   81 use File::Copy;
  13         26  
  13         669  
14 13     13   74 use File::Path qw( make_path );
  13         20  
  13         537  
15 13     13   70 use File::Spec;
  13         24  
  13         312  
16 13     13   5274 use LWP::Simple;
  13         248814  
  13         94  
17 13     13   9543 use Dist::Metadata ();
  13         362827  
  13         3215  
18              
19             =head1 NAME
20              
21             CPAN::Mini::Inject - Inject modules into a CPAN::Mini mirror.
22              
23             =head1 VERSION
24              
25             Version 0.37
26              
27             =cut
28              
29             our $VERSION = '0.37';
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 2019345 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 35 my $self = shift;
95              
96 16 50       68 if ( @_ ) { $self->{config_class} = shift }
  0         0  
97              
98 16         218 $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 231 my $self = shift;
111              
112 146 100       324 if ( @_ ) { $self->{config} = shift }
  6         33  
113              
114 146         517 $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 1219 my $self = shift;
127              
128 13 100       123 unless ( $self->{config} ) {
129 10         54 $self->{config} = $self->config_class->new;
130             }
131              
132 13         82 $self->{cfgfile} = $self->{config}->load_config( @_ );
133              
134 12         55 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 90 my $self = shift;
145              
146 15 100       123 unless ( $self->{config} ) {
147 6         32 $self->config( $self->config_class->new );
148             }
149              
150 15         65 $self->config->parse_config( @_ );
151              
152 14         41 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   120 no warnings;
  13         29  
  13         33321  
164 8     8 1 22 my $self = shift;
165              
166 8 100       32 if ( @_ ) { $self->{site} = shift }
  3         16  
167              
168 8 100       56 $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 6 my $self = shift;
185 2         4 my $verbose = shift;
186              
187 2 100       15 $self->site( undef ) if $self->site;
188              
189 2 50       14 $ENV{FTP_PASSIVE} = 1 if ( $self->config->get( 'passive' ) );
190              
191 2         8 for my $site ( split( /\s+/, $self->config->get( 'remote' ) ) ) {
192              
193 3 50       14361 $site .= '/' unless ( $site =~ m/\/$/ );
194              
195 3 50       8 print "Testing site: $site\n" if ( $verbose );
196              
197 3 100       31 if ( get( $site . 'authors/01mailrc.txt.gz' ) ) {
198 2         81416 $self->site( $site );
199              
200 2 50       6 print "\n$site selected.\n" if ( $verbose );
201 2         4 last;
202             }
203             }
204              
205 2 50       6 croak "Unable to connect to any remote site" unless $self->site;
206              
207 2         7 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 136 my $self = shift;
218 1         6 my %options = @_;
219              
220 1 50       3 croak 'Can not write to local: ' . $self->config->get( 'local' )
221             unless ( -w $self->config->get( 'local' ) );
222              
223 1 50       5 $ENV{FTP_PASSIVE} = 1 if $self->config->get( 'passive' );
224              
225 1   33     5 $options{local} ||= $self->config->get( 'local' );
226 1   50     52 $options{trace} ||= 0;
227 1   50     20 $options{skip_perl} ||= $self->config->get( 'perl' ) || 1;
      33        
228              
229             $self->testremote( $options{trace} )
230 1 50 33     5 unless ( $self->site || $options{remote} );
231 1   33     3 $options{remote} ||= $self->site;
232              
233 1   33     12 $options{dirmode} ||= oct( $self->config->get( 'dirmode' )
      33        
234             || sprintf( '0%o', 0777 & ~umask ) );
235              
236 1         33 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 1962 my $self = shift;
290 15         63 my %options = @_;
291              
292 15         48 my $optionchk
293             = _optionchk( \%options, qw/authorid file/ );
294              
295 15 100       43 croak "Required option not specified: $optionchk" if $optionchk;
296 14 100       36 croak "No repository configured"
297             unless ( $self->config->get( 'repository' ) );
298 13 50       35 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       237 unless -r $options{file};
304              
305             # attempt to guess module and version
306 12         104 my $distmeta = Dist::Metadata->new( file => $options{file} );
307 12         328 my $packages = $distmeta->package_versions;
308              
309             # include passed in module and version (prefer discovered version)
310 12 100       304926 if ( $options{module} ) {
311 8   66     54 $packages->{ $options{module} } ||= $options{version};
312             }
313              
314             # if no packages were found we need explicit options
315 12 100       41 if ( !keys %$packages ) {
316 1         3 $optionchk
317             = _optionchk( \%options, qw/module version/ );
318              
319 1 50       17 croak "Required option not specified and no modules were found: $optionchk"
320             if $optionchk;
321             }
322              
323 11         567 my $modulefile = basename( $options{file} );
324 11 100       59 $self->readlist unless exists( $self->{modulelist} );
325              
326 11         40 $options{authorid} = uc( $options{authorid} );
327             $self->{authdir} = $self->_authordir( $options{authorid},
328 11         47 $self->config->get( 'repository' ) );
329              
330             my $target
331             = $self->config->get( 'repository' )
332             . '/authors/id/'
333             . $self->{authdir} . '/'
334 11         48 . basename( $options{file} );
335              
336 11 50       260 copy( $options{file}, dirname( $target ) )
337             or croak "Copy failed: $!";
338              
339 11         3933 $self->_updperms( $target );
340              
341             {
342 11         38 my $mods = join('|', keys %$packages);
  11         47  
343             # remove old versions from the list
344 11         46 @{ $self->{modulelist} }
345 11         22 = grep { $_ !~ m/\A($mods)\s+/ } @{ $self->{modulelist} };
  11         225  
  11         28  
346             }
347              
348             # make data available afterwards (since method returns $self)
349 11   100     78 push @{ $self->{added_modules} ||= [] },
350 11         20 { file => $modulefile, authorid => $options{authorid}, modules => $packages };
351              
352             push(
353 11         31 @{ $self->{modulelist} },
354             map {
355 11         18 _fmtmodule(
356             $_, File::Spec::Unix->catfile( File::Spec->splitdir( $self->{authdir} ), $modulefile ),
357 13 50       189 defined($packages->{$_}) ? $packages->{$_} : 'undef'
358             )
359             } keys %$packages
360             );
361              
362 11         249 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 3552 my $self = shift;
381 1   50     2 return @{ $self->{added_modules} ||= [] };
  1         19  
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         1 my $verbose = shift;
398              
399 1 50       3 my $dirmode = oct( $self->config->get( 'dirmode' ) )
400             if ( $self->config->get( 'dirmode' ) );
401              
402 1 50       4 $self->readlist unless ( exists( $self->{modulelist} ) );
403              
404 1         2 my %updatedir;
405             my %already_injected;
406 1         2 for my $modline ( @{ $self->{modulelist} } ) {
  1         2  
407 2         13 my ( $module, $version, $file ) = split( /\s+/, $modline );
408              
409 2 50       9 next if $already_injected{$file}++;
410              
411 2         5 my $target = $self->config->get( 'local' ) . '/authors/id/' . $file;
412 2         5 my $source
413             = $self->config->get( 'repository' ) . '/authors/id/' . $file;
414              
415 2         45 $updatedir{ dirname( $file ) } = 1;
416              
417 2         34 my $tdir = dirname $target;
418 2 50       11 _make_path( $tdir, defined $dirmode ? { mode => $dirmode } : {} );
419 2 50       12 copy( $source, $tdir )
420             or croak "Copy $source to $tdir failed: $!";
421              
422 2         577 $self->_updperms( $target );
423 2 50       11 print "$target ... injected $module\n" if $verbose;
424             }
425              
426 1         4 for my $dir ( keys( %updatedir ) ) {
427 2         8 my $root = $self->config->get( 'local' ) . "/authors/id";
428 2         6 my $authdir = "$root/$dir";
429              
430 2         10 updatedir( $authdir, $root );
431 2         132206 $self->_updperms( "$authdir/CHECKSUMS" );
432             }
433              
434 1         10 $self->updpackages;
435 1         70459 $self->updauthors;
436              
437 1         37724 return $self;
438             }
439              
440             =head2 C
441              
442             Update the CPAN::Mini mirror's modules/02packages.details.txt.gz with the
443             injected module information.
444              
445             =cut
446              
447             sub updpackages {
448 1     1 1 4 my $self = shift;
449              
450 1         3 my @modules = sort( @{ $self->{modulelist} } );
  1         6  
451 1         6 my $infile = $self->_readpkgs;
452 1         35 my %packages;
453              
454             # These need to be unique-per-package, with ones that come from the input
455             # file being overridden.
456 1         3 for my $line (@$infile, @modules) {
457 8         21 my ($pkg) = split(/\s+/, $line, 2);
458 8         17 $packages{$pkg} = $line;
459             };
460              
461 1         7 $self->_writepkgs( [ sort { lc $a cmp lc $b } values %packages ] );
  13         26  
462             }
463              
464             =head2 C
465              
466             Update the CPAN::Mini mirror's authors/01mailrc.txt.gz with
467             stub information should the author not actually exist on CPAN
468              
469             =cut
470              
471             sub updauthors {
472 1     1 1 4 my $self = shift;
473              
474 1         6 my $repo_authors = $self->_readauthors;
475             my %author_ids_in_repo = map {
476 1         34 my ( $id ) = $_ =~ /alias \s+ (\S+)/xms;
  4         13  
477 4         13 $id => 1;
478             } @$repo_authors;
479              
480 1         3 my @authors;
481             my %authors_added;
482             AUTHOR:
483 1         3 for my $modline ( @{ $self->{modulelist} } ) {
  1         4  
484 2         10 my ( $module, $version, $file ) = split( /\s+/, $modline );
485              
486             # extract the author from the path
487 2         16 my @dirs = File::Spec->splitdir( $file );
488 2         4 my $author = $dirs[2];
489              
490 2 50       6 next AUTHOR if defined $author_ids_in_repo{$author};
491 2 50       4 next AUTHOR if defined $authors_added{$author};
492              
493 2         11 push @$repo_authors,
494             sprintf( 'alias %-10s "Custom Non-CPAN author "',
495             $author );
496 2         6 $authors_added{$author} = 1;
497             }
498              
499 1         5 $self->_writeauthors( $repo_authors );
500              
501             }
502              
503             =head2 C
504              
505             Load the repository's modulelist.
506              
507             =cut
508              
509             sub _repo_file {
510 7     7   22 File::Spec->catfile( shift->config->get( 'repository' ), @_ );
511             }
512              
513 7     7   23 sub _modulelist { shift->_repo_file( 'modulelist' ) }
514              
515             sub readlist {
516 7     7 1 16 my $self = shift;
517              
518 7         16 $self->{modulelist} = undef;
519              
520 7         34 my $ml = $self->_modulelist;
521 7 100       185 return $self unless -e $ml;
522              
523 2 50       54 open MODLIST, '<', $ml or croak "Can not read module list: $ml ($!)";
524 2         37 while ( ) {
525 6         11 chomp;
526 6         8 push @{ $self->{modulelist} }, $_;
  6         30  
527             }
528 2         18 close MODLIST;
529              
530 2         7 return $self;
531             }
532              
533             =head2 C
534              
535             Write to the repository modulelist.
536              
537             =cut
538              
539             sub writelist {
540 2     2 1 5 my $self = shift;
541              
542             croak 'Can not write module list: '
543             . $self->config->get( 'repository' )
544             . "/modulelist ERROR: $!"
545             unless ( -w $self->{config}{repository} . '/modulelist'
546 2 50 66     50 || -w $self->{config}{repository} );
547 2 50       30 return $self unless defined( $self->{modulelist} );
548              
549 2         7 open( MODLIST,
550             '>' . $self->config->get( 'repository' ) . '/modulelist' );
551 2         10 for ( sort( @{ $self->{modulelist} } ) ) {
  2         22  
552 6         11 chomp;
553 6         17 print MODLIST "$_\n";
554             }
555 2         144 close( MODLIST );
556              
557 2         16 $self->_updperms(
558             $self->config->get( 'repository' ) . '/modulelist' );
559              
560 2         12 return $self;
561             }
562              
563             sub _updperms {
564 17     17   50 my ( $self, $file ) = @_;
565              
566 17 50       49 chmod oct( $self->config->get( 'dirmode' ) ) & 06666, $file
567             if $self->config->get( 'dirmode' );
568             }
569              
570             sub _optionchk {
571 16     16   44 my ( $options, @list ) = @_;
572 16         24 my @missing;
573              
574 16         34 for my $option ( @list ) {
575             push @missing, $option
576 32 100       79 unless defined $$options{$option};
577             }
578              
579 16         51 return join ' ', @missing;
580             }
581              
582             sub _make_path {
583 13     13   105 my $um = umask 0;
584 13         3708 make_path( @_ );
585 13         87 umask $um;
586             }
587              
588             sub _authordir {
589 11     11   24 my ( $self, $author, $dir ) = @_;
590              
591             my @author
592 11         40 = ( substr( $author, 0, 1 ), substr( $author, 0, 2 ), $author );
593              
594 11         33 my $dm = $self->config->get( 'dirmode' );
595             my @new
596 11 50       141 = _make_path( File::Spec->catdir( $dir, 'authors', 'id', @author ),
597             defined $dm ? { mode => oct $dm } : {} );
598              
599 11         132 return return File::Spec->catdir( @author );
600             }
601              
602             sub _fmtmodule {
603 15     15   1067 my ( $module, $file, $version ) = @_;
604 15         31 my $fw = 38 - length $version;
605 15 100       43 $fw = length $module if $fw < length $module;
606 15         104 return sprintf "%-${fw}s %s %s", $module, $version, $file;
607             }
608              
609 0     0   0 sub _cfg { $_[0]->{config}{ $_[1] } }
610              
611             sub _readpkgs {
612 1     1   3 my $self = shift;
613              
614 1 50       4 my $gzread = gzopen(
615             $self->config->get( 'local' )
616             . '/modules/02packages.details.txt.gz', 'rb'
617             ) or croak "Cannot open local 02packages.details.txt.gz: $gzerrno";
618              
619 1         2715 my $inheader = 1;
620 1         3 my @packages;
621             my $package;
622              
623 1         4 while ( $gzread->gzreadline( $package ) ) {
624 15 100       1455 if ( $inheader ) {
625 9 100       27 $inheader = 0 unless $package =~ /\S/;
626 9         19 next;
627             }
628 6         12 chomp( $package );
629 6         15 push( @packages, $package );
630             }
631              
632 1         129 $gzread->gzclose;
633              
634 1         93 return \@packages;
635             }
636              
637             sub _writepkgs {
638 1     1   3 my $self = shift;
639 1         1 my $pkgs = shift;
640              
641 1 50       4 my $gzwrite = gzopen(
642             $self->config->get( 'local' )
643             . '/modules/02packages.details.txt.gz', 'wb'
644             )
645             or croak
646             "Can't open local 02packages.details.txt.gz for writing: $gzerrno";
647              
648 1         1758 $gzwrite->gzwrite( "File: 02packages.details.txt\n" );
649 1         137 $gzwrite->gzwrite(
650             "URL: http://www.perl.com/CPAN/modules/02packages.details.txt\n"
651             );
652 1         73 $gzwrite->gzwrite(
653             'Description: Package names found in directory $CPAN/authors/id/'
654             . "\n" );
655 1         68 $gzwrite->gzwrite( "Columns: package name, version, path\n" );
656 1         67 $gzwrite->gzwrite(
657             "Intended-For: Automated fetch routines, namespace documentation.\n"
658             );
659 1         87 $gzwrite->gzwrite( "Written-By: CPAN::Mini::Inject $VERSION\n" );
660 1         74 $gzwrite->gzwrite( "Line-Count: " . scalar( @$pkgs ) . "\n" );
661             # Last-Updated: Sat, 19 Mar 2005 19:49:10 GMT
662 1         70 $gzwrite->gzwrite( "Last-Updated: " . _fmtdate() . "\n\n" );
663              
664 1         76 $gzwrite->gzwrite( "$_\n" ) for ( @$pkgs );
665              
666 1         605 $gzwrite->gzclose;
667              
668             }
669              
670             sub _readauthors {
671 1     1   2 my $self = shift;
672 1 50       6 my $gzread
673             = gzopen( $self->config->get( 'local' ) . '/authors/01mailrc.txt.gz',
674             'rb' )
675             or croak "Cannot open "
676             . $self->config->get( 'local' )
677             . "/authors/01mailrc.txt.gz: $gzerrno";
678              
679 1         2533 my @authors;
680             my $author;
681              
682 1         6 while ( $gzread->gzreadline( $author ) ) {
683 4         447 chomp( $author );
684 4         10 push( @authors, $author );
685             }
686              
687 1         126 $gzread->gzclose;
688              
689 1         89 return \@authors;
690             }
691              
692             sub _writeauthors {
693 1     1   2 my $self = shift;
694 1         2 my $authors = shift;
695              
696 1 50       3 my $gzwrite
697             = gzopen( $self->config->get( 'local' ) . '/authors/01mailrc.txt.gz',
698             'wb' )
699             or croak
700             "Can't open local authors/01mailrc.txt.gz for writing: $gzerrno";
701              
702 1         1405 $gzwrite->gzwrite( "$_\n" ) for ( sort @$authors );
703              
704 1         457 $gzwrite->gzclose;
705              
706             }
707              
708             sub _fmtdate {
709 1     1   18 my @date = split( /\s+/, scalar( gmtime ) );
710 1         10 return "$date[0], $date[2] $date[1] $date[4] $date[3] GMT";
711             }
712              
713             =head1 See Also
714              
715             L
716              
717             =head1 Current Maintainer
718              
719             Christian Walde C<< >>
720              
721             =head1 Original Author
722              
723             Shawn Sorichetti, C<< >>
724              
725             =head1 Acknowledgements
726              
727             Special thanks to David Bartle, for bringing this module up
728             to date, and resolving the reported bugs.
729              
730             Thanks to Jozef Kutej for numerous patches.
731              
732             =head1 Bugs
733              
734             Please report any bugs or feature requests to
735             C, or through the web interface at
736             L. I will be notified, and then you'll automatically
737             be notified of progress on your bug as I make changes.
738              
739             =head1 Copyright & License
740              
741             Copyright 2008-2009 Shawn Sorichetti, Andy Armstrong, All Rights Reserved.
742              
743             This program is free software; you can redistribute it and/or modify it
744             under the same terms as Perl itself.
745              
746             =cut
747              
748             1; # End of CPAN::Mini::Inject