Upgrade to Parse::CPAN::Meta 1.38
authorSteffen Mueller <smueller@cpan.org>
Sun, 17 May 2009 09:55:50 +0000 (11:55 +0200)
committerDavid Mitchell <davem@iabyn.com>
Wed, 20 May 2009 21:49:31 +0000 (22:49 +0100)
(cherry picked from commit de044c3605bd12a0b679b024ec9c16b44093c54b)

16 files changed:
lib/Parse/CPAN/Meta.pm
lib/Parse/CPAN/Meta/Changes
lib/Parse/CPAN/Meta/t/02_basic.t
lib/Parse/CPAN/Meta/t/03_regression.t
lib/Parse/CPAN/Meta/t/05_export.t [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/11_meta_yml.t
lib/Parse/CPAN/Meta/t/12_plagger.t
lib/Parse/CPAN/Meta/t/13_perl_smith.t
lib/Parse/CPAN/Meta/t/14_yaml_org.t
lib/Parse/CPAN/Meta/t/15_multibyte.t
lib/Parse/CPAN/Meta/t/17_toolbar.t
lib/Parse/CPAN/Meta/t/18_tap.t [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/19_errors.t [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/21_bom.t [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/data/Spreadsheet-Read.yml [new file with mode: 0644]
t/lib/Parse/CPAN/Meta/Test.pm

index afbb587..d65d7bb 100644 (file)
-package Parse::CPAN::Meta;
-
-use strict;
-use Carp 'croak';
-BEGIN {
-       require 5.004;
-       require Exporter;
-       $Parse::CPAN::Meta::VERSION   = '0.05';
-       @Parse::CPAN::Meta::ISA       = qw{ Exporter      };
-       @Parse::CPAN::Meta::EXPORT_OK = qw{ Load LoadFile };
-}
-
-# Prototypes
-sub LoadFile ($);
-sub Load     ($);
-sub _scalar  ($$$);
-sub _array   ($$$);
-sub _hash    ($$$);
-
-# Printable characters for escapes
-my %UNESCAPES = (
-       z => "\x00", a => "\x07", t    => "\x09",
-       n => "\x0a", v => "\x0b", f    => "\x0c",
-       r => "\x0d", e => "\x1b", '\\' => '\\',
-);
-
-
-my %BOM = (                                                       
-       "\357\273\277" => 'UTF-8',                                    
-       "\376\377"     => 'UTF-16BE',                                 
-       "\377\376"     => 'UTF-16LE',                                 
-       "\0\0\376\377" => 'UTF-32BE',                                 
-       "\377\376\0\0" => 'UTF-32LE'                                  
-);                                                                
-                                                                  
-sub BOM_MIN_LENGTH () { 2 }                                       
-sub BOM_MAX_LENGTH () { 4 }                                       
-sub HAVE_UTF8      () { $] >= 5.007003 }                          
-                                                                  
-BEGIN { require utf8 if HAVE_UTF8 }
-
-
-#####################################################################
-# Implementation
-
-# Create an object from a file
-sub LoadFile ($) {
-       # Check the file
-       my $file = shift;
-       croak('You did not specify a file name')            unless $file;
-       croak( "File '$file' does not exist" )              unless -e $file;
-       croak( "'$file' is a directory, not a file" )       unless -f _;
-       croak( "Insufficient permissions to read '$file'" ) unless -r _;
-
-       # Slurp in the file
-       local $/ = undef;
-       open( CFG, $file ) or croak("Failed to open file '$file': $!");
-       my $yaml = <CFG>;
-       close CFG          or croak("Failed to close file '$file': $!");
-
-       # Hand off to the actual parser
-       Load( $yaml );
-}
-
-# Parse a document from a string.
-# Doing checks on $_[0] prevents us having to do a string copy.
-sub Load ($) {
-
-       my $str = $_[0];
-
-       # Handle special cases
-       foreach my $length ( BOM_MIN_LENGTH .. BOM_MAX_LENGTH ) {
-               if ( my $enc = $BOM{substr($str, 0, $length)} ) {
-                       croak("Stream has a non UTF-8 BOM") unless $enc eq 'UTF-8';
-                       substr($str, 0, $length) = ''; # strip UTF-8 bom if found, we'll just ignore it
-               }
-       }
-
-       if ( HAVE_UTF8 ) {
-               utf8::decode($str); # try to decode as utf8
-       }
-
-       unless ( defined $str ) {
-               croak("Did not provide a string to Load");
-       }
-       return() unless length $str;
-       unless ( $str =~ /[\012\015]+$/ ) {
-               croak("Stream does not end with newline character");
-       }
-
-       # Split the file into lines
-       my @lines = grep { ! /^\s*(?:\#.*)?$/ }
-                   split /(?:\015{1,2}\012|\015|\012)/, $str;
-
-       # A nibbling parser
-       my @documents = ();
-       while ( @lines ) {
-               # Do we have a document header?
-               if ( $lines[0] =~ /^---\s*(?:(.+)\s*)?$/ ) {
-                       # Handle scalar documents
-                       shift @lines;
-                       if ( defined $1 and $1 !~ /^(?:\#.+|\%YAML:[\d\.]+)$/ ) {
-                               push @documents, _scalar( "$1", [ undef ], \@lines );
-                               next;
-                       }
-               }
-
-               if ( ! @lines or $lines[0] =~ /^---\s*(?:(.+)\s*)?$/ ) {
-                       # A naked document
-                       push @documents, undef;
-
-               } elsif ( $lines[0] =~ /^\s*\-/ ) {
-                       # An array at the root
-                       my $document = [ ];
-                       push @documents, $document;
-                       _array( $document, [ 0 ], \@lines );
-
-               } elsif ( $lines[0] =~ /^(\s*)\w/ ) {
-                       # A hash at the root
-                       my $document = { };
-                       push @documents, $document;
-                       _hash( $document, [ length($1) ], \@lines );
-
-               } else {
-                       croak("Parse::CPAN::Meta does not support the line '$lines[0]'");
-               }
-       }
-
-       if ( wantarray ) {
-               return @documents;
-       } else {
-               return $documents[-1];
-       }
-}
-
-# Deparse a scalar string to the actual scalar
-sub _scalar ($$$) {
-       my $string = shift;
-       my $indent = shift;
-       my $lines  = shift;
-
-       # Trim trailing whitespace
-       $string =~ s/\s*$//;
-
-       # Explitic null/undef
-       return undef if $string eq '~';
-
-       # Quotes
-       if ( $string =~ /^\'(.*?)\'$/ ) {
-               return '' unless defined $1;
-               my $rv = $1;
-               $rv =~ s/\'\'/\'/g;
-               return $rv;
-       }
-       if ( $string =~ /^\"((?:\\.|[^\"])*)\"$/ ) {
-               my $str = $1;
-               $str =~ s/\\"/"/g;
-               $str =~ s/\\([never\\fartz]|x([0-9a-fA-F]{2}))/(length($1)>1)?pack("H2",$2):$UNESCAPES{$1}/gex;
-               return $str;
-       }
-       if ( $string =~ /^[\'\"]/ ) {
-               # A quote with folding... we don't support that
-               croak("Parse::CPAN::Meta does not support multi-line quoted scalars");
-       }
-
-       # Null hash and array
-       if ( $string eq '{}' ) {
-               # Null hash
-               return {};              
-       }
-       if ( $string eq '[]' ) {
-               # Null array
-               return [];
-       }
-
-       # Regular unquoted string
-       return $string unless $string =~ /^[>|]/;
-
-       # Error
-       croak("Multi-line scalar content missing") unless @$lines;
-
-       # Check the indent depth
-       $lines->[0] =~ /^(\s*)/;
-       $indent->[-1] = length("$1");
-       if ( defined $indent->[-2] and $indent->[-1] <= $indent->[-2] ) {
-               croak("Illegal line indenting");
-       }
-
-       # Pull the lines
-       my @multiline = ();
-       while ( @$lines ) {
-               $lines->[0] =~ /^(\s*)/;
-               last unless length($1) >= $indent->[-1];
-               push @multiline, substr(shift(@$lines), length($1));
-       }
-
-       my $j = (substr($string, 0, 1) eq '>') ? ' ' : "\n";
-       my $t = (substr($string, 1, 1) eq '-') ? '' : "\n";
-       return join( $j, @multiline ) . $t;
-}
-
-# Parse an array
-sub _array ($$$) {
-       my $array  = shift;
-       my $indent = shift;
-       my $lines  = shift;
-
-       while ( @$lines ) {
-               # Check for a new document
-               return 1 if $lines->[0] =~ /^---\s*(?:(.+)\s*)?$/;
-
-               # Check the indent level
-               $lines->[0] =~ /^(\s*)/;
-               if ( length($1) < $indent->[-1] ) {
-                       return 1;
-               } elsif ( length($1) > $indent->[-1] ) {
-                       croak("Hash line over-indented");
-               }
-
-               if ( $lines->[0] =~ /^(\s*\-\s+)[^\'\"]\S*\s*:(?:\s+|$)/ ) {
-                       # Inline nested hash
-                       my $indent2 = length("$1");
-                       $lines->[0] =~ s/-/ /;
-                       push @$array, { };
-                       _hash( $array->[-1], [ @$indent, $indent2 ], $lines );
-
-               } elsif ( $lines->[0] =~ /^\s*\-(\s*)(.+?)\s*$/ ) {
-                       # Array entry with a value
-                       shift @$lines;
-                       push @$array, _scalar( "$2", [ @$indent, undef ], $lines );
-
-               } elsif ( $lines->[0] =~ /^\s*\-\s*$/ ) {
-                       shift @$lines;
-                       unless ( @$lines ) {
-                               push @$array, undef;
-                               return 1;
-                       }
-                       if ( $lines->[0] =~ /^(\s*)\-/ ) {
-                               my $indent2 = length("$1");
-                               if ( $indent->[-1] == $indent2 ) {
-                                       # Null array entry
-                                       push @$array, undef;
-                               } else {
-                                       # Naked indenter
-                                       push @$array, [ ];
-                                       _array( $array->[-1], [ @$indent, $indent2 ], $lines );
-                               }
-
-                       } elsif ( $lines->[0] =~ /^(\s*)\w/ ) {
-                               push @$array, { };
-                               _hash( $array->[-1], [ @$indent, length("$1") ], $lines );
-
-                       } else {
-                               croak("Parse::CPAN::Meta does not support the line '$lines->[0]'");
-                       }
-
-               } elsif ( defined $indent->[-2] and $indent->[-1] == $indent->[-2] ) {
-                       # This is probably a structure like the following...
-                       # ---
-                       # foo:
-                       # - list
-                       # bar: value
-                       #
-                       # ... so lets return and let the hash parser handle it
-                       return 1;
-
-               } else {
-                       croak("Parse::CPAN::Meta does not support the line '$lines->[0]'");
-               }
-       }
-
-       return 1;
-}
-
-# Parse an array
-sub _hash ($$$) {
-       my $hash   = shift;
-       my $indent = shift;
-       my $lines  = shift;
-
-       while ( @$lines ) {
-               # Check for a new document
-               return 1 if $lines->[0] =~ /^---\s*(?:(.+)\s*)?$/;
-
-               # Check the indent level
-               $lines->[0] =~/^(\s*)/;
-               if ( length($1) < $indent->[-1] ) {
-                       return 1;
-               } elsif ( length($1) > $indent->[-1] ) {
-                       croak("Hash line over-indented");
-               }
-
-               # Get the key
-               unless ( $lines->[0] =~ s/^\s*([^\'\"][^\n]*?)\s*:(\s+|$)// ) {
-                       croak("Bad hash line");
-               }
-               my $key = $1;
-
-               # Do we have a value?
-               if ( length $lines->[0] ) {
-                       # Yes
-                       $hash->{$key} = _scalar( shift(@$lines), [ @$indent, undef ], $lines );
-                       next;
-               }
-
-               # An indent
-               shift @$lines;
-               unless ( @$lines ) {
-                       $hash->{$key} = undef;
-                       return 1;
-               }
-               if ( $lines->[0] =~ /^(\s*)-/ ) {
-                       $hash->{$key} = [];
-                       _array( $hash->{$key}, [ @$indent, length($1) ], $lines );
-               } elsif ( $lines->[0] =~ /^(\s*)./ ) {
-                       my $indent2 = length("$1");
-                       if ( $indent->[-1] >= $indent2 ) {
-                               # Null hash entry
-                               $hash->{$key} = undef;
-                       } else {
-                               $hash->{$key} = {};
-                               _hash( $hash->{$key}, [ @$indent, length($1) ], $lines );
-                       }
-               }
-       }
-
-       return 1;
-}
-
-1;
-
-__END__
-
-=pod
-
-=head1 NAME
-
-Parse::CPAN::Meta - Parse META.yml and other similar CPAN metadata files
-
-=head1 SYNOPSIS
-
-    #############################################
-    # In your file
-    
-    ---
-    rootproperty: blah
-    section:
-      one: two
-      three: four
-      Foo: Bar
-      empty: ~
-    
-    
-    
-    #############################################
-    # In your program
-    
-    use Parse::CPAN::Meta;
-    
-    # Create a YAML file
-    my @yaml = Parse::CPAN::Meta::LoadFile( 'Meta.yml' );
-    
-    # Reading properties
-    my $root = $yaml[0]->{rootproperty};
-    my $one  = $yaml[0]->{section}->{one};
-    my $Foo  = $yaml[0]->{section}->{Foo};
-
-=head1 DESCRIPTION
-
-B<Parse::CPAN::Meta> is a parser for META.yml files, based on the
-parser half of L<YAML::Tiny>.
-
-It supports a basic subset of the full YAML specification, enough to
-implement parsing of typical META.yml files, and other similarly simple
-YAML files.
-
-If you need something with more power, move up to a full YAML parser such
-as L<YAML>, L<YAML::Syck> or L<YAML::LibYAML>.
-
-Parse::CPAN::Meta provides a very simply API of only two functions, based
-on the YAML functions of the same name. Wherever possible, identical
-calling semantics are used.
-
-All error reporting is done with exceptions (dieing).
-
-=head1 FUNCTIONS
-
-For maintenance clarity, no functions are exported.
-
-=head2 Load( $string )
-
-  my @documents = Load( $string );
-
-Parses a string containing a valid YAML stream into a list of Perl data
-structures.
-
-=head2 LoadFile( $file_name )
-
-Reads the YAML stream from a file instead of a string.
-
-=head1 SUPPORT
-
-Bugs should be reported via the CPAN bug tracker at
-
-L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Parse-CPAN-Meta>
-
-=head1 AUTHOR
-
-Adam Kennedy E<lt>adamk@cpan.orgE<gt>
-
-=head1 SEE ALSO
-
-L<YAML::Tiny>, L<YAML>, L<YAML::Syck>
-
-=head1 COPYRIGHT
-
-Copyright 2006 - 2009 Adam Kennedy.
-
-This program is free software; you can redistribute
-it and/or modify it under the same terms as Perl itself.
-
-The full text of the license can be found in the
-LICENSE file included with this module.
-
-=cut
+package Parse::CPAN::Meta;\r
+\r
+use strict;\r
+use Carp 'croak';\r
+\r
+# UTF Support?\r
+sub HAVE_UTF8 () { $] >= 5.007003 }\r
+BEGIN {\r
+       if ( HAVE_UTF8 ) {\r
+               # The string eval helps hide this from Test::MinimumVersion\r
+               eval "require utf8;";\r
+               die "Failed to load UTF-8 support" if $@;\r
+       }\r
+\r
+       # Class structure\r
+       require 5.004;\r
+       require Exporter;\r
+       $Parse::CPAN::Meta::VERSION   = '1.38';\r
+       @Parse::CPAN::Meta::ISA       = qw{ Exporter      };\r
+       @Parse::CPAN::Meta::EXPORT_OK = qw{ Load LoadFile };\r
+}\r
+\r
+# Prototypes\r
+sub LoadFile ($);\r
+sub Load     ($);\r
+sub _scalar  ($$$);\r
+sub _array   ($$$);\r
+sub _hash    ($$$);\r
+\r
+# Printable characters for escapes\r
+my %UNESCAPES = (\r
+       z => "\x00", a => "\x07", t    => "\x09",\r
+       n => "\x0a", v => "\x0b", f    => "\x0c",\r
+       r => "\x0d", e => "\x1b", '\\' => '\\',\r
+);\r
+\r
+\r
+\r
+\r
+\r
+#####################################################################\r
+# Implementation\r
+\r
+# Create an object from a file\r
+sub LoadFile ($) {\r
+       # Check the file\r
+       my $file = shift;\r
+       croak('You did not specify a file name')            unless $file;\r
+       croak( "File '$file' does not exist" )              unless -e $file;\r
+       croak( "'$file' is a directory, not a file" )       unless -f _;\r
+       croak( "Insufficient permissions to read '$file'" ) unless -r _;\r
+\r
+       # Slurp in the file\r
+       local $/ = undef;\r
+       local *CFG;\r
+       unless ( open( CFG, $file ) ) {\r
+               croak("Failed to open file '$file': $!");\r
+       }\r
+       my $yaml = <CFG>;\r
+       unless ( close(CFG) ) {\r
+               croak("Failed to close file '$file': $!");\r
+       }\r
+\r
+       # Hand off to the actual parser\r
+       Load( $yaml );\r
+}\r
+\r
+# Parse a document from a string.\r
+# Doing checks on $_[0] prevents us having to do a string copy.\r
+sub Load ($) {\r
+       my $string = $_[0];\r
+       unless ( defined $string ) {\r
+               croak("Did not provide a string to load");\r
+       }\r
+\r
+       # Byte order marks\r
+       if ( $string =~ /^(?:\376\377|\377\376|\377\376\0\0|\0\0\376\377)/ ) {\r
+               croak("Stream has a non UTF-8 Unicode Byte Order Mark");\r
+       } else {\r
+               # Strip UTF-8 bom if found, we'll just ignore it\r
+               $string =~ s/^\357\273\277//;\r
+       }\r
+\r
+       # Try to decode as utf8\r
+       utf8::decode($string) if HAVE_UTF8;\r
+\r
+       # Check for some special cases\r
+       return () unless length $string;\r
+       unless ( $string =~ /[\012\015]+\z/ ) {\r
+               croak("Stream does not end with newline character");\r
+       }\r
+\r
+       # Split the file into lines\r
+       my @lines = grep { ! /^\s*(?:\#.*)?\z/ }\r
+                   split /(?:\015{1,2}\012|\015|\012)/, $string;\r
+\r
+       # Strip the initial YAML header\r
+       @lines and $lines[0] =~ /^\%YAML[: ][\d\.]+.*\z/ and shift @lines;\r
+\r
+       # A nibbling parser\r
+       my @documents = ();\r
+       while ( @lines ) {\r
+               # Do we have a document header?\r
+               if ( $lines[0] =~ /^---\s*(?:(.+)\s*)?\z/ ) {\r
+                       # Handle scalar documents\r
+                       shift @lines;\r
+                       if ( defined $1 and $1 !~ /^(?:\#.+|\%YAML[: ][\d\.]+)\z/ ) {\r
+                               push @documents, _scalar( "$1", [ undef ], \@lines );\r
+                               next;\r
+                       }\r
+               }\r
+\r
+               if ( ! @lines or $lines[0] =~ /^(?:---|\.\.\.)/ ) {\r
+                       # A naked document\r
+                       push @documents, undef;\r
+                       while ( @lines and $lines[0] !~ /^---/ ) {\r
+                               shift @lines;\r
+                       }\r
+\r
+               } elsif ( $lines[0] =~ /^\s*\-/ ) {\r
+                       # An array at the root\r
+                       my $document = [ ];\r
+                       push @documents, $document;\r
+                       _array( $document, [ 0 ], \@lines );\r
+\r
+               } elsif ( $lines[0] =~ /^(\s*)\S/ ) {\r
+                       # A hash at the root\r
+                       my $document = { };\r
+                       push @documents, $document;\r
+                       _hash( $document, [ length($1) ], \@lines );\r
+\r
+               } else {\r
+                       croak("Parse::CPAN::Meta failed to classify line '$lines[0]'");\r
+               }\r
+       }\r
+\r
+       if ( wantarray ) {\r
+               return @documents;\r
+       } else {\r
+               return $documents[-1];\r
+       }\r
+}\r
+\r
+# Deparse a scalar string to the actual scalar\r
+sub _scalar ($$$) {\r
+       my ($string, $indent, $lines) = @_;\r
+\r
+       # Trim trailing whitespace\r
+       $string =~ s/\s*\z//;\r
+\r
+       # Explitic null/undef\r
+       return undef if $string eq '~';\r
+\r
+       # Quotes\r
+       if ( $string =~ /^\'(.*?)\'\z/ ) {\r
+               return '' unless defined $1;\r
+               $string = $1;\r
+               $string =~ s/\'\'/\'/g;\r
+               return $string;\r
+       }\r
+       if ( $string =~ /^\"((?:\\.|[^\"])*)\"\z/ ) {\r
+               # Reusing the variable is a little ugly,\r
+               # but avoids a new variable and a string copy.\r
+               $string = $1;\r
+               $string =~ s/\\"/"/g;\r
+               $string =~ s/\\([never\\fartz]|x([0-9a-fA-F]{2}))/(length($1)>1)?pack("H2",$2):$UNESCAPES{$1}/gex;\r
+               return $string;\r
+       }\r
+\r
+       # Special cases\r
+       if ( $string =~ /^[\'\"!&]/ ) {\r
+               croak("Parse::CPAN::Meta does not support a feature in line '$lines->[0]'");\r
+       }\r
+       return {} if $string eq '{}';\r
+       return [] if $string eq '[]';\r
+\r
+       # Regular unquoted string\r
+       return $string unless $string =~ /^[>|]/;\r
+\r
+       # Error\r
+       croak("Parse::CPAN::Meta failed to find multi-line scalar content") unless @$lines;\r
+\r
+       # Check the indent depth\r
+       $lines->[0]   =~ /^(\s*)/;\r
+       $indent->[-1] = length("$1");\r
+       if ( defined $indent->[-2] and $indent->[-1] <= $indent->[-2] ) {\r
+               croak("Parse::CPAN::Meta found bad indenting in line '$lines->[0]'");\r
+       }\r
+\r
+       # Pull the lines\r
+       my @multiline = ();\r
+       while ( @$lines ) {\r
+               $lines->[0] =~ /^(\s*)/;\r
+               last unless length($1) >= $indent->[-1];\r
+               push @multiline, substr(shift(@$lines), length($1));\r
+       }\r
+\r
+       my $j = (substr($string, 0, 1) eq '>') ? ' ' : "\n";\r
+       my $t = (substr($string, 1, 1) eq '-') ? ''  : "\n";\r
+       return join( $j, @multiline ) . $t;\r
+}\r
+\r
+# Parse an array\r
+sub _array ($$$) {\r
+       my ($array, $indent, $lines) = @_;\r
+\r
+       while ( @$lines ) {\r
+               # Check for a new document\r
+               if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {\r
+                       while ( @$lines and $lines->[0] !~ /^---/ ) {\r
+                               shift @$lines;\r
+                       }\r
+                       return 1;\r
+               }\r
+\r
+               # Check the indent level\r
+               $lines->[0] =~ /^(\s*)/;\r
+               if ( length($1) < $indent->[-1] ) {\r
+                       return 1;\r
+               } elsif ( length($1) > $indent->[-1] ) {\r
+                       croak("Parse::CPAN::Meta found bad indenting in line '$lines->[0]'");\r
+               }\r
+\r
+               if ( $lines->[0] =~ /^(\s*\-\s+)[^\'\"]\S*\s*:(?:\s+|$)/ ) {\r
+                       # Inline nested hash\r
+                       my $indent2 = length("$1");\r
+                       $lines->[0] =~ s/-/ /;\r
+                       push @$array, { };\r
+                       _hash( $array->[-1], [ @$indent, $indent2 ], $lines );\r
+\r
+               } elsif ( $lines->[0] =~ /^\s*\-(\s*)(.+?)\s*\z/ ) {\r
+                       # Array entry with a value\r
+                       shift @$lines;\r
+                       push @$array, _scalar( "$2", [ @$indent, undef ], $lines );\r
+\r
+               } elsif ( $lines->[0] =~ /^\s*\-\s*\z/ ) {\r
+                       shift @$lines;\r
+                       unless ( @$lines ) {\r
+                               push @$array, undef;\r
+                               return 1;\r
+                       }\r
+                       if ( $lines->[0] =~ /^(\s*)\-/ ) {\r
+                               my $indent2 = length("$1");\r
+                               if ( $indent->[-1] == $indent2 ) {\r
+                                       # Null array entry\r
+                                       push @$array, undef;\r
+                               } else {\r
+                                       # Naked indenter\r
+                                       push @$array, [ ];\r
+                                       _array( $array->[-1], [ @$indent, $indent2 ], $lines );\r
+                               }\r
+\r
+                       } elsif ( $lines->[0] =~ /^(\s*)\S/ ) {\r
+                               push @$array, { };\r
+                               _hash( $array->[-1], [ @$indent, length("$1") ], $lines );\r
+\r
+                       } else {\r
+                               croak("Parse::CPAN::Meta failed to classify line '$lines->[0]'");\r
+                       }\r
+\r
+               } elsif ( defined $indent->[-2] and $indent->[-1] == $indent->[-2] ) {\r
+                       # This is probably a structure like the following...\r
+                       # ---\r
+                       # foo:\r
+                       # - list\r
+                       # bar: value\r
+                       #\r
+                       # ... so lets return and let the hash parser handle it\r
+                       return 1;\r
+\r
+               } else {\r
+                       croak("Parse::CPAN::Meta failed to classify line '$lines->[0]'");\r
+               }\r
+       }\r
+\r
+       return 1;\r
+}\r
+\r
+# Parse an array\r
+sub _hash ($$$) {\r
+       my ($hash, $indent, $lines) = @_;\r
+\r
+       while ( @$lines ) {\r
+               # Check for a new document\r
+               if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {\r
+                       while ( @$lines and $lines->[0] !~ /^---/ ) {\r
+                               shift @$lines;\r
+                       }\r
+                       return 1;\r
+               }\r
+\r
+               # Check the indent level\r
+               $lines->[0] =~ /^(\s*)/;\r
+               if ( length($1) < $indent->[-1] ) {\r
+                       return 1;\r
+               } elsif ( length($1) > $indent->[-1] ) {\r
+                       croak("Parse::CPAN::Meta found bad indenting in line '$lines->[0]'");\r
+               }\r
+\r
+               # Get the key\r
+               unless ( $lines->[0] =~ s/^\s*([^\'\" ][^\n]*?)\s*:(\s+|$)// ) {\r
+                       if ( $lines->[0] =~ /^\s*[?\'\"]/ ) {\r
+                               croak("Parse::CPAN::Meta does not support a feature in line '$lines->[0]'");\r
+                       }\r
+                       croak("Parse::CPAN::Meta failed to classify line '$lines->[0]'");\r
+               }\r
+               my $key = $1;\r
+\r
+               # Do we have a value?\r
+               if ( length $lines->[0] ) {\r
+                       # Yes\r
+                       $hash->{$key} = _scalar( shift(@$lines), [ @$indent, undef ], $lines );\r
+               } else {\r
+                       # An indent\r
+                       shift @$lines;\r
+                       unless ( @$lines ) {\r
+                               $hash->{$key} = undef;\r
+                               return 1;\r
+                       }\r
+                       if ( $lines->[0] =~ /^(\s*)-/ ) {\r
+                               $hash->{$key} = [];\r
+                               _array( $hash->{$key}, [ @$indent, length($1) ], $lines );\r
+                       } elsif ( $lines->[0] =~ /^(\s*)./ ) {\r
+                               my $indent2 = length("$1");\r
+                               if ( $indent->[-1] >= $indent2 ) {\r
+                                       # Null hash entry\r
+                                       $hash->{$key} = undef;\r
+                               } else {\r
+                                       $hash->{$key} = {};\r
+                                       _hash( $hash->{$key}, [ @$indent, length($1) ], $lines );\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       return 1;\r
+}\r
+\r
+1;\r
+\r
+__END__\r
+\r
+=pod\r
+\r
+=head1 NAME\r
+\r
+Parse::CPAN::Meta - Parse META.yml and other similar CPAN metadata files\r
+\r
+=head1 SYNOPSIS\r
+\r
+    #############################################\r
+    # In your file\r
+    \r
+    ---\r
+    rootproperty: blah\r
+    section:\r
+      one: two\r
+      three: four\r
+      Foo: Bar\r
+      empty: ~\r
+    \r
+    \r
+    \r
+    #############################################\r
+    # In your program\r
+    \r
+    use Parse::CPAN::Meta;\r
+    \r
+    # Create a YAML file\r
+    my @yaml = Parse::CPAN::Meta::LoadFile( 'Meta.yml' );\r
+    \r
+    # Reading properties\r
+    my $root = $yaml[0]->{rootproperty};\r
+    my $one  = $yaml[0]->{section}->{one};\r
+    my $Foo  = $yaml[0]->{section}->{Foo};\r
+\r
+=head1 DESCRIPTION\r
+\r
+B<Parse::CPAN::Meta> is a parser for F<META.yml> files, based on the\r
+parser half of L<YAML::Tiny>.\r
+\r
+It supports a basic subset of the full YAML specification, enough to\r
+implement parsing of typical F<META.yml> files, and other similarly simple\r
+YAML files.\r
+\r
+If you need something with more power, move up to a full YAML parser such\r
+as L<YAML>, L<YAML::Syck> or L<YAML::LibYAML>.\r
+\r
+B<Parse::CPAN::Meta> provides a very simply API of only two functions,\r
+based on the YAML functions of the same name. Wherever possible,\r
+identical calling semantics are used.\r
+\r
+All error reporting is done with exceptions (die'ing).\r
+\r
+=head1 FUNCTIONS\r
+\r
+For maintenance clarity, no functions are exported.\r
+\r
+=head2 Load\r
+\r
+  my @yaml = Load( $string );\r
+\r
+Parses a string containing a valid YAML stream into a list of Perl data\r
+structures.\r
+\r
+=head2 LoadFile\r
+\r
+  my @yaml = LoadFile( 'META.yml' );\r
+\r
+Reads the YAML stream from a file instead of a string.\r
+\r
+=head1 SUPPORT\r
+\r
+Bugs should be reported via the CPAN bug tracker at\r
+\r
+L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Parse-CPAN-Meta>\r
+\r
+=head1 AUTHOR\r
+\r
+Adam Kennedy E<lt>adamk@cpan.orgE<gt>\r
+\r
+=head1 SEE ALSO\r
+\r
+L<YAML>, L<YAML::Syck>, L<Config::Tiny>, L<CSS::Tiny>,\r
+L<http://use.perl.org/~Alias/journal/29427>, L<http://ali.as/>\r
+\r
+=head1 COPYRIGHT\r
+\r
+Copyright 2006 - 2009 Adam Kennedy.\r
+\r
+This program is free software; you can redistribute\r
+it and/or modify it under the same terms as Perl itself.\r
+\r
+The full text of the license can be found in the\r
+LICENSE file included with this module.\r
+\r
+=cut\r
index d602d4a..21fc896 100644 (file)
@@ -1,5 +1,13 @@
 Revision history for Perl extension Parse-CPAN-Meta
 
+1.38 Sat 16 May 2009
+       - Synchorised version numbers with YAML::Tiny
+       - Code shrinkages to the bloaty Unicode/BOM code
+       - Merging a ton more tests from YAML::Tiny
+       - Added back some pointless options to yaml_ok that keep our
+         codebase closer to YAML::Tiny (which should help with syncing)
+       - Changed exceptions to be closer to YAML::Tiny
+
 0.05 Wed 11 Mar 2009
        - Upgrade previous release to a stable release.
 
index 6b65e02..f6a91c1 100644 (file)
@@ -56,26 +56,31 @@ yaml_ok(
        "---\n",
        [ undef ],
        'only_header',
+       noyamlperl => 1,
 );
 yaml_ok(
        "---\n---\n",
        [ undef, undef ],
        'two_header',
+       noyamlperl => 1,
 );
 yaml_ok(
        "--- ~\n",
        [ undef ],
        'one_undef',
+       noyamlperl => 1,
 );
 yaml_ok(
        "---  ~\n",
        [ undef ],
        'one_undef2',
+       noyamlperl => 1,
 );
 yaml_ok(
        "--- ~\n---\n",
        [ undef, undef ],
        'two_undef',
+       noyamlperl => 1,
 );
 
 # Just a scalar
@@ -110,6 +115,7 @@ yaml_ok(
        "---\n- ~\n- bar\n",
        [ [ undef, 'bar' ] ],
        'one_listundef',
+       noyamlperl => 1,
 );
 
 # Simple hashs
@@ -123,6 +129,7 @@ yaml_ok(
        "---\nfoo: bar\nthis: ~\n",
        [ { this => undef, foo => 'bar' } ],
        'one_hash2',
+       noyamlperl => 1,
 );
 
 # Simple array inside a hash with an undef
@@ -136,6 +143,7 @@ foo:
 END_YAML
        [ { foo => [ 'bar', undef, 'baz' ] } ],
        'array_in_hash',
+       noyamlperl => 1,
 );
 
 # Simple hash inside a hash with an undef
@@ -148,6 +156,7 @@ bar:
 END_YAML
        [ { foo => undef, bar => { foo => 'bar' } } ],
        'hash_in_hash',
+       noyamlperl => 1,
 );
 
 # Mixed hash and scalars inside an array
@@ -170,6 +179,7 @@ END_YAML
                { foo => 'bar', this => 'that' },
        ] ],
        'hash_in_array',
+       noyamlperl => 1,
 );
 
 # Simple single quote
@@ -194,12 +204,16 @@ yaml_ok(
        "--- \"  \"\n",
        [ '  ' ],
        "only_spaces",
+       noyamlpm   => 1,
+       noyamlperl => 1,
 );
 
 yaml_ok(
        "--- \"  foo\"\n--- \"bar  \"\n",
        [ "  foo", "bar  " ],
        "leading_trailing_spaces",
+       noyamlpm   => 1,
+       noyamlperl => 1,
 );
 
 # Implicit document start
@@ -225,6 +239,7 @@ yaml_ok(
 END_YAML
        [ [ undef, { foo => 'bar', this => 'that' }, 'baz' ] ],
        'inline_nested_hash',
+       noyamlperl => 1,
 );
 
 # Empty comments
index 1a1436a..e9fb70a 100644 (file)
@@ -20,7 +20,7 @@ BEGIN {
 
 use File::Spec::Functions ':ALL';
 use Parse::CPAN::Meta::Test;
-use Test::More tests(20);
+use Test::More tests(37);
 
 
 
@@ -62,42 +62,91 @@ yaml_ok(
 );
 
 # Piped multi-line scalar
-yaml_ok( <<'END_YAML', [ [ "foo\nbar\n", 1 ] ], 'indented', nosyck => 1 );
+yaml_ok(
+       <<'END_YAML',
 ---
 - |
   foo
   bar
 - 1
 END_YAML
+       [ [ "foo\nbar\n", 1 ] ],
+       'indented',
+);
 
 # ... with a pointless hyphen
-yaml_ok( <<'END_YAML', [ [ "foo\nbar", 1 ] ], 'indented', nosyck => 1 );
+yaml_ok( <<'END_YAML',
 ---
 - |-
   foo
   bar
 - 1
 END_YAML
-
+       [ [ "foo\nbar", 1 ] ],
+       'indented',
+);
 
 
 
 
 
 #####################################################################
-# Support for YAML document version declarations
+# Support for YAML version directives
 
-# Simple case
+# Simple inline case (comment variant)
 yaml_ok(
        <<'END_YAML',
 --- #YAML:1.0
 foo: bar
 END_YAML
        [ { foo => 'bar' } ],
-       'simple_doctype',
+       'simple_doctype_comment',
+       nosyck   => 1,
+);
+
+# Simple inline case (percent variant)
+yaml_ok(
+       <<'END_YAML',
+--- %YAML:1.0
+foo: bar
+END_YAML
+       [ { foo => 'bar' } ],
+       'simple_doctype_percent',
+       noyamlpm   => 1,
+       noxs       => 1,
+       noyamlperl => 1,
+);
+
+# Simple header (comment variant)
+yaml_ok(
+       <<'END_YAML',
+%YAML:1.0
+---
+foo: bar
+END_YAML
+       [ { foo => 'bar' } ],
+       'predocument_1_0',
+       noyamlpm   => 1,
+       nosyck     => 1,
+       noxs       => 1,
+       noyamlperl => 1,
 );
 
-# Multiple documents
+# Simple inline case (comment variant)
+yaml_ok(
+       <<'END_YAML',
+%YAML 1.1
+---
+foo: bar
+END_YAML
+       [ { foo => 'bar' } ],
+       'predocument_1_1',
+       noyamlpm   => 1,
+       nosyck     => 1,
+       noyamlperl => 1,
+);
+
+# Multiple inline documents (comment variant)
 yaml_ok(
        <<'END_YAML',
 --- #YAML:1.0
@@ -108,7 +157,32 @@ foo: bar
 foo: bar
 END_YAML
        [ { foo => 'bar' }, [ 1 ], { foo => 'bar' } ],
-       'multi_doctype',
+       'multi_doctype_comment',
+);
+
+# Simple pre-document case (comment variant)
+yaml_ok(
+       <<'END_YAML',
+%YAML 1.1
+---
+foo: bar
+END_YAML
+       [ { foo => 'bar' } ],
+       'predocument_percent',
+       noyamlpm   => 1,
+       nosyck     => 1,
+       noyamlperl => 1,
+);
+
+# Simple pre-document case (comment variant)
+yaml_ok(
+       <<'END_YAML',
+#YAML 1.1
+---
+foo: bar
+END_YAML
+       [ { foo => 'bar' } ],
+       'predocument_comment',
 );
 
 
@@ -192,8 +266,13 @@ arr:
   - ~
   - 'bar'  
 END_YAML
-       [ { abstract => 'Generate fractal curves', foo => undef, arr => [ 'foo', undef, 'bar' ] } ],
+       [ {
+               abstract => 'Generate fractal curves',
+               foo      => undef,
+               arr      => [ 'foo', undef, 'bar' ],
+       } ],
        'trailing whitespace',
+       noyamlperl => 1,
 );
 
 
@@ -218,15 +297,35 @@ END_YAML
 
 
 #####################################################################
-# Single Quote Idiosyncracy
+# Quote and Escaping Idiosyncracies
+
+yaml_ok(
+       <<'END_YAML',
+---
+name1: 'O''Reilly'
+name2: 'O''Reilly O''Tool'
+name3: 'Double '''' Quote'
+END_YAML
+       [ {
+               name1 => "O'Reilly",
+               name2 => "O'Reilly O'Tool",
+               name3 => "Double '' Quote",
+       } ],
+       'single quote subtleties',
+);
 
 yaml_ok(
        <<'END_YAML',
 ---
-slash: '\\'
-name: 'O''Reilly'
+slash1: '\\'
+slash2: '\\foo'
+slash3: '\\foo\\\\'
 END_YAML
-       [ { slash => "\\\\", name => "O'Reilly" } ],
+       [ {
+               slash1 => "\\\\",
+               slash2 => "\\\\foo",
+               slash3 => "\\\\foo\\\\\\\\",
+       } ],
        'single quote subtleties',
 );
 
@@ -246,6 +345,8 @@ build_requires:
 END_YAML
        [ { foo => 0, requires => undef, build_requires => undef } ],
        'empty hash keys',
+       noyamlpm   => 1,
+       noyamlperl => 1,
 );
 
 yaml_ok(
@@ -257,6 +358,8 @@ yaml_ok(
 END_YAML
        [ [ 'foo', undef, undef ] ],
        'empty array keys',
+       noyamlpm   => 1,
+       noyamlperl => 1,
 );
 
 
@@ -273,6 +376,8 @@ foo: bar
 END_YAML
        [ { foo => 'bar' } ],
        'comment header',
+       noyamlpm   => 1,
+       noyamlperl => 1,
 );
 
 
@@ -295,6 +400,163 @@ END_YAML
 
 
 
+#####################################################################
+# Confirm we can read the synopsis
+
+yaml_ok(
+       <<'END_YAML',
+---
+rootproperty: blah
+section:
+  one: two
+  three: four
+  Foo: Bar
+  empty: ~
+END_YAML
+       [ {
+               rootproperty => 'blah',
+               section      => {
+                       one   => 'two',
+                       three => 'four',
+                       Foo   => 'Bar',
+                       empty => undef,
+               },
+       } ],
+       'synopsis',
+       noyamlperl => 1,
+);
+
+
+
+
+
+#####################################################################
+# Unprintable Characters
+
+yaml_ok(
+       "--- \"foo\\n\\x00\"\n",
+       [ "foo\n\0" ],
+       'unprintable',
+);
+
+
+
+
+
+#####################################################################
+# Empty Quote Line
+
+yaml_ok(
+       <<'END_YAML',
+---
+- foo
+#
+- bar
+END_YAML
+       [ [ "foo", "bar" ] ],
+);
+
+
+
+
+
+#####################################################################
+# Indentation after empty hash value
+
+yaml_ok(
+       <<'END_YAML',
+---
+Test:
+  optmods:
+    Bad: 0
+    Foo: 1
+    Long: 0
+  version: 5
+Test_IncludeA:
+  optmods:
+Test_IncludeB:
+  optmods:
+_meta:
+  name: 'test profile'
+  note: 'note this test profile'
+END_YAML
+       [ {
+               Test => {
+                       optmods => {
+                               Bad => 0,
+                               Foo => 1,
+                               Long => 0,
+                       },
+                       version => 5,
+               },
+               Test_IncludeA => {
+                       optmods => undef,
+               },
+               Test_IncludeB => {
+                       optmods => undef,
+               },
+               _meta => {
+                       name => 'test profile',
+                       note => 'note this test profile',
+               },
+       } ],
+       'Indentation after empty hash value',
+       noyamlperl => 1,
+);
+
+
+
+
+
+#####################################################################
+# Spaces in the Key
+
+yaml_ok(
+       <<'END_YAML',
+---
+the key: the value
+END_YAML
+       [ { 'the key' => 'the value' } ],
+);
+
+
+
+
+
+#####################################################################
+# Ticker #32402
+
+# Tests a particular pathological case
+
+yaml_ok(
+       <<'END_YAML',
+---
+- value
+- '><'
+END_YAML
+       [ [ 'value', '><' ] ],
+       'Pathological >< case',
+);
+
+
+
+
+
+#####################################################################
+# Special Characters
+
+#yaml_ok(
+#      <<'END_YAML',
+#---
+#- "Ingy d\xC3\xB6t Net"
+#END_YAML
+#      [ [ "Ingy d\xC3\xB6t Net" ] ],
+#);
+
+
+
+
+
 
 ######################################################################
 # Non-Indenting Sub-List
@@ -308,4 +570,53 @@ bar: value
 END_YAML
        [ { foo => [ 'list' ], bar => 'value' } ],
        'Non-indenting sub-list',
+       noyamlpm   => 1,
+       noyamlperl => 1,
+);
+
+
+
+
+
+
+#####################################################################
+# Check Multiple-Escaping
+
+# RT #42119: write of two single quotes
+yaml_ok(
+       "--- \"A'B'C\"\n",
+       [ "A'B'C" ],
+       'Multiple escaping of quote ok',
+);
+
+# Escapes without whitespace
+yaml_ok(
+       "--- A\\B\\C\n",
+       [ "A\\B\\C" ],
+       'Multiple escaping of escape ok',
+);
+
+# Escapes with whitespace
+yaml_ok(
+       "--- 'A\\B \\C'\n",
+       [ "A\\B \\C" ],
+       'Multiple escaping of escape with whitespace ok',
+);
+
+
+
+
+
+######################################################################
+# Check illegal characters that are in legal places
+
+yaml_ok(
+       "--- 'Wow!'\n",
+       [ "Wow!" ],
+       'Bang in a quote',
+);
+yaml_ok(
+       "--- 'This&that'\n",
+       [ "This&that" ],
+       'Ampersand in a quote',
 );
diff --git a/lib/Parse/CPAN/Meta/t/05_export.t b/lib/Parse/CPAN/Meta/t/05_export.t
new file mode 100644 (file)
index 0000000..f82cc91
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/perl
+
+# Testing of basic document structures
+
+BEGIN {
+       if( $ENV{PERL_CORE} ) {
+               chdir 't';
+               @INC = ('../lib', 'lib');
+       }
+       else {
+               unshift @INC, 't/lib/';
+       }
+}
+
+use strict;
+BEGIN {
+       $|  = 1;
+       $^W = 1;
+}
+
+use Test::More tests => 4;
+use Parse::CPAN::Meta;
+
+
+
+ok not(defined &main::Load), 'Load is not exported';
+ok not(defined &main::Dump), 'Dump is not exported';
+ok not(defined &main::LoadFile), 'LoadFile is not exported';
+ok not(defined &main::DumpFile), 'DumpFile is not exported';
index c5a4934..fe95d1a 100644 (file)
@@ -20,14 +20,14 @@ BEGIN {
 
 use File::Spec::Functions ':ALL';
 use Parse::CPAN::Meta::Test;
-use Test::More tests(8, 2);
+use Test::More tests(8, 3);
 
 
 
 
 
 #####################################################################
-# Testing YAML::Tiny's META.yml file
+# Testing YAML::Tiny's own META.yml file
 
 yaml_ok(
        <<'END_YAML',
@@ -198,6 +198,7 @@ END_YAML
                generated_by => 'ExtUtils::MakeMaker version 6.17',
        } ],
        'Acme-Time-Baby',
+       noyamlperl => 1,
 );
 
 
@@ -207,30 +208,6 @@ END_YAML
 #####################################################################
 # File with a YAML header
 
-yaml_ok(
-       <<'END_YAML',
---- %YAML:1.0
-name:     Data-Swap
-version:  0.05
-license:  perl
-distribution_type: module
-requires:
-   perl:  5.6.0
-dynamic_config: 0
-END_YAML
-       [ {
-               name => 'Data-Swap',
-               version => '0.05',
-               license => 'perl',
-               distribution_type => 'module',
-               requires => {
-                       perl => '5.6.0',
-               },
-               dynamic_config => '0',
-       } ],
-       'Data-Swap',
-);
-
 yaml_ok(
        <<'END_YAML',
 --- #YAML:1.0
@@ -253,6 +230,7 @@ END_YAML
                dynamic_config => '0',
        } ],
        'Data-Swap',
+       nosyck => 1,
 );
 
 
@@ -293,6 +271,7 @@ SCOPE: {
                        version => '1.2.1',
                } ],
                'Template-Provider-Unicode-Japanese',
+               noyamlperl => 1,
        );
 }
 
@@ -314,5 +293,113 @@ SCOPE: {
                        version  => '0.04',
                } ],
                'HTML-WebDAO',
+               nosyck => 1,
+       );
+}
+
+SCOPE: {
+       my $content = load_ok(
+               'Spreadsheet-Read.yml',
+               catfile( test_data_directory(), 'Spreadsheet-Read.yml' ),
+               100
+       );
+       yaml_ok(
+               $content,
+               [ {
+                       'resources' => {
+                               'license' => 'http://dev.perl.org/licenses/'
+                       },
+                       'meta-spec' => {
+                               'version' => '1.4',
+                               'url' => 'http://module-build.sourceforge.net/META-spec-v1.4.html'
+                       },
+                       'distribution_type' => 'module',
+                       'generated_by' => 'Author',
+                       'version' => 'VERSION',
+                       'name' => 'Read',
+                       'author' => [
+                               'H.Merijn Brand <h.m.brand@xs4all.nl>'
+                       ],
+                       'license' => 'perl',
+                       'build_requires' => {
+                               'Test::More' => '0',
+                               'Test::Harness' => '0',
+                               'perl' => '5.006'
+                       },
+                       'provides' => {
+                               'Spreadsheet::Read' => {
+                                       'version' => 'VERSION',
+                                       'file' => 'Read.pm'
+                               }
+                       },
+                       'optional_features' => [
+                               {
+                                       'opt_csv' => {
+                                               'requires' => {
+                                                       'Text::CSV_XS' => '0.23'
+                                               },
+                                               'recommends' => {
+                                                       'Text::CSV_PP' => '1.10',
+                                                       'Text::CSV_XS' => '0.58',
+                                                       'Text::CSV' => '1.10'
+                                               },
+                                               'description' => 'Provides parsing of CSV streams'
+                                       }
+                               },
+                               {
+                                       'opt_excel' => {
+                                               'requires' => {
+                                                       'Spreadsheet::ParseExcel' => '0.26',
+                                                       'Spreadsheet::ParseExcel::FmtDefault' => '0'
+                                               },
+                                               'recommends' => {
+                                                       'Spreadsheet::ParseExcel' => '0.42'
+                                               },
+                                               'description' => 'Provides parsing of Microsoft Excel files'
+                                       }
+                               },
+                               {
+                                       'opt_excelx' => {
+                                               'requires' => {
+                                                       'Spreadsheet::XLSX' => '0.07'
+                                               },
+                                               'description' => 'Provides parsing of Microsoft Excel 2007 files'
+                                       }
+                               },
+                               {
+                                       'opt_oo' => {
+                                               'requires' => {
+                                                       'Spreadsheet::ReadSXC' => '0.2'
+                                               },
+                                               'description' => 'Provides parsing of OpenOffice spreadsheets'
+                                       }
+                               },
+                               {
+                                       'opt_tools' => {
+                                               'recommends' => {
+                                                       'Tk::TableMatrix::Spreadsheet' => '0',
+                                                       'Tk::NoteBook' => '0',
+                                                       'Tk' => '0'
+                                               },
+                                               'description' => 'Spreadsheet tools'
+                                       }
+                               }
+                       ],
+                       'requires' => {
+                               'perl' => '5.006',
+                               'Data::Dumper' => '0',
+                               'Exporter' => '0',
+                               'Carp' => '0'
+                       },
+                       'recommends' => {
+                               'perl' => '5.008005',
+                               'IO::Scalar' => '0',
+                               'File::Temp' => '0.14'
+                       },
+                       'abstract' => 'Meta-Wrapper for reading spreadsheet data'
+               } ],
+               'Spreadsheet-Read',
+               noyamlpm   => 1,
+               noyamlperl => 1,
        );
 }
index 68f3761..5e186f3 100644 (file)
@@ -101,8 +101,8 @@ plugins:
      nickname: plaggerbot
      server_host: chat.freenode.net
      server_channels:
-       - #plagger-ja
-       - #plagger
+       - '#plagger-ja'
+       - '#plagger'
 
    
 END_YAML
index 557f9bb..9a8b8be 100644 (file)
@@ -153,4 +153,6 @@ yaml_ok(
                },
        } ],
        'vanilla.yml',
+       nosyck     => 1,
+       noyamlperl => 1,
 );
index 5f01274..254bd7a 100644 (file)
@@ -68,5 +68,5 @@ Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.
 END_TEXT
        } ],
        'sample.yml',
-
+       # nosyck => 1,
 );
index a67c0f9..214b8b0 100644 (file)
@@ -51,6 +51,3 @@ SKIP: {
        skip "no utf8 support", 1 unless Parse::CPAN::Meta::HAVE_UTF8();
        ok( utf8::is_utf8($yaml[0]->{author}), "utf8 decoded" );
 }
-
-exit(0);
-
index 2b2e06e..e0d3cf9 100644 (file)
@@ -55,4 +55,5 @@ yaml_ok(
                ]
        } ],
        'toolbar.yml',
+       noyamlperl => 1,
 );
diff --git a/lib/Parse/CPAN/Meta/t/18_tap.t b/lib/Parse/CPAN/Meta/t/18_tap.t
new file mode 100644 (file)
index 0000000..62b84f9
--- /dev/null
@@ -0,0 +1,90 @@
+#!/usr/bin/perl
+
+# Testing relating to functionality in the Test Anything Protocol
+
+BEGIN {
+       if( $ENV{PERL_CORE} ) {
+               chdir 't';
+               @INC = ('../lib', 'lib');
+       }
+       else {
+               unshift @INC, 't/lib/';
+       }
+}
+
+use strict;
+BEGIN {
+       $|  = 1;
+       $^W = 1;
+}
+
+use File::Spec::Functions ':ALL';
+use Parse::CPAN::Meta::Test;
+use Test::More tests(5, 0, 0);
+use Parse::CPAN::Meta ();
+
+
+
+
+
+#####################################################################
+# TAP Tests
+
+# Make sure we support x-foo keys
+yaml_ok(
+       "---\nx-foo: 1\n",
+       [ { 'x-foo' => 1 } ],
+       'x-foo key',
+);
+
+# Document ending (hash)
+yaml_ok(
+         "---\n"
+       . "  foo: bar\n"
+       . "...\n",
+       [ { foo => "bar" } ],
+       'document_end_hash',
+       noyamlpm   => 1,
+       nosyck     => 1,
+       noyamlperl => 1,
+);
+
+# Document ending (array)
+yaml_ok(
+         "---\n"
+       . "- foo\n"
+       . "...\n",
+       [ [ 'foo' ] ],
+       'document_end_array',
+       noyamlpm => 1,
+       noyamlperl => 1,
+);
+
+# Multiple documents (simple)
+yaml_ok(
+         "---\n"
+       . "- foo\n"
+       . "...\n"
+       . "---\n"
+       . "- foo\n"
+       . "...\n",
+       [ [ 'foo' ], [ 'foo' ] ],
+       'multi_document_simple',
+       noyamlpm   => 1,
+       noyamlperl => 1,
+);
+
+# Multiple documents (whitespace-separated)
+yaml_ok(
+         "---\n"
+       . "- foo\n"
+       . "...\n"
+       . "\n"
+       . "---\n"
+       . "- foo\n"
+       . "...\n",
+       [ [ 'foo' ], [ 'foo' ] ],
+       'multi_document_space',
+       noyamlpm   => 1,
+       noyamlperl => 1,
+);
diff --git a/lib/Parse/CPAN/Meta/t/19_errors.t b/lib/Parse/CPAN/Meta/t/19_errors.t
new file mode 100644 (file)
index 0000000..baa06ad
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/perl
+
+# Testing documents that should fail
+
+BEGIN {
+       if( $ENV{PERL_CORE} ) {
+               chdir 't';
+               @INC = ('../lib', 'lib');
+       }
+       else {
+               unshift @INC, 't/lib/';
+       }
+}
+
+use strict;
+BEGIN {
+       $|  = 1;
+       $^W = 1;
+}
+
+use File::Spec::Functions ':ALL';
+use Parse::CPAN::Meta::Test;
+use Test::More tests => 1;
+
+
+
+
+
+#####################################################################
+# Missing Features
+
+# We don't support raw nodes
+yaml_error( <<'END_YAML', 'does not support a feature' );
+---
+version: !!perl/hash:version 
+  original: v2.0.2
+  qv: 1
+  version: 
+    - 2
+    - 0
+    - 2
+END_YAML
+
diff --git a/lib/Parse/CPAN/Meta/t/21_bom.t b/lib/Parse/CPAN/Meta/t/21_bom.t
new file mode 100644 (file)
index 0000000..706cf77
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/perl
+
+BEGIN {
+       if( $ENV{PERL_CORE} ) {
+               chdir 't';
+               @INC = ('../lib', 'lib');
+       }
+       else {
+               unshift @INC, 't/lib/';
+       }
+}
+
+use strict;
+BEGIN {
+       $|  = 1;
+       $^W = 1;
+}
+
+use File::Spec::Functions ':ALL';
+use Parse::CPAN::Meta::Test;
+use Test::More tests(0, 1, 3);
+
+
+
+
+
+#####################################################################
+# Testing that Perl::Smith config files work
+
+my $sample_file = catfile( test_data_directory(), 'utf_16_le_bom.yml' );
+my $sample      = load_ok( 'utf_16_le_bom.yml', $sample_file, 3 );
+
+# Does the string parse to the structure
+my $name      = "utf-16";
+my $yaml_copy = $sample;
+my $yaml      = eval { Parse::CPAN::Meta::Load( $yaml_copy ); };
+is( $yaml_copy, $sample, "$name: Parse::CPAN::Meta::Load does not modify the input string" );
+is( $yaml, undef, "file not parsed" );
+ok( $@ =~ "Stream has a non UTF-8 Unicode Byte Order Mark", "correct error" );
diff --git a/lib/Parse/CPAN/Meta/t/data/Spreadsheet-Read.yml b/lib/Parse/CPAN/Meta/t/data/Spreadsheet-Read.yml
new file mode 100644 (file)
index 0000000..f402ab8
--- /dev/null
@@ -0,0 +1,61 @@
+--- #YAML:1.1
+name:                   Read
+version:                VERSION
+abstract:               Meta-Wrapper for reading spreadsheet data
+license:                perl
+author:                 
+  - H.Merijn Brand <h.m.brand@xs4all.nl>
+generated_by:           Author
+distribution_type:      module
+provides:
+  Spreadsheet::Read:
+    file:               Read.pm
+    version:            VERSION
+requires:                       
+  perl:                 5.006
+  Exporter:             0
+  Carp:                 0
+  Data::Dumper:         0
+recommends:
+  perl:                 5.008005
+  File::Temp:           0.14
+  IO::Scalar:           0
+build_requires:
+  perl:                 5.006
+  Test::Harness:        0
+  Test::More:           0
+optional_features:
+- opt_csv:
+    description:        Provides parsing of CSV streams
+    requires:
+      Text::CSV_XS:     0.23
+    recommends:
+      Text::CSV:        1.10
+      Text::CSV_PP:     1.10
+      Text::CSV_XS:     0.58
+- opt_excel:
+    description:        Provides parsing of Microsoft Excel files
+    requires:
+      Spreadsheet::ParseExcel: 0.26
+      Spreadsheet::ParseExcel::FmtDefault: 0
+    recommends:
+      Spreadsheet::ParseExcel: 0.42
+- opt_excelx:
+    description:        Provides parsing of Microsoft Excel 2007 files
+    requires:
+      Spreadsheet::XLSX:       0.07
+- opt_oo:
+    description:        Provides parsing of OpenOffice spreadsheets
+    requires:
+      Spreadsheet::ReadSXC:    0.2
+- opt_tools:
+    description:        Spreadsheet tools
+    recommends:
+      Tk:                           0
+      Tk::NoteBook:                 0
+      Tk::TableMatrix::Spreadsheet: 0
+resources:
+  license:      http://dev.perl.org/licenses/
+meta-spec:
+  version:      1.4
+  url:          http://module-build.sourceforge.net/META-spec-v1.4.html
index 319317a..46f967b 100644 (file)
@@ -9,7 +9,10 @@ use vars qw{@ISA @EXPORT};
 BEGIN {
        require Exporter;
        @ISA    = qw{ Exporter };
-       @EXPORT = qw{ tests  yaml_ok  slurp  load_ok  test_data_directory };
+       @EXPORT = qw{
+               tests  yaml_ok  yaml_error  slurp  load_ok
+               test_data_directory
+       };
 }
 
 sub test_data_directory {
@@ -53,6 +56,12 @@ sub yaml_ok {
        return 1;
 }
 
+sub yaml_error {
+       my $string = shift;
+       my $yaml   = eval { Parse::CPAN::Meta::Load( $string ); };
+       Test::More::like( $@, qr/$_[0]/, "YAML::Tiny throws expected error" );
+}
+
 sub slurp {
        my $file = shift;
        local $/ = undef;