Add CPAN::Meta as a dual-life module
authorDavid Golden <dagolden@cpan.org>
Wed, 16 Feb 2011 03:35:53 +0000 (22:35 -0500)
committerDavid Golden <dagolden@cpan.org>
Wed, 16 Feb 2011 03:50:32 +0000 (22:50 -0500)
CPAN::Meta version 2.110440 has been added as a dual-life module. It
provides a standard library to read, interpret and write CPAN distribution
metadata files (e.g. META.json and META.yml) which describes a
distribution, its contents, and the requirements for building it and
installing it. The latest CPAN distribution metadata specification is
included as CPAN::Meta::Spec and notes on changes in the specification
over time are given in CPAN::Meta::History.

CPAN::Meta is required for CPAN.pm and CPANPLUS to read META.json and
MYMETA.json files and is required by Module::Build and
ExtUtils::MakeMaker to generate META.json and MYMETA.json files

59 files changed:
MANIFEST
Porting/Maintainers.pl
cpan/CPAN-Meta/Changes [new file with mode: 0644]
cpan/CPAN-Meta/lib/CPAN/Meta.pm [new file with mode: 0644]
cpan/CPAN-Meta/lib/CPAN/Meta/Converter.pm [new file with mode: 0644]
cpan/CPAN-Meta/lib/CPAN/Meta/Feature.pm [new file with mode: 0644]
cpan/CPAN-Meta/lib/CPAN/Meta/History.pm [new file with mode: 0644]
cpan/CPAN-Meta/lib/CPAN/Meta/Prereqs.pm [new file with mode: 0644]
cpan/CPAN-Meta/lib/CPAN/Meta/Spec.pm [new file with mode: 0644]
cpan/CPAN-Meta/lib/CPAN/Meta/Validator.pm [new file with mode: 0644]
cpan/CPAN-Meta/t/converter-bad.t [new file with mode: 0644]
cpan/CPAN-Meta/t/converter-fail.t [new file with mode: 0644]
cpan/CPAN-Meta/t/converter.t [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/107650337-META.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/1122575719-META.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/1206545041-META.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/1598804075-META.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/1927486199-META.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/1985684504-META.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/1985980974-META.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/2031017050-META.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/284247103-META.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/344981821-META.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/35478989-META.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/476602558-META.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/98042513-META.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/META-1_0.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/META-1_1.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/META-1_2.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/META-1_3.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/META-1_4.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/META-2.json [new file with mode: 0644]
cpan/CPAN-Meta/t/data-bad/restrictive-2.json [new file with mode: 0644]
cpan/CPAN-Meta/t/data-fail/META-1_0.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-fail/META-1_1.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-fail/META-1_2.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-fail/META-1_3.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-fail/META-1_4.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data-fail/META-2.json [new file with mode: 0644]
cpan/CPAN-Meta/t/data/META-1_0.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data/META-1_1.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data/META-1_2.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data/META-1_3.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data/META-1_4.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data/META-2.json [new file with mode: 0644]
cpan/CPAN-Meta/t/data/resources.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/data/restricted-2.json [new file with mode: 0644]
cpan/CPAN-Meta/t/data/restrictive-1_4.yml [new file with mode: 0644]
cpan/CPAN-Meta/t/load-bad.t [new file with mode: 0644]
cpan/CPAN-Meta/t/meta-obj.t [new file with mode: 0644]
cpan/CPAN-Meta/t/no-index.t [new file with mode: 0644]
cpan/CPAN-Meta/t/prereqs-finalize.t [new file with mode: 0644]
cpan/CPAN-Meta/t/prereqs-merge.t [new file with mode: 0644]
cpan/CPAN-Meta/t/prereqs.t [new file with mode: 0644]
cpan/CPAN-Meta/t/repository.t [new file with mode: 0644]
cpan/CPAN-Meta/t/save-load.t [new file with mode: 0644]
cpan/CPAN-Meta/t/validator.t [new file with mode: 0644]
lib/.gitignore
pod/perldelta.pod

index 11323a7..f4ea501 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -308,6 +308,61 @@ cpan/CPAN/lib/CPAN/Shell.pm
 cpan/CPAN/lib/CPAN/Tarzip.pm           helper package for CPAN.pm
 cpan/CPAN/lib/CPAN/URL.pm
 cpan/CPAN/lib/CPAN/Version.pm          Simple math with different flavors of version strings
+cpan/CPAN-Meta/Changes
+cpan/CPAN-Meta/lib/CPAN/Meta/Converter.pm
+cpan/CPAN-Meta/lib/CPAN/Meta/Feature.pm
+cpan/CPAN-Meta/lib/CPAN/Meta/History.pm
+cpan/CPAN-Meta/lib/CPAN/Meta.pm
+cpan/CPAN-Meta/lib/CPAN/Meta/Prereqs.pm
+cpan/CPAN-Meta/lib/CPAN/Meta/Spec.pm
+cpan/CPAN-Meta/lib/CPAN/Meta/Validator.pm
+cpan/CPAN-Meta/t/converter-bad.t
+cpan/CPAN-Meta/t/converter-fail.t
+cpan/CPAN-Meta/t/converter.t
+cpan/CPAN-Meta/t/data-bad/107650337-META.yml
+cpan/CPAN-Meta/t/data-bad/1122575719-META.yml
+cpan/CPAN-Meta/t/data-bad/1206545041-META.yml
+cpan/CPAN-Meta/t/data-bad/1598804075-META.yml
+cpan/CPAN-Meta/t/data-bad/1927486199-META.yml
+cpan/CPAN-Meta/t/data-bad/1985684504-META.yml
+cpan/CPAN-Meta/t/data-bad/1985980974-META.yml
+cpan/CPAN-Meta/t/data-bad/2031017050-META.yml
+cpan/CPAN-Meta/t/data-bad/284247103-META.yml
+cpan/CPAN-Meta/t/data-bad/344981821-META.yml
+cpan/CPAN-Meta/t/data-bad/35478989-META.yml
+cpan/CPAN-Meta/t/data-bad/476602558-META.yml
+cpan/CPAN-Meta/t/data-bad/98042513-META.yml
+cpan/CPAN-Meta/t/data-bad/META-1_0.yml
+cpan/CPAN-Meta/t/data-bad/META-1_1.yml
+cpan/CPAN-Meta/t/data-bad/META-1_2.yml
+cpan/CPAN-Meta/t/data-bad/META-1_3.yml
+cpan/CPAN-Meta/t/data-bad/META-1_4.yml
+cpan/CPAN-Meta/t/data-bad/META-2.json
+cpan/CPAN-Meta/t/data-bad/restrictive-2.json
+cpan/CPAN-Meta/t/data-fail/META-1_0.yml
+cpan/CPAN-Meta/t/data-fail/META-1_1.yml
+cpan/CPAN-Meta/t/data-fail/META-1_2.yml
+cpan/CPAN-Meta/t/data-fail/META-1_3.yml
+cpan/CPAN-Meta/t/data-fail/META-1_4.yml
+cpan/CPAN-Meta/t/data-fail/META-2.json
+cpan/CPAN-Meta/t/data/META-1_0.yml
+cpan/CPAN-Meta/t/data/META-1_1.yml
+cpan/CPAN-Meta/t/data/META-1_2.yml
+cpan/CPAN-Meta/t/data/META-1_3.yml
+cpan/CPAN-Meta/t/data/META-1_4.yml
+cpan/CPAN-Meta/t/data/META-2.json
+cpan/CPAN-Meta/t/data/resources.yml
+cpan/CPAN-Meta/t/data/restricted-2.json
+cpan/CPAN-Meta/t/data/restrictive-1_4.yml
+cpan/CPAN-Meta/t/load-bad.t
+cpan/CPAN-Meta/t/meta-obj.t
+cpan/CPAN-Meta/t/no-index.t
+cpan/CPAN-Meta/t/prereqs-finalize.t
+cpan/CPAN-Meta/t/prereqs-merge.t
+cpan/CPAN-Meta/t/prereqs.t
+cpan/CPAN-Meta/t/repository.t
+cpan/CPAN-Meta/t/save-load.t
+cpan/CPAN-Meta/t/validator.t
 cpan/CPAN-Meta-YAML/lib/CPAN/Meta/YAML.pm              CPAN-Meta-YAML files
 cpan/CPAN-Meta-YAML/t/01_compile.t             CPAN-Meta-YAML files
 cpan/CPAN-Meta-YAML/t/02_basic.t               CPAN-Meta-YAML files
index a8df25d..4ccad83 100755 (executable)
@@ -440,6 +440,18 @@ use File::Glob qw(:case);
        'UPSTREAM'      => 'cpan',
        },
 
+    'CPAN::Meta' =>
+       {
+       'MAINTAINER'    => 'dagolden',
+       'DISTRIBUTION'  => 'DAGOLDEN/CPAN-Meta-2.110440.tar.gz',
+       'FILES'         => q[cpan/CPAN-Meta],
+       'EXCLUDED'      => [
+                               qr/^xt/,
+                               qr/^history/,
+                          ],
+       'UPSTREAM'      => 'cpan',
+       },
+
     'CPAN::Meta::YAML' =>
        {
        'MAINTAINER'    => 'dagolden',
diff --git a/cpan/CPAN-Meta/Changes b/cpan/CPAN-Meta/Changes
new file mode 100644 (file)
index 0000000..7dabb94
--- /dev/null
@@ -0,0 +1,178 @@
+Revision history for CPAN-Meta
+
+2.110440  2011-02-12 22:55:28 EST5EDT
+
+  [BUGFIX]
+
+  - Bump Parse::CPAN::Meta prereq to 1.44, as 1.43 was never released
+
+2.110420  2011-02-11 15:40:36 EST5EDT
+
+  [BUG FIXES]
+
+  - The as_string() method now always returns a character string;
+    previously, JSON strings were UTF-8 encoded.
+
+  - The save() method now always saves with UTF-8 encoding for
+    Perl 5.8.1 or greater; previously, YAML was not encoded
+
+2.110390  2011-02-07 21:00:47 EST5EDT
+
+  [BUG FIXES]
+
+  - Release 2.110360 had a regression where the save() method would no
+    longer return true on success.  That has been fixed.
+
+2.110360  2011-02-04 19:46:21 America/New_York
+
+  [OTHER]
+
+  - Remove autodie dependency so CPAN::Meta can be used on older Perls
+
+  - Remove unused Data::Dumper dependency
+
+2.110350  2011-02-03 19:57:32 America/New_York
+
+  [ENHANCEMENTS]
+
+  - Added "as_string" method similar to "as_struct"
+
+  [OTHER]
+
+  - Bumped Parse::CPAN::Meta prereq to 1.43 and uses that to
+    determine proper YAML/JSON backends
+
+  - Removed unused prereqs
+
+2.110330  2011-02-02 09:42:57 EST5EDT
+
+  [ENHANCEMENTS]
+
+  - Saved META.json files are now sorted by key
+
+  - as_struct() method takes an optional "version" argument to return
+    a down-converted metadata hashref
+
+2.110320  2011-01-31 23:14:30 EST5EDT
+
+  [ENHANCEMENTS]
+
+  - The 'save' method now allows an optional hashref argument, which can be
+    used to set the desired meta spec version.  Metadata is automatically
+    converted to the specified output.
+
+2.110240  2011-01-24 16:28:25 EST5EDT
+
+  - Reading JSON/YAML is delegated entirely to Parse::CPAN::Meta (1.4200)
+  
+  - JSON.pm is dropped as a prerequisite and JSON::PP is added to prepare
+    for CPAN::Meta to be added to the Perl core
+
+  - JSON writing uses the same JSON backend selection as Parse::CPAN::Meta
+    to allow "upgrading" to a non-core JSON backend module if desired
+
+2.102400  2010-08-28 14:06:34 America/New_York
+
+  - 'as_struct' method now returns unblessed data (reported by Chris Prather)
+
+2.102160  2010-08-04 12:27:10 EST5EDT
+
+  - Fix bugtracker conversion bug (RT#60017)
+    
+2.101670  2010-06-15 21:02:42 EST5EDT
+
+  - converting 1.x 'repository' field now puts converted url into the
+    'url' sub-field of 'repository' instead of the 'web' sub-field
+
+2.101610  2010-06-10 18:51:30 EST5EDT
+
+  - fixed converter bug that output 'artistic2' instead of 'artistic_2'
+    for license in 1.X specs
+
+  - 'artistic2' is now converted to 'artistic_2' if it occurs
+
+  - corrected validation for 'artistic_2' and disallowed 'artistic-2.0'
+
+2.101600  2010-06-09 10:07:31 EST5EDT
+
+  - improve conversion of restricted/restrictive license keys between
+    spec versions 1.4 and 2 [reported by Alexander Hartmaier]
+
+2.101591  2010-06-08 09:56:17 EST5EDT
+
+  - added 'as_struct' method to CPAN::Meta to get a deep copy of
+    the metadata hash_ref
+
+  - won't add an optional_features 'description' field if missing
+
+  - improved documentation of CPAN::Meta::Converter and how it deals
+    with bad/missing data
+
+2.101590  2010-06-07 21:49:36 EST5EDT
+
+  - won't automatically add 'unknown' as repository type when converting; 
+    instead, will only add a repository type if a repository 'url' is
+    present and it is of the 'svn:' or 'git:' scheme.
+
+  - squelched some uninitialized value warnings [Graham Barr]
+
+2.101580  2010-06-07 16:44:13 EST5EDT
+
+  - add "lazy_validation" option to constructors
+
+  - add ability to "convert" to same version and clean-up any fixable
+    errors
+
+  - fix validation bugs (adhere closer to the spec)
+
+  - improve lots of heuristics during conversion
+
+2.101461  2010-05-26 16:57:02 America/New_York
+
+  - accessors deep clone list and map keys before returning them
+
+  - add custom_keys() and custom() methods
+
+2.101460  2010-05-25 23:12:27 America/New_York
+
+  - loosen URL validation -- only a scheme and authority are required
+    without restrictions on either
+
+2.101450  2010-05-25 17:59:32 America/New_York
+
+  - when downconverting from 2, leave custom keys unchanged
+    (except in resources, where x_ is changed to X_)
+
+  - when converting to 2, don't prepend x_ to custom keys that already
+    matched qr{\Ax_}i
+
+2.101410  2010-05-21 10:39:18 EST5EDT
+
+  - when downconverting from 2, merge test requirements into build requirements
+
+2.101390  2010-05-19 10:49:50 EST5EDT
+
+  - do not clobber generated_by when converting
+
+2.101380  2010-05-17 23:39:23 EST5EDT
+
+  - added support for down-converting to older versions of the spec
+
+  - improved test coverage for conversion and validation
+
+2.101110  2010-04-21 11:06:52 EST5EDT
+
+  - clarified that .XXXXXX versions of the CPAN-Meta distribution will not
+    change the meaning of the CPAN::Meta::Spec, but may fix typos or
+    clarify prose
+
+  - fixed a typo regarding dotted-integer versions: keeping components
+    in the range 0-999 was corrected to a "should" instead of a "must"
+
+  - fixed validation bugs for certain types of nested data structures
+    (based on a patch provided by Barbie)
+
+2.101091  2010-04-19 06:32:13 EST5EDT
+
+  - keep the old specs from getting indexed so as not to confuse people
+
diff --git a/cpan/CPAN-Meta/lib/CPAN/Meta.pm b/cpan/CPAN-Meta/lib/CPAN/Meta.pm
new file mode 100644 (file)
index 0000000..ef79855
--- /dev/null
@@ -0,0 +1,696 @@
+use 5.006;
+use strict;
+use warnings;
+package CPAN::Meta;
+BEGIN {
+  $CPAN::Meta::VERSION = '2.110440';
+}
+# ABSTRACT: the distribution metadata for a CPAN dist
+
+
+use Carp qw(carp croak);
+use CPAN::Meta::Feature;
+use CPAN::Meta::Prereqs;
+use CPAN::Meta::Converter;
+use CPAN::Meta::Validator;
+use Parse::CPAN::Meta 1.44 ();
+
+
+BEGIN {
+  my @STRING_READERS = qw(
+    abstract
+    description
+    dynamic_config
+    generated_by
+    name
+    release_status
+    version
+  );
+
+  no strict 'refs';
+  for my $attr (@STRING_READERS) {
+    *$attr = sub { $_[0]{ $attr } };
+  }
+}
+
+
+BEGIN {
+  my @LIST_READERS = qw(
+    author
+    keywords
+    license
+  );
+
+  no strict 'refs';
+  for my $attr (@LIST_READERS) {
+    *$attr = sub {
+      my $value = $_[0]{ $attr };
+      croak "$attr must be called in list context"
+        unless wantarray;
+      return @{ Storable::dclone($value) } if ref $value;
+      return $value;
+    };
+  }
+}
+
+sub authors  { $_[0]->author }
+sub licenses { $_[0]->license }
+
+
+BEGIN {
+  my @MAP_READERS = qw(
+    meta-spec
+    resources
+    provides
+    no_index
+
+    prereqs
+    optional_features
+  );
+
+  no strict 'refs';
+  for my $attr (@MAP_READERS) {
+    (my $subname = $attr) =~ s/-/_/;
+    *$subname = sub {
+      my $value = $_[0]{ $attr };
+      return Storable::dclone($value) if $value;
+      return {};
+    };
+  }
+}
+
+
+sub custom_keys {
+  return grep { /^x_/i } keys %{$_[0]};
+}
+
+sub custom {
+  my ($self, $attr) = @_;
+  my $value = $self->{$attr};
+  return Storable::dclone($value) if ref $value;
+  return $value;
+}
+
+
+sub _new {
+  my ($class, $struct, $options) = @_;
+  my $self;
+
+  if ( $options->{lazy_validation} ) {
+    # try to convert to a valid structure; if succeeds, then return it
+    my $cmc = CPAN::Meta::Converter->new( $struct );
+    $self = $cmc->convert( version => 2 ); # valid or dies
+    return bless $self, $class;
+  }
+  else {
+    # validate original struct
+    my $cmv = CPAN::Meta::Validator->new( $struct );
+    unless ( $cmv->is_valid) {
+      die "Invalid metadata structure. Errors: "
+        . join(", ", $cmv->errors) . "\n";
+    }
+  }
+
+  # up-convert older spec versions
+  my $version = $struct->{'meta-spec'}{version} || '1.0';
+  if ( $version == 2 ) {
+    $self = $struct;
+  }
+  else {
+    my $cmc = CPAN::Meta::Converter->new( $struct );
+    $self = $cmc->convert( version => 2 );
+  }
+
+  return bless $self, $class;
+}
+
+sub new {
+  my ($class, $struct, $options) = @_;
+  my $self = eval { $class->_new($struct, $options) };
+  croak($@) if $@;
+  return $self;
+}
+
+
+sub create {
+  my ($class, $struct, $options) = @_;
+  my $version = __PACKAGE__->VERSION || 2;
+  $struct->{generated_by} ||= __PACKAGE__ . " version $version" ;
+  $struct->{'meta-spec'}{version} ||= int($version);
+  my $self = eval { $class->_new($struct, $options) };
+  croak ($@) if $@;
+  return $self;
+}
+
+
+sub load_file {
+  my ($class, $file, $options) = @_;
+  $options->{lazy_validation} = 1 unless exists $options->{lazy_validation};
+
+  croak "load_file() requires a valid, readable filename"
+    unless -r $file;
+
+  my $self;
+  eval {
+    my $struct = Parse::CPAN::Meta->load_file( $file );
+    $self = $class->_new($struct, $options);
+  };
+  croak($@) if $@;
+  return $self;
+}
+
+
+sub load_yaml_string {
+  my ($class, $yaml, $options) = @_;
+  $options->{lazy_validation} = 1 unless exists $options->{lazy_validation};
+
+  my $self;
+  eval {
+    my ($struct) = Parse::CPAN::Meta->load_yaml_string( $yaml );
+    $self = $class->_new($struct, $options);
+  };
+  croak($@) if $@;
+  return $self;
+}
+
+
+sub load_json_string {
+  my ($class, $json, $options) = @_;
+  $options->{lazy_validation} = 1 unless exists $options->{lazy_validation};
+
+  my $self;
+  eval {
+    my $struct = Parse::CPAN::Meta->load_json_string( $json );
+    $self = $class->_new($struct, $options);
+  };
+  croak($@) if $@;
+  return $self;
+}
+
+
+sub save {
+  my ($self, $file, $options) = @_;
+
+  my $version = $options->{version} || '2';
+  my $layer = $] ge '5.008001' ? ':utf8' : '';
+
+  if ( $version ge '2' ) {
+    carp "'$file' should end in '.json'"
+      unless $file =~ m{\.json$};
+  }
+  else {
+    carp "'$file' should end in '.yml'"
+      unless $file =~ m{\.yml$};
+  }
+
+  my $data = $self->as_string( $options );
+  open my $fh, ">$layer", $file
+    or die "Error opening '$file' for writing: $!\n";
+
+  print {$fh} $data;
+  close $fh
+    or die "Error closing '$file': $!\n";
+
+  return 1;
+}
+
+
+# XXX Do we need this if we always upconvert? -- dagolden, 2010-04-14
+sub meta_spec_version {
+  my ($self) = @_;
+  return $self->meta_spec->{version};
+}
+
+
+sub effective_prereqs {
+  my ($self, $features) = @_;
+  $features ||= [];
+
+  my $prereq = CPAN::Meta::Prereqs->new($self->prereqs);
+
+  return $prereq unless @$features;
+
+  my @other = map {; $self->feature($_)->prereqs } @$features;
+
+  return $prereq->with_merged_prereqs(\@other);
+}
+
+
+sub should_index_file {
+  my ($self, $filename) = @_;
+
+  for my $no_index_file (@{ $self->no_index->{file} || [] }) {
+    return if $filename eq $no_index_file;
+  }
+
+  for my $no_index_dir (@{ $self->no_index->{directory} }) {
+    $no_index_dir =~ s{$}{/} unless $no_index_dir =~ m{/\z};
+    return if index($filename, $no_index_dir) == 0;
+  }
+
+  return 1;
+}
+
+
+sub should_index_package {
+  my ($self, $package) = @_;
+
+  for my $no_index_pkg (@{ $self->no_index->{package} || [] }) {
+    return if $package eq $no_index_pkg;
+  }
+
+  for my $no_index_ns (@{ $self->no_index->{namespace} }) {
+    return if index($package, "${no_index_ns}::") == 0;
+  }
+
+  return 1;
+}
+
+
+sub features {
+  my ($self) = @_;
+
+  my $opt_f = $self->optional_features;
+  my @features = map {; CPAN::Meta::Feature->new($_ => $opt_f->{ $_ }) }
+                 keys %$opt_f;
+
+  return @features;
+}
+
+
+sub feature {
+  my ($self, $ident) = @_;
+
+  croak "no feature named $ident"
+    unless my $f = $self->optional_features->{ $ident };
+
+  return CPAN::Meta::Feature->new($ident, $f);
+}
+
+
+sub as_struct {
+  my ($self, $options) = @_;
+  my $backend = Parse::CPAN::Meta->json_backend();
+  my $struct = $backend->new->decode(
+    $backend->new->convert_blessed->encode($self)
+  );
+  if ( $options->{version} ) {
+    my $cmc = CPAN::Meta::Converter->new( $struct );
+    $struct = $cmc->convert( version => $options->{version} );
+  }
+  return $struct;
+}
+
+
+sub as_string {
+  my ($self, $options) = @_;
+
+  my $version = $options->{version} || '2';
+
+  my $struct;
+  if ( $self->version ne $version ) {
+    my $cmc = CPAN::Meta::Converter->new( $self->as_struct );
+    $struct = $cmc->convert( version => $version );
+  }
+  else {
+    $struct = $self->as_struct;
+  }
+
+  my ($data, $backend);
+  if ( $version ge '2' ) {
+    $backend = Parse::CPAN::Meta->json_backend();
+    $data = $backend->new->pretty->canonical->encode($struct);
+  }
+  else {
+    $backend = Parse::CPAN::Meta->yaml_backend();
+    $data = eval { no strict 'refs'; &{"$backend\::Dump"}($struct) };
+    if ( $@ ) {
+      croak $backend->can('errstr') ? $backend->errstr : $@
+    }
+  }
+
+  return $data;
+}
+
+# Used by JSON::PP, etc. for "convert_blessed"
+sub TO_JSON {
+  return { %{ $_[0] } };
+}
+
+1;
+
+
+
+=pod
+
+=head1 NAME
+
+CPAN::Meta - the distribution metadata for a CPAN dist
+
+=head1 VERSION
+
+version 2.110440
+
+=head1 SYNOPSIS
+
+  my $meta = CPAN::Meta->load_file('META.json');
+
+  printf "testing requirements for %s version %s\n",
+    $meta->name,
+    $meta->version;
+
+  my $prereqs = $meta->requirements_for('configure');
+
+  for my $module ($prereqs->required_modules) {
+    my $version = get_local_version($module);
+
+    die "missing required module $module" unless defined $version;
+    die "version for $module not in range"
+      unless $prereqs->accepts_module($module, $version);
+  }
+
+=head1 DESCRIPTION
+
+Software distributions released to the CPAN include a F<META.json> or, for
+older distributions, F<META.yml>, which describes the distribution, its
+contents, and the requirements for building and installing the distribution.
+The data structure stored in the F<META.json> file is described in
+L<CPAN::Meta::Spec>.
+
+CPAN::Meta provides a simple class to represent this distribution metadata (or
+I<distmeta>), along with some helpful methods for interrogating that data.
+
+The documentation below is only for the methods of the CPAN::Meta object.  For
+information on the meaning of individual fields, consult the spec.
+
+=head1 METHODS
+
+=head2 new
+
+  my $meta = CPAN::Meta->new($distmeta_struct, \%options);
+
+Returns a valid CPAN::Meta object or dies if the supplied metadata hash
+reference fails to validate.  Older-format metadata will be up-converted to
+version 2 if they validate against the original stated specification.
+
+It takes an optional hashref of options. Valid options include:
+
+=over
+
+=item *
+
+lazy_validation -- if true, new will attempt to convert the given metadata
+to version 2 before attempting to validate it.  This means than any
+fixable errors will be handled by CPAN::Meta::Converter before validation.
+(Note that this might result in invalid optional data being silently
+dropped.)  The default is false.
+
+=back
+
+=head2 create
+
+  my $meta = CPAN::Meta->create($distmeta_struct, \%options);
+
+This is same as C<new()>, except that C<generated_by> and C<meta-spec> fields
+will be generated if not provided.  This means the metadata structure is
+assumed to otherwise follow the latest L<CPAN::Meta::Spec>.
+
+=head2 load_file
+
+  my $meta = CPAN::Meta->load_file($distmeta_file, \%options);
+
+Given a pathname to a file containing metadata, this deserializes the file
+according to its file suffix and constructs a new C<CPAN::Meta> object, just
+like C<new()>.  It will die if the deserialized version fails to validate
+against its stated specification version.
+
+It takes the same options as C<new()> but C<lazy_validation> defaults to
+true.
+
+=head2 load_yaml_string
+
+  my $meta = CPAN::Meta->load_yaml_string($yaml, \%options);
+
+This method returns a new CPAN::Meta object using the first document in the
+given YAML string.  In other respects it is identical to C<load_file()>.
+
+=head2 load_json_string
+
+  my $meta = CPAN::Meta->load_json_string($json, \%options);
+
+This method returns a new CPAN::Meta object using the structure represented by
+the given JSON string.  In other respects it is identical to C<load_file()>.
+
+=head2 save
+
+  $meta->save($distmeta_file, \%options);
+
+Serializes the object as JSON and writes it to the given file.  The only valid
+option is C<version>, which defaults to '2'. On Perl 5.8.1 or later, the file
+is saved with UTF-8 encoding.
+
+For C<version> 2 (or higher), the filename should end in '.json'.  L<JSON::PP>
+is the default JSON backend. Using another JSON backend requires L<JSON> 2.5 or
+later and you must set the C<$ENV{PERL_JSON_BACKEND}> to a supported alternate
+backend like L<JSON::XS>.
+
+For C<version> less than 2, the filename should end in '.yml'.
+L<CPAN::Meta::Converter> is used to generate an older metadata structure, which
+is serialized to YAML.  CPAN::Meta::YAML is the default YAML backend.  You may
+set the C<$ENV{PERL_YAML_BACKEND}> to a supported alternative backend, though
+this is not recommended due to subtle incompatibilities between YAML parsers on
+CPAN.
+
+=head2 meta_spec_version
+
+This method returns the version part of the C<meta_spec> entry in the distmeta
+structure.  It is equivalent to:
+
+  $meta->meta_spec->{version};
+
+=head2 effective_prereqs
+
+  my $prereqs = $meta->effective_prereqs;
+
+  my $prereqs = $meta->effective_prereqs( \@feature_identifiers );
+
+This method returns a L<CPAN::Meta::Prereqs> object describing all the
+prereqs for the distribution.  If an arrayref of feature identifiers is given,
+the prereqs for the identified features are merged together with the
+distribution's core prereqs before the CPAN::Meta::Prereqs object is returned.
+
+=head2 should_index_file
+
+  ... if $meta->should_index_file( $filename );
+
+This method returns true if the given file should be indexed.  It decides this
+by checking the C<file> and C<directory> keys in the C<no_index> property of
+the distmeta structure.
+
+C<$filename> should be given in unix format.
+
+=head2 should_index_package
+
+  ... if $meta->should_index_package( $package );
+
+This method returns true if the given package should be indexed.  It decides
+this by checking the C<package> and C<namespace> keys in the C<no_index>
+property of the distmeta structure.
+
+=head2 features
+
+  my @feature_objects = $meta->features;
+
+This method returns a list of L<CPAN::Meta::Feature> objects, one for each
+optional feature described by the distribution's metadata.
+
+=head2 feature
+
+  my $feature_object = $meta->feature( $identifier );
+
+This method returns a L<CPAN::Meta::Feature> object for the optional feature
+with the given identifier.  If no feature with that identifier exists, an
+exception will be raised.
+
+=head2 as_struct
+
+  my $copy = $meta->as_struct( \%options );
+
+This method returns a deep copy of the object's metadata as an unblessed has
+reference.  It takes an optional hashref of options.  If the hashref contains
+a C<version> argument, the copied metadata will be converted to the version
+of the specification and returned.  For example:
+
+  my $old_spec = $meta->as_struct( {version => "1.4"} );
+
+=head2 as_string
+
+  my $string = $meta->as_string( \%options );
+
+This method returns a serialized copy of the object's metadata as a character
+string.  (The strings are B<not> UTF-8 encoded.)  It takes an optional hashref
+of options.  If the hashref contains a C<version> argument, the copied metadata
+will be converted to the version of the specification and returned.  For
+example:
+
+  my $string = $meta->as_struct( {version => "1.4"} );
+
+For C<version> greater than or equal to 2, the string will be serialized as
+JSON.  For C<version> less than 2, the string will be serialized as YAML.  In
+both cases, the same rules are followed as in the C<save()> method for choosing
+a serialization backend.
+
+=head1 STRING DATA
+
+The following methods return a single value, which is the value for the
+corresponding entry in the distmeta structure.  Values should be either undef
+or strings.
+
+=over 4
+
+=item *
+
+abstract
+
+=item *
+
+description
+
+=item *
+
+dynamic_config
+
+=item *
+
+generated_by
+
+=item *
+
+name
+
+=item *
+
+release_status
+
+=item *
+
+version
+
+=back
+
+=head1 LIST DATA
+
+These methods return lists of string values, which might be represented in the
+distmeta structure as arrayrefs or scalars:
+
+=over 4
+
+=item *
+
+authors
+
+=item *
+
+keywords
+
+=item *
+
+licenses
+
+=back
+
+The C<authors> and C<licenses> methods may also be called as C<author> and
+C<license>, respectively, to match the field name in the distmeta structure.
+
+=head1 MAP DATA
+
+These readers return hashrefs of arbitrary unblessed data structures, each
+described more fully in the specification:
+
+=over 4
+
+=item *
+
+meta_spec
+
+=item *
+
+resources
+
+=item *
+
+provides
+
+=item *
+
+no_index
+
+=item *
+
+prereqs
+
+=item *
+
+optional_features
+
+=back
+
+=head1 CUSTOM DATA
+
+A list of custom keys are available from the C<custom_keys> method and
+particular keys may be retrieved with the C<custom> method.
+
+  say $meta->custom($_) for $meta->custom_keys;
+
+If a custom key refers to a data structure, a deep clone is returned.
+
+=head1 BUGS
+
+Please report any bugs or feature using the CPAN Request Tracker.
+Bugs can be submitted through the web interface at
+L<http://rt.cpan.org/Dist/Display.html?Queue=CPAN-Meta>
+
+When submitting a bug or request, please include a test-file or a patch to an
+existing test-file that illustrates the bug or desired feature.
+
+=head1 SEE ALSO
+
+=over 4
+
+=item *
+
+L<CPAN::Meta::Converter>
+
+=item *
+
+L<CPAN::Meta::Validator>
+
+=back
+
+=head1 AUTHORS
+
+=over 4
+
+=item *
+
+David Golden <dagolden@cpan.org>
+
+=item *
+
+Ricardo Signes <rjbs@cpan.org>
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2010 by David Golden and Ricardo Signes.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
+
+
+__END__
+
+
diff --git a/cpan/CPAN-Meta/lib/CPAN/Meta/Converter.pm b/cpan/CPAN-Meta/lib/CPAN/Meta/Converter.pm
new file mode 100644 (file)
index 0000000..2c6ce85
--- /dev/null
@@ -0,0 +1,1365 @@
+use 5.006;
+use strict;
+use warnings;
+package CPAN::Meta::Converter;
+BEGIN {
+  $CPAN::Meta::Converter::VERSION = '2.110440';
+}
+# ABSTRACT: Convert CPAN distribution metadata structures
+
+
+use CPAN::Meta::Validator;
+use Storable qw/dclone/;
+use version 0.82 ();
+
+my %known_specs = (
+    '2'   => 'http://search.cpan.org/perldoc?CPAN::Meta::Spec',
+    '1.4' => 'http://module-build.sourceforge.net/META-spec-v1.4.html',
+    '1.3' => 'http://module-build.sourceforge.net/META-spec-v1.3.html',
+    '1.2' => 'http://module-build.sourceforge.net/META-spec-v1.2.html',
+    '1.1' => 'http://module-build.sourceforge.net/META-spec-v1.1.html',
+    '1.0' => 'http://module-build.sourceforge.net/META-spec-v1.0.html'
+);
+
+my @spec_list = sort { $a <=> $b } keys %known_specs;
+my ($LOWEST, $HIGHEST) = @spec_list[0,-1];
+
+#--------------------------------------------------------------------------#
+# converters
+#
+# called as $converter->($element, $field_name, $full_meta, $to_version)
+#
+# defined return value used for field
+# undef return value means field is skipped
+#--------------------------------------------------------------------------#
+
+sub _keep { $_[0] }
+
+sub _keep_or_one { defined($_[0]) ? $_[0] : 1 }
+
+sub _keep_or_zero { defined($_[0]) ? $_[0] : 0 }
+
+sub _keep_or_unknown { defined($_[0]) && length($_[0]) ? $_[0] : "unknown" }
+
+sub _generated_by {
+  my $gen = shift;
+  my $sig = __PACKAGE__ . " version " . (__PACKAGE__->VERSION || "<dev>");
+
+  return $sig unless defined $gen and length $gen;
+  return $gen if $gen =~ /(, )\Q$sig/;
+  return "$gen, $sig";
+}
+
+sub _listify { ! defined $_[0] ? undef : ref $_[0] eq 'ARRAY' ? $_[0] : [$_[0]] }
+
+sub _prefix_custom {
+  my $key = shift;
+  $key =~ s/^(?!x_)   # Unless it already starts with x_
+             (?:x-?)? # Remove leading x- or x (if present)
+           /x_/ix;    # and prepend x_
+  return $key;
+}
+
+sub _ucfirst_custom {
+  my $key = shift;
+  $key = ucfirst $key unless $key =~ /[A-Z]/;
+  return $key;
+}
+
+sub _change_meta_spec {
+  my ($element, undef, undef, $version) = @_;
+  $element->{version} = $version;
+  $element->{url} = $known_specs{$version};
+  return $element;
+}
+
+my @valid_licenses_1 = (
+  'perl',
+  'gpl',
+  'apache',
+  'artistic',
+  'artistic_2',
+  'lgpl',
+  'bsd',
+  'gpl',
+  'mit',
+  'mozilla',
+  'open_source',
+  'unrestricted',
+  'restrictive',
+  'unknown',
+);
+
+my %license_map_1 = (
+  ( map { $_ => $_ } @valid_licenses_1 ),
+  artistic2 => 'artistic_2',
+);
+
+sub _license_1 {
+  my ($element) = @_;
+  return 'unknown' unless defined $element;
+  if ( $license_map_1{lc $element} ) {
+    return $license_map_1{lc $element};
+  }
+  return 'unknown';
+}
+
+my @valid_licenses_2 = qw(
+  agpl_3
+  apache_1_1
+  apache_2_0
+  artistic_1
+  artistic_2
+  bsd
+  freebsd
+  gfdl_1_2
+  gfdl_1_3
+  gpl_1
+  gpl_2
+  gpl_3
+  lgpl_2_1
+  lgpl_3_0
+  mit
+  mozilla_1_0
+  mozilla_1_1
+  openssl
+  perl_5
+  qpl_1_0
+  ssleay
+  sun
+  zlib
+  open_source
+  restricted
+  unrestricted
+  unknown
+);
+
+my %license_map_2 = (
+  ( map { $_ => $_ } @valid_licenses_2 ),
+  apache => 'apache_2_0',
+  artistic => 'artistic_1',
+  artistic2 => 'artistic_2',
+  gpl => 'gpl_1',
+  lgpl => 'lgpl_2_1',
+  mozilla => 'mozilla_1_0',
+  perl => 'perl_5',
+  restrictive => 'restricted',
+);
+
+sub _license_2 {
+  my ($element) = @_;
+  return [ 'unknown' ] unless defined $element;
+  $element = [ $element ] unless ref $element eq 'ARRAY';
+  my @new_list;
+  for my $lic ( @$element ) {
+    next unless defined $lic;
+    if ( my $new = $license_map_2{lc $lic} ) {
+      push @new_list, $new;
+    }
+  }
+  return @new_list ? \@new_list : [ 'unknown' ];
+}
+
+my %license_downgrade_map = qw(
+  agpl_3            open_source
+  apache_1_1        apache
+  apache_2_0        apache
+  artistic_1        artistic
+  artistic_2        artistic_2
+  bsd               bsd
+  freebsd           open_source
+  gfdl_1_2          open_source
+  gfdl_1_3          open_source
+  gpl_1             gpl
+  gpl_2             gpl
+  gpl_3             gpl
+  lgpl_2_1          lgpl
+  lgpl_3_0          lgpl
+  mit               mit
+  mozilla_1_0       mozilla
+  mozilla_1_1       mozilla
+  openssl           open_source
+  perl_5            perl
+  qpl_1_0           open_source
+  ssleay            open_source
+  sun               open_source
+  zlib              open_source
+  open_source       open_source
+  restricted        restrictive
+  unrestricted      unrestricted
+  unknown           unknown
+);
+
+sub _downgrade_license {
+  my ($element) = @_;
+  if ( ! defined $element ) {
+    return "unknown";
+  }
+  elsif( ref $element eq 'ARRAY' ) {
+    if ( @$element == 1 ) {
+      return $license_downgrade_map{$element->[0]} || "unknown";
+    }
+  }
+  elsif ( ! ref $element ) {
+    return $license_downgrade_map{$element} || "unknown";
+  }
+  return "unknown";
+}
+
+my $no_index_spec_1_2 = {
+  'file' => \&_listify,
+  'dir' => \&_listify,
+  'package' => \&_listify,
+  'namespace' => \&_listify,
+};
+
+my $no_index_spec_1_3 = {
+  'file' => \&_listify,
+  'directory' => \&_listify,
+  'package' => \&_listify,
+  'namespace' => \&_listify,
+};
+
+my $no_index_spec_2 = {
+  'file' => \&_listify,
+  'directory' => \&_listify,
+  'package' => \&_listify,
+  'namespace' => \&_listify,
+  ':custom'  => \&_prefix_custom,
+};
+
+sub _no_index_1_2 {
+  my (undef, undef, $meta) = @_;
+  my $no_index = $meta->{no_index} || $meta->{private};
+  return unless $no_index;
+
+  # cleanup wrong format
+  if ( ! ref $no_index ) {
+    my $item = $no_index;
+    $no_index = { dir => [ $item ], file => [ $item ] };
+  }
+  elsif ( ref $no_index eq 'ARRAY' ) {
+    my $list = $no_index;
+    $no_index = { dir => [ @$list ], file => [ @$list ] };
+  }
+
+  # common mistake: files -> file
+  if ( exists $no_index->{files} ) {
+    $no_index->{file} = delete $no_index->{file};
+  }
+  # common mistake: modules -> module
+  if ( exists $no_index->{modules} ) {
+    $no_index->{module} = delete $no_index->{module};
+  }
+  return _convert($no_index, $no_index_spec_1_2);
+}
+
+sub _no_index_directory {
+  my ($element, $key, $meta, $version) = @_;
+  return unless $element;
+
+  # cleanup wrong format
+  if ( ! ref $element ) {
+    my $item = $element;
+    $element = { directory => [ $item ], file => [ $item ] };
+  }
+  elsif ( ref $element eq 'ARRAY' ) {
+    my $list = $element;
+    $element = { directory => [ @$list ], file => [ @$list ] };
+  }
+
+  if ( exists $element->{dir} ) {
+    $element->{directory} = delete $element->{dir};
+  }
+  # common mistake: files -> file
+  if ( exists $element->{files} ) {
+    $element->{file} = delete $element->{file};
+  }
+  # common mistake: modules -> module
+  if ( exists $element->{modules} ) {
+    $element->{module} = delete $element->{module};
+  }
+  my $spec = $version == 2 ? $no_index_spec_2 : $no_index_spec_1_3;
+  return _convert($element, $spec);
+}
+
+sub _is_module_name {
+  my $mod = shift;
+  return unless defined $mod && length $mod;
+  return $mod =~ m{^[A-Za-z][A-Za-z0-9_]*(?:::[A-Za-z0-9_]+)*$};
+}
+
+sub _clean_version {
+  my ($element, $key, $meta, $to_version) = @_;
+  return 0 if ! defined $element;
+
+  $element =~ s{^\s*}{};
+  $element =~ s{\s*$}{};
+  $element =~ s{^\.}{0.};
+
+  return 0 if ! length $element;
+  return 0 if ( $element eq 'undef' || $element eq '<undef>' );
+
+  if ( my $v = eval { version->new($element) } ) {
+    return $v->is_qv ? $v->normal : $element;
+  }
+  else {
+    return 0;
+  }
+}
+
+sub _version_map {
+  my ($element) = @_;
+  return undef unless defined $element;
+  if ( ref $element eq 'HASH' ) {
+    my $new_map = {};
+    for my $k ( keys %$element ) {
+      next unless _is_module_name($k);
+      my $value = $element->{$k};
+      if ( ! ( defined $value && length $value ) ) {
+        $new_map->{$k} = 0;
+      }
+      elsif ( $value eq 'undef' || $value eq '<undef>' ) {
+        $new_map->{$k} = 0;
+      }
+      elsif ( _is_module_name( $value ) ) { # some weird, old META have this
+        $new_map->{$k} = 0;
+        $new_map->{$value} = 0;
+      }
+      else {
+        $new_map->{$k} = _clean_version($value);
+      }
+    }
+    return $new_map;
+  }
+  elsif ( ref $element eq 'ARRAY' ) {
+    my $hashref = { map { $_ => 0 } @$element };
+    return _version_map($hashref); # cleanup any weird stuff
+  }
+  elsif ( ref $element eq '' && length $element ) {
+    return { $element => 0 }
+  }
+  return;
+}
+
+sub _prereqs_from_1 {
+  my (undef, undef, $meta) = @_;
+  my $prereqs = {};
+  for my $phase ( qw/build configure/ ) {
+    my $key = "${phase}_requires";
+    $prereqs->{$phase}{requires} = _version_map($meta->{$key})
+      if $meta->{$key};
+  }
+  for my $rel ( qw/requires recommends conflicts/ ) {
+    $prereqs->{runtime}{$rel} = _version_map($meta->{$rel})
+      if $meta->{$rel};
+  }
+  return $prereqs;
+}
+
+my $prereqs_spec = {
+  configure => \&_prereqs_rel,
+  build     => \&_prereqs_rel,
+  test      => \&_prereqs_rel,
+  runtime   => \&_prereqs_rel,
+  develop   => \&_prereqs_rel,
+  ':custom'  => \&_prefix_custom,
+};
+
+my $relation_spec = {
+  requires   => \&_version_map,
+  recommends => \&_version_map,
+  suggests   => \&_version_map,
+  conflicts  => \&_version_map,
+  ':custom'  => \&_prefix_custom,
+};
+
+sub _cleanup_prereqs {
+  my ($prereqs, $key, $meta, $to_version) = @_;
+  return unless $prereqs && ref $prereqs eq 'HASH';
+  return _convert( $prereqs, $prereqs_spec, $to_version );
+}
+
+sub _prereqs_rel {
+  my ($relation, $key, $meta, $to_version) = @_;
+  return unless $relation && ref $relation eq 'HASH';
+  return _convert( $relation, $relation_spec, $to_version );
+}
+
+
+BEGIN {
+  my @old_prereqs = qw(
+    requires
+    configure_requires
+    recommends
+    conflicts
+  );
+
+  for ( @old_prereqs ) {
+    my $sub = "_get_$_";
+    my ($phase,$type) = split qr/_/, $_;
+    if ( ! defined $type ) {
+      $type = $phase;
+      $phase = 'runtime';
+    }
+    no strict 'refs';
+    *{$sub} = sub { _extract_prereqs($_[2]->{prereqs},$phase,$type) };
+  }
+}
+
+sub _get_build_requires {
+  my ($data, $key, $meta) = @_;
+
+  my $test_h  = _extract_prereqs($_[2]->{prereqs}, qw(test  requires)) || {};
+  my $build_h = _extract_prereqs($_[2]->{prereqs}, qw(build requires)) || {};
+
+  require Version::Requirements;
+  my $test_req  = Version::Requirements->from_string_hash($test_h);
+  my $build_req = Version::Requirements->from_string_hash($build_h);
+
+  $test_req->add_requirements($build_req)->as_string_hash;
+}
+
+sub _extract_prereqs {
+  my ($prereqs, $phase, $type) = @_;
+  return unless ref $prereqs eq 'HASH';
+  return $prereqs->{$phase}{$type};
+}
+
+sub _downgrade_optional_features {
+  my (undef, undef, $meta) = @_;
+  return undef unless exists $meta->{optional_features};
+  my $origin = $meta->{optional_features};
+  my $features = {};
+  for my $name ( keys %$origin ) {
+    $features->{$name} = {
+      description => $origin->{$name}{description},
+      requires => _extract_prereqs($origin->{$name}{prereqs},'runtime','requires'),
+      configure_requires => _extract_prereqs($origin->{$name}{prereqs},'runtime','configure_requires'),
+      build_requires => _extract_prereqs($origin->{$name}{prereqs},'runtime','build_requires'),
+      recommends => _extract_prereqs($origin->{$name}{prereqs},'runtime','recommends'),
+      conflicts => _extract_prereqs($origin->{$name}{prereqs},'runtime','conflicts'),
+    };
+    for my $k (keys %{$features->{$name}} ) {
+      delete $features->{$name}{$k} unless defined $features->{$name}{$k};
+    }
+  }
+  return $features;
+}
+
+sub _upgrade_optional_features {
+  my (undef, undef, $meta) = @_;
+  return undef unless exists $meta->{optional_features};
+  my $origin = $meta->{optional_features};
+  my $features = {};
+  for my $name ( keys %$origin ) {
+    $features->{$name} = {
+      description => $origin->{$name}{description},
+      prereqs => _prereqs_from_1(undef, undef, $origin->{$name}),
+    };
+    delete $features->{$name}{prereqs}{configure};
+  }
+  return $features;
+}
+
+my $optional_features_2_spec = {
+  description => \&_keep,
+  prereqs => \&_cleanup_prereqs,
+  ':custom'  => \&_prefix_custom,
+};
+
+sub _feature_2 {
+  my ($element, $key, $meta, $to_version) = @_;
+  return unless $element && ref $element eq 'HASH';
+  _convert( $element, $optional_features_2_spec, $to_version );
+}
+
+sub _cleanup_optional_features_2 {
+  my ($element, $key, $meta, $to_version) = @_;
+  return unless $element && ref $element eq 'HASH';
+  my $new_data = {};
+  for my $k ( keys %$element ) {
+    $new_data->{$k} = _feature_2( $element->{$k}, $k, $meta, $to_version );
+  }
+  return unless keys %$new_data;
+  return $new_data;
+}
+
+sub _optional_features_1_4 {
+  my ($element) = @_;
+  return unless $element;
+  $element = _optional_features_as_map($element);
+  for my $name ( keys %$element ) {
+    for my $drop ( qw/requires_packages requires_os excluded_os/ ) {
+      delete $element->{$name}{$drop};
+    }
+  }
+  return $element;
+}
+
+sub _optional_features_as_map {
+  my ($element) = @_;
+  return unless $element;
+  if ( ref $element eq 'ARRAY' ) {
+    my %map;
+    for my $feature ( @$element ) {
+      my (@parts) = %$feature;
+      $map{$parts[0]} = $parts[1];
+    }
+    $element = \%map;
+  }
+  return $element;
+}
+
+sub _is_urlish { defined $_[0] && $_[0] =~ m{\A[-+.a-z0-9]+:.+}i }
+
+sub _url_or_drop {
+  my ($element) = @_;
+  return $element if _is_urlish($element);
+  return;
+}
+
+sub _url_list {
+  my ($element) = @_;
+  return unless $element;
+  $element = _listify( $element );
+  $element = [ grep { _is_urlish($_) } @$element ];
+  return unless @$element;
+  return $element;
+}
+
+sub _author_list {
+  my ($element) = @_;
+  return [ 'unknown' ] unless $element;
+  $element = _listify( $element );
+  $element = [ map { defined $_ && length $_ ? $_ : 'unknown' } @$element ];
+  return [ 'unknown' ] unless @$element;
+  return $element;
+}
+
+my $resource2_upgrade = {
+  license    => sub { return _is_urlish($_[0]) ? _listify( $_[0] ) : undef },
+  homepage   => \&_url_or_drop,
+  bugtracker => sub {
+    my ($item) = @_;
+    return unless $item;
+    if ( $item =~ m{^mailto:(.*)$} ) { return { mailto => $1 } }
+    elsif( _is_urlish($item) ) { return { web => $item } }
+    else { return undef }
+  },
+  repository => sub { return _is_urlish($_[0]) ? { url => $_[0] } : undef },
+  ':custom'  => \&_prefix_custom,
+};
+
+sub _upgrade_resources_2 {
+  my (undef, undef, $meta, $version) = @_;
+  return undef unless exists $meta->{resources};
+  return _convert($meta->{resources}, $resource2_upgrade);
+}
+
+my $bugtracker2_spec = {
+  web => \&_url_or_drop,
+  mailto => \&_keep,
+  ':custom'  => \&_prefix_custom,
+};
+
+sub _repo_type {
+  my ($element, $key, $meta, $to_version) = @_;
+  return $element if defined $element;
+  return unless exists $meta->{url};
+  my $repo_url = $meta->{url};
+  for my $type ( qw/git svn/ ) {
+    return $type if $repo_url =~ m{\A$type};
+  }
+  return;
+}
+
+my $repository2_spec = {
+  web => \&_url_or_drop,
+  url => \&_url_or_drop,
+  type => \&_repo_type,
+  ':custom'  => \&_prefix_custom,
+};
+
+my $resources2_cleanup = {
+  license    => \&_url_list,
+  homepage   => \&_url_or_drop,
+  bugtracker => sub { ref $_[0] ? _convert( $_[0], $bugtracker2_spec ) : undef },
+  repository => sub { my $data = shift; ref $data ? _convert( $data, $repository2_spec ) : undef },
+  ':custom'  => \&_prefix_custom,
+};
+
+sub _cleanup_resources_2 {
+  my ($resources, $key, $meta, $to_version) = @_;
+  return undef unless $resources && ref $resources eq 'HASH';
+  return _convert($resources, $resources2_cleanup, $to_version);
+}
+
+my $resource1_spec = {
+  license    => \&_url_or_drop,
+  homepage   => \&_url_or_drop,
+  bugtracker => \&_url_or_drop,
+  repository => \&_url_or_drop,
+  ':custom'  => \&_keep,
+};
+
+sub _resources_1_3 {
+  my (undef, undef, $meta, $version) = @_;
+  return undef unless exists $meta->{resources};
+  return _convert($meta->{resources}, $resource1_spec);
+}
+
+*_resources_1_4 = *_resources_1_3;
+
+sub _resources_1_2 {
+  my (undef, undef, $meta) = @_;
+  my $resources = $meta->{resources} || {};
+  if ( $meta->{license_url} && ! $resources->{license} ) {
+    $resources->{license} = $meta->license_url
+      if _is_urlish($meta->{license_url});
+  }
+  return undef unless keys %$resources;
+  return _convert($resources, $resource1_spec);
+}
+
+my $resource_downgrade_spec = {
+  license    => sub { return ref $_[0] ? $_[0]->[0] : $_[0] },
+  homepage   => \&_url_or_drop,
+  bugtracker => sub { return $_[0]->{web} },
+  repository => sub { return $_[0]->{url} || $_[0]->{web} },
+  ':custom'  => \&_ucfirst_custom,
+};
+
+sub _downgrade_resources {
+  my (undef, undef, $meta, $version) = @_;
+  return undef unless exists $meta->{resources};
+  return _convert($meta->{resources}, $resource_downgrade_spec);
+}
+
+sub _release_status {
+  my ($element, undef, $meta) = @_;
+  return $element if $element && $element =~ m{\A(?:stable|testing|unstable)\z};
+  return _release_status_from_version(undef, undef, $meta);
+}
+
+sub _release_status_from_version {
+  my (undef, undef, $meta) = @_;
+  my $version = $meta->{version} || '';
+  return ( $version =~ /_/ ) ? 'testing' : 'stable';
+}
+
+my $provides_spec = {
+  file => \&_keep,
+  version => \&_clean_version,
+};
+
+my $provides_spec_2 = {
+  file => \&_keep,
+  version => \&_clean_version,
+  ':custom'  => \&_prefix_custom,
+};
+
+sub _provides {
+  my ($element, $key, $meta, $to_version) = @_;
+  return unless defined $element && ref $element eq 'HASH';
+  my $spec = $to_version == 2 ? $provides_spec_2 : $provides_spec;
+  my $new_data = {};
+  for my $k ( keys %$element ) {
+    $new_data->{$k} = _convert($element->{$k}, $spec, $to_version);
+  }
+  return $new_data;
+}
+
+sub _convert {
+  my ($data, $spec, $to_version) = @_;
+
+  my $new_data = {};
+  for my $key ( keys %$spec ) {
+    next if $key eq ':custom' || $key eq ':drop';
+    next unless my $fcn = $spec->{$key};
+    die "spec for '$key' is not a coderef"
+      unless ref $fcn && ref $fcn eq 'CODE';
+    my $new_value = $fcn->($data->{$key}, $key, $data, $to_version);
+    $new_data->{$key} = $new_value if defined $new_value;
+  }
+
+  my $drop_list   = $spec->{':drop'};
+  my $customizer  = $spec->{':custom'} || \&_keep;
+
+  for my $key ( keys %$data ) {
+    next if $drop_list && grep { $key eq $_ } @$drop_list;
+    next if exists $spec->{$key}; # we handled it
+    $new_data->{ $customizer->($key) } = $data->{$key};
+  }
+
+  return $new_data;
+}
+
+#--------------------------------------------------------------------------#
+# define converters for each conversion
+#--------------------------------------------------------------------------#
+
+# each converts from prior version
+# special ":custom" field is used for keys not recognized in spec
+my %up_convert = (
+  '2-from-1.4' => {
+    # PRIOR MANDATORY
+    'abstract'            => \&_keep_or_unknown,
+    'author'              => \&_author_list,
+    'generated_by'        => \&_generated_by,
+    'license'             => \&_license_2,
+    'meta-spec'           => \&_change_meta_spec,
+    'name'                => \&_keep,
+    'version'             => \&_keep,
+    # CHANGED TO MANDATORY
+    'dynamic_config'      => \&_keep_or_one,
+    # ADDED MANDATORY
+    'release_status'      => \&_release_status_from_version,
+    # PRIOR OPTIONAL
+    'keywords'            => \&_keep,
+    'no_index'            => \&_no_index_directory,
+    'optional_features'   => \&_upgrade_optional_features,
+    'provides'            => \&_provides,
+    'resources'           => \&_upgrade_resources_2,
+    # ADDED OPTIONAL
+    'description'         => \&_keep,
+    'prereqs'             => \&_prereqs_from_1,
+
+    # drop these deprecated fields, but only after we convert
+    ':drop' => [ qw(
+        build_requires
+        configure_requires
+        conflicts
+        distribution_type
+        license_url
+        private
+        recommends
+        requires
+    ) ],
+
+    # other random keys need x_ prefixing
+    ':custom'              => \&_prefix_custom,
+  },
+  '1.4-from-1.3' => {
+    # PRIOR MANDATORY
+    'abstract'            => \&_keep_or_unknown,
+    'author'              => \&_author_list,
+    'generated_by'        => \&_generated_by,
+    'license'             => \&_license_1,
+    'meta-spec'           => \&_change_meta_spec,
+    'name'                => \&_keep,
+    'version'             => \&_keep,
+    # PRIOR OPTIONAL
+    'build_requires'      => \&_version_map,
+    'conflicts'           => \&_version_map,
+    'distribution_type'   => \&_keep,
+    'dynamic_config'      => \&_keep_or_one,
+    'keywords'            => \&_keep,
+    'no_index'            => \&_no_index_directory,
+    'optional_features'   => \&_optional_features_1_4,
+    'provides'            => \&_provides,
+    'recommends'          => \&_version_map,
+    'requires'            => \&_version_map,
+    'resources'           => \&_resources_1_4,
+    # ADDED OPTIONAL
+    'configure_requires'  => \&_keep,
+
+    # drop these deprecated fields, but only after we convert
+    ':drop' => [ qw(
+      license_url
+      private
+    )],
+
+    # other random keys are OK if already valid
+    ':custom'              => \&_keep
+  },
+  '1.3-from-1.2' => {
+    # PRIOR MANDATORY
+    'abstract'            => \&_keep_or_unknown,
+    'author'              => \&_author_list,
+    'generated_by'        => \&_generated_by,
+    'license'             => \&_license_1,
+    'meta-spec'           => \&_change_meta_spec,
+    'name'                => \&_keep,
+    'version'             => \&_keep,
+    # PRIOR OPTIONAL
+    'build_requires'      => \&_version_map,
+    'conflicts'           => \&_version_map,
+    'distribution_type'   => \&_keep,
+    'dynamic_config'      => \&_keep_or_one,
+    'keywords'            => \&_keep,
+    'no_index'            => \&_no_index_directory,
+    'optional_features'   => \&_optional_features_as_map,
+    'provides'            => \&_provides,
+    'recommends'          => \&_version_map,
+    'requires'            => \&_version_map,
+    'resources'           => \&_resources_1_3,
+
+    # drop these deprecated fields, but only after we convert
+    ':drop' => [ qw(
+      license_url
+      private
+    )],
+
+    # other random keys are OK if already valid
+    ':custom'              => \&_keep
+  },
+  '1.2-from-1.1' => {
+    # PRIOR MANDATORY
+    'version'             => \&_keep,
+    # CHANGED TO MANDATORY
+    'license'             => \&_license_1,
+    'name'                => \&_keep,
+    'generated_by'        => \&_generated_by,
+    # ADDED MANDATORY
+    'abstract'            => \&_keep_or_unknown,
+    'author'              => \&_author_list,
+    'meta-spec'           => \&_change_meta_spec,
+    # PRIOR OPTIONAL
+    'build_requires'      => \&_version_map,
+    'conflicts'           => \&_version_map,
+    'distribution_type'   => \&_keep,
+    'dynamic_config'      => \&_keep_or_one,
+    'recommends'          => \&_version_map,
+    'requires'            => \&_version_map,
+    # ADDED OPTIONAL
+    'keywords'            => \&_keep,
+    'no_index'            => \&_no_index_1_2,
+    'optional_features'   => \&_optional_features_as_map,
+    'provides'            => \&_provides,
+    'resources'           => \&_resources_1_2,
+
+    # drop these deprecated fields, but only after we convert
+    ':drop' => [ qw(
+      license_url
+      private
+    )],
+
+    # other random keys are OK if already valid
+    ':custom'              => \&_keep
+  },
+  '1.1-from-1.0' => {
+    # CHANGED TO MANDATORY
+    'version'             => \&_keep,
+    # IMPLIED MANDATORY
+    'name'                => \&_keep,
+    # PRIOR OPTIONAL
+    'build_requires'      => \&_version_map,
+    'conflicts'           => \&_version_map,
+    'distribution_type'   => \&_keep,
+    'dynamic_config'      => \&_keep_or_one,
+    'generated_by'        => \&_generated_by,
+    'license'             => \&_license_1,
+    'recommends'          => \&_version_map,
+    'requires'            => \&_version_map,
+    # ADDED OPTIONAL
+    'license_url'         => \&_url_or_drop,
+    'private'             => \&_keep,
+
+    # other random keys are OK if already valid
+    ':custom'              => \&_keep
+  },
+);
+
+my %down_convert = (
+  '1.4-from-2' => {
+    # MANDATORY
+    'abstract'            => \&_keep_or_unknown,
+    'author'              => \&_author_list,
+    'generated_by'        => \&_generated_by,
+    'license'             => \&_downgrade_license,
+    'meta-spec'           => \&_change_meta_spec,
+    'name'                => \&_keep,
+    'version'             => \&_keep,
+    # OPTIONAL
+    'build_requires'      => \&_get_build_requires,
+    'configure_requires'  => \&_get_configure_requires,
+    'conflicts'           => \&_get_conflicts,
+    'distribution_type'   => \&_keep,
+    'dynamic_config'      => \&_keep_or_one,
+    'keywords'            => \&_keep,
+    'no_index'            => \&_no_index_directory,
+    'optional_features'   => \&_downgrade_optional_features,
+    'provides'            => \&_provides,
+    'recommends'          => \&_get_recommends,
+    'requires'            => \&_get_requires,
+    'resources'           => \&_downgrade_resources,
+
+    # drop these unsupported fields (after conversion)
+    ':drop' => [ qw(
+      description
+      prereqs
+      release_status
+    )],
+
+    # custom keys will be left unchanged
+    ':custom'              => \&_keep
+  },
+  '1.3-from-1.4' => {
+    # MANDATORY
+    'abstract'            => \&_keep_or_unknown,
+    'author'              => \&_author_list,
+    'generated_by'        => \&_generated_by,
+    'license'             => \&_license_1,
+    'meta-spec'           => \&_change_meta_spec,
+    'name'                => \&_keep,
+    'version'             => \&_keep,
+    # OPTIONAL
+    'build_requires'      => \&_version_map,
+    'conflicts'           => \&_version_map,
+    'distribution_type'   => \&_keep,
+    'dynamic_config'      => \&_keep_or_one,
+    'keywords'            => \&_keep,
+    'no_index'            => \&_no_index_directory,
+    'optional_features'   => \&_optional_features_as_map,
+    'provides'            => \&_provides,
+    'recommends'          => \&_version_map,
+    'requires'            => \&_version_map,
+    'resources'           => \&_resources_1_3,
+
+    # drop these unsupported fields, but only after we convert
+    ':drop' => [ qw(
+      configure_requires
+    )],
+
+    # other random keys are OK if already valid
+    ':custom'              => \&_keep,
+  },
+  '1.2-from-1.3' => {
+    # MANDATORY
+    'abstract'            => \&_keep_or_unknown,
+    'author'              => \&_author_list,
+    'generated_by'        => \&_generated_by,
+    'license'             => \&_license_1,
+    'meta-spec'           => \&_change_meta_spec,
+    'name'                => \&_keep,
+    'version'             => \&_keep,
+    # OPTIONAL
+    'build_requires'      => \&_version_map,
+    'conflicts'           => \&_version_map,
+    'distribution_type'   => \&_keep,
+    'dynamic_config'      => \&_keep_or_one,
+    'keywords'            => \&_keep,
+    'no_index'            => \&_no_index_1_2,
+    'optional_features'   => \&_optional_features_as_map,
+    'provides'            => \&_provides,
+    'recommends'          => \&_version_map,
+    'requires'            => \&_version_map,
+    'resources'           => \&_resources_1_3,
+
+    # other random keys are OK if already valid
+    ':custom'              => \&_keep,
+  },
+  '1.1-from-1.2' => {
+    # MANDATORY
+    'version'             => \&_keep,
+    # IMPLIED MANDATORY
+    'name'                => \&_keep,
+    'meta-spec'           => \&_change_meta_spec,
+    # OPTIONAL
+    'build_requires'      => \&_version_map,
+    'conflicts'           => \&_version_map,
+    'distribution_type'   => \&_keep,
+    'dynamic_config'      => \&_keep_or_one,
+    'generated_by'        => \&_generated_by,
+    'license'             => \&_license_1,
+    'private'             => \&_keep,
+    'recommends'          => \&_version_map,
+    'requires'            => \&_version_map,
+
+    # drop unsupported fields
+    ':drop' => [ qw(
+      abstract
+      author
+      provides
+      no_index
+      keywords
+      resources
+    )],
+
+    # other random keys are OK if already valid
+    ':custom'              => \&_keep,
+  },
+  '1.0-from-1.1' => {
+    # IMPLIED MANDATORY
+    'name'                => \&_keep,
+    'meta-spec'           => \&_change_meta_spec,
+    'version'             => \&_keep,
+    # PRIOR OPTIONAL
+    'build_requires'      => \&_version_map,
+    'conflicts'           => \&_version_map,
+    'distribution_type'   => \&_keep,
+    'dynamic_config'      => \&_keep_or_one,
+    'generated_by'        => \&_generated_by,
+    'license'             => \&_license_1,
+    'recommends'          => \&_version_map,
+    'requires'            => \&_version_map,
+
+    # other random keys are OK if already valid
+    ':custom'              => \&_keep,
+  },
+);
+
+my %cleanup = (
+  '2' => {
+    # PRIOR MANDATORY
+    'abstract'            => \&_keep_or_unknown,
+    'author'              => \&_author_list,
+    'generated_by'        => \&_generated_by,
+    'license'             => \&_license_2,
+    'meta-spec'           => \&_change_meta_spec,
+    'name'                => \&_keep,
+    'version'             => \&_keep,
+    # CHANGED TO MANDATORY
+    'dynamic_config'      => \&_keep_or_one,
+    # ADDED MANDATORY
+    'release_status'      => \&_release_status,
+    # PRIOR OPTIONAL
+    'keywords'            => \&_keep,
+    'no_index'            => \&_no_index_directory,
+    'optional_features'   => \&_cleanup_optional_features_2,
+    'provides'            => \&_provides,
+    'resources'           => \&_cleanup_resources_2,
+    # ADDED OPTIONAL
+    'description'         => \&_keep,
+    'prereqs'             => \&_cleanup_prereqs,
+
+    # drop these deprecated fields, but only after we convert
+    ':drop' => [ qw(
+        build_requires
+        configure_requires
+        conflicts
+        distribution_type
+        license_url
+        private
+        recommends
+        requires
+    ) ],
+
+    # other random keys need x_ prefixing
+    ':custom'              => \&_prefix_custom,
+  },
+  '1.4' => {
+    # PRIOR MANDATORY
+    'abstract'            => \&_keep_or_unknown,
+    'author'              => \&_author_list,
+    'generated_by'        => \&_generated_by,
+    'license'             => \&_license_1,
+    'meta-spec'           => \&_change_meta_spec,
+    'name'                => \&_keep,
+    'version'             => \&_keep,
+    # PRIOR OPTIONAL
+    'build_requires'      => \&_version_map,
+    'conflicts'           => \&_version_map,
+    'distribution_type'   => \&_keep,
+    'dynamic_config'      => \&_keep_or_one,
+    'keywords'            => \&_keep,
+    'no_index'            => \&_no_index_directory,
+    'optional_features'   => \&_optional_features_1_4,
+    'provides'            => \&_provides,
+    'recommends'          => \&_version_map,
+    'requires'            => \&_version_map,
+    'resources'           => \&_resources_1_4,
+    # ADDED OPTIONAL
+    'configure_requires'  => \&_keep,
+
+    # other random keys are OK if already valid
+    ':custom'             => \&_keep
+  },
+  '1.3' => {
+    # PRIOR MANDATORY
+    'abstract'            => \&_keep_or_unknown,
+    'author'              => \&_author_list,
+    'generated_by'        => \&_generated_by,
+    'license'             => \&_license_1,
+    'meta-spec'           => \&_change_meta_spec,
+    'name'                => \&_keep,
+    'version'             => \&_keep,
+    # PRIOR OPTIONAL
+    'build_requires'      => \&_version_map,
+    'conflicts'           => \&_version_map,
+    'distribution_type'   => \&_keep,
+    'dynamic_config'      => \&_keep_or_one,
+    'keywords'            => \&_keep,
+    'no_index'            => \&_no_index_directory,
+    'optional_features'   => \&_optional_features_as_map,
+    'provides'            => \&_provides,
+    'recommends'          => \&_version_map,
+    'requires'            => \&_version_map,
+    'resources'           => \&_resources_1_3,
+
+    # other random keys are OK if already valid
+    ':custom'             => \&_keep
+  },
+  '1.2' => {
+    # PRIOR MANDATORY
+    'version'             => \&_keep,
+    # CHANGED TO MANDATORY
+    'license'             => \&_license_1,
+    'name'                => \&_keep,
+    'generated_by'        => \&_generated_by,
+    # ADDED MANDATORY
+    'abstract'            => \&_keep_or_unknown,
+    'author'              => \&_author_list,
+    'meta-spec'           => \&_change_meta_spec,
+    # PRIOR OPTIONAL
+    'build_requires'      => \&_version_map,
+    'conflicts'           => \&_version_map,
+    'distribution_type'   => \&_keep,
+    'dynamic_config'      => \&_keep_or_one,
+    'recommends'          => \&_version_map,
+    'requires'            => \&_version_map,
+    # ADDED OPTIONAL
+    'keywords'            => \&_keep,
+    'no_index'            => \&_no_index_1_2,
+    'optional_features'   => \&_optional_features_as_map,
+    'provides'            => \&_provides,
+    'resources'           => \&_resources_1_2,
+
+    # other random keys are OK if already valid
+    ':custom'             => \&_keep
+  },
+  '1.1' => {
+    # CHANGED TO MANDATORY
+    'version'             => \&_keep,
+    # IMPLIED MANDATORY
+    'name'                => \&_keep,
+    'meta-spec'           => \&_change_meta_spec,
+    # PRIOR OPTIONAL
+    'build_requires'      => \&_version_map,
+    'conflicts'           => \&_version_map,
+    'distribution_type'   => \&_keep,
+    'dynamic_config'      => \&_keep_or_one,
+    'generated_by'        => \&_generated_by,
+    'license'             => \&_license_1,
+    'recommends'          => \&_version_map,
+    'requires'            => \&_version_map,
+    # ADDED OPTIONAL
+    'license_url'         => \&_url_or_drop,
+    'private'             => \&_keep,
+
+    # other random keys are OK if already valid
+    ':custom'             => \&_keep
+  },
+  '1.0' => {
+    # IMPLIED MANDATORY
+    'name'                => \&_keep,
+    'meta-spec'           => \&_change_meta_spec,
+    'version'             => \&_keep,
+    # IMPLIED OPTIONAL
+    'build_requires'      => \&_version_map,
+    'conflicts'           => \&_version_map,
+    'distribution_type'   => \&_keep,
+    'dynamic_config'      => \&_keep_or_one,
+    'generated_by'        => \&_generated_by,
+    'license'             => \&_license_1,
+    'recommends'          => \&_version_map,
+    'requires'            => \&_version_map,
+
+    # other random keys are OK if already valid
+    ':custom'             => \&_keep,
+  },
+);
+
+#--------------------------------------------------------------------------#
+# Code
+#--------------------------------------------------------------------------#
+
+
+sub new {
+  my ($class,$data) = @_;
+
+  # create an attributes hash
+  my $self = {
+    'data'    => $data,
+    'spec'    => $data->{'meta-spec'}{'version'} || "1.0",
+  };
+
+  # create the object
+  return bless $self, $class;
+}
+
+
+sub convert {
+  my ($self, %args) = @_;
+  my $args = { %args };
+
+  my $new_version = $args->{version} || $HIGHEST;
+
+  my ($old_version) = $self->{spec};
+  my $converted = dclone $self->{data};
+
+  if ( $old_version == $new_version ) {
+    $converted = _convert( $converted, $cleanup{$old_version}, $old_version );
+    my $cmv = CPAN::Meta::Validator->new( $converted );
+    unless ( $cmv->is_valid ) {
+      my $errs = join("\n", $cmv->errors);
+      die "Failed to clean-up $old_version metadata. Errors:\n$errs\n";
+    }
+    return $converted;
+  }
+  elsif ( $old_version > $new_version )  {
+    my @vers = sort { $b <=> $a } keys %known_specs;
+    for my $i ( 0 .. $#vers-1 ) {
+      next if $vers[$i] > $old_version;
+      last if $vers[$i+1] < $new_version;
+      my $spec_string = "$vers[$i+1]-from-$vers[$i]";
+      $converted = _convert( $converted, $down_convert{$spec_string}, $vers[$i+1] );
+      my $cmv = CPAN::Meta::Validator->new( $converted );
+      unless ( $cmv->is_valid ) {
+        my $errs = join("\n", $cmv->errors);
+        die "Failed to downconvert metadata to $vers[$i+1]. Errors:\n$errs\n";
+      }
+    }
+    return $converted;
+  }
+  else {
+    my @vers = sort { $a <=> $b } keys %known_specs;
+    for my $i ( 0 .. $#vers-1 ) {
+      next if $vers[$i] < $old_version;
+      last if $vers[$i+1] > $new_version;
+      my $spec_string = "$vers[$i+1]-from-$vers[$i]";
+      $converted = _convert( $converted, $up_convert{$spec_string}, $vers[$i+1] );
+      my $cmv = CPAN::Meta::Validator->new( $converted );
+      unless ( $cmv->is_valid ) {
+        my $errs = join("\n", $cmv->errors);
+        die "Failed to upconvert metadata to $vers[$i+1]. Errors:\n$errs\n";
+      }
+    }
+    return $converted;
+  }
+}
+
+1;
+
+
+
+=pod
+
+=head1 NAME
+
+CPAN::Meta::Converter - Convert CPAN distribution metadata structures
+
+=head1 VERSION
+
+version 2.110440
+
+=head1 SYNOPSIS
+
+  my $struct = decode_json_file('META.json');
+
+  my $cmc = CPAN::Meta::Converter->new( $struct );
+
+  my $new_struct = $cmc->convert( version => "2" );
+
+=head1 DESCRIPTION
+
+This module converts CPAN Meta structures from one form to another.  The
+primary use is to convert older structures to the most modern version of
+the specification, but other transformations may be implemented in the
+future as needed.  (E.g. stripping all custom fields or stripping all
+optional fields.)
+
+=head1 METHODS
+
+=head2 new
+
+  my $cmc = CPAN::Meta::Converter->new( $struct );
+
+The constructor should be passed a valid metadata structure but invalid
+structures are accepted.  If no meta-spec version is provided, version 1.0 will
+be assumed.
+
+=head2 convert
+
+  my $new_struct = $cmc->convert( version => "2" );
+
+Returns a new hash reference with the metadata converted to a different form.
+C<convert> will die if any conversion/standardization still results in an
+invalid structure.
+
+Valid parameters include:
+
+=over
+
+=item *
+
+C<version> -- Indicates the desired specification version (e.g. "1.0", "1.1" ... "1.4", "2").
+Defaults to the latest version of the CPAN Meta Spec.
+
+=back
+
+Conversion proceeds through each version in turn.  For example, a version 1.2
+structure might be converted to 1.3 then 1.4 then finally to version 2. The
+conversion process attempts to clean-up simple errors and standardize data.
+For example, if C<author> is given as a scalar, it will converted to an array
+reference containing the item. (Converting a structure to its own version will
+also clean-up and standardize.)
+
+When data are cleaned and standardized, missing or invalid fields will be
+replaced with sensible defaults when possible.  This may be lossy or imprecise.
+For example, some badly structured META.yml files on CPAN have prerequisite
+modules listed as both keys and values:
+
+  requires => { 'Foo::Bar' => 'Bam::Baz' }
+
+These would be split and each converted to a prerequisite with a minimum
+version of zero.
+
+When some mandatory fields are missing or invalid, the conversion will attempt
+to provide a sensible default or will fill them with a value of 'unknown'.  For
+example a missing or unrecognized C<license> field will result in a C<license>
+field of 'unknown'.  Fields that may get an 'unknown' include:
+
+=over 4
+
+=item *
+
+abstract
+
+=item *
+
+author
+
+=item *
+
+license
+
+=back
+
+=head1 BUGS
+
+Please report any bugs or feature using the CPAN Request Tracker.
+Bugs can be submitted through the web interface at
+L<http://rt.cpan.org/Dist/Display.html?Queue=CPAN-Meta>
+
+When submitting a bug or request, please include a test-file or a patch to an
+existing test-file that illustrates the bug or desired feature.
+
+=head1 AUTHORS
+
+=over 4
+
+=item *
+
+David Golden <dagolden@cpan.org>
+
+=item *
+
+Ricardo Signes <rjbs@cpan.org>
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2010 by David Golden and Ricardo Signes.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
+
+
+__END__
+
+
diff --git a/cpan/CPAN-Meta/lib/CPAN/Meta/Feature.pm b/cpan/CPAN-Meta/lib/CPAN/Meta/Feature.pm
new file mode 100644 (file)
index 0000000..d3575e5
--- /dev/null
@@ -0,0 +1,116 @@
+use 5.006;
+use strict;
+use warnings;
+package CPAN::Meta::Feature;
+BEGIN {
+  $CPAN::Meta::Feature::VERSION = '2.110440';
+}
+# ABSTRACT: an optional feature provided by a CPAN distribution
+
+use CPAN::Meta::Prereqs;
+
+
+sub new {
+  my ($class, $identifier, $spec) = @_;
+
+  my %guts = (
+    identifier  => $identifier,
+    description => $spec->{description},
+    prereqs     => CPAN::Meta::Prereqs->new($spec->{prereqs}),
+  );
+
+  bless \%guts => $class;
+}
+
+
+sub identifier  { $_[0]{identifier}  }
+
+
+sub description { $_[0]{description} }
+
+
+sub prereqs     { $_[0]{prereqs} }
+
+1;
+
+
+
+=pod
+
+=head1 NAME
+
+CPAN::Meta::Feature - an optional feature provided by a CPAN distribution
+
+=head1 VERSION
+
+version 2.110440
+
+=head1 DESCRIPTION
+
+A CPAN::Meta::Feature object describes an optional feature offered by a CPAN
+distribution and specified in the distribution's F<META.json> (or F<META.yml>)
+file.
+
+For the most part, this class will only be used when operating on the result of
+the C<feature> or C<features> methods on a L<CPAN::Meta> object.
+
+=head1 METHODS
+
+=head2 new
+
+  my $feature = CPAN::Meta::Feature->new( $identifier => \%spec );
+
+This returns a new Feature object.  The C<%spec> argument to the constructor
+should be the same as the value of the C<optional_feature> entry in the
+distmeta.  It must contain entries for C<description> and C<prereqs>.
+
+=head2 identifier
+
+This method returns the feature's identifier.
+
+=head2 description
+
+This method returns the feature's long description.
+
+=head2 prereqs
+
+This method returns the feature's prerequisites as a L<CPAN::Meta::Prereqs>
+object.
+
+=head1 BUGS
+
+Please report any bugs or feature using the CPAN Request Tracker.
+Bugs can be submitted through the web interface at
+L<http://rt.cpan.org/Dist/Display.html?Queue=CPAN-Meta>
+
+When submitting a bug or request, please include a test-file or a patch to an
+existing test-file that illustrates the bug or desired feature.
+
+=head1 AUTHORS
+
+=over 4
+
+=item *
+
+David Golden <dagolden@cpan.org>
+
+=item *
+
+Ricardo Signes <rjbs@cpan.org>
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2010 by David Golden and Ricardo Signes.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
+
+
+__END__
+
+
+
diff --git a/cpan/CPAN-Meta/lib/CPAN/Meta/History.pm b/cpan/CPAN-Meta/lib/CPAN/Meta/History.pm
new file mode 100644 (file)
index 0000000..ab03690
--- /dev/null
@@ -0,0 +1,315 @@
+# vi:tw=72
+use 5.006;
+use strict;
+use warnings;
+package CPAN::Meta::History;
+BEGIN {
+  $CPAN::Meta::History::VERSION = '2.110440';
+}
+# ABSTRACT: history of CPAN Meta Spec changes
+1;
+
+
+
+__END__
+=pod
+
+=head1 NAME
+
+CPAN::Meta::History - history of CPAN Meta Spec changes
+
+=head1 VERSION
+
+version 2.110440
+
+=head1 DESCRIPTION
+
+The CPAN Meta Spec has gone through several iterations.  It was
+originally written in HTML and later revised into POD (though published
+in HTML generated from the POD).  Fields were added, removed or changed,
+sometimes by design and sometimes to reflect real-world usage after the
+fact.
+
+This document reconstructs the history of the CPAN Meta Spec based on
+change logs, repository commit messages and the published HTML files.
+In some cases, particularly prior to version 1.2, the exact version
+when certain fields were introduced or changed is inconsistent between
+sources.  When in doubt, the published HTML files for versions 1.0 to
+1.4 as they existed when version 2 was developed are used as the
+definitive source.
+
+Starting with version 2, the specification document is part of the
+CPAN-Meta distribution and will be published on CPAN as
+L<CPAN::Meta::Spec>.
+
+Going forward, specification version numbers will be integers and
+decimal portions will correspond to a release date for the CPAN::Meta
+library.
+
+=head1 HISTORY
+
+=head2 Version 2
+
+April 2010
+
+=over
+
+=item *
+
+Revised spec examples as perl data structures rather than YAML
+
+=item *
+
+Switched to JSON serialization from YAML
+
+=item *
+
+Specified allowed version number formats
+
+=item *
+
+Replaced 'requires', 'build_requires', 'configure_requires',
+'recommends' and 'conflicts' with new 'prereqs' data structure divided
+by I<phase> (configure, build, test, runtime, etc.) and I<relationship>
+(requires, recommends, suggests, conflicts)
+
+=item *
+
+Added support for 'develop' phase for requirements for maintaining
+a list of authoring tools
+
+=item *
+
+Changed 'license' to a list and revised the set of valid licenses
+
+=item *
+
+Made 'dynamic_config' mandatory to reduce confusion
+
+=item *
+
+Changed 'resources' subkey 'repository' to a hash that clarifies
+repository type, url for browsing and url for checkout
+
+=item *
+
+Changed 'resources' subkey 'bugtracker' to a hash for either web
+or mailto resource
+
+=item *
+
+Changed specification of 'optional_features':
+
+=over
+
+=item *
+
+Added formal specification and usage guide instead of just example
+
+=item *
+
+Changed to use new prereqs data structure instead of individual keys
+
+=back
+
+=item *
+
+Clarified intended use of 'author' as generalized contact list
+
+=item *
+
+Added 'release_status' field to indicate stable, testing or unstable
+status to provide hints to indexers
+
+=item *
+
+Added 'description' field for a longer description of the distribution
+
+=item *
+
+Formalized use of "x_" or "X_" for all custom keys not listed in the
+official spec
+
+=back
+
+=head2 Version 1.4
+
+June 2008
+
+=over
+
+=item *
+
+Noted explicit support for 'perl' in prerequisites
+
+=item *
+
+Added 'configure_requires' prerequisite type
+
+=item *
+
+Changed 'optional_features'
+
+=over
+
+=item *
+
+Example corrected to show map of maps instead of list of maps
+(though descriptive text said 'map' even in v1.3)
+
+=item *
+
+Removed 'requires_packages', 'requires_os' and 'excluded_os'
+as valid subkeys
+
+=back
+
+=back
+
+=head2 Version 1.3
+
+November 2006
+
+=over
+
+=item *
+
+Clarified that all prerequisites take version range specifications
+
+=item *
+
+Added 'no_index' subkey 'directory' and removed 'dir' to match actual
+usage in the wild
+
+=item *
+
+Added a 'repository' subkey to 'resources'
+
+=back
+
+=head2 Version 1.2
+
+August 2005
+
+=over
+
+=item *
+
+Re-wrote and restructured spec in POD syntax
+
+=item *
+
+Changed 'name' to be mandatory
+
+=item *
+
+Changed 'generated_by' to be mandatory
+
+=item *
+
+Changed 'license' to be mandatory
+
+=item *
+
+Added required 'abstract' field
+
+=item *
+
+Added required 'author' field
+
+=item *
+
+Added required 'meta-spec' field to define 'version' (and 'url') of the
+CPAN Meta Spec used for metadata
+
+=item *
+
+Added 'provides' field
+
+=item *
+
+Added 'no_index' field and deprecated 'private' field.  'no_index'
+subkeys include 'file', 'dir', 'package' and 'namespace'
+
+=item *
+
+Added 'keywords' field
+
+=item *
+
+Added 'resources' field with subkeys 'homepage', 'license', and
+'bugtracker'
+
+=item *
+
+Added 'optional_features' field as an alterate under 'recommends'.
+Includes 'description', 'requires', 'build_requires', 'conflicts',
+'requires_packages', 'requires_os' and 'excluded_os' as valid subkeys
+
+=item *
+
+Removed 'license_uri' field
+
+=back
+
+=head2 Version 1.1
+
+May 2003
+
+=over
+
+=item *
+
+Changed 'version' to be mandatory
+
+=item *
+
+Added 'private' field
+
+=item *
+
+Added 'license_uri' field
+
+=back
+
+=head2 Version 1.0
+
+March 2003
+
+=over
+
+=item *
+
+Original release (in HTML format only)
+
+=item *
+
+Included 'name', 'version', 'license', 'distribution_type', 'requires',
+'recommends', 'build_requires', 'conflicts', 'dynamic_config',
+'generated_by'
+
+=back
+
+=head1 AUTHORS
+
+=over 4
+
+=item *
+
+David Golden <dagolden@cpan.org>
+
+=item *
+
+Ricardo Signes <rjbs@cpan.org>
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2010 by David Golden and Ricardo Signes.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
+
diff --git a/cpan/CPAN-Meta/lib/CPAN/Meta/Prereqs.pm b/cpan/CPAN-Meta/lib/CPAN/Meta/Prereqs.pm
new file mode 100644 (file)
index 0000000..4fc2093
--- /dev/null
@@ -0,0 +1,277 @@
+use 5.006;
+use strict;
+use warnings;
+package CPAN::Meta::Prereqs;
+BEGIN {
+  $CPAN::Meta::Prereqs::VERSION = '2.110440';
+}
+# ABSTRACT: a set of distribution prerequisites by phase and type
+
+
+use Carp qw(confess);
+use Scalar::Util qw(blessed);
+use Version::Requirements 0.101020; # finalize
+
+
+sub __legal_phases { qw(configure build test runtime develop)   }
+sub __legal_types  { qw(requires recommends suggests conflicts) }
+
+# expect a prereq spec from META.json -- rjbs, 2010-04-11
+sub new {
+  my ($class, $prereq_spec) = @_;
+  $prereq_spec ||= {};
+
+  my %is_legal_phase = map {; $_ => 1 } $class->__legal_phases;
+  my %is_legal_type  = map {; $_ => 1 } $class->__legal_types;
+
+  my %guts;
+  PHASE: for my $phase (keys %$prereq_spec) {
+    next PHASE unless $phase =~ /\Ax_/i or $is_legal_phase{$phase};
+
+    my $phase_spec = $prereq_spec->{ $phase };
+    next PHASE unless keys %$phase_spec;
+
+    TYPE: for my $type (keys %$phase_spec) {
+      next TYPE unless $type =~ /\Ax_/i or $is_legal_type{$type};
+
+      my $spec = $phase_spec->{ $type };
+
+      next TYPE unless keys %$spec;
+
+      $guts{prereqs}{$phase}{$type} = Version::Requirements->from_string_hash(
+        $spec
+      );
+    }
+  }
+
+  return bless \%guts => $class;
+}
+
+
+sub requirements_for {
+  my ($self, $phase, $type) = @_;
+
+  confess "requirements_for called without phase" unless defined $phase;
+  confess "requirements_for called without type"  unless defined $type;
+
+  unless ($phase =~ /\Ax_/i or grep { $phase eq $_ } $self->__legal_phases) {
+    confess "requested requirements for unknown phase: $phase";
+  }
+
+  unless ($type =~ /\Ax_/i or grep { $type eq $_ } $self->__legal_types) {
+    confess "requested requirements for unknown type: $type";
+  }
+
+  my $req = ($self->{prereqs}{$phase}{$type} ||= Version::Requirements->new);
+
+  $req->finalize if $self->is_finalized;
+
+  return $req;
+}
+
+
+sub with_merged_prereqs {
+  my ($self, $other) = @_;
+
+  my @other = blessed($other) ? $other : @$other;
+
+  my @prereq_objs = ($self, @other);
+
+  my %new_arg;
+
+  for my $phase ($self->__legal_phases) {
+    for my $type ($self->__legal_types) {
+      my $req = Version::Requirements->new;
+
+      for my $prereq (@prereq_objs) {
+        my $this_req = $prereq->requirements_for($phase, $type);
+        next unless $this_req->required_modules;
+
+        $req->add_requirements($this_req);
+      }
+
+      next unless $req->required_modules;
+
+      $new_arg{ $phase }{ $type } = $req->as_string_hash;
+    }
+  }
+
+  return (ref $self)->new(\%new_arg);
+}
+
+
+sub as_string_hash {
+  my ($self) = @_;
+
+  my %hash;
+
+  for my $phase ($self->__legal_phases) {
+    for my $type ($self->__legal_types) {
+      my $req = $self->requirements_for($phase, $type);
+      next unless $req->required_modules;
+
+      $hash{ $phase }{ $type } = $req->as_string_hash;
+    }
+  }
+
+  return \%hash;
+}
+
+
+sub is_finalized { $_[0]{finalized} }
+
+
+sub finalize {
+  my ($self) = @_;
+
+  $self->{finalized} = 1;
+
+  for my $phase (keys %{ $self->{prereqs} }) {
+    $_->finalize for values %{ $self->{prereqs}{$phase} };
+  }
+}
+
+
+sub clone {
+  my ($self) = @_;
+
+  my $clone = (ref $self)->new( $self->as_string_hash );
+}
+
+1;
+
+
+
+=pod
+
+=head1 NAME
+
+CPAN::Meta::Prereqs - a set of distribution prerequisites by phase and type
+
+=head1 VERSION
+
+version 2.110440
+
+=head1 DESCRIPTION
+
+A CPAN::Meta::Prereqs object represents the prerequisites for a CPAN
+distribution or one of its optional features.  Each set of prereqs is
+organized by phase and type, as described in L<CPAN::Meta::Prereqs>.
+
+=head1 METHODS
+
+=head2 new
+
+  my $prereq = CPAN::Meta::Prereqs->new( \%prereq_spec );
+
+This method returns a new set of Prereqs.  The input should look like the
+contents of the C<prereqs> field described in L<CPAN::Meta::Spec>, meaning
+something more or less like this:
+
+  my $prereq = CPAN::Meta::Prereqs->new({
+    runtime => {
+      requires => {
+        'Some::Module' => '1.234',
+        ...,
+      },
+      ...,
+    },
+    ...,
+  });
+
+You can also construct an empty set of prereqs with:
+
+  my $prereqs = CPAN::Meta::Prereqs->new;
+
+This empty set of prereqs is useful for accumulating new prereqs before finally
+dumping the whole set into a structure or string.
+
+=head2 requirements_for
+
+  my $requirements = $prereqs->requirements_for( $phase, $type );
+
+This method returns a L<Version::Requirements> object for the given phase/type
+combination.  If no prerequisites are registered for that combination, a new
+Version::Requirements object will be returned, and it may be added to as
+needed.
+
+If C<$phase> or C<$type> are undefined or otherwise invalid, an exception will
+be raised.
+
+=head2 with_merged_prereqs
+
+  my $new_prereqs = $prereqs->with_merged_prereqs( $other_prereqs );
+
+  my $new_prereqs = $prereqs->with_merged_prereqs( \@other_prereqs );
+
+This method returns a new CPAN::Meta::Prereqs objects in which all the
+other prerequisites given are merged into the current set.  This is primarily
+provided for combining a distribution's core prereqs with the prereqs of one of
+its optional features.
+
+The new prereqs object has no ties to the originals, and altering it further
+will not alter them.
+
+=head2 as_string_hash
+
+This method returns a hashref containing structures suitable for dumping into a
+distmeta data structure.  It is made up of hashes and strings, only; there will
+be no Prereqs, Version::Requirements, or C<version> objects inside it.
+
+=head2 is_finalized
+
+This method returns true if the set of prereqs has been marked "finalized," and
+cannot be altered.
+
+=head2 finalize
+
+Calling C<finalize> on a Prereqs object will close it for further modification.
+Attempting to make any changes that would actually alter the prereqs will
+result in an exception being thrown.
+
+=head2 clone
+
+  my $cloned_prereqs = $prereqs->clone;
+
+This method returns a Prereqs object that is identical to the original object,
+but can be altered without affecting the original object.  Finalization does
+not survive cloning, meaning that you may clone a finalized set of prereqs and
+then modify the clone.
+
+=head1 BUGS
+
+Please report any bugs or feature using the CPAN Request Tracker.
+Bugs can be submitted through the web interface at
+L<http://rt.cpan.org/Dist/Display.html?Queue=CPAN-Meta>
+
+When submitting a bug or request, please include a test-file or a patch to an
+existing test-file that illustrates the bug or desired feature.
+
+=head1 AUTHORS
+
+=over 4
+
+=item *
+
+David Golden <dagolden@cpan.org>
+
+=item *
+
+Ricardo Signes <rjbs@cpan.org>
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2010 by David Golden and Ricardo Signes.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
+
+
+__END__
+
+
+
diff --git a/cpan/CPAN-Meta/lib/CPAN/Meta/Spec.pm b/cpan/CPAN-Meta/lib/CPAN/Meta/Spec.pm
new file mode 100644 (file)
index 0000000..8f94c71
--- /dev/null
@@ -0,0 +1,1145 @@
+# vi:tw=72
+use 5.006;
+use strict;
+use warnings;
+package CPAN::Meta::Spec;
+BEGIN {
+  $CPAN::Meta::Spec::VERSION = '2.110440';
+}
+# ABSTRACT: specification for CPAN distribution metadata
+1;
+
+
+
+__END__
+=pod
+
+=head1 NAME
+
+CPAN::Meta::Spec - specification for CPAN distribution metadata
+
+=head1 VERSION
+
+version 2.110440
+
+=head1 SYNOPSIS
+
+  my $distmeta = {
+    name => 'Module-Build',
+    abstract => 'Build and install Perl modules',
+    description =>  "Module::Build is a system for "
+      . "building, testing, and installing Perl modules. "
+      . "It is meant to ... blah blah blah ...",
+    version  => '0.36',
+    author   => [
+      'Ken Williams <kwilliams@cpan.org>',
+      'Module-Build List <module-build@perl.org>', # additional contact
+    ],
+    license  => [ 'perl_5' ],
+    prereqs => {
+      runtime => {
+        requires => {
+          'perl'   => '5.006',
+          'ExtUtils::Install' => '0',
+          'File::Basename' => '0',
+          'File::Compare'  => '0',
+          'IO::File'   => '0',
+        },
+        recommends => {
+          'Archive::Tar' => '1.00',
+          'ExtUtils::Install' => '0.3',
+          'ExtUtils::ParseXS' => '2.02',
+        },
+      },
+      build => {
+        requires => {
+          'Test::More' => '0',
+        },
+      }
+    },
+    resources => {
+      license => ['http://dev.perl.org/licenses/'],
+    },
+    optional_features => {
+      domination => {
+        description => 'Take over the world',
+        prereqs     => {
+          develop => { requires => { 'Genius::Evil'     => '1.234' } },
+          runtime => { requires => { 'Machine::Weather' => '2.0'   } },
+        },
+      },
+    },
+    dynamic_config => 1,
+    keywords => [ qw/ toolchain cpan dual-life / ],
+    'meta-spec' => {
+      version => '2',
+      url     => 'http://search.cpan.org/perldoc?CPAN::Meta::Spec',
+    },
+    generated_by => 'Module::Build version 0.36',
+  };
+
+=head1 DESCRIPTION
+
+This document describes version 2 of the CPAN distribution metadata
+specification, also known as the "CPAN Meta Spec".
+
+Revisions of this specification for typo corrections and prose
+clarifications may be issued as CPAN::Meta::Spec 2.I<x>.  These
+revisions will never change semantics or add or remove specified
+behavior.
+
+Distribution metadata describe important properties of Perl
+distributions. Distribution building tools like Module::Build,
+Module::Install, ExtUtils::MakeMaker or Dist::Zilla should create a
+metadata file in accordance with this specification and include it with
+the distribution for use by automated tools that index, examine, package
+or install Perl distributions.
+
+=head1 TERMINOLOGY
+
+=over 4
+
+=item distribution
+
+This is the primary object described by the metadata. In the context of
+this document it usually refers to a collection of modules, scripts,
+and/or documents that are distributed together for other developers to
+use.  Examples of distributions are C<Class-Container>, C<libwww-perl>,
+or C<DBI>.
+
+=item module
+
+This refers to a reusable library of code contained in a single file.
+Modules usually contain one or more packages and are often referred
+to by the name of a primary package that can be mapped to the file
+name. For example, one might refer to C<File::Spec> instead of
+F<File/Spec.pm>
+
+=item package
+
+This refers to a namespace declared with the Perl C<package> statement.
+In Perl, packages often have a version number property given by the
+C<$VERSION> variable in the namespace.
+
+=item consumer
+
+This refers to code that reads a metadata file, deserializes it into a
+data structure in memory, or interprets a data structure of metadata
+elements.
+
+=item producer
+
+This refers to code that constructs a metadata data structure,
+serializes into a bytestream and/or writes it to disk.
+
+=item must, should, may, etc.
+
+These terms are interpreted as described in IETF RFC 2119.
+
+=back
+
+=head1 DATA TYPES
+
+Fields in the L</STRUCTURE> section describe data elements, each of
+which has an associated data type as described herein.  There are four
+primitive types: Boolean, String, List and Map.  Other types are
+subtypes of primitives and define compound data structures or define
+constraints on the values of a data element.
+
+=head2 Boolean
+
+A I<Boolean> is used to provide a true or false value.  It B<must> be
+represented as a defined value.
+
+=head2 String
+
+A I<String> is data element containing a non-zero length sequence of
+Unicode characters, such as an ordinary Perl scalar that is not a
+reference.
+
+=head2 List
+
+A I<List> is an ordered collection of zero or more data elements.
+Elements of a List may be of mixed types.
+
+Producers B<must> represent List elements using a data structure which
+unambiguously indicates that multiple values are possible, such as a
+reference to a Perl array (an "arrayref").
+
+Consumers expecting a List B<must> consider a String as equivalent to a
+List of length 1.
+
+=head2 Map
+
+A I<Map> is an unordered collection of zero or more data elements
+("values"), indexed by associated String elements ("keys").  The Map's
+value elements may be of mixed types.
+
+=head2 License String
+
+A I<License String> is a subtype of String with a restricted set of
+values.  Valid values are described in detail in the description of
+the L</license> field.
+
+=head2 URL
+
+I<URL> is a subtype of String containing a Uniform Resource Locator or
+Identifier.  [ This type is called URL and not URI for historical reasons. ]
+
+=head2 Version
+
+A I<Version> is a subtype of String containing a value that describes
+the version number of packages or distributions.  Restrictions on format
+are described in detail in the L</Version Formats> section.
+
+=head2 Version Range
+
+The I<Version Range> type is a subtype of String.  It describes a range
+of Versions that may be present or installed to fulfill prerequisites.
+It is specified in detail in the L</Version Ranges> section.
+
+=head1 STRUCTURE
+
+The metadata structure is a data element of type Map.  This section
+describes valid keys within the Map.
+
+Any keys not described in this specification document (whether top-level
+or within compound data structures described herein) are considered
+I<custom keys> and B<must> begin with an "x" or "X" and be followed by an
+underscore; i.e. they must match the pattern: C<< qr{\Ax_}i >>.  If a
+custom key refers to a compound data structure, subkeys within it do not
+need an "x_" or "X_" prefix.
+
+Consumers of metadata may ignore any or all custom keys.  All other keys
+not described herein are invalid and should be ignored by consumers.
+Producers must not generate or output invalid keys.
+
+For each key, an example is provided followed by a description.  The
+description begins with the version of spec in which the key was added
+or in which the definition was modified, whether the key is I<required>
+or I<optional> and the data type of the corresponding data element.
+These items are in parentheses, brackets and braces, respectively.
+
+If a data type is a Map or Map subtype, valid subkeys will be described
+as well.
+
+Some fields are marked I<Deprecated>.  These are shown for historical
+context and must not be produced in or consumed from any metadata structure
+of version 2 or higher.
+
+=head2 REQUIRED FIELDS
+
+=head3 abstract
+
+Example:
+
+  abstract => 'Build and install Perl modules'
+
+(Spec 1.2) [required] {String}
+
+This is a short description of the purpose of the distribution.
+
+=head3 author
+
+Example:
+
+  author => [ 'Ken Williams <kwilliams@cpan.org>' ]
+
+(Spec 1.2) [required] {List of one or more Strings}
+
+This List indicates the person(s) to contact concerning the
+distribution. The preferred form of the contact string is:
+
+  contact-name <email-address>
+
+This field provides a general contact list independent of other
+structured fields provided within the L</resources> field, such as
+C<bugtracker>.  The addressee(s) can be contacted for any purpose
+including but not limited to (security) problems with the distribution,
+questions about the distribution or bugs in the distribution.
+
+A distribution's original author is usually the contact listed within
+this field.  Co-maintainers, successor maintainers or mailing lists
+devoted to the distribution may also be listed in addition to or instead
+of the original author.
+
+=head3 dynamic_config
+
+Example:
+
+  dynamic_config => 1
+
+(Spec 2) [required] {Boolean}
+
+A boolean flag indicating whether a F<Build.PL> or F<Makefile.PL> (or
+similar) must be executed to determine prerequisites.
+
+This field should be set to a true value if the distribution performs
+some dynamic configuration (asking questions, sensing the environment,
+etc.) as part of its configuration.  This field should be set to a false
+value to indicate that prerequisites included in metadata may be
+considered final and valid for static analysis.
+
+This field explicitly B<does not> indicate whether installation may be
+safely performed without using a Makefile or Build file, as there may be
+special files to install or custom installation targets (e.g. for
+dual-life modules that exist on CPAN as well as in the Perl core).  This
+field only defines whether prerequisites are complete as given in the
+metadata.
+
+=head3 generated_by
+
+Example:
+
+  generated_by => 'Module::Build version 0.36'
+
+(Spec 1.0) [required] {String}
+
+This field indicates the tool that was used to create this metadata.
+There are no defined semantics for this field, but it is traditional to
+use a string in the form "Generating::Package version 1.23" or the
+author's name, if the file was generated by hand.
+
+=head3 license
+
+Example:
+
+  license => [ 'perl_5' ]
+
+  license => [ 'apache_2', 'mozilla_1_0' ]
+
+(Spec 2) [required] {List of one or more License Strings}
+
+One or more licenses that apply to some or all of the files in the
+distribution.  If multiple licenses are listed, the distribution
+documentation should be consulted to clarify the interpretation of
+multiple licenses.
+
+The following list of license strings are valid:
+
+ string          description
+ -------------   -----------------------------------------------
+ agpl_3          GNU Affero General Public License, Version 3
+ apache_1_1      Apache Software License, Version 1.1
+ apache_2_0      Apache License, Version 2.0
+ artistic_1      Artistic License, (Version 1)
+ artistic_2      Artistic License, Version 2.0
+ bsd             BSD License (three-clause)
+ freebsd         FreeBSD License (two-clause)
+ gfdl_1_2        GNU Free Documentation License, Version 1.2
+ gfdl_1_3        GNU Free Documentation License, Version 1.3
+ gpl_1           GNU General Public License, Version 1
+ gpl_2           GNU General Public License, Version 2
+ gpl_3           GNU General Public License, Version 3
+ lgpl_2_1        GNU Lesser General Public License, Version 2.1
+ lgpl_3_0        GNU Lesser General Public License, Version 3.0
+ mit             MIT (aka X11) License
+ mozilla_1_0     Mozilla Public License, Version 1.0
+ mozilla_1_1     Mozilla Public License, Version 1.1
+ openssl         OpenSSL License
+ perl_5          The Perl 5 License (Artistic 1 & GPL 1 or later)
+ qpl_1_0         Q Public License, Version 1.0
+ ssleay          Original SSLeay License
+ sun             Sun Internet Standards Source License (SISSL)
+ zlib            zlib License
+
+The following license strings are also valid and indicate other
+licensing not described above:
+
+ string          description
+ -------------   -----------------------------------------------
+ open_source     Other Open Source Initiative (OSI) approved license
+ restricted      Requires special permission from copyright holder
+ unrestricted    Not an OSI approved license, but not restricted
+ unknown         License not provided in metadata
+
+All other strings are invalid in the license field.
+
+=head3 meta-spec
+
+Example:
+
+  'meta-spec' => {
+    version => '2',
+    url     => 'http://search.cpan.org/perldoc?CPAN::Meta::Spec',
+  }
+
+(Spec 1.2) [required] {Map}
+
+This field indicates the version of the CPAN Meta Spec that should be
+used to interpret the metadata.  Consumers must check this key as soon
+as possible and abort further metadata processing if the meta-spec
+version is not supported by the consumer.
+
+The following keys are valid, but only C<version> is required.
+
+=over
+
+=item version
+
+This subkey gives the integer I<Version> of the CPAN Meta Spec against
+which the document was generated.
+
+=item url
+
+This is a I<URL> of the metadata specification document corresponding to
+the given version.  This is strictly for human-consumption and should
+not impact the interpretation of the document.
+
+=back
+
+=head3 name
+
+Example:
+
+  name => 'Module-Build'
+
+(Spec 1.0) [required] {String}
+
+This field is the name of the distribution.  This is often created by
+taking the "main package" in the distribution and changing C<::> to
+C<->, but the name may be completely unrelated to the packages within
+the distribution.  C.f. L<http://search.cpan.org/dist/libwww-perl/>.
+
+=head3 release_status
+
+Example:
+
+  release_status => 'stable'
+
+(Spec 2) [required] {String}
+
+This field provides the  release status of this distribution.  If the
+C<version> field contains an underscore character, then
+C<release_status> B<must not> be "stable."
+
+The C<release_status> field B<must> have one of the following values:
+
+=over
+
+=item stable
+
+This indicates an ordinary, "final" release that should be indexed by PAUSE
+or other indexers.
+
+=item testing
+
+This indicates a "beta" release that is substantially complete, but has an
+elevated risk of bugs and requires additional testing.  The distribution
+should not be installed over a stable release without an explicit request
+or other confirmation from a user.  This release status may also be used
+for "release candidate" versions of a distribution.
+
+=item unstable
+
+This indicates an "alpha" release that is under active development, but has
+been released for early feedback or testing and may be missing features or
+may have serious bugs.  The distribution should not be installed over a
+stable release without an explicit request or other confirmation from a
+user.
+
+=back
+
+Consumers B<may> use this field to determine how to index the
+distribution for CPAN or other repositories in addition to or in
+replacement of heuristics based on version number or file name.
+
+=head3 version
+
+Example:
+
+  version => '0.36'
+
+(Spec 1.0) [required] {Version}
+
+This field gives the version of the distribution to which the metadata
+structure refers.
+
+=head2 OPTIONAL FIELDS
+
+=head3 description
+
+Example:
+
+    description =>  "Module::Build is a system for "
+      . "building, testing, and installing Perl modules. "
+      . "It is meant to ... blah blah blah ...",
+
+(Spec 2) [optional] {String}
+
+A longer, more complete description of the purpose or intended use of
+the distribution than the one provided by the C<abstract> key.
+
+=head3 keywords
+
+Example:
+
+  keywords => [ qw/ toolchain cpan dual-life / ]
+
+(Spec 1.1) [optional] {List of zero or more Strings}
+
+A List of keywords that describe this distribution.  Keywords
+B<must not> include whitespace.
+
+=head3 no_index
+
+Example:
+
+  no_index => {
+    file      => [ 'My/Module.pm' ],
+    directory => [ 'My/Private' ],
+    package   => [ 'My::Module::Secret' ],
+    namespace => [ 'My::Module::Sample' ],
+  }
+
+(Spec 1.2) [optional] {Map}
+
+This Map describes any files, directories, packages, and namespaces that
+are private to the packaging or implementation of the distribution and
+should be ignored by indexing or search tools.
+
+Valid subkeys are as follows:
+
+=over
+
+=item file
+
+A I<List> of relative paths to files.  Paths B<must be> specified with
+unix convetions.
+
+=item directory
+
+A I<List> of relative paths to directories.  Paths B<must be> specified
+with unix convetions.
+
+[ Note: previous editions of the spec had C<dir> instead of C<directory> ]
+
+=item package
+
+A I<List> of package names.
+
+=item namespace
+
+A I<List> of package namespaces, where anything below the namespace
+must be ignored, but I<not> the namespace itself.
+
+In the example above for C<no_index>, C<My::Module::Sample::Foo> would
+be ignored, but C<My::Module::Sample> would not.
+
+=back
+
+=head3 optional_features
+
+Example:
+
+  optional_features => {
+    sqlite => {
+      description => 'Provides SQLite support',
+      prereqs => {
+        runtime => {
+          requires => {
+            'DBD::SQLite' => '1.25'
+          }
+        }
+      }
+    }
+  }
+
+(Spec 2) [optional] {Map}
+
+This Map describes optional features with incremental prerequisites.
+Each key of the C<optional_features> Map is a String used to identify
+the feature and each value is a Map with additional information about
+the feature.  Valid subkeys include:
+
+=over
+
+=item description
+
+This is a String describing the feature.  Every optional feature
+should provide a description
+
+=item prereqs
+
+This entry is required and has the same structure as that of the
+C<L</prereqs>> key.  It provides a list of package requirements
+that must be satisfied for the feature to be supported or enabled.
+
+There is one crucial restriction:  the preqreqs of an optional feature
+B<must not> include C<configure> phase prereqs.
+
+=back
+
+Consumers B<must not> include optional features as prerequisites without
+explict instruction from users (whether via interactive prompting,
+a function parameter or a configuration value, etc. ).
+
+If an optional feature is used by a consumer to add additional
+prerequisites, the consumer should merge the optional feature
+prerequisites into those given by the C<prereqs> key using the same
+semantics.  See L</Merging and Resolving Prerequisites> for details on
+merging prerequisites.
+
+I<Suggestion for disuse:> Because there is currently no way for a
+distribution to specify a dependency on an optional feature of another
+dependency, the use of C<optional_feature> is discouraged.  Instead,
+create a separate, installable distribution that ensures the desired
+feature is available.  For example, if C<Foo::Bar> has a "Baz" feature,
+release a separate C<Foo-Bar-Baz> distribution that satisfies
+requirements for the feature.
+
+=head3 prereqs
+
+Example:
+
+  prereqs => {
+    runtime => {
+      requires => {
+        'perl'          => '5.006',
+        'File::Spec'    => '0.86',
+        'JSON'          => '2.16',
+      },
+      recommends => {
+        'JSON::XS'      => '2.26',
+      },
+      suggests => {
+        'Archive::Tar'  => '0',
+      },
+    },
+    build => {
+      requires => {
+        'Alien::SDL'    => '1.00',
+      },
+    },
+    test => {
+      recommends => {
+        'Test::Deep'    => '0.10',
+      },
+    }
+  }
+
+(Spec 2) [optional] {Map}
+
+This is a Map that describes all the prerequisites of the distribution.
+The keys are phases of activity, such as C<configure>, C<build>, C<test>
+or C<runtime>.  Values are Maps in which the keys name the type of
+prerequisite relationship such as C<requires>, C<recommends>, or
+C<suggests> and the value provides a set of prerequisite relations.  The
+set of relations B<must> be specified as a Map of package names to
+version ranges.
+
+The full definition for this field is given in the L</Prereq Spec>
+section.
+
+=head3 provides
+
+Example:
+
+  provides => {
+    'Foo::Bar' => {
+      file    => 'lib/Foo/Bar.pm',
+      version => 0.27_02
+    },
+    'Foo::Bar::Blah' => {
+      file    => 'lib/Foo/Bar/Blah.pm',
+    },
+    'Foo::Bar::Baz' => {
+      file    => 'lib/Foo/Bar/Baz.pm',
+      version => 0.3,
+    },
+  }
+
+(Spec 1.2) [optional] {Map}
+
+This describes all packages provided by this distribution.  This
+information is used by distribution and automation mechanisms like
+PAUSE, CPAN, and search.cpan.org to build indexes saying in which
+distribution various packages can be found.
+
+The keys of C<provides> are package names that can be found within
+the distribution.  The values are Maps with the following valid subkeys:
+
+=over
+
+=item file
+
+This field is required.  The value must contain a relative file path
+from the root of the distribution to the module containing the package.
+
+=item version
+
+This field contains a I<Version> String for the package, if one exists.
+
+=back
+
+=head3 resources
+
+Example:
+
+  resources => {
+    license     => [ 'http://dev.perl.org/licenses/' ],
+    homepage    => 'http://sourceforge.net/projects/module-build',
+    bugtracker  => {
+      web    => 'http://github.com/dagolden/cpan-meta-spec/issues',
+      mailto => 'meta-bugs@example.com',
+    },
+    repository  => {
+      url  => 'git://github.com/dagolden/cpan-meta-spec.git',
+      web  => 'http://github.com/dagolden/cpan-meta-spec',
+      type => 'git',
+    },
+    x_twitter   => 'http://twitter.com/cpan_linked/',
+  }
+
+(Spec 2) [optional] {Map}
+
+This field describes resources related to this distribution.
+
+Valid subkeys include:
+
+=over
+
+=item homepage
+
+The official home of this project on the web.
+
+=item license
+
+A List of I<URL>'s that relate to this distribution's license.  As with the
+top-level C<license> field, distribution documentation should be consulted
+to clarify the interpretation of multiple licenses provided here.
+
+=item bugtracker
+
+This entry describes the bug tracking system for this distribution.  It
+is a Map with the following valid keys:
+
+  web    - a URL pointing to a web front-end for the bug tracker
+  mailto - an email address to which bugs can be sent
+
+=item repository
+
+This entry describes the source control repository for this distribution.  It
+is a Map with the following valid keys:
+
+  url  - a URL pointing to the repository itself
+  web  - a URL pointing to a web front-end for the repository
+  type - a lowercase string indicating the VCS used
+
+Because a url like C<http://myrepo.example.com/> is ambiguous as to
+type, producers should provide a C<type> whenever a C<url> key is given.
+The C<type> field should be the name of the most common program used
+to work with the repository, e.g. git, svn, cvs, darcs, bzr or hg.
+
+=back
+
+=head2 DEPRECATED FIELDS
+
+=head3 build_requires
+
+I<(Deprecated in Spec 2)> [optional] {String}
+
+Replaced by C<prereqs>
+
+=head3 configure_requires
+
+I<(Deprecated in Spec 2)> [optional] {String}
+
+Replaced by C<prereqs>
+
+=head3 conflicts
+
+I<(Deprecated in Spec 2)> [optional] {String}
+
+Replaced by C<prereqs>
+
+=head3 distribution_type
+
+I<(Deprecated in Spec 2)> [optional] {String}
+
+This field indicated 'module' or 'script' but was considered
+meaningless, since many distributions are hybrids of several kinds of
+things.
+
+=head3 license_uri
+
+I<(Deprecated in Spec 1.2)> [optional] {URL}
+
+Replaced by C<license> in C<resources>
+
+=head3 private
+
+I<(Deprecated in Spec 1.2)> [optional] {Map}
+
+This field has been renamed to L</"no_index">.
+
+=head3 recommends
+
+I<(Deprecated in Spec 2)> [optional] {String}
+
+Replaced by C<prereqs>
+
+=head3 requires
+
+I<(Deprecated in Spec 2)> [optional] {String}
+
+Replaced by C<prereqs>
+
+=head1 VERSION NUMBERS
+
+=head2 Version Formats
+
+This section defines the Version type, used by several fields in the
+CPAN Meta Spec.
+
+Version numbers must be treated as strings, not numbers.  For
+example, C<1.200> B<must not> be serialized as C<1.2>.  Version
+comparison should be delegated to the Perl L<version> module, version
+0.80 or newer.
+
+Unless otherwise specified, version numbers B<must> appear in one of two
+formats:
+
+=over
+
+=item Decimal versions
+
+Decimal versions are regular "decimal numbers", with some limitations.
+They B<must> be non-negative and B<must> begin and end with a digit.  A
+single underscore B<may> be included, but B<must> be between two digits.
+They B<must not> use exponential notation ("1.23e-2").
+
+   version => '1.234'       # OK
+   version => '1.23_04'     # OK
+
+   version => '1.23_04_05'  # Illegal
+   version => '1.'          # Illegal
+   version => '.1'          # Illegal
+
+=item Dotted-integer versions
+
+Dotted-integer (also known as dotted-decimal) versions consist of
+positive integers separated by full stop characters (i.e. "dots",
+"periods" or "decimal points").  This are equivalent in format to Perl
+"v-strings", with some additional restrictions on form.  They must be
+given in "normal" form, which has a leading "v" character and at least
+three integer components.  To retain a one-to-one mapping with decimal
+versions, all components after the first B<should> be restricted to the
+range 0 to 999.  The final component B<may> be separated by an
+underscore character instead of a period.
+
+   version => 'v1.2.3'      # OK
+   version => 'v1.2_3'      # OK
+   version => 'v1.2.3.4'    # OK
+   version => 'v1.2.3_4'    # OK
+   version => 'v2009.10.31' # OK
+
+   version => 'v1.2'          # Illegal
+   version => '1.2.3'         # Illegal
+   version => 'v1.2_3_4'      # Illegal
+   version => 'v1.2009.10.31' # Not recommended
+
+=back
+
+=head2 Version Ranges
+
+Some fields (prereq, optional_features) indicate the particular
+version(s) of some other module that may be required as a prerequisite.
+This section details the Version Range type used to provide this
+information.
+
+The simplest format for a Version Range is just the version
+number itself, e.g. C<2.4>.  This means that B<at least> version 2.4
+must be present.  To indicate that B<any> version of a prerequisite is
+okay, even if the prerequisite doesn't define a version at all, use
+the version C<0>.
+
+Alternatively, a version range B<may> use the operators E<lt> (less than),
+E<lt>= (less than or equal), E<gt> (greater than), E<gt>= (greater than
+or equal), == (equal), and != (not equal).  For example, the
+specification C<E<lt> 2.0> means that any version of the prerequisite
+less than 2.0 is suitable.
+
+For more complicated situations, version specifications B<may> be AND-ed
+together using commas.  The specification C<E<gt>= 1.2, != 1.5, E<lt>
+2.0> indicates a version that must be B<at least> 1.2, B<less than> 2.0,
+and B<not equal to> 1.5.
+
+=head1 PREREQUISITES
+
+=head2 Prereq Spec
+
+The C<prereqs> key in the top-level metadata and within
+C<optional_features> define the relationship between a distribution and
+other packages.  The prereq spec structure is a hierarchical data
+structure which divides prerequisites into I<Phases> of activity in the
+installation process and I<Relationships> that indicate how
+prerequisites should be resolved.
+
+For example, to specify that C<Data::Dumper> is C<required> during the
+C<test> phase, this entry would appear in the distribution metadata:
+
+  prereqs => {
+    test => {
+      requires => {
+        'Data::Dumper' => '2.00'
+      }
+    }
+  }
+
+=head3 Phases
+
+Requirements for regular use must be listed in the C<runtime> phase.
+Other requirements should be listed in the earliest stage in which they
+are required and consumers must accumulate and satisfy requirements
+across phases before executing the activity. For example, C<build>
+requirements must also be available during the C<test> phase.
+
+  before action       requirements that must be met
+  ----------------    --------------------------------
+  perl Build.PL       configure
+  perl Makefile.PL
+
+  make                configure, runtime, build
+  Build
+
+  make test           configure, runtime, build, test
+  Build test
+
+Consumers that install the distribution must ensure that
+I<runtime> requirements are also installed and may install
+dependencies from other phases.
+
+  after action        requirements that must be met
+  ----------------    --------------------------------
+  make install        runtime
+  Build install
+
+=over
+
+=item configure
+
+The configure phase occurs before any dynamic configuration has been
+attempted.  Libraries required by the configure phase B<must> be
+available for use before the distribution building tool has been
+executed.
+
+=item build
+
+The build phase is when the distribution's source code is compiled (if
+necessary) and otherwise made ready for installation.
+
+=item test
+
+The test phase is when the distribution's automated test suite is run.
+Any library that is needed only for testing and not for subsequent use
+should be listed here.
+
+=item runtime
+
+The runtime phase refers not only to when the distribution's contents
+are installed, but also to its continued use.  Any library that is a
+prerequisite for regular use of this distribution should be indicated
+here.
+
+=item develop
+
+The develop phase's prereqs are libraries needed to work on the
+distribution's source code as its author does.  These tools might be
+needed to build a release tarball, to run author-only tests, or to
+perform other tasks related to developing new versions of the
+distribution.
+
+=back
+
+=head3 Relationships
+
+=over
+
+=item requires
+
+These dependencies B<must> be installed for proper completion of the
+phase.
+
+=item recommends
+
+Recommended dependencies are I<strongly> encouraged and should be
+satisfied except in resource constrained environments.
+
+=item suggests
+
+These dependencies are optional, but are suggested for enhanced operation
+of the described distribution.
+
+=item conflicts
+
+These libraries cannot be installed when the phase is in operation.
+This is a very rare situation, and the C<conflicts> relationship should
+be used with great caution, or not at all.
+
+=back
+
+=head2 Merging and Resolving Prerequisites
+
+Whenever metadata consumers merge prerequisites, either from different
+phases or from C<optional_features>, they should merged in a way which
+preserves the intended semantics of the prerequisite structure.  Generally,
+this means concatenating the version specifications using commas, as
+described in the L<Version Ranges> section.
+
+Another subtle error that can occur in resolving prerequisites comes from
+the way that modules in prerequisites are indexed to distribution files on
+CPAN.  When a module is deleted from a distribution, prerequisites calling
+for that module could indicate an older distribution should installed,
+potentially overwriting files from a newer distribution.
+
+For example, as of Oct 31, 2009, the CPAN index file contained these
+module-distribution mappings:
+
+  Class::MOP                   0.94  D/DR/DROLSKY/Class-MOP-0.94.tar.gz
+  Class::MOP::Class            0.94  D/DR/DROLSKY/Class-MOP-0.94.tar.gz
+  Class::MOP::Class::Immutable 0.04  S/ST/STEVAN/Class-MOP-0.36.tar.gz
+
+Consider the case where "Class::MOP" 0.94 is installed.  If a
+distribution specified "Class::MOP::Class::Immutable" as a prerequisite,
+it could result in Class-MOP-0.36.tar.gz being installed, overwriting
+any files from Class-MOP-0.94.tar.gz.
+
+Consumers of metadata B<should> test whether prerequisites would result
+in installed module files being "downgraded" to an older version and
+B<may> warn users or ignore the prerequisite that would cause such a
+result.
+
+=head1 SERIALIZATION
+
+Distribution metadata should be serialized (as a hashref) as
+JSON-encoded data and packaged with distributions as the file
+F<META.json>.
+
+In the past, the distribution metadata structure had been packed with
+distributions as F<META.yml>, a file in the YAML Tiny format (for which,
+see L<YAML::Tiny>).  Tools that consume distribution metadata from disk
+should be capable of loading F<META.yml>, but should prefer F<META.json>
+if both are found.
+
+=head1 NOTES FOR IMPLEMENTORS
+
+=head2 Extracting Version Numbers from Perl Modules
+
+To get the version number from a Perl module, consumers should use the
+C<< MM->parse_version($file) >> method provided by L<ExtUtils::MakeMaker> or
+the L<Module::Build::ModuleInfo> module provided with L<Module::Build>.  For
+example, for the module given by C<$mod>, the version may be retrieved in one
+of the following ways:
+
+  # via ExtUtils::MakeMaker
+  my $file = MM->_installed_file_for_module($mod);
+  my $version = MM->parse_version($file)
+
+The private C<_installed_file_for_module> method may be replaced with
+other methods for locating a module in C<@INC>.
+
+  # via Module::Build
+  my $info = Module::Build::ModuleInfo->new_from_module($mod);
+  my $version = $info->version;
+
+If only a filename is available, the following approach may be used:
+
+  # via Module::Build
+  my $info = Module::Build::ModuleInfo->new_from_file($file);
+  my $version = $info->version;
+
+=head2 Comparing Version Numbers
+
+The L<version> module provides the most reliable way to compare version
+numbers in all the various ways they might be provided or might exist
+within modules.  Given two strings containing version numbers, C<$v1> and
+C<$v2>, they should be converted to C<version> objects before using
+ordinary comparison operators.  For example:
+
+  use version;
+  if ( version->new($v1) <=> version->new($v2) ) {
+    print "Versions are not equal\n";
+  }
+
+If the only comparison needed is whether an installed module is of a
+sufficiently high version, a direct test may be done using the string
+form of C<eval> and the C<use> function.  For example, for module C<$mod>
+and version prerequisite C<$prereq>:
+
+  if ( eval "use $mod $prereq (); 1" ) {
+    print "Module $mod version is OK.\n";
+  }
+
+If the values of C<$mod> and C<$prereq> have not been scrubbed, however,
+this presents security implications.
+
+=head1 SEE ALSO
+
+CPAN, L<http://www.cpan.org/>
+
+CPAN.pm, L<http://search.cpan.org/dist/CPAN/>
+
+CPANPLUS, L<http://search.cpan.org/dist/CPANPLUS/>
+
+ExtUtils::MakeMaker, L<http://search.cpan.org/dist/ExtUtils-MakeMaker/>
+
+Module::Build, L<http://search.cpan.org/dist/Module-Build/>
+
+Module::Install, L<http://search.cpan.org/dist/Module-Install/>
+
+JSON, L<http://json.org/>
+
+YAML, L<http://www.yaml.org/>
+
+=head1 CONTRIBUTORS
+
+Ken Williams wrote the original CPAN Meta Spec (also known as the
+"META.yml spec") in 2003 and maintained it through several revisions
+with input from various members of the community.  In 2005, Randy
+Sims redrafted it from HTML to POD for the version 1.2 release.  Ken
+continued to maintain the spec through version 1.4.
+
+In late 2009, David Golden organized the version 2 proposal review
+process.  David and Ricardo Signes drafted the final version 2 spec
+in April 2010 based on the version 1.4 spec and patches contributed
+during the proposal process.
+
+Several others have contributed patches over the years.  The full list
+of contributors in the repository history currently includes:
+
+  2shortplanks
+  Avar Arnfjord Bjarmason
+  Christopher J. Madsen
+  Damyan Ivanov
+  David Golden
+  Eric Wilhelm
+  Ken Williams
+  Lars DIECKOW
+  Michael G. Schwern
+  Randy Sims
+  Ricardo Signes
+
+=head1 AUTHORS
+
+=over 4
+
+=item *
+
+David Golden <dagolden@cpan.org>
+
+=item *
+
+Ricardo Signes <rjbs@cpan.org>
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2010 by David Golden and Ricardo Signes.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
+
diff --git a/cpan/CPAN-Meta/lib/CPAN/Meta/Validator.pm b/cpan/CPAN-Meta/lib/CPAN/Meta/Validator.pm
new file mode 100644 (file)
index 0000000..a203621
--- /dev/null
@@ -0,0 +1,1002 @@
+use 5.006;
+use strict;
+use warnings;
+package CPAN::Meta::Validator;
+BEGIN {
+  $CPAN::Meta::Validator::VERSION = '2.110440';
+}
+# ABSTRACT: validate CPAN distribution metadata structures
+
+
+#--------------------------------------------------------------------------#
+# This code copied and adapted from Test::CPAN::Meta
+# by Barbie, <barbie@cpan.org> for Miss Barbell Productions,
+# L<http://www.missbarbell.co.uk>
+#--------------------------------------------------------------------------#
+
+#--------------------------------------------------------------------------#
+# Specification Definitions
+#--------------------------------------------------------------------------#
+
+my %known_specs = (
+    '1.4' => 'http://module-build.sourceforge.net/META-spec-v1.4.html',
+    '1.3' => 'http://module-build.sourceforge.net/META-spec-v1.3.html',
+    '1.2' => 'http://module-build.sourceforge.net/META-spec-v1.2.html',
+    '1.1' => 'http://module-build.sourceforge.net/META-spec-v1.1.html',
+    '1.0' => 'http://module-build.sourceforge.net/META-spec-v1.0.html'
+);
+my %known_urls = map {$known_specs{$_} => $_} keys %known_specs;
+
+my $module_map1 = { 'map' => { ':key' => { name => \&module, value => \&exversion } } };
+
+my $module_map2 = { 'map' => { ':key' => { name => \&module, value => \&version   } } };
+
+my $no_index_2 = {
+    'map'       => { file       => { list => { value => \&string } },
+                     directory  => { list => { value => \&string } },
+                     'package'  => { list => { value => \&string } },
+                     namespace  => { list => { value => \&string } },
+                    ':key'      => { name => \&custom_2, value => \&anything },
+    }
+};
+
+my $no_index_1_3 = {
+    'map'       => { file       => { list => { value => \&string } },
+                     directory  => { list => { value => \&string } },
+                     'package'  => { list => { value => \&string } },
+                     namespace  => { list => { value => \&string } },
+                     ':key'     => { name => \&string, value => \&anything },
+    }
+};
+
+my $no_index_1_2 = {
+    'map'       => { file       => { list => { value => \&string } },
+                     dir        => { list => { value => \&string } },
+                     'package'  => { list => { value => \&string } },
+                     namespace  => { list => { value => \&string } },
+                     ':key'     => { name => \&string, value => \&anything },
+    }
+};
+
+my $no_index_1_1 = {
+    'map'       => { ':key'     => { name => \&string, list => { value => \&string } },
+    }
+};
+
+my $prereq_map = {
+  map => {
+    ':key' => {
+      name => \&phase,
+      'map' => {
+        ':key'  => {
+          name => \&relation,
+          %$module_map1,
+        },
+      },
+    }
+  },
+};
+
+my %definitions = (
+  '2' => {
+    # REQUIRED
+    'abstract'            => { mandatory => 1, value => \&string  },
+    'author'              => { mandatory => 1, lazylist => { value => \&string } },
+    'dynamic_config'      => { mandatory => 1, value => \&boolean },
+    'generated_by'        => { mandatory => 1, value => \&string  },
+    'license'             => { mandatory => 1, lazylist => { value => \&license } },
+    'meta-spec' => {
+      mandatory => 1,
+      'map' => {
+        version => { mandatory => 1, value => \&version},
+        url     => { value => \&url },
+        ':key' => { name => \&custom_2, value => \&anything },
+      }
+    },
+    'name'                => { mandatory => 1, value => \&string  },
+    'release_status'      => { mandatory => 1, value => \&release_status },
+    'version'             => { mandatory => 1, value => \&version },
+
+    # OPTIONAL
+    'description' => { value => \&string },
+    'keywords'    => { lazylist => { value => \&string } },
+    'no_index'    => $no_index_2,
+    'optional_features'   => {
+      'map'       => {
+        ':key'  => {
+          name => \&string,
+          'map'   => {
+            description        => { value => \&string },
+            prereqs => $prereq_map,
+            ':key' => { name => \&custom_2, value => \&anything },
+          }
+        }
+      }
+    },
+    'prereqs' => $prereq_map,
+    'provides'    => {
+      'map'       => {
+        ':key' => {
+          name  => \&module,
+          'map' => {
+            file    => { mandatory => 1, value => \&file },
+            version => { value => \&version },
+            ':key' => { name => \&custom_2, value => \&anything },
+          }
+        }
+      }
+    },
+    'resources'   => {
+      'map'       => {
+        license    => { lazylist => { value => \&url } },
+        homepage   => { value => \&url },
+        bugtracker => {
+          'map' => {
+            web => { value => \&url },
+            mailto => { value => \&string},
+            ':key' => { name => \&custom_2, value => \&anything },
+          }
+        },
+        repository => {
+          'map' => {
+            web => { value => \&url },
+            url => { value => \&url },
+            type => { value => \&string },
+            ':key' => { name => \&custom_2, value => \&anything },
+          }
+        },
+        ':key'     => { value => \&string, name => \&custom_2 },
+      }
+    },
+
+    # CUSTOM -- additional user defined key/value pairs
+    # note we can only validate the key name, as the structure is user defined
+    ':key'        => { name => \&custom_2, value => \&anything },
+  },
+
+'1.4' => {
+  'meta-spec'           => {
+    mandatory => 1,
+    'map' => {
+      version => { mandatory => 1, value => \&version},
+      url     => { mandatory => 1, value => \&urlspec },
+      ':key'  => { name => \&string, value => \&anything },
+    },
+  },
+
+  'name'                => { mandatory => 1, value => \&string  },
+  'version'             => { mandatory => 1, value => \&version },
+  'abstract'            => { mandatory => 1, value => \&string  },
+  'author'              => { mandatory => 1, list  => { value => \&string } },
+  'license'             => { mandatory => 1, value => \&license },
+  'generated_by'        => { mandatory => 1, value => \&string  },
+
+  'distribution_type'   => { value => \&string  },
+  'dynamic_config'      => { value => \&boolean },
+
+  'requires'            => $module_map1,
+  'recommends'          => $module_map1,
+  'build_requires'      => $module_map1,
+  'configure_requires'  => $module_map1,
+  'conflicts'           => $module_map2,
+
+  'optional_features'   => {
+    'map'       => {
+        ':key'  => { name => \&string,
+            'map'   => { description        => { value => \&string },
+                         requires           => $module_map1,
+                         recommends         => $module_map1,
+                         build_requires     => $module_map1,
+                         conflicts          => $module_map2,
+                         ':key'  => { name => \&string, value => \&anything },
+            }
+        }
+     }
+  },
+
+  'provides'    => {
+    'map'       => {
+      ':key' => { name  => \&module,
+        'map' => {
+          file    => { mandatory => 1, value => \&file },
+          version => { value => \&version },
+          ':key'  => { name => \&string, value => \&anything },
+        }
+      }
+    }
+  },
+
+  'no_index'    => $no_index_1_3,
+  'private'     => $no_index_1_3,
+
+  'keywords'    => { list => { value => \&string } },
+
+  'resources'   => {
+    'map'       => { license    => { value => \&url },
+                     homepage   => { value => \&url },
+                     bugtracker => { value => \&url },
+                     repository => { value => \&url },
+                     ':key'     => { value => \&string, name => \&custom_1 },
+    }
+  },
+
+  # additional user defined key/value pairs
+  # note we can only validate the key name, as the structure is user defined
+  ':key'        => { name => \&string, value => \&anything },
+},
+
+'1.3' => {
+  'meta-spec'           => {
+    mandatory => 1,
+    'map' => {
+      version => { mandatory => 1, value => \&version},
+      url     => { mandatory => 1, value => \&urlspec },
+      ':key'  => { name => \&string, value => \&anything },
+    },
+  },
+
+  'name'                => { mandatory => 1, value => \&string  },
+  'version'             => { mandatory => 1, value => \&version },
+  'abstract'            => { mandatory => 1, value => \&string  },
+  'author'              => { mandatory => 1, list  => { value => \&string } },
+  'license'             => { mandatory => 1, value => \&license },
+  'generated_by'        => { mandatory => 1, value => \&string  },
+
+  'distribution_type'   => { value => \&string  },
+  'dynamic_config'      => { value => \&boolean },
+
+  'requires'            => $module_map1,
+  'recommends'          => $module_map1,
+  'build_requires'      => $module_map1,
+  'conflicts'           => $module_map2,
+
+  'optional_features'   => {
+    'map'       => {
+        ':key'  => { name => \&string,
+            'map'   => { description        => { value => \&string },
+                         requires           => $module_map1,
+                         recommends         => $module_map1,
+                         build_requires     => $module_map1,
+                         conflicts          => $module_map2,
+                         ':key'  => { name => \&string, value => \&anything },
+            }
+        }
+     }
+  },
+
+  'provides'    => {
+    'map'       => {
+      ':key' => { name  => \&module,
+        'map' => {
+          file    => { mandatory => 1, value => \&file },
+          version => { value => \&version },
+          ':key'  => { name => \&string, value => \&anything },
+        }
+      }
+    }
+  },
+
+
+  'no_index'    => $no_index_1_3,
+  'private'     => $no_index_1_3,
+
+  'keywords'    => { list => { value => \&string } },
+
+  'resources'   => {
+    'map'       => { license    => { value => \&url },
+                     homepage   => { value => \&url },
+                     bugtracker => { value => \&url },
+                     repository => { value => \&url },
+                     ':key'     => { value => \&string, name => \&custom_1 },
+    }
+  },
+
+  # additional user defined key/value pairs
+  # note we can only validate the key name, as the structure is user defined
+  ':key'        => { name => \&string, value => \&anything },
+},
+
+# v1.2 is misleading, it seems to assume that a number of fields where created
+# within v1.1, when they were created within v1.2. This may have been an
+# original mistake, and that a v1.1 was retro fitted into the timeline, when
+# v1.2 was originally slated as v1.1. But I could be wrong ;)
+'1.2' => {
+  'meta-spec'           => {
+    mandatory => 1,
+    'map' => {
+      version => { mandatory => 1, value => \&version},
+      url     => { mandatory => 1, value => \&urlspec },
+      ':key'  => { name => \&string, value => \&anything },
+    },
+  },
+
+
+  'name'                => { mandatory => 1, value => \&string  },
+  'version'             => { mandatory => 1, value => \&version },
+  'license'             => { mandatory => 1, value => \&license },
+  'generated_by'        => { mandatory => 1, value => \&string  },
+  'author'              => { mandatory => 1, list => { value => \&string } },
+  'abstract'            => { mandatory => 1, value => \&string  },
+
+  'distribution_type'   => { value => \&string  },
+  'dynamic_config'      => { value => \&boolean },
+
+  'keywords'            => { list => { value => \&string } },
+
+  'private'             => $no_index_1_2,
+  '$no_index'           => $no_index_1_2,
+
+  'requires'            => $module_map1,
+  'recommends'          => $module_map1,
+  'build_requires'      => $module_map1,
+  'conflicts'           => $module_map2,
+
+  'optional_features'   => {
+    'map'       => {
+        ':key'  => { name => \&string,
+            'map'   => { description        => { value => \&string },
+                         requires           => $module_map1,
+                         recommends         => $module_map1,
+                         build_requires     => $module_map1,
+                         conflicts          => $module_map2,
+                         ':key'  => { name => \&string, value => \&anything },
+            }
+        }
+     }
+  },
+
+  'provides'    => {
+    'map'       => {
+      ':key' => { name  => \&module,
+        'map' => {
+          file    => { mandatory => 1, value => \&file },
+          version => { value => \&version },
+          ':key'  => { name => \&string, value => \&anything },
+        }
+      }
+    }
+  },
+
+  'resources'   => {
+    'map'       => { license    => { value => \&url },
+                     homepage   => { value => \&url },
+                     bugtracker => { value => \&url },
+                     repository => { value => \&url },
+                     ':key'     => { value => \&string, name => \&custom_1 },
+    }
+  },
+
+  # additional user defined key/value pairs
+  # note we can only validate the key name, as the structure is user defined
+  ':key'        => { name => \&string, value => \&anything },
+},
+
+# note that the 1.1 spec only specifies 'version' as mandatory
+'1.1' => {
+  'name'                => { value => \&string  },
+  'version'             => { mandatory => 1, value => \&version },
+  'license'             => { value => \&license },
+  'generated_by'        => { value => \&string  },
+
+  'license_uri'         => { value => \&url },
+  'distribution_type'   => { value => \&string  },
+  'dynamic_config'      => { value => \&boolean },
+
+  'private'             => $no_index_1_1,
+
+  'requires'            => $module_map1,
+  'recommends'          => $module_map1,
+  'build_requires'      => $module_map1,
+  'conflicts'           => $module_map2,
+
+  # additional user defined key/value pairs
+  # note we can only validate the key name, as the structure is user defined
+  ':key'        => { name => \&string, value => \&anything },
+},
+
+# note that the 1.0 spec doesn't specify optional or mandatory fields
+# but we will treat version as mandatory since otherwise META 1.0 is
+# completely arbitrary and pointless
+'1.0' => {
+  'name'                => { value => \&string  },
+  'version'             => { mandatory => 1, value => \&version },
+  'license'             => { value => \&license },
+  'generated_by'        => { value => \&string  },
+
+  'license_uri'         => { value => \&url },
+  'distribution_type'   => { value => \&string  },
+  'dynamic_config'      => { value => \&boolean },
+
+  'requires'            => $module_map1,
+  'recommends'          => $module_map1,
+  'build_requires'      => $module_map1,
+  'conflicts'           => $module_map2,
+
+  # additional user defined key/value pairs
+  # note we can only validate the key name, as the structure is user defined
+  ':key'        => { name => \&string, value => \&anything },
+},
+);
+
+#--------------------------------------------------------------------------#
+# Code
+#--------------------------------------------------------------------------#
+
+
+sub new {
+  my ($class,$data) = @_;
+
+  # create an attributes hash
+  my $self = {
+    'data'    => $data,
+    'spec'    => $data->{'meta-spec'}{'version'} || "1.0",
+    'errors'  => undef,
+  };
+
+  # create the object
+  return bless $self, $class;
+}
+
+
+sub is_valid {
+    my $self = shift;
+    my $data = $self->{data};
+    my $spec_version = $self->{spec};
+    $self->check_map($definitions{$spec_version},$data);
+    return ! $self->errors;
+}
+
+
+sub errors {
+    my $self = shift;
+    return ()   unless(defined $self->{errors});
+    return @{$self->{errors}};
+}
+
+
+my $spec_error = "Missing validation action in specification. "
+  . "Must be one of 'map', 'list', 'lazylist', or 'value'";
+
+sub check_map {
+    my ($self,$spec,$data) = @_;
+
+    if(ref($spec) ne 'HASH') {
+        $self->_error( "Unknown META specification, cannot validate." );
+        return;
+    }
+
+    if(ref($data) ne 'HASH') {
+        $self->_error( "Expected a map structure from string or file." );
+        return;
+    }
+
+    for my $key (keys %$spec) {
+        next    unless($spec->{$key}->{mandatory});
+        next    if(defined $data->{$key});
+        push @{$self->{stack}}, $key;
+        $self->_error( "Missing mandatory field, '$key'" );
+        pop @{$self->{stack}};
+    }
+
+    for my $key (keys %$data) {
+        push @{$self->{stack}}, $key;
+        if($spec->{$key}) {
+            if($spec->{$key}{value}) {
+                $spec->{$key}{value}->($self,$key,$data->{$key});
+            } elsif($spec->{$key}{'map'}) {
+                $self->check_map($spec->{$key}{'map'},$data->{$key});
+            } elsif($spec->{$key}{'list'}) {
+                $self->check_list($spec->{$key}{'list'},$data->{$key});
+            } elsif($spec->{$key}{'lazylist'}) {
+                $self->check_lazylist($spec->{$key}{'lazylist'},$data->{$key});
+            } else {
+                $self->_error( "$spec_error for '$key'" );
+            }
+
+        } elsif ($spec->{':key'}) {
+            $spec->{':key'}{name}->($self,$key,$key);
+            if($spec->{':key'}{value}) {
+                $spec->{':key'}{value}->($self,$key,$data->{$key});
+            } elsif($spec->{':key'}{'map'}) {
+                $self->check_map($spec->{':key'}{'map'},$data->{$key});
+            } elsif($spec->{':key'}{'list'}) {
+                $self->check_list($spec->{':key'}{'list'},$data->{$key});
+            } elsif($spec->{':key'}{'lazylist'}) {
+                $self->check_lazylist($spec->{':key'}{'lazylist'},$data->{$key});
+            } else {
+                $self->_error( "$spec_error for ':key'" );
+            }
+
+
+        } else {
+            $self->_error( "Unknown key, '$key', found in map structure" );
+        }
+        pop @{$self->{stack}};
+    }
+}
+
+# if it's a string, make it into a list and check the list
+sub check_lazylist {
+    my ($self,$spec,$data) = @_;
+
+    if ( defined $data && ! ref($data) ) {
+      $data = [ $data ];
+    }
+
+    $self->check_list($spec,$data);
+}
+
+sub check_list {
+    my ($self,$spec,$data) = @_;
+
+    if(ref($data) ne 'ARRAY') {
+        $self->_error( "Expected a list structure" );
+        return;
+    }
+
+    if(defined $spec->{mandatory}) {
+        if(!defined $data->[0]) {
+            $self->_error( "Missing entries from mandatory list" );
+        }
+    }
+
+    for my $value (@$data) {
+        push @{$self->{stack}}, $value || "<undef>";
+        if(defined $spec->{value}) {
+            $spec->{value}->($self,'list',$value);
+        } elsif(defined $spec->{'map'}) {
+            $self->check_map($spec->{'map'},$value);
+        } elsif(defined $spec->{'list'}) {
+            $self->check_list($spec->{'list'},$value);
+        } elsif(defined $spec->{'lazylist'}) {
+            $self->check_lazylist($spec->{'lazylist'},$value);
+        } elsif ($spec->{':key'}) {
+            $self->check_map($spec,$value);
+        } else {
+          $self->_error( "$spec_error associated with '$self->{stack}[-2]'" );
+        }
+        pop @{$self->{stack}};
+    }
+}
+
+
+sub header {
+    my ($self,$key,$value) = @_;
+    if(defined $value) {
+        return 1    if($value && $value =~ /^--- #YAML:1.0/);
+    }
+    $self->_error( "file does not have a valid YAML header." );
+    return 0;
+}
+
+sub release_status {
+  my ($self,$key,$value) = @_;
+  if(defined $value) {
+    my $version = $self->{data}{version} || '';
+    if ( $version =~ /_/ ) {
+      return 1 if ( $value =~ /\A(?:testing|unstable)\z/ );
+      $self->_error( "'$value' for '$key' is invalid for version '$version'" );
+    }
+    else {
+      return 1 if ( $value =~ /\A(?:stable|testing|unstable)\z/ );
+      $self->_error( "'$value' for '$key' is invalid" );
+    }
+  }
+  else {
+    $self->_error( "'$key' is not defined" );
+  }
+  return 0;
+}
+
+# _uri_split taken from URI::Split by Gisle Aas, Copyright 2003
+sub _uri_split {
+     return $_[0] =~ m,(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?,;
+}
+
+sub url {
+    my ($self,$key,$value) = @_;
+    if(defined $value) {
+      my ($scheme, $auth, $path, $query, $frag) = _uri_split($value);
+      unless ( defined $scheme && length $scheme ) {
+        $self->_error( "'$value' for '$key' does not have a URL scheme" );
+        return 0;
+      }
+      unless ( defined $auth && length $auth ) {
+        $self->_error( "'$value' for '$key' does not have a URL authority" );
+        return 0;
+      }
+      return 1;
+    }
+    $value ||= '';
+    $self->_error( "'$value' for '$key' is not a valid URL." );
+    return 0;
+}
+
+sub urlspec {
+    my ($self,$key,$value) = @_;
+    if(defined $value) {
+        return 1    if($value && $known_specs{$self->{spec}} eq $value);
+        if($value && $known_urls{$value}) {
+            $self->_error( 'META specification URL does not match version' );
+            return 0;
+        }
+    }
+    $self->_error( 'Unknown META specification' );
+    return 0;
+}
+
+sub anything { return 1 }
+
+sub string {
+    my ($self,$key,$value) = @_;
+    if(defined $value) {
+        return 1    if($value || $value =~ /^0$/);
+    }
+    $self->_error( "value is an undefined string" );
+    return 0;
+}
+
+sub string_or_undef {
+    my ($self,$key,$value) = @_;
+    return 1    unless(defined $value);
+    return 1    if($value || $value =~ /^0$/);
+    $self->_error( "No string defined for '$key'" );
+    return 0;
+}
+
+sub file {
+    my ($self,$key,$value) = @_;
+    return 1    if(defined $value);
+    $self->_error( "No file defined for '$key'" );
+    return 0;
+}
+
+sub exversion {
+    my ($self,$key,$value) = @_;
+    if(defined $value && ($value || $value =~ /0/)) {
+        my $pass = 1;
+        for(split(",",$value)) { $self->version($key,$_) or ($pass = 0); }
+        return $pass;
+    }
+    $value = '<undef>'  unless(defined $value);
+    $self->_error( "'$value' for '$key' is not a valid version." );
+    return 0;
+}
+
+sub version {
+    my ($self,$key,$value) = @_;
+    if(defined $value) {
+        return 0    unless($value || $value =~ /0/);
+        return 1    if($value =~ /^\s*((<|<=|>=|>|!=|==)\s*)?v?\d+((\.\d+((_|\.)\d+)?)?)/);
+    } else {
+        $value = '<undef>';
+    }
+    $self->_error( "'$value' for '$key' is not a valid version." );
+    return 0;
+}
+
+sub boolean {
+    my ($self,$key,$value) = @_;
+    if(defined $value) {
+        return 1    if($value =~ /^(0|1|true|false)$/);
+    } else {
+        $value = '<undef>';
+    }
+    $self->_error( "'$value' for '$key' is not a boolean value." );
+    return 0;
+}
+
+my %v1_licenses = (
+    'perl'         => 'http://dev.perl.org/licenses/',
+    'gpl'          => 'http://www.opensource.org/licenses/gpl-license.php',
+    'apache'       => 'http://apache.org/licenses/LICENSE-2.0',
+    'artistic'     => 'http://opensource.org/licenses/artistic-license.php',
+    'artistic_2'   => 'http://opensource.org/licenses/artistic-license-2.0.php',
+    'lgpl'         => 'http://www.opensource.org/licenses/lgpl-license.phpt',
+    'bsd'          => 'http://www.opensource.org/licenses/bsd-license.php',
+    'gpl'          => 'http://www.opensource.org/licenses/gpl-license.php',
+    'mit'          => 'http://opensource.org/licenses/mit-license.php',
+    'mozilla'      => 'http://opensource.org/licenses/mozilla1.1.php',
+    'open_source'  => undef,
+    'unrestricted' => undef,
+    'restrictive'  => undef,
+    'unknown'      => undef,
+);
+
+my %v2_licenses = map { $_ => 1 } qw(
+  agpl_3
+  apache_1_1
+  apache_2_0
+  artistic_1
+  artistic_2
+  bsd
+  freebsd
+  gfdl_1_2
+  gfdl_1_3
+  gpl_1
+  gpl_2
+  gpl_3
+  lgpl_2_1
+  lgpl_3_0
+  mit
+  mozilla_1_0
+  mozilla_1_1
+  openssl
+  perl_5
+  qpl_1_0
+  ssleay
+  sun
+  zlib
+  open_source
+  restricted
+  unrestricted
+  unknown
+);
+
+sub license {
+    my ($self,$key,$value) = @_;
+    my $licenses = $self->{spec} < 2 ? \%v1_licenses : \%v2_licenses;
+    if(defined $value) {
+        return 1    if($value && exists $licenses->{$value});
+    } else {
+        $value = '<undef>';
+    }
+    $self->_error( "License '$value' is invalid" );
+    return 0;
+}
+
+sub custom_1 {
+    my ($self,$key) = @_;
+    if(defined $key) {
+        # a valid user defined key should be alphabetic
+        # and contain at least one capital case letter.
+        return 1    if($key && $key =~ /^[_a-z]+$/i && $key =~ /[A-Z]/);
+    } else {
+        $key = '<undef>';
+    }
+    $self->_error( "Custom resource '$key' must be in CamelCase." );
+    return 0;
+}
+
+sub custom_2 {
+    my ($self,$key) = @_;
+    if(defined $key) {
+        return 1    if($key && $key =~ /^x_/i);  # user defined
+    } else {
+        $key = '<undef>';
+    }
+    $self->_error( "Custom key '$key' must begin with 'x_' or 'X_'." );
+    return 0;
+}
+
+sub identifier {
+    my ($self,$key) = @_;
+    if(defined $key) {
+        return 1    if($key && $key =~ /^([a-z][_a-z]+)$/i);    # spec 2.0 defined
+    } else {
+        $key = '<undef>';
+    }
+    $self->_error( "Key '$key' is not a legal identifier." );
+    return 0;
+}
+
+sub module {
+    my ($self,$key) = @_;
+    if(defined $key) {
+        return 1    if($key && $key =~ /^[A-Za-z0-9_]+(::[A-Za-z0-9_]+)*$/);
+    } else {
+        $key = '<undef>';
+    }
+    $self->_error( "Key '$key' is not a legal module name." );
+    return 0;
+}
+
+my @valid_phases = qw/ configure build test runtime develop /;
+sub phase {
+    my ($self,$key) = @_;
+    if(defined $key) {
+        return 1 if( length $key && grep { $key eq $_ } @valid_phases );
+        return 1 if $key =~ /x_/i;
+    } else {
+        $key = '<undef>';
+    }
+    $self->_error( "Key '$key' is not a legal phase." );
+    return 0;
+}
+
+my @valid_relations = qw/ requires recommends suggests conflicts /;
+sub relation {
+    my ($self,$key) = @_;
+    if(defined $key) {
+        return 1 if( length $key && grep { $key eq $_ } @valid_relations );
+        return 1 if $key =~ /x_/i;
+    } else {
+        $key = '<undef>';
+    }
+    $self->_error( "Key '$key' is not a legal prereq relationship." );
+    return 0;
+}
+
+sub _error {
+    my $self = shift;
+    my $mess = shift;
+
+    $mess .= ' ('.join(' -> ',@{$self->{stack}}).')'  if($self->{stack});
+    $mess .= " [Validation: $self->{spec}]";
+
+    push @{$self->{errors}}, $mess;
+}
+
+1;
+
+
+
+=pod
+
+=head1 NAME
+
+CPAN::Meta::Validator - validate CPAN distribution metadata structures
+
+=head1 VERSION
+
+version 2.110440
+
+=head1 SYNOPSIS
+
+  my $struct = decode_json_file('META.json');
+
+  my $cmv = CPAN::Meta::Validator->new( $struct );
+
+  unless ( $cmv->is_valid ) {
+    my $msg = "Invalid META structure.  Errors found:\n";
+    $msg .= join( "\n", $cmv->errors );
+    die $msg;
+  }
+
+=head1 DESCRIPTION
+
+This module validates a CPAN Meta structure against the version of the
+the specification claimed in the C<meta-spec> field of the structure.
+
+=head1 METHODS
+
+=head2 new
+
+  my $cmv = CPAN::Meta::Validator->new( $struct )
+
+The constructor must be passed a metadata structure.
+
+=head2 is_valid
+
+  if ( $cmv->is_valid ) {
+    ...
+  }
+
+Returns a boolean value indicating whether the metadata provided
+is valid.
+
+=head2 errors
+
+  warn( join "\n", $cmv->errors );
+
+Returns a list of errors seen during validation.
+
+=begin internals
+
+=head2 Check Methods
+
+=over
+
+=item * check_map($spec,$data)
+
+Checks whether a map (or hash) part of the data structure conforms to the
+appropriate specification definition.
+=item * check_list($spec,$data)
+
+Checks whether a list (or array) part of the data structure conforms to
+the appropriate specification definition.
+=item * check_lazylist($spec,$data)
+
+Checks whether a list conforms, but converts strings to a single-element list
+=back
+
+=head2 Validator Methods
+
+=over
+
+=item * header($self,$key,$value)
+
+Validates that the header is valid.
+
+Note: No longer used as we now read the data structure, not the file.=item * url($self,$key,$value)
+
+Validates that a given value is in an acceptable URL format
+=item * urlspec($self,$key,$value)
+
+Validates that the URL to a META specification is a known one.
+=item * string_or_undef($self,$key,$value)
+
+Validates that the value is either a string or an undef value. Bit of a
+catchall function for parts of the data structure that are completely user
+defined.
+=item * string($self,$key,$value)
+
+Validates that a string exists for the given key.
+=item * file($self,$key,$value)
+
+Validate that a file is passed for the given key. This may be made more
+thorough in the future. For now it acts like \&string.
+=item * exversion($self,$key,$value)
+
+Validates a list of versions, e.g. '<= 5, >=2, ==3, !=4, >1, <6, 0'.
+=item * version($self,$key,$value)
+
+Validates a single version string. Versions of the type '5.8.8' and '0.00_00'
+are both valid. A leading 'v' like 'v1.2.3' is also valid.
+=item * boolean($self,$key,$value)
+
+Validates for a boolean value. Currently these values are '1', '0', 'true',
+'false', however the latter 2 may be removed.
+=item * license($self,$key,$value)
+
+Validates that a value is given for the license. Returns 1 if an known license
+type, or 2 if a value is given but the license type is not a recommended one.
+=item * custom_1($self,$key,$value)
+
+Validates that the given key is in CamelCase, to indicate a user defined
+keyword and only has characters in the class [-_a-zA-Z].  In version 1.X
+of the spec, this was only explicitly stated for 'resources'.
+=item * custom_2($self,$key,$value)
+
+Validates that the given key begins with 'x_' or 'X_', to indicate a user
+defined keyword and only has characters in the class [-_a-zA-Z]
+=item * identifier($self,$key,$value)
+
+Validates that key is in an acceptable format for the META specification,
+for an identifier, i.e. any that matches the regular expression
+qr/[a-z][a-z_]/i.
+=item * module($self,$key,$value)
+
+Validates that a given key is in an acceptable module name format, e.g.
+'Test::CPAN::Meta::Version'.
+=back
+
+=end internals
+
+=head1 BUGS
+
+Please report any bugs or feature using the CPAN Request Tracker.
+Bugs can be submitted through the web interface at
+L<http://rt.cpan.org/Dist/Display.html?Queue=CPAN-Meta>
+
+When submitting a bug or request, please include a test-file or a patch to an
+existing test-file that illustrates the bug or desired feature.
+
+=head1 AUTHORS
+
+=over 4
+
+=item *
+
+David Golden <dagolden@cpan.org>
+
+=item *
+
+Ricardo Signes <rjbs@cpan.org>
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2010 by David Golden and Ricardo Signes.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
+
+
+__END__
+
+
+
diff --git a/cpan/CPAN-Meta/t/converter-bad.t b/cpan/CPAN-Meta/t/converter-bad.t
new file mode 100644 (file)
index 0000000..1225e42
--- /dev/null
@@ -0,0 +1,71 @@
+use strict;
+use warnings;
+use Test::More 0.88;
+
+use CPAN::Meta;
+use CPAN::Meta::Validator;
+use CPAN::Meta::Converter;
+use File::Spec;
+use IO::Dir;
+use Parse::CPAN::Meta 1.4400;
+
+my $data_dir = IO::Dir->new( 't/data-bad' );
+my @files = sort grep { /^\w/ } $data_dir->read;
+
+sub _spec_version { return $_[0]->{'meta-spec'}{version} || "1.0" }
+
+#use Data::Dumper;
+
+for my $f ( reverse sort @files ) {
+  my $path = File::Spec->catfile('t','data-bad',$f);
+  my $original = Parse::CPAN::Meta->load_file( $path  );
+  ok( $original, "loaded $f" );
+  my $original_v = _spec_version($original);
+  # UPCONVERSION
+  if ( _spec_version( $original ) lt '2' ) {
+    my $cmc = CPAN::Meta::Converter->new( $original );
+    my $converted = $cmc->convert( version => 2 );
+    is ( _spec_version($converted), 2, "up converted spec version $original_v to spec version 2");
+    my $cmv = CPAN::Meta::Validator->new( $converted );
+    ok ( $cmv->is_valid, "up converted META is valid" )
+      or diag( "ERRORS:\n" . join( "\n", $cmv->errors )
+#      . "\nMETA:\n" . Dumper($converted)
+    );
+  }
+  # UPCONVERSION - partial
+  if ( _spec_version( $original ) lt '1.4' ) {
+    my $cmc = CPAN::Meta::Converter->new( $original );
+    my $converted = $cmc->convert( version => '1.4' );
+    is ( _spec_version($converted), 1.4, "up converted spec version $original_v to spec version 1.4");
+    my $cmv = CPAN::Meta::Validator->new( $converted );
+    ok ( $cmv->is_valid, "up converted META is valid" )
+      or diag( "ERRORS:\n" . join( "\n", $cmv->errors )
+#      . "\nMETA:\n" . Dumper($converted)
+    );
+  }
+  # DOWNCONVERSION - partial
+  if ( _spec_version( $original ) gt '1.2' ) {
+    my $cmc = CPAN::Meta::Converter->new( $original );
+    my $converted = $cmc->convert( version => '1.2' );
+    is ( _spec_version($converted), '1.2', "down converted spec version $original_v to spec version 1.2");
+    my $cmv = CPAN::Meta::Validator->new( $converted );
+    ok ( $cmv->is_valid, "down converted META is valid" )
+      or diag( "ERRORS:\n" . join( "\n", $cmv->errors )
+#      . "\nMETA:\n" . Dumper($converted)
+    );
+  }
+  # DOWNCONVERSION
+  if ( _spec_version( $original ) gt '1.0' ) {
+    my $cmc = CPAN::Meta::Converter->new( $original );
+    my $converted = $cmc->convert( version => '1.0' );
+    is ( _spec_version($converted), '1.0', "down converted spec version $original_v to spec version 1.0");
+    my $cmv = CPAN::Meta::Validator->new( $converted );
+    ok ( $cmv->is_valid, "down converted META is valid" )
+      or diag( "ERRORS:\n" . join( "\n", $cmv->errors )
+#      . "\nMETA:\n" . Dumper($converted)
+    );
+  }
+}
+
+done_testing;
+
diff --git a/cpan/CPAN-Meta/t/converter-fail.t b/cpan/CPAN-Meta/t/converter-fail.t
new file mode 100644 (file)
index 0000000..3a82f2d
--- /dev/null
@@ -0,0 +1,39 @@
+use strict;
+use warnings;
+use Test::More 0.88;
+
+use CPAN::Meta;
+use CPAN::Meta::Validator;
+use CPAN::Meta::Converter;
+use File::Spec;
+use IO::Dir;
+use Parse::CPAN::Meta 1.4400;
+
+my $data_dir = IO::Dir->new( 't/data-fail' );
+my @files = sort grep { /^\w/ } $data_dir->read;
+
+sub _spec_version { return $_[0]->{'meta-spec'}{version} || "1.0" }
+
+use Data::Dumper;
+
+for my $f ( reverse sort @files ) {
+  my $path = File::Spec->catfile('t','data-fail',$f);
+  my $original = Parse::CPAN::Meta->load_file( $path  );
+  ok( $original, "loaded invalid $f" );
+  my $original_v = _spec_version($original);
+  # UPCONVERSION
+  if ( _spec_version( $original ) lt '2' ) {
+    my $cmc = CPAN::Meta::Converter->new( $original );
+    eval { $cmc->convert( version => 2 ) };
+    ok ( $@, "error thrown up converting" );
+  }
+  # DOWNCONVERSION
+  if ( _spec_version( $original ) gt '1.0' ) {
+    my $cmc = CPAN::Meta::Converter->new( $original );
+    eval { $cmc->convert( version => '1.0' ) };
+    ok ( $@, "error thrown down converting" );
+  }
+}
+
+done_testing;
+
diff --git a/cpan/CPAN-Meta/t/converter.t b/cpan/CPAN-Meta/t/converter.t
new file mode 100644 (file)
index 0000000..53ea5ee
--- /dev/null
@@ -0,0 +1,139 @@
+use strict;
+use warnings;
+use Test::More 0.88;
+
+use CPAN::Meta;
+use CPAN::Meta::Validator;
+use CPAN::Meta::Converter;
+use File::Spec;
+use IO::Dir;
+use Parse::CPAN::Meta 1.4400;
+
+my $data_dir = IO::Dir->new( 't/data' );
+my @files = sort grep { /^\w/ } $data_dir->read;
+
+sub _spec_version { return $_[0]->{'meta-spec'}{version} || "1.0" }
+
+#use Data::Dumper;
+
+for my $f ( reverse sort @files ) {
+  my $path = File::Spec->catfile('t','data',$f);
+  my $original = Parse::CPAN::Meta->load_file( $path  );
+  ok( $original, "loaded $f" );
+  my $original_v = _spec_version($original);
+  # UPCONVERSION
+  {
+    my $cmc = CPAN::Meta::Converter->new( $original );
+    my $converted = $cmc->convert( version => 2 );
+    is ( _spec_version($converted), 2, "up converted spec version $original_v to spec version 2");
+    my $cmv = CPAN::Meta::Validator->new( $converted );
+    ok ( $cmv->is_valid, "up converted META is valid" )
+      or diag( "ERRORS:\n" . join( "\n", $cmv->errors )
+#      . "\nMETA:\n" . Dumper($converted)
+    );
+  }
+  # UPCONVERSION - partial
+  if ( _spec_version( $original ) < 2 ) {
+    my $cmc = CPAN::Meta::Converter->new( $original );
+    my $converted = $cmc->convert( version => '1.4' );
+    is ( _spec_version($converted), 1.4, "up converted spec version $original_v to spec version 1.4");
+    my $cmv = CPAN::Meta::Validator->new( $converted );
+    ok ( $cmv->is_valid, "up converted META is valid" )
+      or diag( "ERRORS:\n" . join( "\n", $cmv->errors )
+#      . "\nMETA:\n" . Dumper($converted)
+    );
+  }
+  # DOWNCONVERSION - partial
+  if ( _spec_version( $original ) >= 1.2 ) {
+    my $cmc = CPAN::Meta::Converter->new( $original );
+    my $converted = $cmc->convert( version => '1.2' );
+    is ( _spec_version($converted), '1.2', "down converted spec version $original_v to spec version 1.2");
+    my $cmv = CPAN::Meta::Validator->new( $converted );
+    ok ( $cmv->is_valid, "down converted META is valid" )
+      or diag( "ERRORS:\n" . join( "\n", $cmv->errors )
+#      . "\nMETA:\n" . Dumper($converted)
+    );
+
+    if (_spec_version( $original ) == 2) {
+      is_deeply(
+        $converted->{build_requires},
+        {
+          'Test::More'      => '0.88',
+          'Build::Requires' => '1.1',
+          'Test::Requires'  => '1.2',
+        },
+        "downconversion from 2 merge test and build requirements",
+      );
+    }
+  }
+  # DOWNCONVERSION
+  {
+    my $cmc = CPAN::Meta::Converter->new( $original );
+    my $converted = $cmc->convert( version => '1.0' );
+    is ( _spec_version($converted), '1.0', "down converted spec version $original_v to spec version 1.0");
+    my $cmv = CPAN::Meta::Validator->new( $converted );
+    ok ( $cmv->is_valid, "down converted META is valid" )
+      or diag( "ERRORS:\n" . join( "\n", $cmv->errors )
+#      . "\nMETA:\n" . Dumper($converted)
+    );
+
+    unless ($original_v eq '1.0') {
+      like ( $converted->{generated_by},
+        qr(\Q$original->{generated_by}\E, CPAN::Meta::Converter version \S+$),
+        "added converter mark to generated_by",
+      );
+    }
+  }
+}
+
+# specific test for custom key handling
+{
+  my $path = File::Spec->catfile('t','data','META-1_4.yml');
+  my $original = Parse::CPAN::Meta->load_file( $path  );
+  ok( $original, "loaded META-1_4.yml" );
+  my $cmc = CPAN::Meta::Converter->new( $original );
+  my $up_converted = $cmc->convert( version => 2 );
+  ok ( $up_converted->{x_whatever} && ! $up_converted->{'x-whatever'},
+    "up converted 'x-' to 'x_'"
+  );
+  ok ( $up_converted->{x_whatelse},
+    "up converted 'x_' as 'x_'"
+  );
+  ok ( $up_converted->{x_WhatNow} && ! $up_converted->{XWhatNow},
+    "up converted 'XFoo' to 'x_Foo'"
+  ) or diag join("\n", keys %$up_converted);
+}
+
+# specific test for custom key handling
+{
+  my $path = File::Spec->catfile('t','data','META-2.json');
+  my $original = Parse::CPAN::Meta->load_file( $path  );
+  ok( $original, "loaded META-2.json" );
+  my $cmc = CPAN::Meta::Converter->new( $original );
+  my $up_converted = $cmc->convert( version => 1.4 );
+  ok ( $up_converted->{x_whatever},
+    "down converted 'x_' as 'x_'"
+  );
+}
+
+# specific test for upconverting resources
+{
+  my $path = File::Spec->catfile('t','data','resources.yml');
+  my $original = Parse::CPAN::Meta->load_file( $path  );
+  ok( $original, "loaded resources.yml" );
+  my $cmc = CPAN::Meta::Converter->new( $original );
+  my $converted = $cmc->convert( version => 2 );
+  is_deeply(
+    $converted->{resources},
+    { x_MailingList => 'http://groups.google.com/group/www-mechanize-users',
+      x_Repository  => 'http://code.google.com/p/www-mechanize/source',
+      homepage      => 'http://code.google.com/p/www-mechanize/',
+      bugtracker    => {web => 'http://code.google.com/p/www-mechanize/issues/list',},
+      license       => ['http://dev.perl.org/licenses/'],
+    },
+    "upconversion of resources"
+  );
+}
+
+done_testing;
+
diff --git a/cpan/CPAN-Meta/t/data-bad/107650337-META.yml b/cpan/CPAN-Meta/t/data-bad/107650337-META.yml
new file mode 100644 (file)
index 0000000..84080ef
--- /dev/null
@@ -0,0 +1,25 @@
+---
+name: FabForce-DBDesigner4-DBIC
+version: 0.0802
+author:
+  - 'Renee Baecker <module@renee-baecker.de>'
+abstract: create DBIC scheme for DBDesigner4 xml file
+license: artistic2
+requires:
+  FabForce::DBDesigner4: 0.3
+  File::Spec: 3.12
+build_requires:
+  Carp: 0
+  Test::CheckManifest: 1
+  Test::More: 0
+generated_by: Module::Build version 0.2808
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.2.html
+  version: 1.2
+provides:
+  FabForce::DBDesigner4::DBIC:
+    file: lib/FabForce/DBDesigner4/DBIC.pm
+    version: 0.0802
+resources:
+  license: http://dev.perl.org/licenses/
+
diff --git a/cpan/CPAN-Meta/t/data-bad/1122575719-META.yml b/cpan/CPAN-Meta/t/data-bad/1122575719-META.yml
new file mode 100644 (file)
index 0000000..f37cb28
--- /dev/null
@@ -0,0 +1,28 @@
+---
+abstract: 'Generate Catalyst application menus'
+author:
+  - 'David P.C. Wollmann <converter42@gmail.com>'
+build_requires:
+  ExtUtils::MakeMaker: 6.42
+  Test::More: 0
+configure_requires:
+  ExtUtils::MakeMaker: 6.42
+distribution_type: module
+generated_by: 'Module::Install version 0.87'
+license: perl
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.4.html
+  version: 1.4
+name: CatalystX-Menu-Tree
+no_index:
+  directory:
+    - inc
+    - t
+  inc: []
+requires:
+  Catalyst::Runtime: 0
+  MRO::Compat: 0
+  perl: 5.8.0
+resources:
+  license: http://dev.perl.org/licenses/
+version: 0.02
diff --git a/cpan/CPAN-Meta/t/data-bad/1206545041-META.yml b/cpan/CPAN-Meta/t/data-bad/1206545041-META.yml
new file mode 100644 (file)
index 0000000..a8be520
--- /dev/null
@@ -0,0 +1,105 @@
+---
+name: PDF-Template
+version: 0.29_02
+author: ~
+abstract: PDF::Template
+license: perl
+requires:
+  Encode: 0.01
+  PDF::Writer: 0.05
+  perl: 5.6.0
+build_requires:
+  IO::Scalar: 0.01
+  Test::More: 0.47
+provides:
+  PDF::Template:
+    file: lib/PDF/Template.pm
+    version: 0.29_02
+  PDF::Template::Base:
+    file: lib/PDF/Template/Base.pm
+    version: ~
+  PDF::Template::Constants:
+    file: lib/PDF/Template/Constants.pm
+    version: ~
+  PDF::Template::Container:
+    file: lib/PDF/Template/Container.pm
+    version: ~
+  PDF::Template::Container::Always:
+    file: lib/PDF/Template/Container/Always.pm
+    version: ~
+  PDF::Template::Container::Conditional:
+    file: lib/PDF/Template/Container/Conditional.pm
+    version: ~
+  PDF::Template::Container::Font:
+    file: lib/PDF/Template/Container/Font.pm
+    version: ~
+  PDF::Template::Container::Footer:
+    file: lib/PDF/Template/Container/Footer.pm
+    version: ~
+  PDF::Template::Container::Header:
+    file: lib/PDF/Template/Container/Header.pm
+    version: ~
+  PDF::Template::Container::Loop:
+    file: lib/PDF/Template/Container/Loop.pm
+    version: ~
+  PDF::Template::Container::Margin:
+    file: lib/PDF/Template/Container/Margin.pm
+    version: ~
+  PDF::Template::Container::PageDef:
+    file: lib/PDF/Template/Container/PageDef.pm
+    version: ~
+  PDF::Template::Container::PdfTemplate:
+    file: lib/PDF/Template/Container/PdfTemplate.pm
+    version: ~
+  PDF::Template::Container::Row:
+    file: lib/PDF/Template/Container/Row.pm
+    version: ~
+  PDF::Template::Container::Scope:
+    file: lib/PDF/Template/Container/Scope.pm
+    version: ~
+  PDF::Template::Container::Section:
+    file: lib/PDF/Template/Container/Section.pm
+    version: ~
+  PDF::Template::Context:
+    file: lib/PDF/Template/Context.pm
+    version: ~
+  PDF::Template::Element:
+    file: lib/PDF/Template/Element.pm
+    version: ~
+  PDF::Template::Element::Bookmark:
+    file: lib/PDF/Template/Element/Bookmark.pm
+    version: ~
+  PDF::Template::Element::Circle:
+    file: lib/PDF/Template/Element/Circle.pm
+    version: ~
+  PDF::Template::Element::HorizontalRule:
+    file: lib/PDF/Template/Element/HorizontalRule.pm
+    version: ~
+  PDF::Template::Element::Image:
+    file: lib/PDF/Template/Element/Image.pm
+    version: ~
+  PDF::Template::Element::Line:
+    file: lib/PDF/Template/Element/Line.pm
+    version: ~
+  PDF::Template::Element::PageBreak:
+    file: lib/PDF/Template/Element/PageBreak.pm
+    version: ~
+  PDF::Template::Element::TextBox:
+    file: lib/PDF/Template/Element/TextBox.pm
+    version: ~
+  PDF::Template::Element::Var:
+    file: lib/PDF/Template/Element/Var.pm
+    version: ~
+  PDF::Template::Element::Weblink:
+    file: lib/PDF/Template/Element/Weblink.pm
+    version: ~
+  PDF::Template::Factory:
+    file: lib/PDF/Template/Factory.pm
+    version: ~
+  PDF::Template::Iterator:
+    file: lib/PDF/Template/Iterator.pm
+    version: ~
+  PDF::Template::TextObject:
+    file: lib/PDF/Template/TextObject.pm
+    version: ~
+generated_by: Module::Build version 0.2701
diff --git a/cpan/CPAN-Meta/t/data-bad/1598804075-META.yml b/cpan/CPAN-Meta/t/data-bad/1598804075-META.yml
new file mode 100644 (file)
index 0000000..868c8cd
--- /dev/null
@@ -0,0 +1,19 @@
+# http://module-build.sourceforge.net/META-spec.html
+#XXXXXXX This is a prototype!!!  It will change in the future!!! XXXXX#
+name:         XML-RDB
+version:      1.3
+version_from: RDB.pm
+installdirs:  site
+requires:
+    Data::Dumper:                  0
+    DBI:                           1.35
+    DBIx::DBSchema:                .16
+    DBIx::Recordset:               .23
+    DBIx::Sequence:                .04
+    Getopt::Std:                   0
+    IO::File:                      1.08
+    URI::Escape:                   3.16
+    XML::DOM:                      1.29
+
+distribution_type: module
+generated_by: ExtUtils::MakeMaker version 6.30
diff --git a/cpan/CPAN-Meta/t/data-bad/1927486199-META.yml b/cpan/CPAN-Meta/t/data-bad/1927486199-META.yml
new file mode 100644 (file)
index 0000000..9b5d8bd
--- /dev/null
@@ -0,0 +1,13 @@
+# http://module-build.sourceforge.net/META-spec.html\r
+#XXXXXXX This is a prototype!!!  It will change in the future!!! XXXXX#\r
+name:         WWW-phpBB-Mod-Installer\r
+version:      0.03\r
+version_from: lib/WWW/phpBB/Mod/Installer.pm\r
+installdirs:  site\r
+requires:\r
+    DBD::mysql:                    3.0002\r
+    DBI:                           1.5\r
+    XML::Xerces:                   -4\r
+\r
+distribution_type: module\r
+generated_by: ExtUtils::MakeMaker version 6.30\r
diff --git a/cpan/CPAN-Meta/t/data-bad/1985684504-META.yml b/cpan/CPAN-Meta/t/data-bad/1985684504-META.yml
new file mode 100644 (file)
index 0000000..4d79f42
--- /dev/null
@@ -0,0 +1,21 @@
+--- #YAML:1.0
+name:                Squatting
+version:             0.30
+abstract:            A Camping-inspired Web Microframework for Perl
+license:             mit
+generated_by:        ExtUtils::MakeMaker version 6.36
+distribution_type:   module
+requires:     
+    Continuity:                    0.991
+    Data::Dump:                    0
+    HTTP::Daemon:                  0
+    JSON::XS:                      0
+    Shell::Perl:                   0
+meta-spec:
+    url:     http://module-build.sourceforge.net/META-spec-v1.2.html
+    version: 1.2
+author:
+    - John BEPPU <beppu@cpan.org>
+no_index:
+    - eg
+    - t
diff --git a/cpan/CPAN-Meta/t/data-bad/1985980974-META.yml b/cpan/CPAN-Meta/t/data-bad/1985980974-META.yml
new file mode 100644 (file)
index 0000000..7814f05
--- /dev/null
@@ -0,0 +1,20 @@
+---
+abstract: 'A Form::Sensible::Form::Reflector subclass to reflect off of DBIC schema classes'
+author:
+  - 'Devin Austin <dhoss@cpan.org>'
+generated_by: 'Dist::Zilla version 1.100160'
+license: perl
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.4.html
+  version: 1.4
+name: Form-Sensible-Reflector-DBIC
+requires:
+  DBIx::Class: "\"\""
+  Data::Dumper: "\"\""
+  DateTime: "\"\""
+  ExtUtils::MakeMaker: 6.11
+  Form::Sensible: "\"\""
+  Moose: 0.93
+  SQL::Translator: 0.11002
+  Test::Simple: 0.88
+version: 0.0341
diff --git a/cpan/CPAN-Meta/t/data-bad/2031017050-META.yml b/cpan/CPAN-Meta/t/data-bad/2031017050-META.yml
new file mode 100644 (file)
index 0000000..c7b3930
--- /dev/null
@@ -0,0 +1,29 @@
+--- #YAML:1.0
+name:               Forks-Super
+version:            0.16
+abstract:           extensions and convenience methods for managing background processes.
+author:
+    - Marty O'Brien <mob@cpan.org>
+license:            perl
+distribution_type:  module
+configure_requires:
+    ExtUtils::MakeMaker:  0
+build_requires:
+    ExtUtils::MakeMaker:  0
+requires:
+    Test::More:  0
+no_index:
+    directory:
+        - t
+        - inc
+generated_by:       ExtUtils::MakeMaker version 6.55_02
+meta-spec:
+    url:      http://module-build.sourceforge.net/META-spec-v1.4.html
+    version:  1.4
+recommends:
+    - Time::HiRes
+    - 0
+    - YAML
+    - 0
+    - Win32::Process
+    - 0
diff --git a/cpan/CPAN-Meta/t/data-bad/284247103-META.yml b/cpan/CPAN-Meta/t/data-bad/284247103-META.yml
new file mode 100644 (file)
index 0000000..e7390a2
--- /dev/null
@@ -0,0 +1,63 @@
+--- #YAML:1.0
+
+meta-spec:
+    version: 1.2
+    url: http://module-build.sourceforge.net/META-spec-v1.2.html
+
+name: Daizu
+version: 0.3
+abstract: Web publishing system built on Subversion
+license: gpl
+
+resources:
+    homepage: http://www.daizucms.org/
+    license: http://www.gnu.org/licenses/gpl.html
+
+author:
+    - 'Geoff Richards <geoff@laxan.com>'
+
+requires:
+    Carp::Assert: 0
+    Compress::Zlib: 0
+    DBD::Pg: 0
+    DateTime: 0
+    DateTime::Format::Pg: 0.08
+    Digest::SHA1: 0
+    File::MMagic: 0
+    HTML::Entities: 1.32
+    HTML::Parser: 0
+    HTML::Tagset: 0
+    Image::Size: 0
+    Math::Round: 0.03
+    Path::Class: 0.02
+    SVN::Ra: 0
+    Template: 2.15
+    URI: 0
+    XML::LibXML: 1.59
+
+build_requires:
+    Module::Build: 0
+
+optional_features:
+    - syntax-highlight:
+        description: Automatically syntax-highlight example code in articles
+        requires:
+            Text::VimColor: 0.09
+    - picture-article:
+        description: Publish pictures as articles, with automatic thumbnails
+        requires:
+            Image::Magick: 0
+    - related-links:
+        description: Add a Related Links box to pages for articles
+        requires:
+            Template::Plugin::Class: 0
+
+no_index:
+    file:
+        - test-repos.dump
+
+dynamic_config: 0
+
+generated_by: Geoff Richards
+
+# vi:ts=4 sw=4 expandtab
diff --git a/cpan/CPAN-Meta/t/data-bad/344981821-META.yml b/cpan/CPAN-Meta/t/data-bad/344981821-META.yml
new file mode 100644 (file)
index 0000000..7a51e87
--- /dev/null
@@ -0,0 +1,26 @@
+---
+abstract: ''
+author:
+  - 'Tokuhiro Matsuno  C<< <tokuhiro __at__ mobilefactory.jp> >>'
+build_requires:
+  Class::DBI: 0
+  Class::DBI::Pager: 0
+  Sledge::TestPages: 0
+  Test::Base: 0
+  Test::More: 0
+  YAML: 0
+distribution_type: module
+generated_by: 'Module::Install version 0.75'
+license: perl
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.3.html
+  version: 1.3
+name: Sledge-Plugin-Pager
+no_index:
+  directory:
+    - inc
+    - t
+requires:
+  Lingua::EN::Inflect: 0
+  String::CamelCase: 0
+version: 0.02
diff --git a/cpan/CPAN-Meta/t/data-bad/35478989-META.yml b/cpan/CPAN-Meta/t/data-bad/35478989-META.yml
new file mode 100644 (file)
index 0000000..42e0c10
--- /dev/null
@@ -0,0 +1,17 @@
+--- #YAML:1.0
+name:                oEdtk
+version:             0.42
+abstract:            ~
+license:             ~
+author:              ~
+generated_by:        ExtUtils::MakeMaker version 6.44
+distribution_type:   module
+requires:     
+    Config::IniFiles:              2.3
+    DBI:                           1.6
+    Spreadsheet::WriteExcel:       1
+    Sys::Hostname:                 Digest::MD5
+    Term::ReadKey:                 POSIX
+meta-spec:
+    url:     http://module-build.sourceforge.net/META-spec-v1.3.html
+    version: 1.3
diff --git a/cpan/CPAN-Meta/t/data-bad/476602558-META.yml b/cpan/CPAN-Meta/t/data-bad/476602558-META.yml
new file mode 100644 (file)
index 0000000..ee78dc4
--- /dev/null
@@ -0,0 +1,28 @@
+---
+abstract: 'Benchmarking with statistical confidence'
+author:
+  - '-2001 Andrew Ho.'
+build_requires:
+  ExtUtils::MakeMaker: 6.42
+  Test::More: 0
+configure_requires:
+  ExtUtils::MakeMaker: 6.42
+distribution_type: module
+generated_by: 'Module::Install version 0.91'
+license: gpl
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.4.html
+  version: 1.4
+module_name: Benchmark::Timer
+name: Benchmark-Timer
+no_index:
+  delta.pl: []
+  directory:
+    - inc
+    - t
+requires:
+  Time::HiRes: 0
+  perl: 5.005
+resources:
+  license: http://opensource.org/licenses/gpl-license.php
+version: 0.7102
diff --git a/cpan/CPAN-Meta/t/data-bad/98042513-META.yml b/cpan/CPAN-Meta/t/data-bad/98042513-META.yml
new file mode 100644 (file)
index 0000000..9a1b25f
--- /dev/null
@@ -0,0 +1,16 @@
+# http://module-build.sourceforge.net/META-spec.html
+#XXXXXXX This is a prototype!!!  It will change in the future!!! XXXXX#
+name:         Apache-ErrorControl
+version:      1.026
+version_from: ErrorControl.pm
+installdirs:  site
+requires:
+    Apache::Constants:             1.09
+    Apache::File:                  1.01
+    Apache::Request:               1.1
+    Class::Date:                   \ 1\ 1\ 6
+    HTML::Template::Set:           1.01
+    MIME::Entity:                  5.404
+
+distribution_type: module
+generated_by: ExtUtils::MakeMaker version 6.17
diff --git a/cpan/CPAN-Meta/t/data-bad/META-1_0.yml b/cpan/CPAN-Meta/t/data-bad/META-1_0.yml
new file mode 100644 (file)
index 0000000..9aaaa11
--- /dev/null
@@ -0,0 +1,4 @@
+# http://module-build.sourceforge.net/META-spec.html
+#XXXXXXX This is a prototype!!!  It will change in the future!!! XXXXX#
+name:         Template-DBI
+version:  1.23
diff --git a/cpan/CPAN-Meta/t/data-bad/META-1_1.yml b/cpan/CPAN-Meta/t/data-bad/META-1_1.yml
new file mode 100644 (file)
index 0000000..d90b133
--- /dev/null
@@ -0,0 +1,6 @@
+--- #YAML:1.0
+name:                Class-Virtual
+version:             1.23
+meta-spec:
+    url: http://module-build.sourceforge.net/META-spec-v1.1.html
+    version: 1.1
diff --git a/cpan/CPAN-Meta/t/data-bad/META-1_2.yml b/cpan/CPAN-Meta/t/data-bad/META-1_2.yml
new file mode 100644 (file)
index 0000000..ef58965
--- /dev/null
@@ -0,0 +1,34 @@
+---
+name: Test-Harness-Straps
+version: 0.30
+author:
+  - 'Michael G Schwern <schwern@pobox.com>'
+license: perl
+resources:
+  license: http://dev.perl.org/licenses/
+requires:
+  File::Spec: 0.6
+provides:
+  Test::Harness::Assert:
+    file: lib/Test/Harness/Assert.pm
+    version: 0.02
+  Test::Harness::Iterator:
+    file: lib/Test/Harness/Iterator.pm
+    version: 0.02
+  Test::Harness::Iterator::ARRAY:
+    file: lib/Test/Harness/Iterator.pm
+  Test::Harness::Iterator::FH:
+    file: lib/Test/Harness/Iterator.pm
+  Test::Harness::Point:
+    file: lib/Test/Harness/Point.pm
+    version: 0.01
+  Test::Harness::Results:
+    file: lib/Test/Harness/Results.pm
+    version: 0.01
+  Test::Harness::Straps:
+    file: lib/Test/Harness/Straps.pm
+    version: 0.30
+generated_by: Module::Build version 0.280801
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.2.html
+  version: 1.2
diff --git a/cpan/CPAN-Meta/t/data-bad/META-1_3.yml b/cpan/CPAN-Meta/t/data-bad/META-1_3.yml
new file mode 100644 (file)
index 0000000..ad215ad
--- /dev/null
@@ -0,0 +1,28 @@
+--- 
+abstract: a modern perl interactive shell
+author: Matt S Trout - mst (at) shadowcatsystems.co.uk (L<http://www.shadowcatsystems.co.uk/>)
+build_requires: 
+  Test::More: 0
+distribution_type: module
+generated_by: Module::Install version 0.67
+license: perl
+meta-spec: 
+  url: http://module-build.sourceforge.net/META-spec-v1.3.html
+  version: 1.3
+name: Devel-REPL
+no_index: 
+  directory: 
+    - inc
+    - t
+requires: 
+  Data::Dump::Streamer: 0
+  File::HomeDir: 0
+  File::Spec: 0
+  Lexical::Persistence: 0
+  Moose: 0
+  MooseX::Getopt: 0
+  MooseX::Object::Pluggable: 0
+  Term::ReadLine: 0
+  namespace::clean: 0
+  perl: 5.8.1
+version: 1.001000
diff --git a/cpan/CPAN-Meta/t/data-bad/META-1_4.yml b/cpan/CPAN-Meta/t/data-bad/META-1_4.yml
new file mode 100644 (file)
index 0000000..1336f10
--- /dev/null
@@ -0,0 +1,128 @@
+---
+author:
+  - 'Ken Williams <kwilliams@cpan.org>'
+  - "Development questions, bug reports, and patches should be sent to the\nModule-Build mailing list at <module-build@perl.org>."
+build_requires:
+  File::Temp: 0.15
+  Test::Harness: 3.16
+  Test::More: 0.49
+generated_by: 'Module::Build version 0.3608'
+license: perl
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.4.html
+  version: 1.4
+name: Module-Build
+provides:
+  Module::Build:
+    file: lib/Module/Build.pm
+    version: 0.36_08
+  Module::Build::Base:
+    file: lib/Module/Build/Base.pm
+    version: 0.36_08
+  Module::Build::Compat:
+    file: lib/Module/Build/Compat.pm
+    version: 0.36_08
+  Module::Build::Config:
+    file: lib/Module/Build/Config.pm
+    version: 0.36_08
+  Module::Build::Cookbook:
+    file: lib/Module/Build/Cookbook.pm
+    version: 0.36_08
+  Module::Build::Dumper:
+    file: lib/Module/Build/Dumper.pm
+    version: 0.36_08
+  Module::Build::ModuleInfo:
+    file: lib/Module/Build/ModuleInfo.pm
+    version: 0.36_08
+  Module::Build::Notes:
+    file: lib/Module/Build/Notes.pm
+    version: 0.36_08
+  Module::Build::PPMMaker:
+    file: lib/Module/Build/PPMMaker.pm
+    version: 0.36_08
+  Module::Build::Platform::Amiga:
+    file: lib/Module/Build/Platform/Amiga.pm
+    version: 0.36_08
+  Module::Build::Platform::Default:
+    file: lib/Module/Build/Platform/Default.pm
+    version: 0.36_08
+  Module::Build::Platform::EBCDIC:
+    file: lib/Module/Build/Platform/EBCDIC.pm
+    version: 0.36_08
+  Module::Build::Platform::MPEiX:
+    file: lib/Module/Build/Platform/MPEiX.pm
+    version: 0.36_08
+  Module::Build::Platform::MacOS:
+    file: lib/Module/Build/Platform/MacOS.pm
+    version: 0.36_08
+  Module::Build::Platform::RiscOS:
+    file: lib/Module/Build/Platform/RiscOS.pm
+    version: 0.36_08
+  Module::Build::Platform::Unix:
+    file: lib/Module/Build/Platform/Unix.pm
+    version: 0.36_08
+  Module::Build::Platform::VMS:
+    file: lib/Module/Build/Platform/VMS.pm
+    version: 0.36_08
+  Module::Build::Platform::VOS:
+    file: lib/Module/Build/Platform/VOS.pm
+    version: 0.36_08
+  Module::Build::Platform::Windows:
+    file: lib/Module/Build/Platform/Windows.pm
+    version: 0.36_08
+  Module::Build::Platform::aix:
+    file: lib/Module/Build/Platform/aix.pm
+    version: 0.36_08
+  Module::Build::Platform::cygwin:
+    file: lib/Module/Build/Platform/cygwin.pm
+    version: 0.36_08
+  Module::Build::Platform::darwin:
+    file: lib/Module/Build/Platform/darwin.pm
+    version: 0.36_08
+  Module::Build::Platform::os2:
+    file: lib/Module/Build/Platform/os2.pm
+    version: 0.36_08
+  Module::Build::PodParser:
+    file: lib/Module/Build/PodParser.pm
+    version: 0.36_08
+  Module::Build::Version:
+    file: lib/Module/Build/Version.pm
+    version: 0.77
+  Module::Build::YAML:
+    file: lib/Module/Build/YAML.pm
+    version: 1.40
+  inc::latest:
+    file: lib/inc/latest.pm
+    version: 0.36_08
+  inc::latest::private:
+    file: lib/inc/latest/private.pm
+    version: 0.36_08
+recommends:
+  ExtUtils::Install: 0.3
+  ExtUtils::Manifest: 1.54
+  version: 0.74
+requires:
+  Cwd: 0
+  Data::Dumper: 0
+  ExtUtils::CBuilder: 0.27
+  ExtUtils::Install: 0
+  ExtUtils::Manifest: 0
+  ExtUtils::Mkbootstrap: 0
+  ExtUtils::ParseXS: 2.21
+  File::Basename: 0
+  File::Compare: 0
+  File::Copy: 0
+  File::Find: 0
+  File::Path: 0
+  File::Spec: 0.82
+  Getopt::Long: 0
+  IO::File: 0
+  Test::Harness: 0
+  Text::Abbrev: 0
+  Text::ParseWords: 0
+  perl: 5.006001
+resources:
+  MailingList: mailto:module-build@perl.org
+  license: http://dev.perl.org/licenses/
+  repository: http://github.com/dagolden/module-build/
+version: 0.36_08
diff --git a/cpan/CPAN-Meta/t/data-bad/META-2.json b/cpan/CPAN-Meta/t/data-bad/META-2.json
new file mode 100644 (file)
index 0000000..6734399
--- /dev/null
@@ -0,0 +1,76 @@
+{
+   "resources" : {
+      "license" : [
+         "http://dev.perl.org/licenses/"
+      ]
+   },
+   "generated_by" : "Module::Build version 0.36",
+   "meta-spec" : {
+      "version" : "2",
+      "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec"
+   },
+   "version" : "0.36",
+   "name" : "Module-Build",
+   "author" : [
+      "Ken Williams <kwilliams@cpan.org>",
+      "Module-Build List <module-build@perl.org>"
+   ],
+   "release_status" : "stable",
+   "license" : [
+      "perl_5"
+   ],
+   "description" : "Module::Build is a system for building, testing, and installing Perl modules.  It is meant to be an alternative to ExtUtils::MakeMaker... blah blah blah",
+   "keywords" : [
+      "toolchain",
+      "cpan",
+      "dual-life"
+   ],
+   "prereqs" : {
+      "runtime" : {
+         "requires" : {
+            "File::Copy" : "0",
+            "IO::File" : "0",
+            "Data::Dumper" : "0",
+            "File::Spec" : "0",
+            "Config" : "0",
+            "ExtUtils::Install" : "0",
+            "perl" : "5.006",
+            "File::Compare" : "0",
+            "File::Find" : "0",
+            "File::Path" : "0",
+            "File::Basename" : "0",
+            "Cwd" : "0"
+         },
+         "recommends" : {
+            "YAML" : "0.35",
+            "ExtUtils::ParseXS" : "2.02",
+            "Pod::Text" : "0",
+            "ExtUtils::Install" : "0.3",
+            "Archive::Tar" : "1.00"
+         }
+      },
+      "build" : {
+         "requires" : {
+            "Test::More" : "0"
+         }
+      }
+   },
+   "optional_features" : {
+      "domination" : {
+         "prereqs" : {
+            "develop" : {
+               "requires" : {
+                  "Genius::Evil" : "1.234"
+               }
+            },
+            "runtime" : {
+               "requires" : {
+                  "Machine::Weather" : "2.0"
+               }
+            }
+         },
+         "description" : "Take over the world"
+      }
+   },
+   "abstract" : "Build and install Perl modules"
+}
diff --git a/cpan/CPAN-Meta/t/data-bad/restrictive-2.json b/cpan/CPAN-Meta/t/data-bad/restrictive-2.json
new file mode 100644 (file)
index 0000000..2fdd9fb
--- /dev/null
@@ -0,0 +1,89 @@
+{
+   "resources" : {
+      "license" : [
+         "http://dev.perl.org/licenses/"
+      ],
+      "repository" : {
+        "url" : "svn://repo.example.com/foo-bar#fakeanchor",
+        "web" : "http://www.example.com"
+      }
+   },
+   "generated_by" : "Module::Build version 0.36",
+   "meta-spec" : {
+      "version" : "2",
+      "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec"
+   },
+   "version" : "0.36",
+   "name" : "Module-Build",
+   "dynamic_config" : 1,
+   "author" : [
+      "Ken Williams <kwilliams@cpan.org>",
+      "Module-Build List <module-build@perl.org>"
+   ],
+   "release_status" : "stable",
+   "license" : [
+      "restrictive"
+   ],
+   "description" : "Module::Build is a system for building, testing, and installing Perl modules.  It is meant to be an alternative to ExtUtils::MakeMaker... blah blah blah",
+   "keywords" : [
+      "toolchain",
+      "cpan",
+      "dual-life"
+   ],
+   "prereqs" : {
+      "runtime" : {
+         "requires" : {
+            "File::Copy" : "0",
+            "IO::File" : "0",
+            "Data::Dumper" : "0",
+            "File::Spec" : "0",
+            "Config" : "0",
+            "ExtUtils::Install" : "0",
+            "perl" : "5.006",
+            "File::Compare" : "0",
+            "File::Find" : "0",
+            "File::Path" : "0",
+            "File::Basename" : "0",
+            "Cwd" : "0"
+         },
+         "recommends" : {
+            "YAML" : "0.35",
+            "ExtUtils::ParseXS" : "2.02",
+            "Pod::Text" : "0",
+            "ExtUtils::Install" : "0.3",
+            "Archive::Tar" : "1.00"
+         }
+      },
+      "build" : {
+         "requires" : {
+            "Build::Requires": "1.1",
+            "Test::More" : "0"
+         }
+      },
+      "test" : {
+         "requires" : {
+            "Test::More" : "0.88",
+            "Test::Requires" : "1.2"
+         }
+      }
+   },
+   "optional_features" : {
+      "domination" : {
+         "prereqs" : {
+            "develop" : {
+               "requires" : {
+                  "Genius::Evil" : "1.234"
+               }
+            },
+            "runtime" : {
+               "requires" : {
+                  "Machine::Weather" : "2.0"
+               }
+            }
+         },
+         "description" : "Take over the world"
+      }
+   },
+   "abstract" : "Build and install Perl modules",
+   "x_whatever" : "Custom key"
+}
diff --git a/cpan/CPAN-Meta/t/data-fail/META-1_0.yml b/cpan/CPAN-Meta/t/data-fail/META-1_0.yml
new file mode 100644 (file)
index 0000000..44b0bbf
--- /dev/null
@@ -0,0 +1,11 @@
+# http://module-build.sourceforge.net/META-spec.html
+#XXXXXXX This is a prototype!!!  It will change in the future!!! XXXXX#
+name:         Template-DBI
+version_from: lib/Template/Plugin/DBI.pm
+installdirs:  site
+license: perl
+requires:
+    DBI:                           1
+    Template:                      2.15
+distribution_type: module
+generated_by: ExtUtils::MakeMaker version 6.17
diff --git a/cpan/CPAN-Meta/t/data-fail/META-1_1.yml b/cpan/CPAN-Meta/t/data-fail/META-1_1.yml
new file mode 100644 (file)
index 0000000..e96665f
--- /dev/null
@@ -0,0 +1,15 @@
+--- #YAML:1.0
+name:                Class-Virtual
+abstract:            ~
+license:             unknown
+generated_by:        ExtUtils::MakeMaker version 6.30_03
+author:              ~
+distribution_type:   module
+requires:     
+    Carp::Assert:                  0.1
+    Class::Data::Inheritable:      0.02
+    Class::ISA:                    0.31
+    Test::More:                    0.5
+meta-spec:
+    url: http://module-build.sourceforge.net/META-spec-v1.1.html
+    version: 1.1
diff --git a/cpan/CPAN-Meta/t/data-fail/META-1_2.yml b/cpan/CPAN-Meta/t/data-fail/META-1_2.yml
new file mode 100644 (file)
index 0000000..4829149
--- /dev/null
@@ -0,0 +1,34 @@
+---
+name: Test-Harness-Straps
+author:
+  - 'Michael G Schwern <schwern@pobox.com>'
+abstract: detailed analysis of test results
+license: perl
+resources:
+  license: http://dev.perl.org/licenses/
+requires:
+  File::Spec: 0.6
+provides:
+  Test::Harness::Assert:
+    file: lib/Test/Harness/Assert.pm
+    version: 0.02
+  Test::Harness::Iterator:
+    file: lib/Test/Harness/Iterator.pm
+    version: 0.02
+  Test::Harness::Iterator::ARRAY:
+    file: lib/Test/Harness/Iterator.pm
+  Test::Harness::Iterator::FH:
+    file: lib/Test/Harness/Iterator.pm
+  Test::Harness::Point:
+    file: lib/Test/Harness/Point.pm
+    version: 0.01
+  Test::Harness::Results:
+    file: lib/Test/Harness/Results.pm
+    version: 0.01
+  Test::Harness::Straps:
+    file: lib/Test/Harness/Straps.pm
+    version: 0.30
+generated_by: Module::Build version 0.280801
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.2.html
+  version: 1.2
diff --git a/cpan/CPAN-Meta/t/data-fail/META-1_3.yml b/cpan/CPAN-Meta/t/data-fail/META-1_3.yml
new file mode 100644 (file)
index 0000000..3bbe071
--- /dev/null
@@ -0,0 +1,28 @@
+--- 
+abstract: a modern perl interactive shell
+author: 
+  - Matt S Trout - mst (at) shadowcatsystems.co.uk (L<http://www.shadowcatsystems.co.uk/>)
+build_requires: 
+  Test::More: 0
+distribution_type: module
+generated_by: Module::Install version 0.67
+license: perl
+meta-spec: 
+  url: http://module-build.sourceforge.net/META-spec-v1.3.html
+  version: 1.3
+name: Devel-REPL
+no_index: 
+  directory: 
+    - inc
+    - t
+requires: 
+  Data::Dump::Streamer: 0
+  File::HomeDir: 0
+  File::Spec: 0
+  Lexical::Persistence: 0
+  Moose: 0
+  MooseX::Getopt: 0
+  MooseX::Object::Pluggable: 0
+  Term::ReadLine: 0
+  namespace::clean: 0
+  perl: 5.8.1
diff --git a/cpan/CPAN-Meta/t/data-fail/META-1_4.yml b/cpan/CPAN-Meta/t/data-fail/META-1_4.yml
new file mode 100644 (file)
index 0000000..cec4d11
--- /dev/null
@@ -0,0 +1,129 @@
+---
+abstract: 'Build and install Perl modules'
+author:
+  - 'Ken Williams <kwilliams@cpan.org>'
+  - "Development questions, bug reports, and patches should be sent to the\nModule-Build mailing list at <module-build@perl.org>."
+build_requires:
+  File::Temp: 0.15
+  Test::Harness: 3.16
+  Test::More: 0.49
+generated_by: 'Module::Build version 0.3608'
+license: perl
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.4.html
+  version: 1.4
+name: Module-Build
+provides:
+  Module::Build:
+    file: lib/Module/Build.pm
+    version: 0.36_08
+  Module::Build::Base:
+    file: lib/Module/Build/Base.pm
+    version: 0.36_08
+  Module::Build::Compat:
+    file: lib/Module/Build/Compat.pm
+    version: 0.36_08
+  Module::Build::Config:
+    file: lib/Module/Build/Config.pm
+    version: 0.36_08
+  Module::Build::Cookbook:
+    file: lib/Module/Build/Cookbook.pm
+    version: 0.36_08
+  Module::Build::Dumper:
+    file: lib/Module/Build/Dumper.pm
+    version: 0.36_08
+  Module::Build::ModuleInfo:
+    file: lib/Module/Build/ModuleInfo.pm
+    version: 0.36_08
+  Module::Build::Notes:
+    file: lib/Module/Build/Notes.pm
+    version: 0.36_08
+  Module::Build::PPMMaker:
+    file: lib/Module/Build/PPMMaker.pm
+    version: 0.36_08
+  Module::Build::Platform::Amiga:
+    file: lib/Module/Build/Platform/Amiga.pm
+    version: 0.36_08
+  Module::Build::Platform::Default:
+    file: lib/Module/Build/Platform/Default.pm
+    version: 0.36_08
+  Module::Build::Platform::EBCDIC:
+    file: lib/Module/Build/Platform/EBCDIC.pm
+    version: 0.36_08
+  Module::Build::Platform::MPEiX:
+    file: lib/Module/Build/Platform/MPEiX.pm
+    version: 0.36_08
+  Module::Build::Platform::MacOS:
+    file: lib/Module/Build/Platform/MacOS.pm
+    version: 0.36_08
+  Module::Build::Platform::RiscOS:
+    file: lib/Module/Build/Platform/RiscOS.pm
+    version: 0.36_08
+  Module::Build::Platform::Unix:
+    file: lib/Module/Build/Platform/Unix.pm
+    version: 0.36_08
+  Module::Build::Platform::VMS:
+    file: lib/Module/Build/Platform/VMS.pm
+    version: 0.36_08
+  Module::Build::Platform::VOS:
+    file: lib/Module/Build/Platform/VOS.pm
+    version: 0.36_08
+  Module::Build::Platform::Windows:
+    file: lib/Module/Build/Platform/Windows.pm
+    version: 0.36_08
+  Module::Build::Platform::aix:
+    file: lib/Module/Build/Platform/aix.pm
+    version: 0.36_08
+  Module::Build::Platform::cygwin:
+    file: lib/Module/Build/Platform/cygwin.pm
+    version: 0.36_08
+  Module::Build::Platform::darwin:
+    file: lib/Module/Build/Platform/darwin.pm
+    version: 0.36_08
+  Module::Build::Platform::os2:
+    file: lib/Module/Build/Platform/os2.pm
+    version: 0.36_08
+  Module::Build::PodParser:
+    file: lib/Module/Build/PodParser.pm
+    version: 0.36_08
+  Module::Build::Version:
+    file: lib/Module/Build/Version.pm
+    version: 0.77
+  Module::Build::YAML:
+    file: lib/Module/Build/YAML.pm
+    version: 1.40
+  inc::latest:
+    file: lib/inc/latest.pm
+    version: 0.36_08
+  inc::latest::private:
+    file: lib/inc/latest/private.pm
+    version: 0.36_08
+recommends:
+  ExtUtils::Install: 0.3
+  ExtUtils::Manifest: 1.54
+  version: 0.74
+requires:
+  Cwd: 0
+  Data::Dumper: 0
+  ExtUtils::CBuilder: 0.27
+  ExtUtils::Install: 0
+  ExtUtils::Manifest: 0
+  ExtUtils::Mkbootstrap: 0
+  ExtUtils::ParseXS: 2.21
+  File::Basename: 0
+  File::Compare: 0
+  File::Copy: 0
+  File::Find: 0
+  File::Path: 0
+  File::Spec: 0.82
+  Getopt::Long: 0
+  IO::File: 0
+  Test::Harness: 0
+  Text::Abbrev: 0
+  Text::ParseWords: 0
+  perl: 5.006001
+resources:
+  MailingList: mailto:module-build@perl.org
+  license: http://dev.perl.org/licenses/
+  repository: http://github.com/dagolden/module-build/
+
diff --git a/cpan/CPAN-Meta/t/data-fail/META-2.json b/cpan/CPAN-Meta/t/data-fail/META-2.json
new file mode 100644 (file)
index 0000000..4cad8f3
--- /dev/null
@@ -0,0 +1,76 @@
+{
+   "resources" : {
+      "license" : [
+         "http://dev.perl.org/licenses/"
+      ]
+   },
+   "generated_by" : "Module::Build version 0.36",
+   "meta-spec" : {
+      "version" : "2",
+      "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec"
+   },
+   "name" : "Module-Build",
+   "dynamic_config" : 1,
+   "author" : [
+      "Ken Williams <kwilliams@cpan.org>",
+      "Module-Build List <module-build@perl.org>"
+   ],
+   "release_status" : "stable",
+   "license" : [
+      "perl_5"
+   ],
+   "description" : "Module::Build is a system for building, testing, and installing Perl modules.  It is meant to be an alternative to ExtUtils::MakeMaker... blah blah blah",
+   "keywords" : [
+      "toolchain",
+      "cpan",
+      "dual-life"
+   ],
+   "prereqs" : {
+      "runtime" : {
+         "requires" : {
+            "File::Copy" : "0",
+            "IO::File" : "0",
+            "Data::Dumper" : "0",
+            "File::Spec" : "0",
+            "Config" : "0",
+            "ExtUtils::Install" : "0",
+            "perl" : "5.006",
+            "File::Compare" : "0",
+            "File::Find" : "0",
+            "File::Path" : "0",
+            "File::Basename" : "0",
+            "Cwd" : "0"
+         },
+         "recommends" : {
+            "YAML" : "0.35",
+            "ExtUtils::ParseXS" : "2.02",
+            "Pod::Text" : "0",
+            "ExtUtils::Install" : "0.3",
+            "Archive::Tar" : "1.00"
+         }
+      },
+      "build" : {
+         "requires" : {
+            "Test::More" : "0"
+         }
+      }
+   },
+   "optional_features" : {
+      "domination" : {
+         "prereqs" : {
+            "develop" : {
+               "requires" : {
+                  "Genius::Evil" : "1.234"
+               }
+            },
+            "runtime" : {
+               "requires" : {
+                  "Machine::Weather" : "2.0"
+               }
+            }
+         },
+         "description" : "Take over the world"
+      }
+   },
+   "abstract" : "Build and install Perl modules"
+}
diff --git a/cpan/CPAN-Meta/t/data/META-1_0.yml b/cpan/CPAN-Meta/t/data/META-1_0.yml
new file mode 100644 (file)
index 0000000..e72d9ed
--- /dev/null
@@ -0,0 +1,12 @@
+# http://module-build.sourceforge.net/META-spec.html
+#XXXXXXX This is a prototype!!!  It will change in the future!!! XXXXX#
+name:         Template-DBI
+version:      2.64
+version_from: lib/Template/Plugin/DBI.pm
+installdirs:  site
+license: perl
+requires:
+    DBI:                           1
+    Template:                      2.15
+distribution_type: module
+generated_by: ExtUtils::MakeMaker version 6.17
diff --git a/cpan/CPAN-Meta/t/data/META-1_1.yml b/cpan/CPAN-Meta/t/data/META-1_1.yml
new file mode 100644 (file)
index 0000000..d770077
--- /dev/null
@@ -0,0 +1,16 @@
+--- #YAML:1.0
+name:                Class-Virtual
+version:             0.06
+abstract:            ~
+license:             unknown
+generated_by:        ExtUtils::MakeMaker version 6.30_03
+author:              ~
+distribution_type:   module
+requires:     
+    Carp::Assert:                  0.1
+    Class::Data::Inheritable:      0.02
+    Class::ISA:                    0.31
+    Test::More:                    0.5
+meta-spec:
+    url: http://module-build.sourceforge.net/META-spec-v1.1.html
+    version: 1.1
diff --git a/cpan/CPAN-Meta/t/data/META-1_2.yml b/cpan/CPAN-Meta/t/data/META-1_2.yml
new file mode 100644 (file)
index 0000000..51c2a96
--- /dev/null
@@ -0,0 +1,35 @@
+---
+name: Test-Harness-Straps
+version: 0.30
+author:
+  - 'Michael G Schwern <schwern@pobox.com>'
+abstract: detailed analysis of test results
+license: perl
+resources:
+  license: http://dev.perl.org/licenses/
+requires:
+  File::Spec: 0.6
+provides:
+  Test::Harness::Assert:
+    file: lib/Test/Harness/Assert.pm
+    version: 0.02
+  Test::Harness::Iterator:
+    file: lib/Test/Harness/Iterator.pm
+    version: 0.02
+  Test::Harness::Iterator::ARRAY:
+    file: lib/Test/Harness/Iterator.pm
+  Test::Harness::Iterator::FH:
+    file: lib/Test/Harness/Iterator.pm
+  Test::Harness::Point:
+    file: lib/Test/Harness/Point.pm
+    version: 0.01
+  Test::Harness::Results:
+    file: lib/Test/Harness/Results.pm
+    version: 0.01
+  Test::Harness::Straps:
+    file: lib/Test/Harness/Straps.pm
+    version: 0.30
+generated_by: Module::Build version 0.280801
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.2.html
+  version: 1.2
diff --git a/cpan/CPAN-Meta/t/data/META-1_3.yml b/cpan/CPAN-Meta/t/data/META-1_3.yml
new file mode 100644 (file)
index 0000000..c26a3e0
--- /dev/null
@@ -0,0 +1,29 @@
+--- 
+abstract: a modern perl interactive shell
+author: 
+  - Matt S Trout - mst (at) shadowcatsystems.co.uk (L<http://www.shadowcatsystems.co.uk/>)
+build_requires: 
+  Test::More: 0
+distribution_type: module
+generated_by: Module::Install version 0.67
+license: perl
+meta-spec: 
+  url: http://module-build.sourceforge.net/META-spec-v1.3.html
+  version: 1.3
+name: Devel-REPL
+no_index: 
+  directory: 
+    - inc
+    - t
+requires: 
+  Data::Dump::Streamer: 0
+  File::HomeDir: 0
+  File::Spec: 0
+  Lexical::Persistence: 0
+  Moose: 0
+  MooseX::Getopt: 0
+  MooseX::Object::Pluggable: 0
+  Term::ReadLine: 0
+  namespace::clean: 0
+  perl: 5.8.1
+version: 1.001000
diff --git a/cpan/CPAN-Meta/t/data/META-1_4.yml b/cpan/CPAN-Meta/t/data/META-1_4.yml
new file mode 100644 (file)
index 0000000..801f579
--- /dev/null
@@ -0,0 +1,132 @@
+---
+abstract: 'Build and install Perl modules'
+author:
+  - 'Ken Williams <kwilliams@cpan.org>'
+  - "Development questions, bug reports, and patches should be sent to the\nModule-Build mailing list at <module-build@perl.org>."
+build_requires:
+  File::Temp: 0.15
+  Test::Harness: 3.16
+  Test::More: 0.49
+generated_by: 'Module::Build version 0.3608'
+license: perl
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.4.html
+  version: 1.4
+name: Module-Build
+provides:
+  Module::Build:
+    file: lib/Module/Build.pm
+    version: 0.36_08
+  Module::Build::Base:
+    file: lib/Module/Build/Base.pm
+    version: 0.36_08
+  Module::Build::Compat:
+    file: lib/Module/Build/Compat.pm
+    version: 0.36_08
+  Module::Build::Config:
+    file: lib/Module/Build/Config.pm
+    version: 0.36_08
+  Module::Build::Cookbook:
+    file: lib/Module/Build/Cookbook.pm
+    version: 0.36_08
+  Module::Build::Dumper:
+    file: lib/Module/Build/Dumper.pm
+    version: 0.36_08
+  Module::Build::ModuleInfo:
+    file: lib/Module/Build/ModuleInfo.pm
+    version: 0.36_08
+  Module::Build::Notes:
+    file: lib/Module/Build/Notes.pm
+    version: 0.36_08
+  Module::Build::PPMMaker:
+    file: lib/Module/Build/PPMMaker.pm
+    version: 0.36_08
+  Module::Build::Platform::Amiga:
+    file: lib/Module/Build/Platform/Amiga.pm
+    version: 0.36_08
+  Module::Build::Platform::Default:
+    file: lib/Module/Build/Platform/Default.pm
+    version: 0.36_08
+  Module::Build::Platform::EBCDIC:
+    file: lib/Module/Build/Platform/EBCDIC.pm
+    version: 0.36_08
+  Module::Build::Platform::MPEiX:
+    file: lib/Module/Build/Platform/MPEiX.pm
+    version: 0.36_08
+  Module::Build::Platform::MacOS:
+    file: lib/Module/Build/Platform/MacOS.pm
+    version: 0.36_08
+  Module::Build::Platform::RiscOS:
+    file: lib/Module/Build/Platform/RiscOS.pm
+    version: 0.36_08
+  Module::Build::Platform::Unix:
+    file: lib/Module/Build/Platform/Unix.pm
+    version: 0.36_08
+  Module::Build::Platform::VMS:
+    file: lib/Module/Build/Platform/VMS.pm
+    version: 0.36_08
+  Module::Build::Platform::VOS:
+    file: lib/Module/Build/Platform/VOS.pm
+    version: 0.36_08
+  Module::Build::Platform::Windows:
+    file: lib/Module/Build/Platform/Windows.pm
+    version: 0.36_08
+  Module::Build::Platform::aix:
+    file: lib/Module/Build/Platform/aix.pm
+    version: 0.36_08
+  Module::Build::Platform::cygwin:
+    file: lib/Module/Build/Platform/cygwin.pm
+    version: 0.36_08
+  Module::Build::Platform::darwin:
+    file: lib/Module/Build/Platform/darwin.pm
+    version: 0.36_08
+  Module::Build::Platform::os2:
+    file: lib/Module/Build/Platform/os2.pm
+    version: 0.36_08
+  Module::Build::PodParser:
+    file: lib/Module/Build/PodParser.pm
+    version: 0.36_08
+  Module::Build::Version:
+    file: lib/Module/Build/Version.pm
+    version: 0.77
+  Module::Build::YAML:
+    file: lib/Module/Build/YAML.pm
+    version: 1.40
+  inc::latest:
+    file: lib/inc/latest.pm
+    version: 0.36_08
+  inc::latest::private:
+    file: lib/inc/latest/private.pm
+    version: 0.36_08
+recommends:
+  ExtUtils::Install: 0.3
+  ExtUtils::Manifest: 1.54
+  version: 0.74
+requires:
+  Cwd: 0
+  Data::Dumper: 0
+  ExtUtils::CBuilder: 0.27
+  ExtUtils::Install: 0
+  ExtUtils::Manifest: 0
+  ExtUtils::Mkbootstrap: 0
+  ExtUtils::ParseXS: 2.21
+  File::Basename: 0
+  File::Compare: 0
+  File::Copy: 0
+  File::Find: 0
+  File::Path: 0
+  File::Spec: 0.82
+  Getopt::Long: 0
+  IO::File: 0
+  Test::Harness: 0
+  Text::Abbrev: 0
+  Text::ParseWords: 0
+  perl: 5.006001
+resources:
+  MailingList: mailto:module-build@perl.org
+  license: http://dev.perl.org/licenses/
+  repository: http://github.com/dagolden/module-build/
+version: 0.36_08
+x-whatever: this is a custom field
+x_whatelse: so is this
+XWhatNow: and this
diff --git a/cpan/CPAN-Meta/t/data/META-2.json b/cpan/CPAN-Meta/t/data/META-2.json
new file mode 100644 (file)
index 0000000..d737fef
--- /dev/null
@@ -0,0 +1,89 @@
+{
+   "resources" : {
+      "license" : [
+         "http://dev.perl.org/licenses/"
+      ],
+      "repository" : {
+        "url" : "svn://repo.example.com/foo-bar#fakeanchor",
+        "web" : "http://www.example.com"
+      }
+   },
+   "generated_by" : "Module::Build version 0.36",
+   "meta-spec" : {
+      "version" : "2",
+      "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec"
+   },
+   "version" : "0.36",
+   "name" : "Module-Build",
+   "dynamic_config" : 1,
+   "author" : [
+      "Ken Williams <kwilliams@cpan.org>",
+      "Module-Build List <module-build@perl.org>"
+   ],
+   "release_status" : "stable",
+   "license" : [
+      "perl_5"
+   ],
+   "description" : "Module::Build is a system for building, testing, and installing Perl modules.  It is meant to be an alternative to ExtUtils::MakeMaker... blah blah blah",
+   "keywords" : [
+      "toolchain",
+      "cpan",
+      "dual-life"
+   ],
+   "prereqs" : {
+      "runtime" : {
+         "requires" : {
+            "File::Copy" : "0",
+            "IO::File" : "0",
+            "Data::Dumper" : "0",
+            "File::Spec" : "0",
+            "Config" : "0",
+            "ExtUtils::Install" : "0",
+            "perl" : "5.006",
+            "File::Compare" : "0",
+            "File::Find" : "0",
+            "File::Path" : "0",
+            "File::Basename" : "0",
+            "Cwd" : "0"
+         },
+         "recommends" : {
+            "YAML" : "0.35",
+            "ExtUtils::ParseXS" : "2.02",
+            "Pod::Text" : "0",
+            "ExtUtils::Install" : "0.3",
+            "Archive::Tar" : "1.00"
+         }
+      },
+      "build" : {
+         "requires" : {
+            "Build::Requires": "1.1",
+            "Test::More" : "0"
+         }
+      },
+      "test" : {
+         "requires" : {
+            "Test::More" : "0.88",
+            "Test::Requires" : "1.2"
+         }
+      }
+   },
+   "optional_features" : {
+      "domination" : {
+         "prereqs" : {
+            "develop" : {
+               "requires" : {
+                  "Genius::Evil" : "1.234"
+               }
+            },
+            "runtime" : {
+               "requires" : {
+                  "Machine::Weather" : "2.0"
+               }
+            }
+         },
+         "description" : "Take over the world"
+      }
+   },
+   "abstract" : "Build and install Perl modules",
+   "x_whatever" : "Custom key"
+}
diff --git a/cpan/CPAN-Meta/t/data/resources.yml b/cpan/CPAN-Meta/t/data/resources.yml
new file mode 100644 (file)
index 0000000..8013137
--- /dev/null
@@ -0,0 +1,49 @@
+--- #YAML:1.0
+name:               WWW-Mechanize
+version:            1.64
+abstract:           Handy web browsing in a Perl object
+author:
+    - Andy Lester <andy@petdance.com>
+license:            perl
+distribution_type:  module
+configure_requires:
+    ExtUtils::MakeMaker:  0
+build_requires:
+    ExtUtils::MakeMaker:  0
+requires:
+    Carp:                 0
+    File::Temp:           0
+    FindBin:              0
+    Getopt::Long:         0
+    HTML::Form:           1.038
+    HTML::HeadParser:     0
+    HTML::Parser:         3.33
+    HTML::TokeParser:     2.28
+    HTTP::Daemon:         0
+    HTTP::Request:        1.3
+    HTTP::Server::Simple:  0.35
+    HTTP::Server::Simple::CGI:  0
+    HTTP::Status:         0
+    LWP:                  5.829
+    LWP::UserAgent:       5.829
+    perl:                 5.008
+    Pod::Usage:           0
+    Test::More:           0.34
+    Test::Warn:           0.11
+    URI:                  1.36
+    URI::file:            0
+    URI::URL:             0
+resources:
+    bugtracker:   http://code.google.com/p/www-mechanize/issues/list
+    homepage:     http://code.google.com/p/www-mechanize/
+    license:      http://dev.perl.org/licenses/
+    MailingList:  http://groups.google.com/group/www-mechanize-users
+    Repository:   http://code.google.com/p/www-mechanize/source
+no_index:
+    directory:
+        - t
+        - inc
+generated_by:       ExtUtils::MakeMaker version 6.56
+meta-spec:
+    url:      http://module-build.sourceforge.net/META-spec-v1.4.html
+    version:  1.4
diff --git a/cpan/CPAN-Meta/t/data/restricted-2.json b/cpan/CPAN-Meta/t/data/restricted-2.json
new file mode 100644 (file)
index 0000000..88886db
--- /dev/null
@@ -0,0 +1,89 @@
+{
+   "resources" : {
+      "license" : [
+         "http://dev.perl.org/licenses/"
+      ],
+      "repository" : {
+        "url" : "svn://repo.example.com/foo-bar#fakeanchor",
+        "web" : "http://www.example.com"
+      }
+   },
+   "generated_by" : "Module::Build version 0.36",
+   "meta-spec" : {
+      "version" : "2",
+      "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec"
+   },
+   "version" : "0.36",
+   "name" : "Module-Build",
+   "dynamic_config" : 1,
+   "author" : [
+      "Ken Williams <kwilliams@cpan.org>",
+      "Module-Build List <module-build@perl.org>"
+   ],
+   "release_status" : "stable",
+   "license" : [
+      "restricted"
+   ],
+   "description" : "Module::Build is a system for building, testing, and installing Perl modules.  It is meant to be an alternative to ExtUtils::MakeMaker... blah blah blah",
+   "keywords" : [
+      "toolchain",
+      "cpan",
+      "dual-life"
+   ],
+   "prereqs" : {
+      "runtime" : {
+         "requires" : {
+            "File::Copy" : "0",
+            "IO::File" : "0",
+            "Data::Dumper" : "0",
+            "File::Spec" : "0",
+            "Config" : "0",
+            "ExtUtils::Install" : "0",
+            "perl" : "5.006",
+            "File::Compare" : "0",
+            "File::Find" : "0",
+            "File::Path" : "0",
+            "File::Basename" : "0",
+            "Cwd" : "0"
+         },
+         "recommends" : {
+            "YAML" : "0.35",
+            "ExtUtils::ParseXS" : "2.02",
+            "Pod::Text" : "0",
+            "ExtUtils::Install" : "0.3",
+            "Archive::Tar" : "1.00"
+         }
+      },
+      "build" : {
+         "requires" : {
+            "Build::Requires": "1.1",
+            "Test::More" : "0"
+         }
+      },
+      "test" : {
+         "requires" : {
+            "Test::More" : "0.88",
+            "Test::Requires" : "1.2"
+         }
+      }
+   },
+   "optional_features" : {
+      "domination" : {
+         "prereqs" : {
+            "develop" : {
+               "requires" : {
+                  "Genius::Evil" : "1.234"
+               }
+            },
+            "runtime" : {
+               "requires" : {
+                  "Machine::Weather" : "2.0"
+               }
+            }
+         },
+         "description" : "Take over the world"
+      }
+   },
+   "abstract" : "Build and install Perl modules",
+   "x_whatever" : "Custom key"
+}
diff --git a/cpan/CPAN-Meta/t/data/restrictive-1_4.yml b/cpan/CPAN-Meta/t/data/restrictive-1_4.yml
new file mode 100644 (file)
index 0000000..b780016
--- /dev/null
@@ -0,0 +1,132 @@
+---
+abstract: 'Build and install Perl modules'
+author:
+  - 'Ken Williams <kwilliams@cpan.org>'
+  - "Development questions, bug reports, and patches should be sent to the\nModule-Build mailing list at <module-build@perl.org>."
+build_requires:
+  File::Temp: 0.15
+  Test::Harness: 3.16
+  Test::More: 0.49
+generated_by: 'Module::Build version 0.3608'
+license: restrictive
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.4.html
+  version: 1.4
+name: Module-Build
+provides:
+  Module::Build:
+    file: lib/Module/Build.pm
+    version: 0.36_08
+  Module::Build::Base:
+    file: lib/Module/Build/Base.pm
+    version: 0.36_08
+  Module::Build::Compat:
+    file: lib/Module/Build/Compat.pm
+    version: 0.36_08
+  Module::Build::Config:
+    file: lib/Module/Build/Config.pm
+    version: 0.36_08
+  Module::Build::Cookbook:
+    file: lib/Module/Build/Cookbook.pm
+    version: 0.36_08
+  Module::Build::Dumper:
+    file: lib/Module/Build/Dumper.pm
+    version: 0.36_08
+  Module::Build::ModuleInfo:
+    file: lib/Module/Build/ModuleInfo.pm
+    version: 0.36_08
+  Module::Build::Notes:
+    file: lib/Module/Build/Notes.pm
+    version: 0.36_08
+  Module::Build::PPMMaker:
+    file: lib/Module/Build/PPMMaker.pm
+    version: 0.36_08
+  Module::Build::Platform::Amiga:
+    file: lib/Module/Build/Platform/Amiga.pm
+    version: 0.36_08
+  Module::Build::Platform::Default:
+    file: lib/Module/Build/Platform/Default.pm
+    version: 0.36_08
+  Module::Build::Platform::EBCDIC:
+    file: lib/Module/Build/Platform/EBCDIC.pm
+    version: 0.36_08
+  Module::Build::Platform::MPEiX:
+    file: lib/Module/Build/Platform/MPEiX.pm
+    version: 0.36_08
+  Module::Build::Platform::MacOS:
+    file: lib/Module/Build/Platform/MacOS.pm
+    version: 0.36_08
+  Module::Build::Platform::RiscOS:
+    file: lib/Module/Build/Platform/RiscOS.pm
+    version: 0.36_08
+  Module::Build::Platform::Unix:
+    file: lib/Module/Build/Platform/Unix.pm
+    version: 0.36_08
+  Module::Build::Platform::VMS:
+    file: lib/Module/Build/Platform/VMS.pm
+    version: 0.36_08
+  Module::Build::Platform::VOS:
+    file: lib/Module/Build/Platform/VOS.pm
+    version: 0.36_08
+  Module::Build::Platform::Windows:
+    file: lib/Module/Build/Platform/Windows.pm
+    version: 0.36_08
+  Module::Build::Platform::aix:
+    file: lib/Module/Build/Platform/aix.pm
+    version: 0.36_08
+  Module::Build::Platform::cygwin:
+    file: lib/Module/Build/Platform/cygwin.pm
+    version: 0.36_08
+  Module::Build::Platform::darwin:
+    file: lib/Module/Build/Platform/darwin.pm
+    version: 0.36_08
+  Module::Build::Platform::os2:
+    file: lib/Module/Build/Platform/os2.pm
+    version: 0.36_08
+  Module::Build::PodParser:
+    file: lib/Module/Build/PodParser.pm
+    version: 0.36_08
+  Module::Build::Version:
+    file: lib/Module/Build/Version.pm
+    version: 0.77
+  Module::Build::YAML:
+    file: lib/Module/Build/YAML.pm
+    version: 1.40
+  inc::latest:
+    file: lib/inc/latest.pm
+    version: 0.36_08
+  inc::latest::private:
+    file: lib/inc/latest/private.pm
+    version: 0.36_08
+recommends:
+  ExtUtils::Install: 0.3
+  ExtUtils::Manifest: 1.54
+  version: 0.74
+requires:
+  Cwd: 0
+  Data::Dumper: 0
+  ExtUtils::CBuilder: 0.27
+  ExtUtils::Install: 0
+  ExtUtils::Manifest: 0
+  ExtUtils::Mkbootstrap: 0
+  ExtUtils::ParseXS: 2.21
+  File::Basename: 0
+  File::Compare: 0
+  File::Copy: 0
+  File::Find: 0
+  File::Path: 0
+  File::Spec: 0.82
+  Getopt::Long: 0
+  IO::File: 0
+  Test::Harness: 0
+  Text::Abbrev: 0
+  Text::ParseWords: 0
+  perl: 5.006001
+resources:
+  MailingList: mailto:module-build@perl.org
+  license: http://dev.perl.org/licenses/
+  repository: http://github.com/dagolden/module-build/
+version: 0.36_08
+x-whatever: this is a custom field
+x_whatelse: so is this
+XWhatNow: and this
diff --git a/cpan/CPAN-Meta/t/load-bad.t b/cpan/CPAN-Meta/t/load-bad.t
new file mode 100644 (file)
index 0000000..d18b65c
--- /dev/null
@@ -0,0 +1,25 @@
+use strict;
+use warnings;
+use Test::More 0.88;
+
+use CPAN::Meta;
+use File::Spec;
+use IO::Dir;
+
+sub _slurp { do { local(@ARGV,$/)=shift(@_); <> } }
+
+my $data_dir = IO::Dir->new( 't/data-bad' );
+my @files = sort grep { /^\w/ } $data_dir->read;
+
+for my $f ( sort @files ) {
+  my $path = File::Spec->catfile('t','data-bad',$f);
+  my $meta = eval { CPAN::Meta->load_file( $path, { fix_errors => 1 }  ) };
+  ok( defined $meta, "load_file('$f')" ) or diag $@;
+  my $string = _slurp($path);
+  my $method =  $path =~ /\.json/ ? "load_json_string" : "load_yaml_string";
+  my $meta2 = eval { CPAN::Meta->$method( $string, { fix_errors => 1 } ) };
+  ok( defined $meta2, "$method(slurp('$f'))" ) or diag $@;
+}
+
+done_testing;
+
diff --git a/cpan/CPAN-Meta/t/meta-obj.t b/cpan/CPAN-Meta/t/meta-obj.t
new file mode 100644 (file)
index 0000000..522f67d
--- /dev/null
@@ -0,0 +1,240 @@
+use strict;
+use warnings;
+use Test::More 0.88;
+
+use CPAN::Meta;
+
+use Scalar::Util qw(blessed);
+
+my $distmeta = {
+  name     => 'Module-Build',
+  abstract => 'Build and install Perl modules',
+  description =>  "Module::Build is a system for building, testing, "
+              .   "and installing Perl modules.  It is meant to be an "
+              .   "alternative to ExtUtils::MakeMaker... blah blah blah",
+  version  => '0.36',
+  author   => [
+    'Ken Williams <kwilliams@cpan.org>',
+    'Module-Build List <module-build@perl.org>', # additional contact
+  ],
+  release_status => 'stable',
+  license  => [ 'perl_5' ],
+  prereqs => {
+    runtime => {
+      requires => {
+        'perl'   => '5.006',
+        'Config' => '0',
+        'Cwd'    => '0',
+        'Data::Dumper' => '0',
+        'ExtUtils::Install' => '0',
+        'File::Basename' => '0',
+        'File::Compare'  => '0',
+        'File::Copy' => '0',
+        'File::Find' => '0',
+        'File::Path' => '0',
+        'File::Spec' => '0',
+        'IO::File'   => '0',
+      },
+      recommends => {
+        'Archive::Tar' => '1.00',
+        'ExtUtils::Install' => '0.3',
+        'ExtUtils::ParseXS' => '2.02',
+        'Pod::Text' => '0',
+        'YAML' => '0.35',
+      },
+    },
+    build => {
+      requires => {
+        'Test::More' => '0',
+      },
+    }
+  },
+  resources => {
+    license => ['http://dev.perl.org/licenses/'],
+  },
+  optional_features => {
+    domination => {
+      description => 'Take over the world',
+      prereqs     => {
+        develop => { requires => { 'Genius::Evil'     => '1.234' } },
+        runtime => { requires => { 'Machine::Weather' => '2.0'   } },
+      },
+    },
+  },
+  dynamic_config => 1,
+  keywords => [ qw/ toolchain cpan dual-life / ],
+  'meta-spec' => {
+    version => '2',
+    url     => 'http://search.cpan.org/perldoc?CPAN::Meta::Spec',
+  },
+  generated_by => 'Module::Build version 0.36',
+  x_authority => 'cpan:FLORA',
+  X_deep => { deep => 'structure' },
+};
+
+my $meta = CPAN::Meta->new($distmeta);
+
+is(
+  blessed($meta->as_struct),
+  undef,
+  "the result of ->as_struct is unblessed",
+);
+
+is_deeply( $meta->as_struct, $distmeta, "->as_struct (deep comparison)" );
+isnt( $meta->as_struct, $distmeta, "->as_struct (is a deep clone)" );
+
+my $old_copy = $meta->as_struct( {version => "1.4"} );
+is( $old_copy->{'meta-spec'}{version}, "1.4", "->as_struct (downconversion)" );
+
+isnt( $meta->resources, $meta->{resources}, "->resource (map values are deep cloned)");
+
+is($meta->name,     'Module-Build', '->name');
+is($meta->abstract, 'Build and install Perl modules', '->abstract');
+
+like($meta->description, qr/Module::Build.+blah blah blah/, '->description');
+
+is($meta->version,   '0.36', '->version');
+
+ok($meta->dynamic_config, "->dynamic_config");
+
+is_deeply(
+  [ $meta->author ],
+  [
+    'Ken Williams <kwilliams@cpan.org>',
+    'Module-Build List <module-build@perl.org>',
+  ],
+  '->author',
+);
+
+is_deeply(
+  [ $meta->authors ],
+  [
+    'Ken Williams <kwilliams@cpan.org>',
+    'Module-Build List <module-build@perl.org>',
+  ],
+  '->authors',
+);
+
+is_deeply(
+  [ $meta->license ],
+  [ qw(perl_5) ],
+  '->license',
+);
+
+is_deeply(
+  [ $meta->licenses ],
+  [ qw(perl_5) ],
+  '->licenses',
+);
+
+is_deeply(
+  [ $meta->keywords ],
+  [ qw/ toolchain cpan dual-life / ],
+  '->keywords',
+);
+
+is_deeply(
+  $meta->resources,
+  { license => [ 'http://dev.perl.org/licenses/' ] },
+  '->resources',
+);
+
+is_deeply(
+  $meta->meta_spec,
+  {
+    version => '2',
+    url     => 'http://search.cpan.org/perldoc?CPAN::Meta::Spec',
+  },
+  '->meta_spec',
+);
+
+is($meta->meta_spec_version, '2', '->meta_spec_version');
+
+like($meta->generated_by, qr/Module::Build version 0.36/, '->generated_by');
+
+my $basic = $meta->effective_prereqs;
+
+is_deeply(
+  $basic->as_string_hash,
+  $distmeta->{prereqs},
+  "->effective_prereqs()"
+);
+
+is_deeply( [ sort $meta->custom_keys ] , [ 'X_deep', 'x_authority' ],
+  "->custom_keys"
+);
+
+is( $meta->custom('x_authority'), 'cpan:FLORA', "->custom(X)" );
+
+is_deeply( $meta->custom('X_deep'), $distmeta->{'X_deep'},
+  "->custom(X) [is_deeply]"
+);
+
+isnt( $meta->custom('X_deep'), $distmeta->{'X_deep'},
+  "->custom(x) [is a deep clone]"
+);
+
+my $with_features = $meta->effective_prereqs([ qw(domination) ]);
+
+is_deeply(
+  $with_features->as_string_hash,
+  {
+    develop => { requires => { 'Genius::Evil'     => '1.234' } },
+    runtime => {
+      requires => {
+        'perl'   => '5.006',
+        'Config' => '0',
+        'Cwd'    => '0',
+        'Data::Dumper' => '0',
+        'ExtUtils::Install' => '0',
+        'File::Basename' => '0',
+        'File::Compare'  => '0',
+        'File::Copy' => '0',
+        'File::Find' => '0',
+        'File::Path' => '0',
+        'File::Spec' => '0',
+        'IO::File'   => '0',
+        'Machine::Weather' => '2.0',
+      },
+      recommends => {
+        'Archive::Tar' => '1.00',
+        'ExtUtils::Install' => '0.3',
+        'ExtUtils::ParseXS' => '2.02',
+        'Pod::Text' => '0',
+        'YAML' => '0.35',
+      },
+    },
+    build => {
+      requires => {
+        'Test::More' => '0',
+      },
+    }
+  },
+  "->effective_prereqs([ qw(domination) ])"
+);
+
+my $chk_feature = sub {
+  my $feature = shift;
+
+  isa_ok($feature, 'CPAN::Meta::Feature');
+
+  is($feature->identifier,  'domination',          '$feature->identifier');
+  is($feature->description, 'Take over the world', '$feature->description');
+
+  is_deeply(
+    $feature->prereqs->as_string_hash,
+    {
+      develop => { requires => { 'Genius::Evil'     => '1.234' } },
+      runtime => { requires => { 'Machine::Weather' => '2.0'   } },
+    },
+    '$feature->prereqs',
+  );
+};
+
+my @features = $meta->features;
+is(@features, 1, "we got one feature");
+$chk_feature->($features[0]);
+
+$chk_feature->( $meta->feature('domination') );
+
+done_testing;
diff --git a/cpan/CPAN-Meta/t/no-index.t b/cpan/CPAN-Meta/t/no-index.t
new file mode 100644 (file)
index 0000000..4f8df45
--- /dev/null
@@ -0,0 +1,86 @@
+use strict;
+use warnings;
+use Test::More 0.88;
+
+use CPAN::Meta;
+
+my %distmeta = (
+  name     => 'Module-Billed',
+  abstract => 'inscrutable',
+  version  => '1',
+  author   => 'Joe',
+  release_status => 'stable',
+  license  => 'perl_5',
+  'meta-spec' => {
+    version => '2',
+    url     => 'http://search.cpan.org/perldoc?CPAN::Meta::Spec',
+  },
+  dynamic_config => 1,
+  generated_by   => 'Module::Build version 0.36',
+);
+
+{
+  my $meta = CPAN::Meta->new({ %distmeta });
+
+  ok(
+    $meta->should_index_package('Foo::Bar::Baz'),
+    'we index any old package, without a no_index rule'
+  );
+
+  ok(
+    $meta->should_index_file('lib/Foo/Bar/Baz.pm'),
+    'we index any old file, without a no_index rule'
+  );
+}
+
+{
+  my $meta = CPAN::Meta->new({
+    %distmeta,
+    no_index => {
+      package   => [ 'Foo::Bar' ],
+      namespace => [ 'Foo::Bar::Baz' ],
+    }
+  });
+
+  ok(
+    ! $meta->should_index_package('Foo::Bar'),
+    'exclude a specific package'
+  );
+
+  ok(
+    $meta->should_index_package('Foo::Bar::Baz'),
+    'namespace X does not exclude package X'
+  );
+
+  ok(
+    ! $meta->should_index_package('Foo::Bar::Baz::Quux'),
+    'exclude something under a namespace'
+  );
+}
+
+{
+  my $meta = CPAN::Meta->new({
+    %distmeta,
+    no_index => {
+      file      => [ 'lib/Foo/Bar.pm'  ],
+      directory => [ 'lib/Foo/Bar/Baz' ],
+    }
+  });
+
+  ok(
+    ! $meta->should_index_file('lib/Foo/Bar.pm'),
+    'exclude a specific file'
+  );
+
+  ok(
+    $meta->should_index_file('lib/Foo/Bar/Baz.pm'),
+    'do not exclude a file with a name like an excluded dir',
+  );
+
+  ok(
+    ! $meta->should_index_file('lib/Foo/Bar/Baz/Quux.pm'),
+    'exclude something under a directory'
+  );
+}
+
+done_testing;
diff --git a/cpan/CPAN-Meta/t/prereqs-finalize.t b/cpan/CPAN-Meta/t/prereqs-finalize.t
new file mode 100644 (file)
index 0000000..040e3f3
--- /dev/null
@@ -0,0 +1,91 @@
+use strict;
+use warnings;
+use Test::More 0.88;
+
+use CPAN::Meta::Prereqs;
+
+sub dies_ok (&@) {
+  my ($code, $qr, $comment) = @_;
+
+  my $lived = eval { $code->(); 1 };
+
+  if ($lived) {
+    fail("$comment: did not die");
+  } else {
+    like($@, $qr, $comment);
+  }
+}
+
+my $prereqs_struct = {
+  runtime => {
+    requires => {
+      'Config' => '1.234',
+      'Cwd'    => '876.5',
+      'IO::File'   => 0,
+      'perl'       => '5.005_03',
+    },
+    recommends => {
+      'Pod::Text' => 0,
+      'YAML'      => '0.35',
+    },
+  },
+  build => {
+    requires => {
+      'Test' => 0,
+    },
+  }
+};
+
+my $prereqs = CPAN::Meta::Prereqs->new($prereqs_struct);
+
+isa_ok($prereqs, 'CPAN::Meta::Prereqs');
+
+$prereqs->finalize;
+
+ok($prereqs->is_finalized, 'cloned obj is not finalized');
+
+is_deeply($prereqs->as_string_hash, $prereqs_struct, '...and still round-trip');
+
+$prereqs->requirements_for(qw(runtime requires))->add_minimum(Cwd => 10);
+
+pass('...we can add a minimum if it has no effect');
+
+dies_ok
+  { $prereqs->requirements_for(qw(runtime requires))->add_minimum(Cwd => 1000) }
+  qr{finalized req},
+  '...but we die if it would alter a finalized prereqs';
+
+$prereqs->requirements_for(qw(develop suggests));
+
+pass('...we can get a V:R object for a previously unconfigured phase');
+
+dies_ok
+  { $prereqs->requirements_for(qw(develop suggests))->add_minimum(Foo => 1) }
+  qr{finalized req},
+  '...but we die if we try to put anything in it';
+
+my $clone = $prereqs->clone;
+
+isa_ok($clone, 'CPAN::Meta::Prereqs', 'cloned prereqs obj');
+
+ok(! $clone->is_finalized, 'cloned obj is not finalized');
+
+is_deeply($clone->as_string_hash, $prereqs_struct, '...it still round-trips');
+
+$clone->requirements_for(qw(runtime requires))->add_minimum(Cwd => 10);
+
+pass('...we can add minimum if it has no effect');
+
+$clone->requirements_for(qw(runtime requires))->add_minimum(Cwd => 1000);
+
+pass('...or if it has an effect');
+
+$clone->requirements_for(qw(develop suggests));
+
+pass('...we can get a V:R object for a previously unconfigured phase');
+
+$clone->requirements_for(qw(develop suggests))->add_minimum(Foo => 1);
+
+pass('...and we can add stuff to it');
+
+done_testing;
diff --git a/cpan/CPAN-Meta/t/prereqs-merge.t b/cpan/CPAN-Meta/t/prereqs-merge.t
new file mode 100644 (file)
index 0000000..63e267e
--- /dev/null
@@ -0,0 +1,104 @@
+use strict;
+use warnings;
+use Test::More 0.88;
+
+use CPAN::Meta::Prereqs;
+
+my $prereq_struct_1 = {
+  runtime => {
+    requires => {
+      'Config' => 0,
+      'Cwd'    => 0,
+      'perl'   => '5.005_03',
+    },
+    recommends => {
+      'Pod::Text' => 0,
+      'YAML'      => 0.35,
+    },
+  },
+  build => {
+    requires => {
+      'Test' => 0,
+    },
+  }
+};
+
+my $prereq_1 = CPAN::Meta::Prereqs->new($prereq_struct_1);
+
+isa_ok($prereq_1, 'CPAN::Meta::Prereqs', 'first prereq');
+
+is_deeply($prereq_1->as_string_hash, $prereq_struct_1, '...and it round trips');
+
+my $prereq_struct_2 = {
+  develop => {
+    requires => {
+      'Dist::Mothra' => '1.230',
+    },
+    suggests => {
+      'Blort::Blortex' => '== 10.20',
+    },
+  },
+  runtime => {
+    requires => {
+      'Config' => 1,
+      'perl'       => '< 6',
+    },
+  },
+  build => {
+    suggests => {
+      'Module::Build::Bob' => '20100101',
+    },
+  }
+};
+
+my $prereq_2 = CPAN::Meta::Prereqs->new($prereq_struct_2);
+
+isa_ok($prereq_2, 'CPAN::Meta::Prereqs', 'second prereq');
+
+is_deeply($prereq_1->as_string_hash, $prereq_struct_1, '...and it round trips');
+
+my $merged = $prereq_1->with_merged_prereqs($prereq_2);
+
+my $want = {
+  develop => {
+    requires => {
+      'Dist::Mothra' => '1.230',
+    },
+    suggests => {
+      'Blort::Blortex' => '== 10.20',
+    },
+  },
+  runtime => {
+    requires => {
+      'Config' => 1,
+      'Cwd'    => 0,
+      'perl'   => '>= 5.005_03, < 6',
+    },
+    recommends => {
+      'Pod::Text' => 0,
+      'YAML'      => 0.35,
+    },
+  },
+  build => {
+    requires => {
+      'Test' => 0,
+    },
+    suggests => {
+      'Module::Build::Bob' => '20100101',
+    },
+  },
+};
+
+is_deeply(
+  $merged->as_string_hash,
+  $want,
+  "we get the right result of merging two prereqs",
+);
+
+is_deeply(
+  $prereq_2->with_merged_prereqs($prereq_1)->as_string_hash,
+  $want,
+  "...and the merge works the same in reverse",
+);
+
+done_testing;
diff --git a/cpan/CPAN-Meta/t/prereqs.t b/cpan/CPAN-Meta/t/prereqs.t
new file mode 100644 (file)
index 0000000..a24f272
--- /dev/null
@@ -0,0 +1,121 @@
+use strict;
+use warnings;
+use Test::More 0.88;
+
+use CPAN::Meta::Prereqs;
+
+my $prereq_struct = {
+  runtime => {
+    requires => {
+      'Config' => 0,
+      'Cwd'    => 0,
+      'Data::Dumper' => 0,
+      'ExtUtils::Install' => 0,
+      'File::Basename' => 0,
+      'File::Compare'  => 0,
+      'File::Copy' => 0,
+      'File::Find' => 0,
+      'File::Path' => 0,
+      'File::Spec' => 0,
+      'IO::File'   => 0,
+      'perl'       => '5.005_03',
+    },
+    recommends => {
+      'Archive::Tar' => '1.00',
+      'ExtUtils::Install' => 0.3,
+      'ExtUtils::ParseXS' => 2.02,
+      'Pod::Text' => 0,
+      'YAML' => 0.35,
+    },
+  },
+  build => {
+    requires => {
+      'Test' => 0,
+    },
+  }
+};
+
+my $prereq = CPAN::Meta::Prereqs->new($prereq_struct);
+
+isa_ok($prereq, 'CPAN::Meta::Prereqs');
+
+is_deeply($prereq->as_string_hash, $prereq_struct, "round-trip okay");
+
+{
+  my $req = $prereq->requirements_for(qw(runtime requires));
+  my @req_mod = $req->required_modules;
+
+  ok(
+    (grep { 'Cwd' eq $_ } @req_mod),
+    "we got the runtime requirements",
+  );
+
+  ok(
+    (! grep { 'YAML' eq $_ } @req_mod),
+    "...but not the runtime recommendations",
+  );
+
+  ok(
+    (! grep { 'Test' eq $_ } @req_mod),
+    "...nor the build requirements",
+  );
+}
+
+{
+  my $req = $prereq->requirements_for(qw(runtime requires));
+  my $rec = $prereq->requirements_for(qw(runtime recommends));
+
+  my $merged = $req->clone->add_requirements($rec);
+
+  my @req_mod = $merged->required_modules;
+
+  ok(
+    (grep { 'Cwd' eq $_ } @req_mod),
+    "we got the runtime requirements",
+  );
+
+  ok(
+    (grep { 'YAML' eq $_ } @req_mod),
+    "...and the runtime recommendations",
+  );
+
+  ok(
+    (! grep { 'Test' eq $_ } @req_mod),
+    "...but not the build requirements",
+  );
+}
+
+{
+  my $req = $prereq->requirements_for(qw(runtime suggests));
+  my @req_mod = $req->required_modules;
+
+  is(@req_mod, 0, "empty set of runtime/suggests requirements");
+}
+
+{
+  my $req = $prereq->requirements_for(qw(develop suggests));
+  my @req_mod = $req->required_modules;
+
+  is(@req_mod, 0, "empty set of develop/suggests requirements");
+}
+
+{
+  my $new_prereq = CPAN::Meta::Prereqs->new;
+
+  $new_prereq
+    ->requirements_for(qw(runtime requires))
+    ->add_minimum(Foo => '1.000');
+
+  $new_prereq
+    ->requirements_for(qw(runtime requires))
+    ->add_minimum(Bar => '2.976');
+
+  is_deeply(
+    $new_prereq->as_string_hash,
+    { runtime => { requires => { Foo => '1.000', Bar => '2.976' } } },
+    'we can accumulate new requirements on a prereq object',
+  );
+}
+
+done_testing;
+
diff --git a/cpan/CPAN-Meta/t/repository.t b/cpan/CPAN-Meta/t/repository.t
new file mode 100644 (file)
index 0000000..b8800d9
--- /dev/null
@@ -0,0 +1,225 @@
+use strict;
+use warnings;
+use Test::More 0.88;
+
+use CPAN::Meta;
+
+# 1.4 repository upgrade
+{
+  my $label = "(version 1.4) old repository winds up in 'url'";
+  my $meta = CPAN::Meta->new(
+    {
+      name     => 'Module-Billed',
+      abstract => 'inscrutable',
+      version  => '1',
+      author   => 'Joe',
+      release_status => 'stable',
+      license  => 'perl_5',
+      dynamic_config => 1,
+      generated_by   => 'hand',
+      'meta-spec' => {
+        version => '1.4',
+        url     => 'http://module-build.sourceforge.net/META-spec-v1.4.html',
+      },
+      resources => {
+        repository => 'http://example.com/',
+      },
+    },
+    { lazy_validation => 1 },
+  );
+
+  is_deeply(
+    $meta->resources,
+    {
+      repository => {
+        url => 'http://example.com/',
+      },
+    },
+    $label,
+  );
+}
+
+{
+  my $label = "(version 2  ) http in web passed through unchanged";
+  my $meta = CPAN::Meta->new(
+    {
+      name     => 'Module-Billed',
+      abstract => 'inscrutable',
+      version  => '1',
+      author   => 'Joe',
+      release_status => 'stable',
+      license  => 'perl_5',
+      dynamic_config => 1,
+      generated_by   => 'hand',
+      'meta-spec' => {
+        version => '2',
+      },
+      resources => {
+        repository => {
+          web => 'http://example.com/',
+        },
+      },
+    },
+    { lazy_validation => 1 },
+  );
+
+
+  is_deeply(
+    $meta->{resources},
+    {
+      repository => {
+        web => 'http://example.com/',
+      },
+    },
+    $label
+  );
+}
+
+{
+  my $label = "(version 2  ) http in url passed through unchanged";
+  my $meta = CPAN::Meta->new(
+    {
+      name     => 'Module-Billed',
+      abstract => 'inscrutable',
+      version  => '1',
+      author   => 'Joe',
+      release_status => 'stable',
+      license  => 'perl_5',
+      dynamic_config => 1,
+      generated_by   => 'hand',
+      'meta-spec' => {
+        version => '2',
+      },
+      resources => {
+        repository => {
+          url => 'http://example.com/',
+        },
+      },
+    },
+    { lazy_validation => 1 },
+  );
+
+
+  is_deeply(
+    $meta->{resources},
+    {
+      repository => {
+        url => 'http://example.com/',
+      },
+    },
+    $label
+  );
+}
+
+{
+  my $label = "(version 2  ) svn in url adds svn type";
+  my $meta = CPAN::Meta->new(
+    {
+      name     => 'Module-Billed',
+      abstract => 'inscrutable',
+      version  => '1',
+      author   => 'Joe',
+      release_status => 'stable',
+      license  => 'perl_5',
+      dynamic_config => 1,
+      generated_by   => 'hand',
+      'meta-spec' => {
+        version => '2',
+      },
+      resources => {
+        repository => {
+          url => 'svn://example.com/',
+        },
+      },
+    },
+    { lazy_validation => 1 },
+  );
+
+
+  is_deeply(
+    $meta->{resources},
+    {
+      repository => {
+        url => 'svn://example.com/',
+        type => 'svn',
+      },
+    },
+    $label
+  );
+}
+
+{
+  my $label = "(version 2  ) git in url adds svn type";
+  my $meta = CPAN::Meta->new(
+    {
+      name     => 'Module-Billed',
+      abstract => 'inscrutable',
+      version  => '1',
+      author   => 'Joe',
+      release_status => 'stable',
+      license  => 'perl_5',
+      dynamic_config => 1,
+      generated_by   => 'hand',
+      'meta-spec' => {
+        version => '2',
+      },
+      resources => {
+        repository => {
+          url => 'git://example.com/',
+        },
+      },
+    },
+    { lazy_validation => 1 },
+  );
+
+
+  is_deeply(
+    $meta->{resources},
+    {
+      repository => {
+        url => 'git://example.com/',
+        type => 'git',
+      },
+    },
+    $label
+  );
+}
+
+{
+  my $label = "(version 2  ) pre-existing type preserved";
+  my $meta = CPAN::Meta->new(
+    {
+      name     => 'Module-Billed',
+      abstract => 'inscrutable',
+      version  => '1',
+      author   => 'Joe',
+      release_status => 'stable',
+      license  => 'perl_5',
+      dynamic_config => 1,
+      generated_by   => 'hand',
+      'meta-spec' => {
+        version => '2',
+      },
+      resources => {
+        repository => {
+          url => 'git://example.com/',
+          type => 'msysgit',
+        },
+      },
+    },
+    { lazy_validation => 1 },
+  );
+
+
+  is_deeply(
+    $meta->{resources},
+    {
+      repository => {
+        url => 'git://example.com/',
+        type => 'msysgit',
+      },
+    },
+    $label
+  );
+}
+done_testing;
diff --git a/cpan/CPAN-Meta/t/save-load.t b/cpan/CPAN-Meta/t/save-load.t
new file mode 100644 (file)
index 0000000..73cac57
--- /dev/null
@@ -0,0 +1,99 @@
+use strict;
+use warnings;
+use Test::More 0.88;
+
+use CPAN::Meta;
+use File::Temp 0.20 ();
+use Parse::CPAN::Meta 1.4400;
+
+my $distmeta = {
+  name     => 'Module-Build',
+  abstract => 'Build and install Perl modules',
+  description =>  "Module::Build is a system for building, testing, "
+              .   "and installing Perl modules.  It is meant to be an "
+              .   "alternative to ExtUtils::MakeMaker... blah blah blah",
+  version  => '0.36',
+  author   => [
+    'Ken Williams <kwilliams@cpan.org>',
+    'Module-Build List <module-build@perl.org>', # additional contact
+  ],
+  release_status => 'stable',
+  license  => [ 'perl_5' ],
+  prereqs => {
+    runtime => {
+      requires => {
+        'perl'   => '5.006',
+        'Config' => '0',
+        'Cwd'    => '0',
+        'Data::Dumper' => '0',
+        'ExtUtils::Install' => '0',
+        'File::Basename' => '0',
+        'File::Compare'  => '0',
+        'File::Copy' => '0',
+        'File::Find' => '0',
+        'File::Path' => '0',
+        'File::Spec' => '0',
+        'IO::File'   => '0',
+      },
+      recommends => {
+        'Archive::Tar' => '1.00',
+        'ExtUtils::Install' => '0.3',
+        'ExtUtils::ParseXS' => '2.02',
+        'Pod::Text' => '0',
+        'YAML' => '0.35',
+      },
+    },
+    build => {
+      requires => {
+        'Test::More' => '0',
+      },
+    }
+  },
+  resources => {
+    license => ['http://dev.perl.org/licenses/'],
+  },
+  optional_features => {
+    domination => {
+      description => 'Take over the world',
+      prereqs     => {
+        develop => { requires => { 'Genius::Evil'     => '1.234' } },
+        runtime => { requires => { 'Machine::Weather' => '2.0'   } },
+      },
+    },
+  },
+  dynamic_config => 1,
+  keywords => [ qw/ toolchain cpan dual-life / ],
+  'meta-spec' => {
+    version => '2',
+    url     => 'http://search.cpan.org/perldoc?CPAN::Meta::Spec',
+  },
+  generated_by => 'Module::Build version 0.36',
+};
+
+my $meta = CPAN::Meta->new( $distmeta );
+
+my $tmpdir = File::Temp->newdir();
+my $metafile = File::Spec->catfile( $tmpdir, 'META.json' );
+
+ok( $meta->save($metafile), "save returns true" );
+ok( -f $metafile, "save meta to file" );
+
+ok( my $loaded = Parse::CPAN::Meta->load_file($metafile), 'load saved file' );
+is($loaded->{name},     'Module-Build', 'name correct');
+
+
+ok( $loaded = Parse::CPAN::Meta->load_file('t/data/META-1_4.yml'), 'load META-1.4' );
+is($loaded->{name},     'Module-Build', 'name correct');
+
+# Test saving with conversion
+
+my $metayml = File::Spec->catfile( $tmpdir, 'META.yml' );
+
+$meta->save($metayml, {version => "1.4"});
+ok( -f $metayml, "save meta to META.yml with conversion" );
+
+ok( $loaded = Parse::CPAN::Meta->load_file($metayml), 'load saved file' );
+is( $loaded->{name},     'Module-Build', 'name correct');
+is( $loaded->{requires}{perl}, "5.006", 'prereq correct' );
+
+done_testing;
diff --git a/cpan/CPAN-Meta/t/validator.t b/cpan/CPAN-Meta/t/validator.t
new file mode 100644 (file)
index 0000000..e76fcc2
--- /dev/null
@@ -0,0 +1,35 @@
+use strict;
+use warnings;
+use Test::More 0.88;
+
+use CPAN::Meta;
+use CPAN::Meta::Validator;
+use File::Spec;
+use IO::Dir;
+use Parse::CPAN::Meta 1.4400;
+
+{
+  my $data_dir = IO::Dir->new( 't/data' );
+  my @files = sort grep { /^\w/ } $data_dir->read;
+
+  for my $f ( @files ) {
+    my $meta = Parse::CPAN::Meta->load_file( File::Spec->catfile('t','data',$f) );
+    my $cmv = CPAN::Meta::Validator->new({%$meta});
+    ok( $cmv->is_valid, "$f validates" )
+      or diag( "ERRORS:\n" . join( "\n", $cmv->errors ) );
+  }
+}
+
+{
+  my $data_dir = IO::Dir->new( 't/data-fail' );
+  my @files = sort grep { /^\w/ } $data_dir->read;
+
+  for my $f ( @files ) {
+    my $meta = Parse::CPAN::Meta->load_file( File::Spec->catfile('t','data-fail',$f) );
+    my $cmv = CPAN::Meta::Validator->new({%$meta});
+    ok( ! $cmv->is_valid, "invalid $f doesn't validate" );
+  }
+}
+
+done_testing;
+
index 91f73cd..d49572d 100644 (file)
@@ -39,6 +39,8 @@
 /CPAN/Kwalify/distroprefs.yml
 /CPAN/LWP
 /CPAN/LWP/UserAgent.pm
+/CPAN/Meta
+/CPAN/Meta.pm
 /CPAN/Module.pm
 /CPAN/Nox.pm
 /CPAN/PAUSE2003.pub
index e28ce51..20652e2 100644 (file)
@@ -170,6 +170,16 @@ cribbed.
 
 =item *
 
+C<CPAN::Meta> version 2.110440 has been added as a dual-life module. It
+provides a standard library to read, interpret and write CPAN distribution
+metadata files (e.g. META.json and META.yml) which describes a
+distribution, its contents, and the requirements for building it and
+installing it. The latest CPAN distribution metadata specification is
+included as C<CPAN::Meta::Spec> and notes on changes in the specification
+over time are given in C<CPAN::Meta::History>.
+
+=item *
+
 C<Version::Requirements> version 0.101020 has been added as a dual-life
 module.  It provides a standard library to model and manipulates module
 prerequisites and version constraints as defined in the L<CPAN::Meta::Spec>.