File Coverage

blib/lib/Test/CPAN/Meta/JSON/Version.pm
Criterion Covered Total %
statement 203 215 94.4
branch 125 146 85.6
condition 66 68 97.0
subroutine 30 30 100.0
pod 25 25 100.0
total 449 484 92.7


line stmt bran cond sub pod time code
1             package Test::CPAN::Meta::JSON::Version;
2              
3 15     15   208544 use warnings;
  15         35  
  15         574  
4 15     15   82 use strict;
  15         27  
  15         551  
5              
6 15     15   83 use vars qw($VERSION);
  15         27  
  15         90603  
7             $VERSION = '0.15';
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, lazylist => { 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 => { lazylist => { 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             #############################################################################
389             #Code #
390             #############################################################################
391              
392             =head1 CLASS CONSTRUCTOR
393              
394             =over
395              
396             =item * new( data => $data [, spec => $version] )
397              
398             The constructor must be passed a valid data structure.
399              
400             Optionally you may also provide a specification version. This version is then
401             use to ensure that the given data structure meets the respective
402             specification definition. If no version is provided the module will attempt to
403             deduce the appropriate specification version from the data structure itself.
404              
405             =back
406              
407             =cut
408              
409             sub new {
410 64     64 1 78351 my ($class,%hash) = @_;
411              
412             # create an attributes hash
413 64         245 my $atts = {
414             'spec' => $hash{spec},
415             'data' => $hash{data},
416             };
417              
418             # create the object
419 64         298 my $self = bless $atts, $class;
420             }
421              
422             =head1 METHODS
423              
424             =head2 Main Methods
425              
426             =over
427              
428             =item * parse()
429              
430             Using the given data structure provided with the constructor, attempts to
431             parse and validate according to the appropriate specification definition.
432              
433             Returns 1 if any errors found, otherwise returns 0.
434              
435             =item * errors()
436              
437             Returns a list of the errors found during parsing.
438              
439             =back
440              
441             =cut
442              
443             sub parse {
444 58     58 1 191 my $self = shift;
445 58         115 my $data = $self->{data};
446              
447 58 100       173 unless($self->{spec}) {
448 28 100 100     209 $self->{spec} = $data->{'meta-spec'} && $data->{'meta-spec'}{'version'} ? $data->{'meta-spec'}{'version'} : '2';
449             }
450              
451 58         210 $self->check_map($definitions{$self->{spec}},$data);
452 58 100       324 return defined $self->{errors} ? 1 : 0;
453             }
454              
455             sub errors {
456 80     80 1 304 my $self = shift;
457 80 100       261 return () unless($self->{errors});
458 55         64 return @{$self->{errors}};
  55         192  
459             }
460              
461             =head2 Check Methods
462              
463             =over
464              
465             =item * check_map($spec,$data)
466              
467             Checks whether a map (or hash) part of the data structure conforms to the
468             appropriate specification definition.
469              
470             =item * check_lazylist($spec,$data)
471              
472             If it's a string, make it into a list and check the list
473              
474             =item * check_list($spec,$data)
475              
476             Checks whether a list (or array) part of the data structure conforms to
477             the appropriate specification definition.
478              
479             =back
480              
481             =cut
482              
483             sub check_map {
484 550     550 1 10400 my ($self,$spec,$data) = @_;
485              
486 550 100       1366 if(ref($spec) ne 'HASH') {
487 1         4 $self->_error( "Unknown META.yml specification, cannot validate." );
488 1         2 return;
489             }
490              
491 549 100       1527 if(ref($data) ne 'HASH') {
492 8         32 $self->_error( "Expected a map structure from data string or file." );
493 8         19 return;
494             }
495              
496 541         3964 for my $key (keys %$spec) {
497 1892 100       5385 next unless($spec->{$key}->{mandatory});
498 571 100       1332 next if(defined $data->{$key});
499 14         20 push @{$self->{stack}}, $key;
  14         68  
500 14         188 $self->_error( "Missing mandatory field, '$key'" );
501 14         21 pop @{$self->{stack}};
  14         36  
502             }
503              
504 541         1973 for my $key (keys %$data) {
505 1800         2064 push @{$self->{stack}}, $key;
  1800         3520  
506 1800 100       5159 if($spec->{$key}) {
    50          
507 1046 100       13394 if($spec->{$key}{value}) {
    100          
    100          
    50          
508 616         1455 $spec->{$key}{value}->($self,$key,$data->{$key});
509             } elsif($spec->{$key}{'map'}) {
510 296         820 $self->check_map($spec->{$key}{'map'},$data->{$key});
511             } elsif($spec->{$key}{'list'}) {
512 82         245 $self->check_list($spec->{$key}{'list'},$data->{$key});
513             } elsif($spec->{$key}{'lazylist'}) {
514 52         162 $self->check_lazylist($spec->{$key}{'lazylist'},$data->{$key});
515             } else {
516 0         0 $self->_error( "$spec_error for '$key'" );
517             }
518              
519             } elsif ($spec->{':key'}) {
520 754         1702 $spec->{':key'}{name}->($self,$key,$key);
521 754 100       1873 if($spec->{':key'}{value}) {
    50          
    0          
    0          
    0          
522 570         1430 $spec->{':key'}{value}->($self,$key,$data->{$key});
523             } elsif($spec->{':key'}{'map'}) {
524 184         701 $self->check_map($spec->{':key'}{'map'},$data->{$key});
525             } elsif($spec->{':key'}{'list'}) {
526 0         0 $self->check_list($spec->{':key'}{'list'},$data->{$key});
527             } elsif($spec->{':key'}{'lazylist'}) {
528 0         0 $self->check_list($spec->{':key'}{'lazylist'},$data->{$key});
529             } elsif(!$spec->{':key'}{name}) {
530 0         0 $self->_error( "$spec_error for ':key'" );
531             }
532              
533             } else {
534 0         0 $self->_error( "Unknown key, '$key', found in map structure" );
535             }
536 1794         2234 pop @{$self->{stack}};
  1794         4554  
537             }
538             }
539              
540             sub check_lazylist {
541 52     52 1 74 my ($self,$spec,$data) = @_;
542              
543 52 100 66     242 if ( defined $data && ! ref($data) ) {
544 8         23 $data = [ $data ];
545             }
546              
547 52         115 $self->check_list($spec,$data);
548             }
549              
550             sub check_list {
551 146     146 1 15345 my ($self,$spec,$data) = @_;
552              
553 146 100       595 if(ref($data) ne 'ARRAY') {
554 8         29 $self->_error( "Expected a list structure" );
555 8         29 return;
556             }
557              
558 138 50       343 if(defined $spec->{mandatory}) {
559 0 0       0 if(!defined $data->[0]) {
560 0         0 $self->_error( "Missing entries from mandatory list" );
561             }
562             }
563              
564 138         234 for my $value (@$data) {
565 209         235 push @{$self->{stack}}, $value;
  209         404  
566 209 50       537 if(defined $spec->{value}) {
    0          
    0          
    0          
    0          
567 209         545 $spec->{value}->($self,'list',$value);
568             } elsif(defined $spec->{'map'}) {
569 0         0 $self->check_map($spec->{'map'},$value);
570             } elsif(defined $spec->{'list'}) {
571 0         0 $self->check_list($spec->{'list'},$value);
572             } elsif(defined $spec->{'lazylist'}) {
573 0         0 $self->check_lazylist($spec->{'lazylist'},$value);
574              
575             } elsif ($spec->{':key'}) {
576 0         0 $self->check_map($spec,$value);
577              
578             } else {
579 0         0 $self->_error( "$spec_error associated with '$self->{stack}[-2]'" );
580             }
581 197         234 pop @{$self->{stack}};
  197         628  
582             }
583             }
584              
585             =head2 Validator Methods
586              
587             =over
588              
589             =item * url($self,$key,$value)
590              
591             Validates that a given value is in an acceptable URL format
592              
593             =item * urlspec($self,$key,$value)
594              
595             Validates that the URL to a META.yml specification is a known one.
596              
597             =item * string_or_undef($self,$key,$value)
598              
599             Validates that the value is either a string or an undef value. Bit of a
600             catchall function for parts of the data structure that are completely user
601             defined.
602              
603             =item * string($self,$key,$value)
604              
605             Validates that a string exists for the given key.
606              
607             =item * file($self,$key,$value)
608              
609             Validate that a file is passed for the given key. This may be made more
610             thorough in the future. For now it acts like \&string.
611              
612             =item * exversion($self,$key,$value)
613              
614             Validates a list of versions, e.g. '<= 5, >=2, ==3, !=4, >1, <6, 0'.
615              
616             =item * version($self,$key,$value)
617              
618             Validates a single version string. Versions of the type '5.8.8' and '0.00_00'
619             are both valid. A leading 'v' like 'v1.2.3' is also valid.
620              
621             =item * boolean($self,$key,$value)
622              
623             Validates for a boolean value. Currently these values are '1', '0', 'true',
624             'false', however the latter 2 may be removed.
625              
626             =item * license($self,$key,$value)
627              
628             Validates that a value is given for the license. Returns 1 if an known license
629             type, or 2 if a value is given but the license type is not a recommended one.
630              
631             =item * resource($self,$key,$value)
632              
633             Validates that the given key is in CamelCase, to indicate a user defined
634             keyword.
635              
636             =item * keyword($self,$key,$value)
637              
638             Validates that key is in an acceptable format for the META.yml specification,
639             i.e. any in the character class [-_a-z].
640              
641             For user defined keys, although not explicitly stated in the specifications
642             (v1.0 - v1.4), the convention is to precede the key with a pattern matching
643             qr{\Ax_}i. Following this any character from the character class [-_a-zA-Z]
644             can be used. This clarification has been added to v2.0 of the specification.
645              
646             =item * identifier($self,$key,$value)
647              
648             Validates that key is in an acceptable format for the META.yml specification,
649             for an identifier, i.e. any that matches the regular expression
650             qr/[a-z][a-z_]/i.
651              
652             =item * module($self,$key,$value)
653              
654             Validates that a given key is in an acceptable module name format, e.g.
655             'Test::CPAN::Meta::JSON::Version'.
656              
657             =item * release_status($self,$key,$value)
658              
659             Validates that the value for 'release_status' is set appropriately for one of
660             'stable', 'testing' or 'unstable'.
661              
662             =item * custom_1($self,$key,$value)
663              
664             Validates custom keys based on camelcase only.
665              
666             =item * custom_2($self,$key,$value)
667              
668             Validates custom keys based on user defined (i.e. /^[xX]_/) only.
669              
670             =item * phase($self,$key,$value)
671              
672             Validates for a legal phase of a pre-requisite map.
673              
674             =item * relation($self,$key,$value)
675              
676             Validates for a legal relation, within a phase, of a pre-requisite map.
677              
678             =item * anything($self,$key,$value)
679              
680             Usually reserved for user defined structures, allowing them to be considered
681             valid without a need for a specification definition for the structure.
682              
683             =back
684              
685             =cut
686              
687             sub _uri_split {
688 82     82   686 return $_[0] =~ m,(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?,;
689             }
690              
691             sub url {
692 94     94 1 216 my ($self,$key,$value) = @_;
693 94 100       191 if($value) {
694 82         168 my ($scheme, $auth, $path, $query, $frag) = _uri_split($value);
695              
696 82 100       213 unless ( $scheme ) {
697 12         56 $self->_error( "'$value' for '$key' does not have a URL scheme" );
698 12         52 return 0;
699             }
700 70 100       141 unless ( $auth ) {
701 14         79 $self->_error( "'$value' for '$key' does not have a URL authority" );
702 14         63 return 0;
703             }
704 56         157 return 1;
705             } else {
706 12         23 $value = '';
707             }
708 12         53 $self->_error( "'$value' for '$key' is not a valid URL." );
709 12         52 return 0;
710             }
711              
712             sub urlspec {
713 62     62 1 117 my ($self,$key,$value) = @_;
714 62 100       239 if(defined $value) {
715 57 100 100     402 return 1 if($value && $known_specs{$self->{spec}} eq $value);
716 20 100 100     140 if($value && $known_urls{$value}) {
717 10         32 $self->_error( 'META.yml specification URL does not match version' );
718 10         31 return 0;
719             }
720             }
721 15         50 $self->_error( 'Unknown META.yml specification' );
722 15         60 return 0;
723             }
724              
725             sub string {
726 429     429 1 15441 my ($self,$key,$value) = @_;
727 429 100       1114 if(defined $value) {
728 423 100 100     1592 return 1 if($value || $value =~ /^0$/);
729             }
730 12         32 $self->_error( "value is an undefined string" );
731 12         43 return 0;
732             }
733              
734             sub string_or_undef {
735 24     24 1 57 my ($self,$key,$value) = @_;
736 24 100       78 return 1 unless(defined $value);
737 18 100 100     132 return 1 if($value || $value =~ /^0$/);
738 6         33 $self->_error( "No string defined for '$key'" );
739 6         26 return 0;
740             }
741              
742             sub file {
743 88     88 1 146 my ($self,$key,$value) = @_;
744 88 100       318 return 1 if(defined $value);
745 6         30 $self->_error( "No file defined for '$key'" );
746 6         26 return 0;
747             }
748              
749             sub exversion {
750 535     535 1 935 my ($self,$key,$value) = @_;
751 535 100 100     2662 if(defined $value && ($value || $value =~ /0/)) {
      66        
752 517         611 my $pass = 1;
753 517 100       1233 for(split(",",$value)) { $self->version($key,$_) or ($pass = 0); }
  547         1106  
754 517         1751 return $pass;
755             }
756 18 100       59 $value = '' unless(defined $value);
757 18         71 $self->_error( "'$value' for '$key' is not a valid version." );
758 18         74 return 0;
759             }
760              
761             sub version {
762 783     783 1 1478 my ($self,$key,$value) = @_;
763 783 100       1308 if(defined $value) {
764 771 100 100     2319 return 0 unless($value || $value =~ /0/);
765 765 100       5516 return 1 if($value =~ /^\s*((<|<=|>=|>|!=|==)\s*)?v?\d+((\.\d+((_|\.)\d+)?)?)/);
766             } else {
767 12         20 $value = '';
768             }
769 26         150 $self->_error( "'$value' for '$key' is not a valid version." );
770 26         105 return 0;
771             }
772              
773             sub boolean {
774 60     60 1 138 my ($self,$key,$value) = @_;
775 60 100       133 if(defined $value) {
776 48 100       344 return 1 if($value =~ /^(0|1|true|false)$/);
777             } else {
778 12         20 $value = '';
779             }
780 24         105 $self->_error( "'$value' for '$key' is not a boolean value." );
781 24         148 return 0;
782             }
783              
784             my %v1_licenses = (
785             'perl' => 'http://dev.perl.org/licenses/',
786             'gpl' => 'http://www.opensource.org/licenses/gpl-license.php',
787             'apache' => 'http://apache.org/licenses/LICENSE-2.0',
788             'artistic' => 'http://opensource.org/licenses/artistic-license.php',
789             'artistic2' => 'http://opensource.org/licenses/artistic-license-2.0.php',
790             'artistic-2.0' => 'http://opensource.org/licenses/artistic-license-2.0.php',
791             'lgpl' => 'http://www.opensource.org/licenses/lgpl-license.phpt',
792             'bsd' => 'http://www.opensource.org/licenses/bsd-license.php',
793             'gpl' => 'http://www.opensource.org/licenses/gpl-license.php',
794             'mit' => 'http://opensource.org/licenses/mit-license.php',
795             'mozilla' => 'http://opensource.org/licenses/mozilla1.1.php',
796             'open_source' => undef,
797             'unrestricted' => undef,
798             'restrictive' => undef,
799             'unknown' => undef,
800             );
801              
802             my %v2_licenses = map { $_ => 1 } qw(
803             agpl_3
804             apache_1_1
805             apache_2_0
806             artistic_1
807             artistic_2
808             bsd
809             freebsd
810             gfdl_1_2
811             gfdl_1_3
812             gpl_1
813             gpl_2
814             gpl_3
815             lgpl_2_1
816             lgpl_3_0
817             mit
818             mozilla_1_0
819             mozilla_1_1
820             openssl
821             perl_5
822             qpl_1_0
823             ssleay
824             sun
825             zlib
826             open_source
827             restricted
828             unrestricted
829             unknown
830             );
831              
832             sub license {
833 178     178 1 58482 my ($self,$key,$value) = @_;
834 178 100       648 my $licenses = $self->{spec} < 2 ? \%v1_licenses : \%v2_licenses;
835 178 100       355 if(defined $value) {
836 172 100 100     3062 return 1 if($value && exists $licenses->{$value});
837              
838             # v1 specs caused problems for some with this field,
839             # so this test is relaxed for v1 tests only.
840 15 100 100     222 return 2 if($value && $self->{spec} < 2);
841             } else {
842 6         16 $value = '';
843             }
844 16         78 $self->_error( "License '$value' is invalid" );
845 16         65 return 0;
846             }
847              
848             sub resource {
849 43     43 1 96 my ($self,$key) = @_;
850 43 100       102 if(defined $key) {
851             # a valid user defined key should be alphabetic
852             # and contain at least one capital case letter.
853 37 100 100     1596 return 1 if($key && $key =~ /^[a-z]+$/i && $key =~ /[A-Z]/);
      100        
854             } else {
855 6         17 $key = '';
856             }
857 19         80 $self->_error( "Resource '$key' must be in CamelCase." );
858 19         84 return 0;
859             }
860              
861             sub keyword {
862 125     125 1 30523 my ($self,$key) = @_;
863 125 100       266 if(defined $key) {
864 119 100 100     891 return 1 if($key && $key =~ /^([a-z][-_a-z]*)$/); # spec defined
865 36 100 100     229 return 1 if($key && $key =~ /^x_([a-z][-_a-z]*)$/i); # user defined
866             } else {
867 6         15 $key = '';
868             }
869 36         132 $self->_error( "Key '$key' is not a legal keyword." );
870 36         152 return 0;
871             }
872              
873             sub identifier {
874 78     78 1 25681 my ($self,$key) = @_;
875 78 100       175 if(defined $key) {
876 72 100 100     634 return 1 if($key && $key =~ /^([a-z][_a-z]+)$/i); # spec 2.0 defined
877             } else {
878 6         16 $key = '';
879             }
880 30         116 $self->_error( "Key '$key' is not a legal identifier." );
881 30         133 return 0;
882             }
883              
884             sub module {
885 603     603 1 1424 my ($self,$key) = @_;
886 603 100       1019 if(defined $key) {
887 597 100 100     4773 return 1 if($key && $key =~ /^[A-Za-z0-9_]+(::[A-Za-z0-9_]+)*$/);
888             } else {
889 6         16 $key = '';
890             }
891 24         90 $self->_error( "Key '$key' is not a legal module name." );
892 24         76 return 0;
893             }
894              
895             sub release_status {
896 26     26 1 5005 my ($self,$key,$value) = @_;
897 26 100       58 if(defined $value) {
898 25   100     128 my $version = $self->{data}{version} || '';
899 25 100       63 if ( $version =~ /_/ ) {
900 3 100       16 return 1 if ( $value =~ /\A(?:testing|unstable)\z/ );
901 1         7 $self->_error( "'$value' for '$key' is invalid for version '$version'" );
902             } else {
903 22 100       143 return 1 if ( $value =~ /\A(?:stable|testing|unstable)\z/ );
904 4         12 $self->_error( "'$value' for '$key' is invalid" );
905             }
906             } else {
907 1         4 $self->_error( "'$key' is not defined" );
908             }
909 6         25 return 0;
910             }
911              
912             sub custom_1 {
913 11     11 1 4565 my ($self,$key) = @_;
914 11 100       21 if(defined $key) {
915             # a valid user defined key should be alphabetic
916             # and contain at least one capital case letter.
917 10 100 100     80 return 1 if($key && $key =~ /^[a-z]+$/i && $key =~ /[A-Z]/);
      100        
918             } else {
919 1         3 $key = '';
920             }
921 9         27 $self->_error( "Custom resource '$key' must be in CamelCase." );
922 9         30 return 0;
923             }
924              
925             sub custom_2 {
926 20     20 1 3835 my ($self,$key) = @_;
927 20 100       39 if(defined $key) {
928             # a valid user defined key should be alphabetic
929             # and begin with x_ or X_
930 19 100 100     110 return 1 if($key && $key =~ /^x_([a-z][-_a-z]*)$/i); # user defined
931             } else {
932 1         3 $key = '';
933             }
934 18         60 $self->_error( "Custom resource '$key' must begin with 'x_' or 'X_'." );
935 18         49 return 0;
936             }
937              
938             my @valid_phases = qw/ configure build test runtime develop /;
939             sub phase {
940 54     54 1 3610 my ($self,$key) = @_;
941 54 100       114 if(defined $key) {
942 53 100 100     330 return 1 if( length $key && grep { $key eq $_ } @valid_phases );
  260         610  
943             } else {
944 1         2 $key = '';
945             }
946 7         26 $self->_error( "Key '$key' is not a legal phase." );
947 7         22 return 0;
948             }
949              
950             my @valid_relations = qw/ requires recommends suggests conflicts /;
951             sub relation {
952 65     65 1 3530 my ($self,$key) = @_;
953 65 100       131 if(defined $key) {
954 64 100 100     223 return 1 if( length $key && grep { $key eq $_ } @valid_relations );
  252         981  
955             } else {
956 1         2 $key = '';
957             }
958 7         29 $self->_error( "Key '$key' is not a legal prereq relationship." );
959 7         25 return 0;
960             }
961              
962 69     69 1 98 sub anything { return 1 }
963              
964             sub _error {
965 358     358   510 my $self = shift;
966 358         454 my $mess = shift;
967              
968 358 100       2381 $mess .= ' ('.join(' -> ',@{$self->{stack}}).')' if($self->{stack});
  61         210  
969 358         921 $mess .= " [Validation: $self->{spec}]";
970              
971 358         1424 push @{$self->{errors}}, $mess;
  358         1494  
972             }
973              
974             q( Currently Listening To: Rainbow - "I Surrender" from 'Outrage - Live in London 1981');
975              
976             __END__