File Coverage

blib/lib/Parse/CPAN/Meta.pm
Criterion Covered Total %
statement 77 86 89.5
branch 28 52 53.8
condition 4 7 57.1
subroutine 16 16 100.0
pod 9 9 100.0
total 134 170 78.8


line stmt bran cond sub pod time code
1 17     17   43792 use 5.008001;
  17         39  
2 17     17   51 use strict;
  17         660  
  17         636  
3             package Parse::CPAN::Meta;
4             # ABSTRACT: Parse META.yml and META.json CPAN metadata files
5              
6             our $VERSION = '2.150009'; # TRIAL
7              
8 17     17   57 use Exporter;
  17         12  
  17         621  
9 17     17   53 use Carp 'croak';
  17         16  
  17         4066  
10              
11             our @ISA = qw/Exporter/;
12             our @EXPORT_OK = qw/Load LoadFile/;
13              
14             sub load_file {
15 149     149 1 18975 my ($class, $filename) = @_;
16              
17 149         373 my $meta = _slurp($filename);
18              
19 149 100       890 if ($filename =~ /\.ya?ml$/) {
    100          
20 103         500 return $class->load_yaml_string($meta);
21             }
22             elsif ($filename =~ /\.json$/) {
23 39         203 return $class->load_json_string($meta);
24             }
25             else {
26 7         31 $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       56 if ( $string =~ /^---/ ) { # looks like YAML
    100          
33 1         13 return $class->load_yaml_string($string);
34             }
35             elsif ( $string =~ /^\s*\{/ ) { # looks like JSON
36 6         16 return $class->load_json_string($string);
37             }
38             else { # maybe doc-marker-free YAML
39 1         2 return $class->load_yaml_string($string);
40             }
41             }
42              
43             sub load_yaml_string {
44 121     121 1 2347 my ($class, $string) = @_;
45 121         263 my $backend = $class->yaml_backend();
46 17     17   66 my $data = eval { no strict 'refs'; &{"$backend\::Load"}($string) };
  17         14  
  17         10134  
  121         222  
  121         167  
  121         649  
47 121 100       179280 croak $@ if $@;
48 120   50     453 return $data || {}; # in case document was valid but empty
49             }
50              
51             sub load_json_string {
52 54     54 1 4937 my ($class, $string) = @_;
53 54         232 require Encode;
54             # load_json_string takes characters, decode_json expects bytes
55 54         213 my $encoded = Encode::encode('UTF-8', $string, Encode::PERLQQ());
56 54         3350 my $data = eval { $class->json_decoder()->can('decode_json')->($encoded) };
  54         175  
57 54 50       414337 croak $@ if $@;
58 54   50     270 return $data || {};
59             }
60              
61             sub yaml_backend {
62 129 50   129 1 6526 if (! defined $ENV{PERL_YAML_BACKEND} ) {
63 129 50       247 _can_load( 'CPAN::Meta::YAML', 0.011 )
64             or croak "CPAN::Meta::YAML 0.011 is not available\n";
65 129         293 return "CPAN::Meta::YAML";
66             }
67             else {
68 0         0 my $backend = $ENV{PERL_YAML_BACKEND};
69 0 0       0 _can_load( $backend )
70             or croak "Could not load PERL_YAML_BACKEND '$backend'\n";
71 0 0       0 $backend->can("Load")
72             or croak "PERL_YAML_BACKEND '$backend' does not implement Load()\n";
73 0         0 return $backend;
74             }
75             }
76              
77             sub json_decoder {
78 55 100   55 1 1587 if (my $decoder = $ENV{CPAN_META_JSON_DECODER}) {
79 2 50       5 _can_load( $decoder )
80             or croak "Could not load CPAN_META_JSON_DECODER '$decoder'\n";
81 2 50       22 $decoder->can('decode_json')
82             or croak "No decode_json sub provided by CPAN_META_JSON_DECODER '$decoder'\n";
83 2         7 return $decoder;
84             }
85 53         137 return $_[0]->json_backend;
86             }
87              
88             sub json_backend {
89 61 50   61 1 6911 if (my $backend = $ENV{CPAN_META_JSON_BACKEND}) {
90 0 0       0 _can_load( $backend )
91             or croak "Could not load CPAN_META_JSON_BACKEND '$backend'\n";
92 0 0       0 $backend->can('new')
93             or croak "No constructor provided by CPAN_META_JSON_BACKEND '$backend'\n";
94 0         0 return $backend;
95             }
96 61 50 66     185 if (! $ENV{PERL_JSON_BACKEND} or $ENV{PERL_JSON_BACKEND} eq 'JSON::PP') {
97 61 50       134 _can_load( 'JSON::PP' => 2.27300 )
98             or croak "JSON::PP 2.27300 is not available\n";
99 61         571 return 'JSON::PP';
100             }
101             else {
102 0 0       0 _can_load( 'JSON' => 2.5 )
103             or croak "JSON 2.5 is required for " .
104             "\$ENV{PERL_JSON_BACKEND} = '$ENV{PERL_JSON_BACKEND}'\n";
105 0         0 return "JSON";
106             }
107             }
108              
109             sub _slurp {
110 150     150   4700 require Encode;
111 150 50       53187 open my $fh, "<:raw", "$_[0]" ## no critic
112             or die "can't open $_[0] for reading: $!";
113 150         193 my $content = do { local $/; <$fh> };
  150         465  
  150         3245  
114 150         806 $content = Encode::decode('UTF-8', $content, Encode::PERLQQ());
115 150         14284 return $content;
116             }
117              
118             sub _can_load {
119 192     192   236 my ($module, $version) = @_;
120 192         710 (my $file = $module) =~ s{::}{/}g;
121 192         524 $file .= ".pm";
122 192 100       782 return 1 if $INC{$file};
123 9 50       25 return 0 if exists $INC{$file}; # prior load failed
124 9 50       17 eval { require $file; 1 }
  9         4881  
  9         35648  
125             or return 0;
126 9 50       27 if ( defined $version ) {
127 9 50       13 eval { $module->VERSION($version); 1 }
  9         177  
  9         37  
128             or return 0;
129             }
130 9         27 return 1;
131             }
132              
133             # Kept for backwards compatibility only
134             # Create an object from a file
135             sub LoadFile ($) { ## no critic
136 1     1 1 3 return Load(_slurp(shift));
137             }
138              
139             # Parse a document from a string.
140             sub Load ($) { ## no critic
141 3     3 1 647 require CPAN::Meta::YAML;
142 3         4040 my $object = eval { CPAN::Meta::YAML::Load(shift) };
  3         7  
143 3 50       933 croak $@ if $@;
144 3         8 return $object;
145             }
146              
147             1;
148              
149             __END__