File Coverage

blib/lib/Module/CPANfile.pm
Criterion Covered Total %
statement 135 141 95.7
branch 27 34 79.4
condition 8 13 61.5
subroutine 30 31 96.7
pod 11 18 61.1
total 211 237 89.0


line stmt bran cond sub pod time code
1             package Module::CPANfile;
2 7     7   139503 use strict;
  7         64  
  7         213  
3 7     7   40 use warnings;
  7         14  
  7         253  
4 7     7   40 use Cwd;
  7         13  
  7         595  
5 7     7   43 use Carp ();
  7         12  
  7         159  
6 7     7   3220 use Module::CPANfile::Environment;
  7         23  
  7         255  
7 7     7   3182 use Module::CPANfile::Requirement;
  7         21  
  7         948  
8              
9             our $VERSION = '1.1004';
10              
11             BEGIN {
12 7 50   7   106 if (${^TAINT}) {
13             *untaint = sub {
14 0         0 my $str = shift;
15 0         0 ($str) = $str =~ /^(.+)$/s;
16 0         0 $str;
17 0         0 };
18             } else {
19 7     27   13730 *untaint = sub { $_[0] };
  27         143  
20             }
21             }
22              
23             sub new {
24 16     16 0 46 my($class, $file) = @_;
25 16         64 bless {}, $class;
26             }
27              
28             sub load {
29 15     15 1 21781 my($proto, $file) = @_;
30              
31 15 50       87 my $self = ref $proto ? $proto : $proto->new;
32 15   66     88 $self->parse($file || _default_cpanfile());
33 13         40 $self;
34             }
35              
36             sub save {
37 1     1 1 6 my($self, $path) = @_;
38              
39 1 50       174 open my $out, ">", $path or die "$path: $!";
40 1         5 print {$out} $self->to_string;
  1         7  
41             }
42              
43             sub parse {
44 15     15 0 58 my($self, $file) = @_;
45              
46 15         35 my $code = do {
47 15 100   1   625 open my $fh, "<", $file or die "$file: $!";
  1         6  
  1         2  
  1         7  
48 14         1535 join '', <$fh>;
49             };
50              
51 14         71 $code = untaint $code;
52              
53 14         134 my $env = Module::CPANfile::Environment->new($file);
54 14 50       61 $env->parse($code) or die $@;
55              
56 13         55 $self->{_mirrors} = $env->mirrors;
57 13         47 $self->{_prereqs} = $env->prereqs;
58             }
59              
60             sub from_prereqs {
61 1     1 1 1524 my($proto, $prereqs) = @_;
62              
63 1         5 my $self = $proto->new;
64 1         8 $self->{_prereqs} = Module::CPANfile::Prereqs->from_cpan_meta($prereqs);
65              
66 1         3 $self;
67             }
68              
69             sub mirrors {
70 9     9 0 2165 my $self = shift;
71 9 100       52 $self->{_mirrors} || [];
72             }
73              
74             sub features {
75 10     10 1 30 my $self = shift;
76 10         47 map $self->feature($_), $self->{_prereqs}->identifiers;
77             }
78              
79             sub feature {
80 7     7 0 16 my($self, $identifier) = @_;
81 7         24 $self->{_prereqs}->feature($identifier);
82             }
83              
84 2     2 0 15 sub prereq { shift->prereqs }
85              
86             sub prereqs {
87 23     23 1 5349 my $self = shift;
88 23         100 $self->{_prereqs}->as_cpan_meta;
89             }
90              
91             sub merged_requirements {
92 0     0 0 0 my $self = shift;
93 0         0 $self->{_prereqs}->merged_requirements;
94             }
95              
96             sub effective_prereqs {
97 2     2 1 3800 my($self, $features) = @_;
98 2 100       6 $self->prereqs_with(@{$features || []});
  2         34  
99             }
100              
101             sub prereqs_with {
102 5     5 1 4629 my($self, @feature_identifiers) = @_;
103              
104 5         10 my @others = map { $self->feature($_)->prereqs } @feature_identifiers;
  3         8  
105 4         513 $self->prereqs->with_merged_prereqs(\@others);
106             }
107              
108             sub prereq_specs {
109 13     13 1 74 my $self = shift;
110 13         56 $self->prereqs->as_string_hash;
111             }
112              
113             sub prereq_for_module {
114 38     38 0 84 my($self, $module) = @_;
115 38         137 $self->{_prereqs}->find($module);
116             }
117              
118             sub options_for_module {
119 38     38 1 6129 my($self, $module) = @_;
120 38 50       98 my $prereq = $self->prereq_for_module($module) or return;
121 38         109 $prereq->requirement->options;
122             }
123              
124             sub merge_meta {
125 1     1 1 7 my($self, $file, $version) = @_;
126              
127 1         565 require CPAN::Meta;
128              
129 1 50 33     26897 $version ||= $file =~ /\.yml$/ ? '1.4' : '2';
130              
131 1         7 my $prereq = $self->prereqs;
132              
133 1         1328 my $meta = CPAN::Meta->load_file($file);
134 1         42529 my $prereqs_hash = $prereq->with_merged_prereqs($meta->effective_prereqs)->as_string_hash;
135 1         4305 my $struct = { %{$meta->as_struct}, prereqs => $prereqs_hash };
  1         7  
136              
137 1         273 CPAN::Meta->new($struct)->save($file, { version => $version });
138             }
139              
140             sub _d($) {
141 54     54   3250 require Data::Dumper;
142 54         33832 chomp(my $value = Data::Dumper->new([$_[0]])->Terse(1)->Dump);
143 54         3078 $value;
144             }
145              
146             sub _default_cpanfile {
147 13     13   308 my $file = Cwd::abs_path('cpanfile');
148 13         75 untaint $file;
149             }
150              
151             sub to_string {
152 8     8 1 5308 my($self, $include_empty) = @_;
153              
154 8         36 my $mirrors = $self->mirrors;
155 8         29 my $prereqs = $self->prereq_specs;
156              
157 8         3584 my $code = '';
158 8         33 $code .= $self->_dump_mirrors($mirrors);
159 8         36 $code .= $self->_dump_prereqs($prereqs, $include_empty);
160              
161 8         40 for my $feature ($self->features) {
162 2         786 $code .= "feature @{[ _d $feature->{identifier} ]}, @{[ _d $feature->{description} ]} => sub {\n";
  2         7  
  2         8  
163 2         11 $code .= $self->_dump_prereqs($feature->{prereqs}->as_string_hash, $include_empty, 4);
164 2         12 $code .= "};\n\n";
165             }
166              
167 8         58 $code =~ s/\n+$/\n/s;
168 8         142 $code;
169             }
170              
171             sub _dump_mirrors {
172 8     8   24 my($self, $mirrors) = @_;
173              
174 8         42 my $code = "";
175              
176 8         43 for my $url (@$mirrors) {
177 4         12 $code .= "mirror @{[ _d $url ]};\n";
  4         10  
178             }
179              
180 8         34 $code =~ s/\n+$/\n/s;
181 8         28 $code;
182             }
183              
184             sub _dump_prereqs {
185 10     10   372 my($self, $prereqs, $include_empty, $base_indent) = @_;
186              
187 10         21 my $code = '';
188 10         33 for my $phase (qw(runtime configure build test develop)) {
189 50 100       157 my $indent = $phase eq 'runtime' ? '' : ' ';
190 50   100     225 $indent .= (' ' x ($base_indent || 0));
191              
192 50         116 my($phase_code, $requirements);
193 50 100       171 $phase_code .= "on $phase => sub {\n" unless $phase eq 'runtime';
194              
195 50         92 for my $type (qw(requires recommends suggests conflicts)) {
196 200         317 for my $mod (sort keys %{$prereqs->{$phase}{$type}}) {
  200         784  
197 26         84 my $ver = $prereqs->{$phase}{$type}{$mod};
198 26 100       97 $phase_code .= $ver eq '0'
199 11         32 ? "${indent}$type @{[ _d $mod ]}"
200 15         44 : "${indent}$type @{[ _d $mod ]}, @{[ _d $ver ]}";
  15         48  
201              
202 26   50     106 my $options = $self->options_for_module($mod) || {};
203 26 100       77 if (%$options) {
204 4         6 my @opts;
205 4         11 for my $key (keys %$options) {
206 5 50       27 my $k = $key =~ /^[a-zA-Z0-9_]+$/ ? $key : _d $key;
207 5         10 push @opts, "$k => @{[ _d $options->{$k} ]}";
  5         11  
208             }
209              
210 4         18 $phase_code .= ",\n" . join(",\n", map " $indent$_", @opts);
211             }
212              
213 26         71 $phase_code .= ";\n";
214 26         85 $requirements++;
215             }
216             }
217              
218 50 100       152 $phase_code .= "\n" unless $requirements;
219 50 100       116 $phase_code .= "};\n" unless $phase eq 'runtime';
220              
221 50 100 66     305 $code .= $phase_code . "\n" if $requirements or $include_empty;
222             }
223              
224 10         91 $code =~ s/\n+$/\n/s;
225 10         44 $code;
226             }
227              
228             1;
229              
230             __END__