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 5     5   54069 use 5.008001;
  5         12  
2 5     5   15 use strict;
  5         5  
  5         194  
3             package Parse::CPAN::Meta;
4             # ABSTRACT: Parse META.yml and META.json CPAN metadata files
5              
6             our $VERSION = '1.4422';
7              
8 5     5   31 use Exporter;
  5         5  
  5         202  
9 5     5   18 use Carp 'croak';
  5         6  
  5         1214  
10              
11             our @ISA = qw/Exporter/;
12             our @EXPORT_OK = qw/Load LoadFile/;
13              
14             sub load_file {
15 7     7 1 15170 my ($class, $filename) = @_;
16              
17 7         18 my $meta = _slurp($filename);
18              
19 7 100       41 if ($filename =~ /\.ya?ml$/) {
    100          
20 1         8 return $class->load_yaml_string($meta);
21             }
22             elsif ($filename =~ /\.json$/) {
23 2         12 return $class->load_json_string($meta);
24             }
25             else {
26 4         17 $class->load_string($meta); # try to detect yaml/json
27             }
28             }
29              
30             sub load_string {
31 4     4 1 6 my ($class, $string) = @_;
32 4 100       26 if ( $string =~ /^---/ ) { # looks like YAML
    100          
33 1         19 return $class->load_yaml_string($string);
34             }
35             elsif ( $string =~ /^\s*\{/ ) { # looks like JSON
36 2         7 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 5     5 1 1884 my ($class, $string) = @_;
45 5         12 my $backend = $class->yaml_backend();
46 5     5   20 my $data = eval { no strict 'refs'; &{"$backend\::Load"}($string) };
  5         5  
  5         3079  
  5         6  
  5         5  
  5         26  
47 5 100       3712 croak $@ if $@;
48 4   50     15 return $data || {}; # in case document was valid but empty
49             }
50              
51             sub load_json_string {
52 8     8 1 3562 my ($class, $string) = @_;
53 8         35 require Encode;
54             # load_json_string takes characters, decode_json expects bytes
55 8         29 my $encoded = Encode::encode('UTF-8', $string, Encode::PERLQQ());
56 8         396 my $data = eval { $class->json_decoder()->can('decode_json')->($encoded) };
  8         24  
57 8 50       41626 croak $@ if $@;
58 8   50     38 return $data || {};
59             }
60              
61             sub yaml_backend {
62 11 50   11 1 5630 if (! defined $ENV{PERL_YAML_BACKEND} ) {
63 11 50       23 _can_load( 'CPAN::Meta::YAML', 0.011 )
64             or croak "CPAN::Meta::YAML 0.011 is not available\n";
65 11         34 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 9 100   9 1 1416 if (my $decoder = $ENV{CPAN_META_JSON_DECODER}) {
79 2 50       6 _can_load( $decoder )
80             or croak "Could not load CPAN_META_JSON_DECODER '$decoder'\n";
81 2 50       23 $decoder->can('decode_json')
82             or croak "No decode_json sub provided by CPAN_META_JSON_DECODER '$decoder'\n";
83 2         9 return $decoder;
84             }
85 7         17 return $_[0]->json_backend;
86             }
87              
88             sub json_backend {
89 14 50   14 1 23148 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 14 50 66     45 if (! $ENV{PERL_JSON_BACKEND} or $ENV{PERL_JSON_BACKEND} eq 'JSON::PP') {
97 14 50       26 _can_load( 'JSON::PP' => 2.27300 )
98             or croak "JSON::PP 2.27300 is not available\n";
99 14         97 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 8     8   545 require Encode;
111 8 50       7068 open my $fh, "<:raw", "$_[0]" ## no critic
112             or die "can't open $_[0] for reading: $!";
113 8         11 my $content = do { local $/; <$fh> };
  8         24  
  8         136  
114 8         39 $content = Encode::decode('UTF-8', $content, Encode::PERLQQ());
115 8         783 return $content;
116             }
117            
118             sub _can_load {
119 27     27   31 my ($module, $version) = @_;
120 27         83 (my $file = $module) =~ s{::}{/}g;
121 27         30 $file .= ".pm";
122 27 100       93 return 1 if $INC{$file};
123 2 50       5 return 0 if exists $INC{$file}; # prior load failed
124 2 50       3 eval { require $file; 1 }
  2         1111  
  2         7983  
125             or return 0;
126 2 50       6 if ( defined $version ) {
127 2 50       2 eval { $module->VERSION($version); 1 }
  2         35  
  2         10  
128             or return 0;
129             }
130 2         6 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 4 return Load(_slurp(shift));
137             }
138              
139             # Parse a document from a string.
140             sub Load ($) { ## no critic
141 3     3 1 581 require CPAN::Meta::YAML;
142 3         3983 my $object = eval { CPAN::Meta::YAML::Load(shift) };
  3         8  
143 3 50       915 croak $@ if $@;
144 3         7 return $object;
145             }
146              
147             1;
148              
149             __END__