File Coverage

blib/lib/Test/CPAN/Meta/JSON/Version.pm
Criterion Covered Total %
statement 202 215 93.9
branch 124 146 84.9
condition 65 68 95.5
subroutine 30 30 100.0
pod 25 25 100.0
total 446 484 92.1


line stmt bran cond sub pod time code
1             package Test::CPAN::Meta::JSON::Version;
2              
3 15     15   118517 use warnings;
  15         29  
  15         524  
4 15     15   62 use strict;
  15         20  
  15         478  
5              
6 15     15   64 use vars qw($VERSION);
  15         17  
  15         61414  
7             $VERSION = '0.16';
8              
9             #----------------------------------------------------------------------------
10              
11             =head1 NAME
12              
13             Test::CPAN::Meta::JSON::Version - Validate CPAN META data against the specification
14              
15             =head1 SYNOPSIS
16              
17             use Test::CPAN::Meta::JSON::Version;
18              
19             =head1 DESCRIPTION
20              
21             This module was written to ensure that a META.json file, provided with a
22             standard distribution uploaded to CPAN, meets the specifications that are
23             slowly being introduced to module uploads, via the use of
24             L, L and L.
25              
26             This module is meant to be used together with L, however
27             the code is self contained enough that you can access it directly.
28              
29             See L for further details of the CPAN Meta Specification.
30              
31             =head1 ABSTRACT
32              
33             Validation of META.json data against the CPAN Meta Specification.
34              
35             =cut
36              
37             #----------------------------------------------------------------------------
38              
39             #############################################################################
40             #Specification Definitions #
41             #############################################################################
42              
43             my $spec_error = "Missing validation action in specification. "
44             . "Must be one of 'map', 'list', 'lazylist' or 'value'";
45              
46             my %known_specs = (
47             '1.4' => 'http://module-build.sourceforge.net/META-spec-v1.4.html',
48             '1.3' => 'http://module-build.sourceforge.net/META-spec-v1.3.html',
49             '1.2' => 'http://module-build.sourceforge.net/META-spec-v1.2.html',
50             '1.1' => 'http://module-build.sourceforge.net/META-spec-v1.1.html',
51             '1.0' => 'http://module-build.sourceforge.net/META-spec-v1.0.html'
52             );
53             my %known_urls = map {$known_specs{$_} => $_} keys %known_specs;
54              
55             my $module_map1 = { 'map' => { ':key' => { name => \&module, value => \&exversion } } };
56             my $module_map2 = { 'map' => { ':key' => { name => \&module, value => \&version } } };
57             my $no_index_1_3 = {
58             'map' => { file => { list => { value => \&string } },
59             directory => { list => { value => \&string } },
60             'package' => { list => { value => \&string } },
61             namespace => { list => { value => \&string } },
62             }
63             };
64             my $no_index_1_2 = {
65             'map' => { file => { list => { value => \&string } },
66             dir => { list => { value => \&string } },
67             'package' => { list => { value => \&string } },
68             namespace => { list => { value => \&string } },
69             }
70             };
71             my $no_index_1_1 = {
72             'map' => { ':key' => { name => \&keyword, list => { value => \&string } },
73             }
74             };
75              
76             my $prereq_map = {
77             'map' => {
78             ':key' => {
79             name => \&phase,
80             'map' => {
81             ':key' => {
82             name => \&relation,
83             %$module_map1
84             }
85             }
86             }
87             }
88             };
89              
90             my %definitions = (
91             '2' => {
92             # REQUIRED
93             'abstract' => { mandatory => 1, value => \&string },
94             'author' => { mandatory => 1, lazylist => { value => \&string } },
95             'dynamic_config' => { mandatory => 1, value => \&boolean },
96             'generated_by' => { mandatory => 1, value => \&string },
97             'license' => { mandatory => 1, list => { value => \&license } },
98             'meta-spec' => {
99             mandatory => 1,
100             'map' => {
101             version => { mandatory => 1, value => \&version},
102             url => { value => \&url }
103             }
104             },
105             'name' => { mandatory => 1, value => \&string },
106             'release_status' => { mandatory => 1, value => \&release_status },
107             'version' => { mandatory => 1, value => \&version },
108              
109             # OPTIONAL
110             'description' => { value => \&string },
111             'keywords' => { lazylist => { value => \&string } },
112             'no_index' => $no_index_1_3,
113             'optional_features' => {
114             'map' => {
115             ':key' => {
116             name => \&identifier,
117             'map' => {
118             description => { value => \&string },
119             prereqs => $prereq_map,
120             }
121             }
122             }
123             },
124             'prereqs' => $prereq_map,
125             'provides' => {
126             'map' => {
127             ':key' => {
128             name => \&module,
129             'map' => {
130             file => { mandatory => 1, value => \&file },
131             version => { value => \&version } } } }
132             },
133             'resources' => {
134             'map' => {
135             license => { list => { value => \&url } },
136             homepage => { value => \&url },
137             bugtracker => {
138             'map' => {
139             web => { value => \&url },
140             mailto => { value => \&string},
141             }},
142             repository => {
143             'map' => {
144             web => { value => \&url },
145             url => { value => \&url },
146             type => { value => \&string },
147             }},
148             ':key' => { value => \&string, name => \&custom_2 },
149             }
150             },
151              
152             # CUSTOM -- additional user defined key/value pairs
153             # note we can only validate the key name, as the structure is user defined
154             ':key' => { name => \&custom_2, value => \&anything },
155             },
156              
157             '1.4' => {
158             'meta-spec' => { mandatory => 1, 'map' => { version => { mandatory => 1, value => \&version},
159             url => { mandatory => 1, value => \&urlspec } } },
160              
161             'name' => { mandatory => 1, value => \&string },
162             'version' => { mandatory => 1, value => \&version },
163             'abstract' => { mandatory => 1, value => \&string },
164             'author' => { mandatory => 1, list => { value => \&string } },
165             'license' => { mandatory => 1, value => \&license },
166             'generated_by' => { mandatory => 1, value => \&string },
167              
168             'distribution_type' => { value => \&string },
169             'dynamic_config' => { value => \&boolean },
170              
171             'requires' => $module_map1,
172             'recommends' => $module_map1,
173             'build_requires' => $module_map1,
174             'configure_requires' => $module_map1,
175             'conflicts' => $module_map2,
176              
177             'optional_features' => {
178             'map' => {
179             ':key' => { name => \&identifier,
180             'map' => { description => { value => \&string },
181             requires_packages => { value => \&string },
182             requires_os => { value => \&string },
183             excludes_os => { value => \&string },
184             requires => $module_map1,
185             recommends => $module_map1,
186             build_requires => $module_map1,
187             conflicts => $module_map2,
188             }
189             }
190             }
191             },
192              
193             'provides' => {
194             'map' => { ':key' => { name => \&module,
195             'map' => { file => { mandatory => 1, value => \&file },
196             version => { value => \&version } } } }
197             },
198              
199             'no_index' => $no_index_1_3,
200             'private' => $no_index_1_3,
201              
202             'keywords' => { list => { value => \&string } },
203              
204             'resources' => {
205             'map' => { license => { value => \&url },
206             homepage => { value => \&url },
207             bugtracker => { value => \&url },
208             repository => { value => \&url },
209             ':key' => { value => \&string, name => \&resource },
210             }
211             },
212              
213             # additional user defined key/value pairs
214             # note we can only validate the key name, as the structure is user defined
215             ':key' => { name => \&keyword, value => \&anything },
216             },
217              
218             '1.3' => {
219             'meta-spec' => { mandatory => 1, 'map' => { version => { mandatory => 1, value => \&version},
220             url => { mandatory => 1, value => \&urlspec } } },
221              
222             'name' => { mandatory => 1, value => \&string },
223             'version' => { mandatory => 1, value => \&version },
224             'abstract' => { mandatory => 1, value => \&string },
225             'author' => { mandatory => 1, list => { value => \&string } },
226             'license' => { mandatory => 1, value => \&license },
227             'generated_by' => { mandatory => 1, value => \&string },
228              
229             'distribution_type' => { value => \&string },
230             'dynamic_config' => { value => \&boolean },
231              
232             'requires' => $module_map1,
233             'recommends' => $module_map1,
234             'build_requires' => $module_map1,
235             'conflicts' => $module_map2,
236              
237             'optional_features' => {
238             'map' => {
239             ':key' => { name => \&identifier,
240             'map' => { description => { value => \&string },
241             requires_packages => { value => \&string },
242             requires_os => { value => \&string },
243             excludes_os => { value => \&string },
244             requires => $module_map1,
245             recommends => $module_map1,
246             build_requires => $module_map1,
247             conflicts => $module_map2,
248             }
249             }
250             }
251             },
252              
253             'provides' => {
254             'map' => { ':key' => { name => \&module,
255             'map' => { file => { mandatory => 1, value => \&file },
256             version => { value => \&version } } } }
257             },
258              
259             'no_index' => $no_index_1_3,
260             'private' => $no_index_1_3,
261              
262             'keywords' => { list => { value => \&string } },
263              
264             'resources' => {
265             'map' => { license => { value => \&url },
266             homepage => { value => \&url },
267             bugtracker => { value => \&url },
268             repository => { value => \&url },
269             ':key' => { value => \&string, name => \&resource },
270             }
271             },
272              
273             # additional user defined key/value pairs
274             # note we can only validate the key name, as the structure is user defined
275             ':key' => { name => \&keyword, value => \&anything },
276             },
277              
278             # v1.2 is misleading, it seems to assume that a number of fields where created
279             # within v1.1, when they were created within v1.2. This may have been an
280             # original mistake, and that a v1.1 was retro fitted into the timeline, when
281             # v1.2 was originally slated as v1.1. But I could be wrong ;)
282             '1.2' => {
283             'meta-spec' => { mandatory => 1, 'map' => { version => { mandatory => 1, value => \&version},
284             url => { mandatory => 1, value => \&urlspec } } },
285              
286             'name' => { mandatory => 1, value => \&string },
287             'version' => { mandatory => 1, value => \&version },
288             'license' => { mandatory => 1, value => \&license },
289             'generated_by' => { mandatory => 1, value => \&string },
290             'author' => { mandatory => 1, list => { value => \&string } },
291             'abstract' => { mandatory => 1, value => \&string },
292              
293             'distribution_type' => { value => \&string },
294             'dynamic_config' => { value => \&boolean },
295              
296             'keywords' => { list => { value => \&string } },
297              
298             'private' => $no_index_1_2,
299             '$no_index' => $no_index_1_2,
300              
301             'requires' => $module_map1,
302             'recommends' => $module_map1,
303             'build_requires' => $module_map1,
304             'conflicts' => $module_map2,
305              
306             'optional_features' => {
307             'map' => {
308             ':key' => { name => \&identifier,
309             'map' => { description => { value => \&string },
310             requires_packages => { value => \&string },
311             requires_os => { value => \&string },
312             excludes_os => { value => \&string },
313             requires => $module_map1,
314             recommends => $module_map1,
315             build_requires => $module_map1,
316             conflicts => $module_map2,
317             }
318             }
319             }
320             },
321              
322             'provides' => {
323             'map' => { ':key' => { name => \&module,
324             'map' => { file => { mandatory => 1, value => \&file },
325             version => { value => \&version } } } }
326             },
327              
328             'resources' => {
329             'map' => { license => { value => \&url },
330             homepage => { value => \&url },
331             bugtracker => { value => \&url },
332             repository => { value => \&url },
333             ':key' => { value => \&string, name => \&resource },
334             }
335             },
336              
337             # additional user defined key/value pairs
338             # note we can only validate the key name, as the structure is user defined
339             ':key' => { name => \&keyword, value => \&anything },
340             },
341              
342             # note that the 1.1 spec doesn't specify optional or mandatory fields, what
343             # appears below is assumed from later specifications.
344             '1.1' => {
345             'name' => { mandatory => 1, value => \&string },
346             'version' => { mandatory => 1, value => \&version },
347             'license' => { mandatory => 1, value => \&license },
348             'license_uri' => { mandatory => 0, value => \&url },
349             'generated_by' => { mandatory => 1, value => \&string },
350              
351             'distribution_type' => { value => \&string },
352             'dynamic_config' => { value => \&boolean },
353              
354             'private' => $no_index_1_1,
355              
356             'requires' => $module_map1,
357             'recommends' => $module_map1,
358             'build_requires' => $module_map1,
359             'conflicts' => $module_map2,
360              
361             # additional user defined key/value pairs
362             # note we can only validate the key name, as the structure is user defined
363             ':key' => { name => \&keyword, value => \&anything },
364             },
365              
366             # note that the 1.0 spec doesn't specify optional or mandatory fields, what
367             # appears below is assumed from later specifications.
368             '1.0' => {
369             'name' => { mandatory => 1, value => \&string },
370             'version' => { mandatory => 1, value => \&version },
371             'license' => { mandatory => 1, value => \&license },
372             'generated_by' => { mandatory => 1, value => \&string },
373              
374             'distribution_type' => { value => \&string },
375             'dynamic_config' => { value => \&boolean },
376              
377             'requires' => $module_map1,
378             'recommends' => $module_map1,
379             'build_requires' => $module_map1,
380             'conflicts' => $module_map2,
381              
382             # additional user defined key/value pairs
383             # note we can only validate the key name, as the structure is user defined
384             ':key' => { name => \&keyword, value => \&anything },
385             },
386             );
387              
388             # aliases
389             $definitions{'2.0'} = $definitions{'2'};
390              
391             #############################################################################
392             #Code #
393             #############################################################################
394              
395             =head1 CLASS CONSTRUCTOR
396              
397             =over
398              
399             =item * new( data => $data [, spec => $version] )
400              
401             The constructor must be passed a valid data structure.
402              
403             Optionally you may also provide a specification version. This version is then
404             use to ensure that the given data structure meets the respective
405             specification definition. If no version is provided the module will attempt to
406             deduce the appropriate specification version from the data structure itself.
407              
408             =back
409              
410             =cut
411              
412             sub new {
413 74     74 1 43170 my ($class,%hash) = @_;
414              
415             # create an attributes hash
416 74         193 my $atts = {
417             'spec' => $hash{spec},
418             'data' => $hash{data},
419             };
420              
421             # create the object
422 74         212 my $self = bless $atts, $class;
423             }
424              
425             =head1 METHODS
426              
427             =head2 Main Methods
428              
429             =over
430              
431             =item * parse()
432              
433             Using the given data structure provided with the constructor, attempts to
434             parse and validate according to the appropriate specification definition.
435              
436             Returns 1 if any errors found, otherwise returns 0.
437              
438             =item * errors()
439              
440             Returns a list of the errors found during parsing.
441              
442             =back
443              
444             =cut
445              
446             sub parse {
447 68     68 1 151 my $self = shift;
448 68         102 my $data = $self->{data};
449              
450 68 100       144 unless($self->{spec}) {
451 33 100 100     209 $self->{spec} = $data->{'meta-spec'} && $data->{'meta-spec'}{'version'} ? $data->{'meta-spec'}{'version'} : '2';
452             }
453              
454 68         155 $self->check_map($definitions{$self->{spec}},$data);
455 68 100       233 return defined $self->{errors} ? 1 : 0;
456             }
457              
458             sub errors {
459 90     90 1 226 my $self = shift;
460 90 100       195 return () unless($self->{errors});
461 61         53 return @{$self->{errors}};
  61         153  
462             }
463              
464             =head2 Check Methods
465              
466             =over
467              
468             =item * check_map($spec,$data)
469              
470             Checks whether a map (or hash) part of the data structure conforms to the
471             appropriate specification definition.
472              
473             =item * check_lazylist($spec,$data)
474              
475             If it's a string, make it into a list and check the list
476              
477             =item * check_list($spec,$data)
478              
479             Checks whether a list (or array) part of the data structure conforms to
480             the appropriate specification definition.
481              
482             =back
483              
484             =cut
485              
486             sub check_map {
487 710     710 1 2666 my ($self,$spec,$data) = @_;
488              
489 710 100       1238 if(ref($spec) ne 'HASH') {
490 1         3 $self->_error( "Unknown META.yml specification, cannot validate." );
491 1         2 return;
492             }
493              
494 709 100       1021 if(ref($data) ne 'HASH') {
495 8         28 $self->_error( "Expected a map structure from data string or file." );
496 8         17 return;
497             }
498              
499 701         1117 for my $key (keys %$spec) {
500 2272 100       4038 next unless($spec->{$key}->{mandatory});
501 671 100       1097 next if(defined $data->{$key});
502 18         17 push @{$self->{stack}}, $key;
  18         27  
503 18         47 $self->_error( "Missing mandatory field, '$key'" );
504 18         17 pop @{$self->{stack}};
  18         25  
505             }
506              
507 701         1338 for my $key (keys %$data) {
508 2286         1706 push @{$self->{stack}}, $key;
  2286         2665  
509 2286 100       3386 if($spec->{$key}) {
    50          
510 1232 100       1962 if($spec->{$key}{value}) {
    100          
    100          
    50          
511 712         997 $spec->{$key}{value}->($self,$key,$data->{$key});
512             } elsif($spec->{$key}{'map'}) {
513 346         524 $self->check_map($spec->{$key}{'map'},$data->{$key});
514             } elsif($spec->{$key}{'list'}) {
515 128         235 $self->check_list($spec->{$key}{'list'},$data->{$key});
516             } elsif($spec->{$key}{'lazylist'}) {
517 46         82 $self->check_lazylist($spec->{$key}{'lazylist'},$data->{$key});
518             } else {
519 0         0 $self->_error( "$spec_error for '$key'" );
520             }
521              
522             } elsif ($spec->{':key'}) {
523 1054         1490 $spec->{':key'}{name}->($self,$key,$key);
524 1054 100       1558 if($spec->{':key'}{value}) {
    50          
    0          
    0          
    0          
525 770         1064 $spec->{':key'}{value}->($self,$key,$data->{$key});
526             } elsif($spec->{':key'}{'map'}) {
527 284         449 $self->check_map($spec->{':key'}{'map'},$data->{$key});
528             } elsif($spec->{':key'}{'list'}) {
529 0         0 $self->check_list($spec->{':key'}{'list'},$data->{$key});
530             } elsif($spec->{':key'}{'lazylist'}) {
531 0         0 $self->check_list($spec->{':key'}{'lazylist'},$data->{$key});
532             } elsif(!$spec->{':key'}{name}) {
533 0         0 $self->_error( "$spec_error for ':key'" );
534             }
535              
536             } else {
537 0         0 $self->_error( "Unknown key, '$key', found in map structure" );
538             }
539 2280         1744 pop @{$self->{stack}};
  2280         2987  
540             }
541             }
542              
543             sub check_lazylist {
544 46     46 1 45 my ($self,$spec,$data) = @_;
545              
546 46 50 33     174 if ( defined $data && ! ref($data) ) {
547 0         0 $data = [ $data ];
548             }
549              
550 46         66 $self->check_list($spec,$data);
551             }
552              
553             sub check_list {
554 186     186 1 4284 my ($self,$spec,$data) = @_;
555              
556 186 100       336 if(ref($data) ne 'ARRAY') {
557 10         30 $self->_error( "Expected a list structure" );
558 10         17 return;
559             }
560              
561 176 50       281 if(defined $spec->{mandatory}) {
562 0 0       0 if(!defined $data->[0]) {
563 0         0 $self->_error( "Missing entries from mandatory list" );
564             }
565             }
566              
567 176         206 for my $value (@$data) {
568 281         212 push @{$self->{stack}}, $value;
  281         341  
569 281 50       379 if(defined $spec->{value}) {
    0          
    0          
    0          
    0          
570 281         450 $spec->{value}->($self,'list',$value);
571             } elsif(defined $spec->{'map'}) {
572 0         0 $self->check_map($spec->{'map'},$value);
573             } elsif(defined $spec->{'list'}) {
574 0         0 $self->check_list($spec->{'list'},$value);
575             } elsif(defined $spec->{'lazylist'}) {
576 0         0 $self->check_lazylist($spec->{'lazylist'},$value);
577              
578             } elsif ($spec->{':key'}) {
579 0         0 $self->check_map($spec,$value);
580              
581             } else {
582 0         0 $self->_error( "$spec_error associated with '$self->{stack}[-2]'" );
583             }
584 269         186 pop @{$self->{stack}};
  269         480  
585             }
586             }
587              
588             =head2 Validator Methods
589              
590             =over
591              
592             =item * url($self,$key,$value)
593              
594             Validates that a given value is in an acceptable URL format
595              
596             =item * urlspec($self,$key,$value)
597              
598             Validates that the URL to a META.yml specification is a known one.
599              
600             =item * string_or_undef($self,$key,$value)
601              
602             Validates that the value is either a string or an undef value. Bit of a
603             catchall function for parts of the data structure that are completely user
604             defined.
605              
606             =item * string($self,$key,$value)
607              
608             Validates that a string exists for the given key.
609              
610             =item * file($self,$key,$value)
611              
612             Validate that a file is passed for the given key. This may be made more
613             thorough in the future. For now it acts like \&string.
614              
615             =item * exversion($self,$key,$value)
616              
617             Validates a list of versions, e.g. '<= 5, >=2, ==3, !=4, >1, <6, 0'.
618              
619             =item * version($self,$key,$value)
620              
621             Validates a single version string. Versions of the type '5.8.8' and '0.00_00'
622             are both valid. A leading 'v' like 'v1.2.3' is also valid.
623              
624             =item * boolean($self,$key,$value)
625              
626             Validates for a boolean value. Currently these values are '1', '0', 'true',
627             'false', however the latter 2 may be removed.
628              
629             =item * license($self,$key,$value)
630              
631             Validates that a value is given for the license. Returns 1 if an known license
632             type, or 2 if a value is given but the license type is not a recommended one.
633              
634             =item * resource($self,$key,$value)
635              
636             Validates that the given key is in CamelCase, to indicate a user defined
637             keyword.
638              
639             =item * keyword($self,$key,$value)
640              
641             Validates that key is in an acceptable format for the META.yml specification,
642             i.e. any in the character class [-_a-z].
643              
644             For user defined keys, although not explicitly stated in the specifications
645             (v1.0 - v1.4), the convention is to precede the key with a pattern matching
646             qr{\Ax_}i. Following this any character from the character class [-_a-zA-Z]
647             can be used. This clarification has been added to v2.0 of the specification.
648              
649             =item * identifier($self,$key,$value)
650              
651             Validates that key is in an acceptable format for the META.yml specification,
652             for an identifier, i.e. any that matches the regular expression
653             qr/[a-z][a-z_]/i.
654              
655             =item * module($self,$key,$value)
656              
657             Validates that a given key is in an acceptable module name format, e.g.
658             'Test::CPAN::Meta::JSON::Version'.
659              
660             =item * release_status($self,$key,$value)
661              
662             Validates that the value for 'release_status' is set appropriately for one of
663             'stable', 'testing' or 'unstable'.
664              
665             =item * custom_1($self,$key,$value)
666              
667             Validates custom keys based on camelcase only.
668              
669             =item * custom_2($self,$key,$value)
670              
671             Validates custom keys based on user defined (i.e. /^[xX]_/) only.
672              
673             =item * phase($self,$key,$value)
674              
675             Validates for a legal phase of a pre-requisite map.
676              
677             =item * relation($self,$key,$value)
678              
679             Validates for a legal relation, within a phase, of a pre-requisite map.
680              
681             =item * anything($self,$key,$value)
682              
683             Usually reserved for user defined structures, allowing them to be considered
684             valid without a need for a specification definition for the structure.
685              
686             =back
687              
688             =cut
689              
690             sub _uri_split {
691 102     102   587 return $_[0] =~ m,(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?,;
692             }
693              
694             sub url {
695 114     114 1 180 my ($self,$key,$value) = @_;
696 114 100       164 if($value) {
697 102         150 my ($scheme, $auth, $path, $query, $frag) = _uri_split($value);
698              
699 102 100       196 unless ( $scheme ) {
700 12         51 $self->_error( "'$value' for '$key' does not have a URL scheme" );
701 12         40 return 0;
702             }
703 90 100       144 unless ( $auth ) {
704 14         439 $self->_error( "'$value' for '$key' does not have a URL authority" );
705 14         54 return 0;
706             }
707 76         140 return 1;
708             } else {
709 12         17 $value = '';
710             }
711 12         44 $self->_error( "'$value' for '$key' is not a valid URL." );
712 12         33 return 0;
713             }
714              
715             sub urlspec {
716 62     62 1 153 my ($self,$key,$value) = @_;
717 62 100       115 if(defined $value) {
718 57 100 100     277 return 1 if($value && $known_specs{$self->{spec}} eq $value);
719 20 100 100     91 if($value && $known_urls{$value}) {
720 10         21 $self->_error( 'META.yml specification URL does not match version' );
721 10         26 return 0;
722             }
723             }
724 15         31 $self->_error( 'Unknown META.yml specification' );
725 15         47 return 0;
726             }
727              
728             sub string {
729 531     531 1 563 my ($self,$key,$value) = @_;
730 531 100       747 if(defined $value) {
731 525 100 100     1298 return 1 if($value || $value =~ /^0$/);
732             }
733 12         52 $self->_error( "value is an undefined string" );
734 12         38 return 0;
735             }
736              
737             sub string_or_undef {
738 24     24 1 48 my ($self,$key,$value) = @_;
739 24 100       72 return 1 unless(defined $value);
740 18 100 100     133 return 1 if($value || $value =~ /^0$/);
741 6         26 $self->_error( "No string defined for '$key'" );
742 6         19 return 0;
743             }
744              
745             sub file {
746 88     88 1 103 my ($self,$key,$value) = @_;
747 88 100       209 return 1 if(defined $value);
748 6         33 $self->_error( "No file defined for '$key'" );
749 6         20 return 0;
750             }
751              
752             sub exversion {
753 735     735 1 775 my ($self,$key,$value) = @_;
754 735 100 100     2463 if(defined $value && ($value || $value =~ /0/)) {
      66        
755 717         538 my $pass = 1;
756 717 100       1108 for(split(",",$value)) { $self->version($key,$_) or ($pass = 0); }
  747         938  
757 717         956 return $pass;
758             }
759 18 100       39 $value = '' unless(defined $value);
760 18         64 $self->_error( "'$value' for '$key' is not a valid version." );
761 18         60 return 0;
762             }
763              
764             sub version {
765 1001     1001 1 1126 my ($self,$key,$value) = @_;
766 1001 100       1196 if(defined $value) {
767 989 100 100     2245 return 0 unless($value || $value =~ /0/);
768 983 100       4557 return 1 if($value =~ /^\s*((<|<=|>=|>|!=|==)\s*)?v?\d+((\.\d+((_|\.)\d+)?)?)/);
769             } else {
770 12         19 $value = '';
771             }
772 26         107 $self->_error( "'$value' for '$key' is not a valid version." );
773 26         79 return 0;
774             }
775              
776             sub boolean {
777 70     70 1 116 my ($self,$key,$value) = @_;
778 70 100       125 if(defined $value) {
779 58 100       303 return 1 if($value =~ /^(0|1|true|false)$/);
780             } else {
781 12         18 $value = '';
782             }
783 24         84 $self->_error( "'$value' for '$key' is not a boolean value." );
784 24         77 return 0;
785             }
786              
787             my %v1_licenses = (
788             'perl' => 'http://dev.perl.org/licenses/',
789             'gpl' => 'http://www.opensource.org/licenses/gpl-license.php',
790             'apache' => 'http://apache.org/licenses/LICENSE-2.0',
791             'artistic' => 'http://opensource.org/licenses/artistic-license.php',
792             'artistic2' => 'http://opensource.org/licenses/artistic-license-2.0.php',
793             'artistic-2.0' => 'http://opensource.org/licenses/artistic-license-2.0.php',
794             'lgpl' => 'http://www.opensource.org/licenses/lgpl-license.phpt',
795             'bsd' => 'http://www.opensource.org/licenses/bsd-license.php',
796             'gpl' => 'http://www.opensource.org/licenses/gpl-license.php',
797             'mit' => 'http://opensource.org/licenses/mit-license.php',
798             'mozilla' => 'http://opensource.org/licenses/mozilla1.1.php',
799             'open_source' => undef,
800             'unrestricted' => undef,
801             'restrictive' => undef,
802             'unknown' => undef,
803             );
804              
805             my %v2_licenses = map { $_ => 1 } qw(
806             agpl_3
807             apache_1_1
808             apache_2_0
809             artistic_1
810             artistic_2
811             bsd
812             freebsd
813             gfdl_1_2
814             gfdl_1_3
815             gpl_1
816             gpl_2
817             gpl_3
818             lgpl_2_1
819             lgpl_3_0
820             mit
821             mozilla_1_0
822             mozilla_1_1
823             openssl
824             perl_5
825             qpl_1_0
826             ssleay
827             sun
828             zlib
829             open_source
830             restricted
831             unrestricted
832             unknown
833             );
834              
835             sub license {
836 186     186 1 35845 my ($self,$key,$value) = @_;
837 186 100       513 my $licenses = $self->{spec} < 2 ? \%v1_licenses : \%v2_licenses;
838 186 100       268 if(defined $value) {
839 180 100 100     1081 return 1 if($value && exists $licenses->{$value});
840              
841             # v1 specs caused problems for some with this field,
842             # so this test is relaxed for v1 tests only.
843 13 100 100     77 return 2 if($value && $self->{spec} < 2);
844             } else {
845 6         11 $value = '';
846             }
847 14         58 $self->_error( "License '$value' is invalid" );
848 14         51 return 0;
849             }
850              
851             sub resource {
852 43     43 1 77 my ($self,$key) = @_;
853 43 100       83 if(defined $key) {
854             # a valid user defined key should be alphabetic
855             # and contain at least one capital case letter.
856 37 100 100     387 return 1 if($key && $key =~ /^[a-z]+$/i && $key =~ /[A-Z]/);
      100        
857             } else {
858 6         13 $key = '';
859             }
860 19         70 $self->_error( "Resource '$key' must be in CamelCase." );
861 19         61 return 0;
862             }
863              
864             sub keyword {
865 125     125 1 18020 my ($self,$key) = @_;
866 125 100       209 if(defined $key) {
867 119 100 100     704 return 1 if($key && $key =~ /^([a-z][-_a-z]*)$/); # spec defined
868 36 100 100     178 return 1 if($key && $key =~ /^x_([a-z][-_a-z]*)$/i); # user defined
869             } else {
870 6         12 $key = '';
871             }
872 36         128 $self->_error( "Key '$key' is not a legal keyword." );
873 36         126 return 0;
874             }
875              
876             sub identifier {
877 88     88 1 18043 my ($self,$key) = @_;
878 88 100       180 if(defined $key) {
879 82 100 100     635 return 1 if($key && $key =~ /^([a-z][_a-z]+)$/i); # spec 2.0 defined
880             } else {
881 6         13 $key = '';
882             }
883 30         101 $self->_error( "Key '$key' is not a legal identifier." );
884 30         98 return 0;
885             }
886              
887             sub module {
888 803     803 1 741 my ($self,$key) = @_;
889 803 100       921 if(defined $key) {
890 797 100 100     4323 return 1 if($key && $key =~ /^[A-Za-z0-9_]+(::[A-Za-z0-9_]+)*$/);
891             } else {
892 6         12 $key = '';
893             }
894 24         79 $self->_error( "Key '$key' is not a legal module name." );
895 24         61 return 0;
896             }
897              
898             sub release_status {
899 36     36 1 4464 my ($self,$key,$value) = @_;
900 36 100       106 if(defined $value) {
901 35   100     86 my $version = $self->{data}{version} || '';
902 35 100       71 if ( $version =~ /_/ ) {
903 3 100       18 return 1 if ( $value =~ /\A(?:testing|unstable)\z/ );
904 1         6 $self->_error( "'$value' for '$key' is invalid for version '$version'" );
905             } else {
906 32 100       149 return 1 if ( $value =~ /\A(?:stable|testing|unstable)\z/ );
907 4         14 $self->_error( "'$value' for '$key' is invalid" );
908             }
909             } else {
910 1         4 $self->_error( "'$key' is not defined" );
911             }
912 6         20 return 0;
913             }
914              
915             sub custom_1 {
916 11     11 1 4059 my ($self,$key) = @_;
917 11 100       21 if(defined $key) {
918             # a valid user defined key should be alphabetic
919             # and contain at least one capital case letter.
920 10 100 100     79 return 1 if($key && $key =~ /^[a-z]+$/i && $key =~ /[A-Z]/);
      100        
921             } else {
922 1         13 $key = '';
923             }
924 9         31 $self->_error( "Custom resource '$key' must be in CamelCase." );
925 9         28 return 0;
926             }
927              
928             sub custom_2 {
929 20     20 1 3234 my ($self,$key) = @_;
930 20 100       33 if(defined $key) {
931             # a valid user defined key should be alphabetic
932             # and begin with x_ or X_
933 19 100 100     106 return 1 if($key && $key =~ /^x_([a-z][-_a-z]*)$/i); # user defined
934             } else {
935 1         1 $key = '';
936             }
937 18         48 $self->_error( "Custom resource '$key' must begin with 'x_' or 'X_'." );
938 18         36 return 0;
939             }
940              
941             my @valid_phases = qw/ configure build test runtime develop /;
942             sub phase {
943 94     94 1 3286 my ($self,$key) = @_;
944 94 100       190 if(defined $key) {
945 93 100 100     214 return 1 if( length $key && grep { $key eq $_ } @valid_phases );
  460         694  
946             } else {
947 1         2 $key = '';
948             }
949 9         32 $self->_error( "Key '$key' is not a legal phase." );
950 9         24 return 0;
951             }
952              
953             my @valid_relations = qw/ requires recommends suggests conflicts /;
954             sub relation {
955 115     115 1 2333 my ($self,$key) = @_;
956 115 100       153 if(defined $key) {
957 114 100 100     222 return 1 if( length $key && grep { $key eq $_ } @valid_relations );
  452         709  
958             } else {
959 1         3 $key = '';
960             }
961 9         30 $self->_error( "Key '$key' is not a legal prereq relationship." );
962 9         23 return 0;
963             }
964              
965 69     69 1 64 sub anything { return 1 }
966              
967             sub _error {
968 366     366   800 my $self = shift;
969 366         751 my $mess = shift;
970              
971 366 100       1704 $mess .= ' ('.join(' -> ',@{$self->{stack}}).')' if($self->{stack});
  69         175  
972 366         1026 $mess .= " [Validation: $self->{spec}]";
973              
974 366         297 push @{$self->{errors}}, $mess;
  366         739  
975             }
976              
977             q( Currently Listening To: Rainbow - "I Surrender" from 'Outrage - Live in London 1981');
978              
979             __END__