The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Changes 023
LICENSE 44
MANIFEST 16
META.json 47
META.yml 811
Makefile.PL 2817
README 11
cpanfile 01
lib/Module/CPANfile/Environment.pm 15124
lib/Module/CPANfile/Prereq.pm 021
lib/Module/CPANfile/Prereqs.pm 0117
lib/Module/CPANfile/Requirement.pm 025
lib/Module/CPANfile/Result.pm 930
lib/Module/CPANfile.pm 2151
lib/cpanfile-faq.pod 510
lib/cpanfile.pod 33
script/cpanfile-dump 12
t/mirror.t 038
t/release-pod-syntax.t 32
t/requirement.t 081
20 files changed (This is a version diff) 187544
@@ -1,5 +1,28 @@
 Revision history for Module::Install::CPANfile
 
+1.1000  2014-08-29 13:48:13 JST
+        - Make it non-development release
+
+1.0905  2013-12-09 16:26:08 PST
+        - Support mirror syntax (masaki) #22
+
+1.0904  2013-09-21 07:56:31 JST
+        - Removed git/ref accessor from Requirement
+        - Added $requirement->has_options
+
+1.0903  2013-09-20 15:36:30 JST
+        - Added $cpanfile->merged_requirements to get the CPAN::Meta::Requirements for all prereqs
+
+1.0902  2013-09-20 13:45:15 JST
+        - s/rev/ref/ for git options
+
+1.0901  2013-09-19 18:59:55 JST
+        - Remove unused code
+
+1.0900  2013-09-19 18:45:59 JST
+        - Experimental support for git URL specification
+        - Complete rewrite of internal to preserve original prereq statement as much as possible
+
 1.0002  2013-09-06 12:26:11 PDT
         - Add cpanfile-dump utility (xdg)
 
@@ -1,4 +1,4 @@
-This software is copyright (c) 2013 by Tatsuhiko Miyagawa.
+This software is copyright (c) 2014 by Tatsuhiko Miyagawa.
 
 This is free software; you can redistribute it and/or modify it under
 the same terms as the Perl 5 programming language system itself.
@@ -12,7 +12,7 @@ b) the "Artistic License"
 
 --- The GNU General Public License, Version 1, February 1989 ---
 
-This software is Copyright (c) 2013 by Tatsuhiko Miyagawa.
+This software is Copyright (c) 2014 by Tatsuhiko Miyagawa.
 
 This is free software, licensed under:
 
@@ -22,7 +22,7 @@ This is free software, licensed under:
                      Version 1, February 1989
 
  Copyright (C) 1989 Free Software Foundation, Inc.
- 51 Franklin St, Suite 500, Boston, MA  02110-1335  USA
+ 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
@@ -272,7 +272,7 @@ That's all there is to it!
 
 --- The Artistic License 1.0 ---
 
-This software is Copyright (c) 2013 by Tatsuhiko Miyagawa.
+This software is Copyright (c) 2014 by Tatsuhiko Miyagawa.
 
 This is free software, licensed under:
 
@@ -1,3 +1,4 @@
+# This file was automatically generated by Dist::Zilla::Plugin::Manifest v5.020.
 Changes
 LICENSE
 MANIFEST
@@ -9,7 +10,9 @@ cpanfile
 dist.ini
 lib/Module/CPANfile.pm
 lib/Module/CPANfile/Environment.pm
-lib/Module/CPANfile/Result.pm
+lib/Module/CPANfile/Prereq.pm
+lib/Module/CPANfile/Prereqs.pm
+lib/Module/CPANfile/Requirement.pm
 lib/cpanfile-faq.pod
 lib/cpanfile.pod
 script/cpanfile-dump
@@ -18,5 +21,7 @@ t/Utils.pm
 t/feature.t
 t/from_prereqs.t
 t/merge.t
+t/mirror.t
 t/parse.t
 t/release-pod-syntax.t
+t/requirement.t
@@ -4,7 +4,7 @@
       "Tatsuhiko Miyagawa"
    ],
    "dynamic_config" : 0,
-   "generated_by" : "Dist::Milla version v1.0.4, Dist::Zilla version 4.300037, CPAN::Meta::Converter version 2.132140",
+   "generated_by" : "Dist::Milla version v1.0.5, Dist::Zilla version 5.020, CPAN::Meta::Converter version 2.142060",
    "license" : [
       "perl_5"
    ],
@@ -26,7 +26,7 @@
    "prereqs" : {
       "configure" : {
          "requires" : {
-            "ExtUtils::MakeMaker" : "6.30"
+            "ExtUtils::MakeMaker" : "0"
          }
       },
       "develop" : {
@@ -40,7 +40,8 @@
          },
          "requires" : {
             "CPAN::Meta" : "2.12091",
-            "CPAN::Meta::Prereqs" : "2.12091"
+            "CPAN::Meta::Prereqs" : "2.12091",
+            "parent" : "0"
          }
       },
       "test" : {
@@ -61,11 +62,13 @@
          "web" : "https://github.com/miyagawa/cpanfile"
       }
    },
-   "version" : "1.0002",
+   "version" : "1.1000",
    "x_contributors" : [
       "Atsushi Kato <ktat@cpan.org>",
       "David Golden <dagolden@cpan.org>",
       "David Steinbrunner <dsteinbrunner@pobox.com>",
+      "Gregory Oschwald <oschwald@gmail.com>",
+      "Kenichi Ishigaki <ishigaki@cpan.org>",
       "Masahiro Honma <hiratara@cpan.org>",
       "Michiel Beijen <michiel.beijen@gmail.com>",
       "grtodd <gtodd@iciti.ca>",
@@ -3,15 +3,15 @@ abstract: 'Parse cpanfile'
 author:
   - 'Tatsuhiko Miyagawa'
 build_requires:
-  Test::More: 0.88
+  Test::More: '0.88'
 configure_requires:
-  ExtUtils::MakeMaker: 6.30
+  ExtUtils::MakeMaker: '0'
 dynamic_config: 0
-generated_by: 'Dist::Milla version v1.0.4, Dist::Zilla version 4.300037, CPAN::Meta::Converter version 2.132140'
+generated_by: 'Dist::Milla version v1.0.5, Dist::Zilla version 5.020, CPAN::Meta::Converter version 2.142060'
 license: perl
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
-  version: 1.4
+  version: '1.4'
 name: Module-CPANfile
 no_index:
   directory:
@@ -22,19 +22,22 @@ no_index:
     - eg
     - examples
 recommends:
-  Pod::Usage: 0
+  Pod::Usage: '0'
 requires:
-  CPAN::Meta: 2.12091
-  CPAN::Meta::Prereqs: 2.12091
+  CPAN::Meta: '2.12091'
+  CPAN::Meta::Prereqs: '2.12091'
+  parent: '0'
 resources:
   bugtracker: https://github.com/miyagawa/cpanfile/issues
   homepage: https://github.com/miyagawa/cpanfile
   repository: https://github.com/miyagawa/cpanfile.git
-version: 1.0002
+version: '1.1000'
 x_contributors:
   - 'Atsushi Kato <ktat@cpan.org>'
   - 'David Golden <dagolden@cpan.org>'
   - 'David Steinbrunner <dsteinbrunner@pobox.com>'
+  - 'Gregory Oschwald <oschwald@gmail.com>'
+  - 'Kenichi Ishigaki <ishigaki@cpan.org>'
   - 'Masahiro Honma <hiratara@cpan.org>'
   - 'Michiel Beijen <michiel.beijen@gmail.com>'
   - 'grtodd <gtodd@iciti.ca>'
@@ -1,19 +1,19 @@
 
+# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v5.020.
 use strict;
 use warnings;
 
 
 
-use ExtUtils::MakeMaker 6.30;
+use ExtUtils::MakeMaker ;
 
 
 
 my %WriteMakefileArgs = (
   "ABSTRACT" => "Parse cpanfile",
   "AUTHOR" => "Tatsuhiko Miyagawa",
-  "BUILD_REQUIRES" => {},
   "CONFIGURE_REQUIRES" => {
-    "ExtUtils::MakeMaker" => "6.30"
+    "ExtUtils::MakeMaker" => 0
   },
   "DISTNAME" => "Module-CPANfile",
   "EXE_FILES" => [
@@ -24,42 +24,31 @@ my %WriteMakefileArgs = (
   "NAME" => "Module::CPANfile",
   "PREREQ_PM" => {
     "CPAN::Meta" => "2.12091",
-    "CPAN::Meta::Prereqs" => "2.12091"
+    "CPAN::Meta::Prereqs" => "2.12091",
+    "parent" => 0
   },
   "TEST_REQUIRES" => {
     "Test::More" => "0.88"
   },
-  "VERSION" => "1.0002",
+  "VERSION" => "1.1000",
   "test" => {
     "TESTS" => "t/*.t"
   }
 );
 
 
-unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) {
-  my $tr = delete $WriteMakefileArgs{TEST_REQUIRES};
-  my $br = $WriteMakefileArgs{BUILD_REQUIRES};
-  for my $mod ( keys %$tr ) {
-    if ( exists $br->{$mod} ) {
-      $br->{$mod} = $tr->{$mod} if $tr->{$mod} > $br->{$mod};
-    }
-    else {
-      $br->{$mod} = $tr->{$mod};
-    }
-  }
-}
+my %FallbackPrereqs = (
+  "CPAN::Meta" => "2.12091",
+  "CPAN::Meta::Prereqs" => "2.12091",
+  "Test::More" => "0.88",
+  "parent" => 0
+);
 
-unless ( eval { ExtUtils::MakeMaker->VERSION(6.56) } ) {
-  my $br = delete $WriteMakefileArgs{BUILD_REQUIRES};
-  my $pp = $WriteMakefileArgs{PREREQ_PM};
-  for my $mod ( keys %$br ) {
-    if ( exists $pp->{$mod} ) {
-      $pp->{$mod} = $br->{$mod} if $br->{$mod} > $pp->{$mod};
-    }
-    else {
-      $pp->{$mod} = $br->{$mod};
-    }
-  }
+
+unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) {
+  delete $WriteMakefileArgs{TEST_REQUIRES};
+  delete $WriteMakefileArgs{BUILD_REQUIRES};
+  $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs;
 }
 
 delete $WriteMakefileArgs{CONFIGURE_REQUIRES}
@@ -67,7 +67,7 @@ METHODS
           # read MYMETA's prereqs and print cpanfile representation of it
           my $meta = CPAN::Meta->load_file('MYMETA.json');
           my $file = Module::CPANfile->from_prereqs($meta->prereqs);
-          print $file->to_sring;
+          print $file->to_string;
 
         By default, it omits the phase where there're no modules registered.
         If you pass the argument of a true value, it will print them as
@@ -1,5 +1,6 @@
 requires 'CPAN::Meta', 2.12091;
 requires 'CPAN::Meta::Prereqs', 2.12091;
+requires 'parent';
 
 recommends 'Pod::Usage';
 
@@ -1,13 +1,14 @@
 package Module::CPANfile::Environment;
 use strict;
 use warnings;
-use Module::CPANfile::Result;
+use Module::CPANfile::Prereqs;
 use Carp ();
 
 my @bindings = qw(
     on requires recommends suggests conflicts
     feature
     osname
+    mirror
     configure_requires build_requires test_requires author_requires
 );
 
@@ -16,48 +17,156 @@ my $file_id = 1;
 sub new {
     my($class, $file) = @_;
     bless {
-        file => $file,
+        file     => $file,
+        phase    => 'runtime', # default phase
+        feature  => undef,
+        features => {},
+        prereqs  => Module::CPANfile::Prereqs->new,
+        mirrors  => [],
     }, $class;
 }
 
 sub bind {
-    my $class = shift;
+    my $self = shift;
     my $pkg = caller;
 
-    my $result = Module::CPANfile::Result->new;
     for my $binding (@bindings) {
         no strict 'refs';
-        *{"$pkg\::$binding"} = sub { $result->$binding(@_) };
+        *{"$pkg\::$binding"} = sub { $self->$binding(@_) };
     }
-
-    return $result;
 }
 
 sub parse {
     my($self, $code) = @_;
 
-    my($res, $err);
-
+    my $err;
     {
         local $@;
         $file_id++;
-        $res = eval <<EVAL;
+        $self->_evaluate(<<EVAL);
 package Module::CPANfile::Sandbox$file_id;
 no warnings;
-my \$_result;
-BEGIN { \$_result = Module::CPANfile::Environment->bind }
+BEGIN { \$_environment->bind }
 
 # line 1 "$self->{file}"
 $code;
-
-\$_result;
 EVAL
         $err = $@;
     }
 
     if ($err) { die "Parsing $self->{file} failed: $err" };
 
-    return $res;
+    return 1;
+}
+
+sub _evaluate {
+    my $_environment = $_[0];
+    eval $_[1];
+}
+
+sub prereqs { $_[0]->{prereqs} }
+
+sub mirrors { $_[0]->{mirrors} }
+
+# DSL goes from here
+
+sub on {
+    my($self, $phase, $code) = @_;
+    local $self->{phase} = $phase;
+    $code->();
+}
+
+sub feature {
+    my($self, $identifier, $description, $code) = @_;
+
+    # shortcut: feature identifier => sub { ... }
+    if (@_ == 3 && ref($description) eq 'CODE') {
+        $code = $description;
+        $description = $identifier;
+    }
+
+    unless (ref $description eq '' && ref $code eq 'CODE') {
+        Carp::croak("Usage: feature 'identifier', 'Description' => sub { ... }");
+    }
+
+    local $self->{feature} = $identifier;
+    $self->prereqs->add_feature($identifier, $description);
+
+    $code->();
+}
+
+sub osname { die "TODO" }
+
+sub mirror {
+    my($self, $url) = @_;
+    push @{$self->{mirrors}}, $url;
+}
+
+sub requirement_for {
+    my($self, $module, @args) = @_;
+
+    my $requirement = 0;
+    $requirement = shift @args if @args % 2;
+
+    return Module::CPANfile::Requirement->new(
+        name    => $module,
+        version => $requirement,
+        @args,
+    );
+}
+
+sub requires {
+    my $self = shift;
+    $self->add_prereq(requires => @_);
+}
+
+sub recommends {
+    my $self = shift;
+    $self->add_prereq(recommends => @_);
+}
+
+sub suggests {
+    my $self = shift;
+    $self->add_prereq(suggests => @_);
+}
+
+sub conflicts {
+    my $self = shift;
+    $self->add_prereq(conflicts => @_);
+}
+
+sub add_prereq {
+    my($self, $type, $module, @args) = @_;
+
+    $self->prereqs->add_prereq(
+        feature => $self->{feature},
+        phase   => $self->{phase},
+        type    => $type,
+        module  => $module,
+        requirement => $self->requirement_for($module, @args),
+    );
+}
+
+# Module::Install compatible shortcuts
+
+sub configure_requires {
+    my($self, @args) = @_;
+    $self->on(configure => sub { $self->requires(@args) });
+}
+
+sub build_requires {
+    my($self, @args) = @_;
+    $self->on(build => sub { $self->requires(@args) });
+}
+
+sub test_requires {
+    my($self, @args) = @_;
+    $self->on(test => sub { $self->requires(@args) });
+}
+
+sub author_requires {
+    my($self, @args) = @_;
+    $self->on(develop => sub { $self->requires(@args) });
 }
 
 1;
@@ -0,0 +1,21 @@
+package Module::CPANfile::Prereq;
+use strict;
+
+sub new {
+    my($class, %options) = @_;
+    bless \%options, $class;
+}
+
+sub feature { $_[0]->{feature} }
+sub phase   { $_[0]->{phase} }
+sub type    { $_[0]->{type} }
+sub module  { $_[0]->{module} }
+sub requirement { $_[0]->{requirement} }
+
+sub match_feature {
+    my($self, $identifier) = @_;
+    no warnings 'uninitialized';
+    $self->feature eq $identifier;
+}
+
+1;
@@ -0,0 +1,117 @@
+package Module::CPANfile::Prereqs;
+use strict;
+use Carp ();
+use CPAN::Meta::Feature;
+use Module::CPANfile::Prereq;
+
+sub from_cpan_meta {
+    my($class, $prereqs) = @_;
+
+    my $self = $class->new;
+
+    for my $phase (keys %$prereqs) {
+        for my $type (keys %{ $prereqs->{$phase} }) {
+            while (my($module, $requirement) = each %{ $prereqs->{$phase}{$type} }) {
+                $self->add_prereq(
+                    phase => $phase,
+                    type  => $type,
+                    module => $module,
+                    requirement => Module::CPANfile::Requirement->new(name => $module, version => $requirement),
+                );
+            }
+        }
+    }
+
+    $self;
+}
+
+sub new {
+    my $class = shift;
+    bless {
+        prereqs => [],
+        features => {},
+    }, $class;
+}
+
+sub add_feature {
+    my($self, $identifier, $description) = @_;
+    $self->{features}{$identifier} = { description => $description };
+}
+
+sub add_prereq {
+    my($self, %args) = @_;
+    $self->add( Module::CPANfile::Prereq->new(%args) );
+}
+
+sub add {
+    my($self, $prereq) = @_;
+    push @{$self->{prereqs}}, $prereq;
+}
+
+sub as_cpan_meta {
+    my $self = shift;
+    $self->{cpanmeta} ||= $self->build_cpan_meta;
+}
+
+sub build_cpan_meta {
+    my($self, $identifier) = @_;
+
+    my $prereq_spec = {};
+    $self->prereq_each($identifier, sub {
+        my $prereq = shift;
+        $prereq_spec->{$prereq->phase}{$prereq->type}{$prereq->module} = $prereq->requirement->version;
+    });
+
+    CPAN::Meta::Prereqs->new($prereq_spec);
+}
+
+sub prereq_each {
+    my($self, $identifier, $code) = @_;
+
+    for my $prereq (@{$self->{prereqs}}) {
+        next unless $prereq->match_feature($identifier);
+        $code->($prereq);
+    }
+}
+
+sub merged_requirements {
+    my $self = shift;
+
+    my $reqs = CPAN::Meta::Requirements->new;
+    for my $prereq (@{$self->{prereqs}}) {
+        $reqs->add_string_requirement($prereq->module, $prereq->requirement->version);
+    }
+
+    $reqs;
+}
+
+sub find {
+    my($self, $module) = @_;
+
+    for my $prereq (@{$self->{prereqs}}) {
+        return $prereq if $prereq->module eq $module;
+    }
+
+    return;
+}
+
+sub identifiers {
+    my $self = shift;
+    keys %{$self->{features}};
+}
+
+sub feature {
+    my($self, $identifier) = @_;
+
+    my $data = $self->{features}{$identifier}
+      or Carp::croak("Unknown feature '$identifier'");
+
+    my $prereqs = $self->build_cpan_meta($identifier);
+
+    CPAN::Meta::Feature->new($identifier, {
+        description => $data->{description},
+        prereqs => $prereqs->as_string_hash,
+    });
+}
+
+1;
@@ -0,0 +1,25 @@
+package Module::CPANfile::Requirement;
+use strict;
+
+sub new {
+    my ($class, %args) = @_;
+
+    $args{version} ||= 0;
+
+    bless +{
+        name    => delete $args{name},
+        version => delete $args{version},
+        options => \%args,
+    }, $class;
+}
+
+sub name    { $_[0]->{name} }
+sub version { $_[0]->{version} }
+
+sub options { $_[0]->{options} }
+
+sub has_options {
+    keys %{$_[0]->{options}} > 0;
+}
+
+1;
@@ -1,93 +0,0 @@
-package Module::CPANfile::Result;
-use strict;
-
-sub from_prereqs {
-    my($class, $spec) = @_;
-    bless {
-        phase => 'runtime',
-        spec => $spec,
-    }, $class;
-}
-
-sub new {
-    bless {
-        phase => 'runtime', # default phase
-        features => {},
-        feature => undef,
-        spec  => {},
-    }, shift;
-}
-
-sub on {
-    my($self, $phase, $code) = @_;
-    local $self->{phase} = $phase;
-    $code->()
-}
-
-sub feature {
-    my($self, $identifier, $description, $code) = @_;
-
-    # shortcut: feature identifier => sub { ... }
-    if (@_ == 3 && ref($description) eq 'CODE') {
-        $code = $description;
-        $description = $identifier;
-    }
-
-    unless (ref $description eq '' && ref $code eq 'CODE') {
-        Carp::croak("Usage: feature 'identifier', 'Description' => sub { ... }");
-    }
-
-    local $self->{feature} = $self->{features}{$identifier}
-      = { identifier => $identifier, description => $description, spec => {} };
-    $code->();
-}
-
-sub osname { die "TODO" }
-
-sub requires {
-    my($self, $module, $requirement) = @_;
-    ($self->{feature} ? $self->{feature}{spec} : $self->{spec})
-      ->{$self->{phase}}{requires}{$module} = $requirement || 0;
-}
-
-sub recommends {
-    my($self, $module, $requirement) = @_;
-    ($self->{feature} ? $self->{feature}{spec} : $self->{spec})
-      ->{$self->{phase}}{recommends}{$module} = $requirement || 0;
-}
-
-sub suggests {
-    my($self, $module, $requirement) = @_;
-    ($self->{feature} ? $self->{feature}{spec} : $self->{spec})
-      ->{$self->{phase}}{suggests}{$module} = $requirement || 0;
-}
-
-sub conflicts {
-    my($self, $module, $requirement) = @_;
-    ($self->{feature} ? $self->{feature}{spec} : $self->{spec})
-      ->{$self->{phase}}{conflicts}{$module} = $requirement || 0;
-}
-
-# Module::Install compatible shortcuts
-
-sub configure_requires {
-    my($self, @args) = @_;
-    $self->on(configure => sub { $self->requires(@args) });
-}
-
-sub build_requires {
-    my($self, @args) = @_;
-    $self->on(build => sub { $self->requires(@args) });
-}
-
-sub test_requires {
-    my($self, @args) = @_;
-    $self->on(test => sub { $self->requires(@args) });
-}
-
-sub author_requires {
-    my($self, @args) = @_;
-    $self->on(develop => sub { $self->requires(@args) });
-}
-
-1;
@@ -4,9 +4,9 @@ use warnings;
 use Cwd;
 use Carp ();
 use Module::CPANfile::Environment;
-use Module::CPANfile::Result;
+use Module::CPANfile::Requirement;
 
-our $VERSION = '1.0002';
+our $VERSION = '1.1000';
 
 sub new {
     my($class, $file) = @_;
@@ -37,42 +37,46 @@ sub parse {
     };
 
     my $env = Module::CPANfile::Environment->new($file);
-    $self->{result} = $env->parse($code) or die $@;
+    $env->parse($code) or die $@;
+
+    $self->{_mirrors} = $env->mirrors;
+    $self->{_prereqs} = $env->prereqs;
 }
 
 sub from_prereqs {
     my($proto, $prereqs) = @_;
 
     my $self = $proto->new;
-    $self->{result} = Module::CPANfile::Result->from_prereqs($prereqs);
+    $self->{_prereqs} = Module::CPANfile::Prereqs->from_cpan_meta($prereqs);
 
     $self;
 }
 
+sub mirrors {
+    my $self = shift;
+    $self->{_mirrors} || [];
+}
+
 sub features {
     my $self = shift;
-    map $self->feature($_), keys %{$self->{result}{features}};
+    map $self->feature($_), $self->{_prereqs}->identifiers;
 }
 
 sub feature {
     my($self, $identifier) = @_;
-
-    my $data = $self->{result}{features}{$identifier}
-      or Carp::croak("Unknown feature '$identifier'");
-
-    require CPAN::Meta::Feature;
-    CPAN::Meta::Feature->new($data->{identifier}, {
-        description => $data->{description},
-        prereqs => $data->{spec},
-    });
+    $self->{_prereqs}->feature($identifier);
 }
 
 sub prereq { shift->prereqs }
 
 sub prereqs {
     my $self = shift;
-    require CPAN::Meta::Prereqs;
-    CPAN::Meta::Prereqs->new($self->prereq_specs);
+    $self->{_prereqs}->as_cpan_meta;
+}
+
+sub merged_requirements {
+    my $self = shift;
+    $self->{_prereqs}->merged_requirements;
 }
 
 sub effective_prereqs {
@@ -91,7 +95,18 @@ sub prereqs_with {
 
 sub prereq_specs {
     my $self = shift;
-    $self->{result}{spec};
+    $self->prereqs->as_string_hash;
+}
+
+sub prereq_for_module {
+    my($self, $module) = @_;
+    $self->{_prereqs}->find($module);
+}
+
+sub options_for_module {
+    my($self, $module) = @_;
+    my $prereq = $self->prereq_for_module($module) or return;
+    $prereq->requirement->options;
 }
 
 sub merge_meta {
@@ -120,12 +135,14 @@ sub _dump {
 sub to_string {
     my($self, $include_empty) = @_;
 
-    my $prereqs = $self->{result}{spec};
+    my $mirrors = $self->mirrors;
+    my $prereqs = $self->prereq_specs;
 
     my $code = '';
-    $code .= $self->_dump_prereqs($self->{result}{spec}, $include_empty);
+    $code .= $self->_dump_mirrors($mirrors);
+    $code .= $self->_dump_prereqs($prereqs, $include_empty);
 
-    for my $feature (values %{$self->{result}{features}}) {
+    for my $feature ($self->features) {
         $code .= sprintf "feature %s, %s => sub {\n", _dump($feature->{identifier}), _dump($feature->{description});
         $code .= $self->_dump_prereqs($feature->{spec}, $include_empty, 4);
         $code .= "}\n\n";
@@ -135,6 +152,19 @@ sub to_string {
     $code;
 }
 
+sub _dump_mirrors {
+    my($self, $mirrors) = @_;
+
+    my $code = "";
+
+    for my $url (@$mirrors) {
+        $code .= "mirror '$url';\n";
+    }
+
+    $code =~ s/\n+$/\n/s;
+    $code;
+}
+
 sub _dump_prereqs {
     my($self, $prereqs, $include_empty, $base_indent) = @_;
 
@@ -250,7 +280,7 @@ if you want to convert L<CPAN::Meta::Prereqs> to a new cpanfile.
   # read MYMETA's prereqs and print cpanfile representation of it
   my $meta = CPAN::Meta->load_file('MYMETA.json');
   my $file = Module::CPANfile->from_prereqs($meta->prereqs);
-  print $file->to_sring;
+  print $file->to_string;
 
 By default, it omits the phase where there're no modules
 registered. If you pass the argument of a true value, it will print
@@ -112,12 +112,17 @@ installation.
 L<Module::Build::Pluggable::CPANfile> merges C<cpanfile> dependencies
 from C<Build.PL> when dumping out MYMETA information.
 
+However you're recommended to switch to an authoring system that emits
+C<Build.PL> with parsed CPANfile information, like L<Dist::Zilla>
+mentioned above.
+
 =item ExtUtils::MakeMaker
 
-L<ExtUtils::MakeMaker> has no direct support for cpanfile yet, but you
-could use L<Module::CPANfile>'s C<merge_meta> method to update
-C<MYMETA.json> files with the contents in C<cpanfile>, or convert the
-structure to appropriate options with L<PREREQ_PM> and C<META_MERGE>
-for C<WriteMakefile>.
+L<ExtUtils::MakeMaker::CPANfile> merges C<cpanfile> prerequisites
+when dumping C<MYMETA> files upon installation.
+
+However you're recommended to switch to an authoring system that emits
+C<Makefile.PL> with parsed CPANfile information, like L<Dist::Zilla>
+mentioned above.
 
 =back
@@ -4,8 +4,8 @@ cpanfile - A format for describing CPAN dependencies for Perl applications
 
 =head1 SYNOPSIS
 
-  requires 'Catalyst', '5.8000'; # 5.8000 or newer
-  requires 'Catalyst::View::JSON', '>= 0.30, < 0.40';
+  requires 'Plack', '1.0'; # 1.0 or newer
+  requires 'JSON', '>= 2.00, < 2.80';
 
   recommends 'JSON::XS', '2.0';
   conflicts 'JSON', '< 1.0';
@@ -25,7 +25,7 @@ cpanfile - A format for describing CPAN dependencies for Perl applications
 
 =head1 VERSION
 
-This doucment describes cpanfile format version 1.0.
+This document describes cpanfile format version 1.0.
 
 =head1 DESCRIPTION  
 
@@ -76,7 +76,8 @@ cpanfile-dump - Dump prerequisites from a cpanfile
 
 This script reads prereqs from a F<cpanfile> and dumps a raw list of
 them to standard output.  This is useful for piping these as input to
-another program such as C<cpan>, C<cpanm> or C<cpanp>.
+another program that doesn't support reading cpanfile directly,
+i.e. C<cpan> or C<cpanp>.
 
 By default, it prints configure, build, test and runtime requirements and
 recommendations.  Command line options can be used to modify the default
@@ -0,0 +1,38 @@
+use strict;
+use Module::CPANfile;
+use Test::More;
+use t::Utils;
+
+{
+    my $r = write_cpanfile(<<FILE);
+mirror 'http://www.cpan.org';
+mirror 'http://backpan.cpan.org';
+
+requires 'DBI';
+requires 'Plack', '0.9970';
+
+on 'test' => sub {
+    requires 'Test::More';
+};
+FILE
+
+    my $file = Module::CPANfile->load;
+
+    my $prereq = $file->prereq;
+    is_deeply $prereq->as_string_hash, {
+        test => {
+            requires => { 'Test::More' => 0  },
+        },
+        runtime => {
+            requires => { 'Plack' => '0.9970', 'DBI' => 0 },
+        },
+    };
+
+    my $mirrors = $file->mirrors;
+    is_deeply $mirrors, [ 'http://www.cpan.org', 'http://backpan.cpan.org' ];
+
+    like $file->to_string, qr{mirror 'http://www.cpan.org';};
+    like $file->to_string, qr{mirror 'http://backpan.cpan.org';};
+}
+
+done_testing;
@@ -7,9 +7,8 @@ BEGIN {
   }
 }
 
+# This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests.
 use Test::More;
-
-eval "use Test::Pod 1.41";
-plan skip_all => "Test::Pod 1.41 required for testing POD" if $@;
+use Test::Pod 1.41;
 
 all_pod_files_ok();
@@ -0,0 +1,81 @@
+use strict;
+use Module::CPANfile;
+use Test::More;
+use t::Utils;
+
+subtest 'full set' => sub {
+    my $r = write_cpanfile(<<FILE);
+requires 'Plack', '0.9970',
+    git => 'git://github.com/plack/Plack.git', ref => '0.9970';
+FILE
+
+    my $file = Module::CPANfile->load;
+    is_deeply $file->prereq_specs, {
+        runtime => {
+            requires => { 'Plack' => '0.9970' },
+        },
+    };
+
+    my $req = $file->prereqs->requirements_for(runtime => 'requires');
+    is $req->requirements_for_module('Plack'), '0.9970';
+
+    is_deeply $file->options_for_module('Plack'), {
+        git => 'git://github.com/plack/Plack.git',
+        ref => '0.9970',
+    };
+};
+
+subtest 'drop version' => sub {
+    my $r = write_cpanfile(<<FILE);
+requires 'Plack', # drop version
+    git => 'git://github.com/plack/Plack.git', ref => '0.9970';
+FILE
+
+    my $file = Module::CPANfile->load;
+    is_deeply $file->prereq_specs, {
+        runtime => {
+            requires => { 'Plack' => 0 },
+        },
+    };
+
+    is_deeply $file->options_for_module('Plack'), {
+        git     => 'git://github.com/plack/Plack.git',
+        ref     => '0.9970',
+    };
+};
+
+subtest 'no ref' => sub {
+    my $r = write_cpanfile(<<FILE);
+requires 'Plack', '0.9970', git => 'git://github.com/plack/Plack.git';
+FILE
+
+    my $file = Module::CPANfile->load;
+    is_deeply $file->prereq_specs, {
+        runtime => {
+            requires => { 'Plack' => '0.9970' },
+        },
+    };
+
+    is_deeply $file->options_for_module('Plack'), {
+        git     => 'git://github.com/plack/Plack.git',
+    };
+};
+
+subtest 'name and git' => sub {
+    my $r = write_cpanfile(<<FILE);
+requires 'Plack', git => 'git://github.com/plack/Plack.git';
+FILE
+
+    my $file = Module::CPANfile->load;
+    is_deeply $file->prereq_specs, {
+        runtime => {
+            requires => { 'Plack' => 0 },
+        },
+    };
+
+    is_deeply $file->options_for_module('Plack'), {
+        git     => 'git://github.com/plack/Plack.git',
+    };
+};
+
+done_testing;