File Coverage

blib/lib/Parse/CPAN/Meta.pm
Criterion Covered Total %
statement 76 85 89.4
branch 28 52 53.8
condition 4 7 57.1
subroutine 16 16 100.0
pod 9 9 100.0
total 133 169 78.7


line stmt bran cond sub pod time code
1 17     17   42824 use 5.008001;
  17         38  
2 17     17   56 use strict;
  17         761  
  17         651  
3             package Parse::CPAN::Meta;
4             # ABSTRACT: Parse META.yml and META.json CPAN metadata files
5              
6             our $VERSION = '2.150008'; # TRIAL
7              
8 17     17   53 use Exporter;
  17         18  
  17         596  
9 17     17   59 use Carp 'croak';
  17         14  
  17         4088  
10              
11             our @ISA = qw/Exporter/;
12             our @EXPORT_OK = qw/Load LoadFile/;
13              
14             sub load_file {
15 149     149 1 15555 my ($class, $filename) = @_;
16              
17 149         345 my $meta = _slurp($filename);
18              
19 149 100       902 if ($filename =~ /\.ya?ml$/) {
    100          
20 103         483 return $class->load_yaml_string($meta);
21             }
22             elsif ($filename =~ /\.json$/) {
23 39         210 return $class->load_json_string($meta);
24             }
25             else {
26 7         29 $class->load_string($meta); # try to detect yaml/json
27             }
28             }
29              
30             sub load_string {
31 8     8 1 14 my ($class, $string) = @_;
32 8 100       68 if ( $string =~ /^---/ ) { # looks like YAML
    100          
33 1         15 return $class->load_yaml_string($string);
34             }
35             elsif ( $string =~ /^\s*\{/ ) { # looks like JSON
36 6         14 return $class->load_json_string($string);
37             }
38             else { # maybe doc-marker-free YAML
39 1         3 return $class->load_yaml_string($string);
40             }
41             }
42              
43             sub load_yaml_string {
44 121     121 1 2263 my ($class, $string) = @_;
45 121         235 my $backend = $class->yaml_backend();
46 17     17   69 my $data = eval { no strict 'refs'; &{"$backend\::Load"}($string) };
  17         16  
  17         10763  
  121         180  
  121         124  
  121         580  
47 121 100       181231 croak $@ if $@;
48 120   50     452 return $data || {}; # in case document was valid but empty
49             }
50              
51             sub load_json_string {
52 54     54 1 4996 my ($class, $string) = @_;
53             # load_json_string takes characters, decode_json expects bytes
54 54         236 my $encoded = Encode::encode('UTF-8', $string, Encode::PERLQQ());
55 54         3266 my $data = eval { $class->json_decoder()->can('decode_json')->($encoded) };
  54         153  
56 54 50       417660 croak $@ if $@;
57 54   50     244 return $data || {};
58             }
59              
60             sub yaml_backend {
61 129 50   129 1 6574 if (! defined $ENV{PERL_YAML_BACKEND} ) {
62 129 50       227 _can_load( 'CPAN::Meta::YAML', 0.011 )
63             or croak "CPAN::Meta::YAML 0.011 is not available\n";
64 129         228 return "CPAN::Meta::YAML";
65             }
66             else {
67 0         0 my $backend = $ENV{PERL_YAML_BACKEND};
68 0 0       0 _can_load( $backend )
69             or croak "Could not load PERL_YAML_BACKEND '$backend'\n";
70 0 0       0 $backend->can("Load")
71             or croak "PERL_YAML_BACKEND '$backend' does not implement Load()\n";
72 0         0 return $backend;
73             }
74             }
75              
76             sub json_decoder {
77 55 100   55 1 1633 if (my $decoder = $ENV{CPAN_META_JSON_DECODER}) {
78 2 50       4 _can_load( $decoder )
79             or croak "Could not load CPAN_META_JSON_DECODER '$decoder'\n";
80 2 50       23 $decoder->can('decode_json')
81             or croak "No decode_json sub provided by CPAN_META_JSON_DECODER '$decoder'\n";
82 2         8 return $decoder;
83             }
84 53         134 return $_[0]->json_backend;
85             }
86              
87             sub json_backend {
88 61 50   61 1 6987 if (my $backend = $ENV{CPAN_META_JSON_BACKEND}) {
89 0 0       0 _can_load( $backend )
90             or croak "Could not load CPAN_META_JSON_BACKEND '$backend'\n";
91 0 0       0 $backend->can('new')
92             or croak "No constructor provided by CPAN_META_JSON_BACKEND '$backend'\n";
93 0         0 return $backend;
94             }
95 61 50 66     229 if (! $ENV{PERL_JSON_BACKEND} or $ENV{PERL_JSON_BACKEND} eq 'JSON::PP') {
96 61 50       121 _can_load( 'JSON::PP' => 2.27300 )
97             or croak "JSON::PP 2.27300 is not available\n";
98 61         515 return 'JSON::PP';
99             }
100             else {
101 0 0       0 _can_load( 'JSON' => 2.5 )
102             or croak "JSON 2.5 is required for " .
103             "\$ENV{PERL_JSON_BACKEND} = '$ENV{PERL_JSON_BACKEND}'\n";
104 0         0 return "JSON";
105             }
106             }
107              
108             sub _slurp {
109 150     150   5142 require Encode;
110 150 50       53590 open my $fh, "<:raw", "$_[0]" ## no critic
111             or die "can't open $_[0] for reading: $!";
112 150         194 my $content = do { local $/; <$fh> };
  150         452  
  150         3238  
113 150         735 $content = Encode::decode('UTF-8', $content, Encode::PERLQQ());
114 150         14220 return $content;
115             }
116            
117             sub _can_load {
118 192     192   226 my ($module, $version) = @_;
119 192         706 (my $file = $module) =~ s{::}{/}g;
120 192         240 $file .= ".pm";
121 192 100       770 return 1 if $INC{$file};
122 9 50       34 return 0 if exists $INC{$file}; # prior load failed
123 9 50       15 eval { require $file; 1 }
  9         5222  
  9         39671  
124             or return 0;
125 9 50       29 if ( defined $version ) {
126 9 50       16 eval { $module->VERSION($version); 1 }
  9         150  
  9         43  
127             or return 0;
128             }
129 9         31 return 1;
130             }
131              
132             # Kept for backwards compatibility only
133             # Create an object from a file
134             sub LoadFile ($) { ## no critic
135 1     1 1 3 return Load(_slurp(shift));
136             }
137              
138             # Parse a document from a string.
139             sub Load ($) { ## no critic
140 3     3 1 620 require CPAN::Meta::YAML;
141 3         3949 my $object = eval { CPAN::Meta::YAML::Load(shift) };
  3         8  
142 3 50       984 croak $@ if $@;
143 3         8 return $object;
144             }
145              
146             1;
147              
148             __END__