File Coverage

blib/lib/Test/CPAN/Meta/YAML/Version.pm
Criterion Covered Total %
statement 166 174 95.4
branch 99 114 86.8
condition 45 48 93.7
subroutine 25 25 100.0
pod 20 20 100.0
total 355 381 93.1


line stmt bran cond sub pod time code
1             package Test::CPAN::Meta::YAML::Version;
2              
3 10     10   35264 use warnings;
  10         16  
  10         331  
4 10     10   37 use strict;
  10         12  
  10         291  
5              
6 10     10   35 use vars qw($VERSION);
  10         9  
  10         30522  
7             $VERSION = '0.23';
8              
9             #----------------------------------------------------------------------------
10              
11             =head1 NAME
12              
13             Test::CPAN::Meta::YAML::Version - Validate CPAN META data against the specification
14              
15             =head1 SYNOPSIS
16              
17             use Test::CPAN::Meta::YAML::Version;
18              
19             =head1 DESCRIPTION
20              
21             This module was written to ensure that a META.yml 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.yml data against the CPAN Meta Specification.
34              
35             =cut
36              
37             #----------------------------------------------------------------------------
38              
39             #############################################################################
40             #Specification Definitions #
41             #############################################################################
42              
43             my %known_specs = (
44             '1.4' => 'http://module-build.sourceforge.net/META-spec-v1.4.html',
45             '1.3' => 'http://module-build.sourceforge.net/META-spec-v1.3.html',
46             '1.2' => 'http://module-build.sourceforge.net/META-spec-v1.2.html',
47             '1.1' => 'http://module-build.sourceforge.net/META-spec-v1.1.html',
48             '1.0' => 'http://module-build.sourceforge.net/META-spec-v1.0.html'
49             );
50             my %known_urls = map {$known_specs{$_} => $_} keys %known_specs;
51              
52             my $module_map1 = { 'map' => { ':key' => { name => \&module, value => \&exversion } } };
53             my $module_map2 = { 'map' => { ':key' => { name => \&module, value => \&version } } };
54             my $no_index_1_3 = {
55             'map' => { file => { list => { value => \&string } },
56             directory => { list => { value => \&string } },
57             'package' => { list => { value => \&string } },
58             namespace => { list => { value => \&string } },
59             }
60             };
61             my $no_index_1_2 = {
62             'map' => { file => { list => { value => \&string } },
63             dir => { list => { value => \&string } },
64             'package' => { list => { value => \&string } },
65             namespace => { list => { value => \&string } },
66             }
67             };
68             my $no_index_1_1 = {
69             'map' => { ':key' => { name => \&keyword, list => { value => \&string } },
70             }
71             };
72              
73             my %definitions = (
74             '1.4' => {
75             # 'header' => { mandatory => 1, value => \&header },
76             'meta-spec' => { mandatory => 1, 'map' => { version => { mandatory => 1, value => \&version},
77             url => { mandatory => 1, value => \&urlspec } } },
78              
79             'name' => { mandatory => 1, value => \&string },
80             'version' => { mandatory => 1, value => \&version },
81             'abstract' => { mandatory => 1, value => \&string },
82             'author' => { mandatory => 1, list => { value => \&string } },
83             'license' => { mandatory => 1, value => \&license },
84             'generated_by' => { mandatory => 1, value => \&string },
85              
86             'distribution_type' => { value => \&string },
87             'dynamic_config' => { value => \&boolean },
88              
89             'requires' => $module_map1,
90             'recommends' => $module_map1,
91             'build_requires' => $module_map1,
92             'configure_requires' => $module_map1,
93             'conflicts' => $module_map2,
94              
95             'optional_features' => {
96             'map' => {
97             ':key' => { name => \&identifier,
98             'map' => { description => { value => \&string },
99             requires_packages => { value => \&string },
100             requires_os => { value => \&string },
101             excludes_os => { value => \&string },
102             requires => $module_map1,
103             recommends => $module_map1,
104             build_requires => $module_map1,
105             conflicts => $module_map2,
106             }
107             }
108             }
109             },
110              
111             'provides' => {
112             'map' => { ':key' => { name => \&module,
113             'map' => { file => { mandatory => 1, value => \&file },
114             version => { value => \&version } } } }
115             },
116              
117             'no_index' => $no_index_1_3,
118             'private' => $no_index_1_3,
119              
120             'keywords' => { list => { value => \&string } },
121              
122             'resources' => {
123             'map' => { license => { value => \&url },
124             homepage => { value => \&url },
125             bugtracker => { value => \&url },
126             repository => { value => \&url },
127             ':key' => { value => \&string, name => \&resource },
128             }
129             },
130              
131             # additional user defined key/value pairs
132             # note we can only validate the key name, as the structure is user defined
133             ':key' => { name => \&keyword, value => \&anything },
134             },
135              
136             '1.3' => {
137             # 'header' => { mandatory => 1, value => \&header },
138             'meta-spec' => { mandatory => 1, 'map' => { version => { mandatory => 1, value => \&version},
139             url => { mandatory => 1, value => \&urlspec } } },
140              
141             'name' => { mandatory => 1, value => \&string },
142             'version' => { mandatory => 1, value => \&version },
143             'abstract' => { mandatory => 1, value => \&string },
144             'author' => { mandatory => 1, list => { value => \&string } },
145             'license' => { mandatory => 1, value => \&license },
146             'generated_by' => { mandatory => 1, value => \&string },
147              
148             'distribution_type' => { value => \&string },
149             'dynamic_config' => { value => \&boolean },
150              
151             'requires' => $module_map1,
152             'recommends' => $module_map1,
153             'build_requires' => $module_map1,
154             'conflicts' => $module_map2,
155              
156             'optional_features' => {
157             'map' => {
158             ':key' => { name => \&identifier,
159             'map' => { description => { value => \&string },
160             requires_packages => { value => \&string },
161             requires_os => { value => \&string },
162             excludes_os => { value => \&string },
163             requires => $module_map1,
164             recommends => $module_map1,
165             build_requires => $module_map1,
166             conflicts => $module_map2,
167             }
168             }
169             }
170             },
171              
172             'provides' => {
173             'map' => { ':key' => { name => \&module,
174             'map' => { file => { mandatory => 1, value => \&file },
175             version => { value => \&version } } } }
176             },
177              
178             'no_index' => $no_index_1_3,
179             'private' => $no_index_1_3,
180              
181             'keywords' => { list => { value => \&string } },
182              
183             'resources' => {
184             'map' => { license => { value => \&url },
185             homepage => { value => \&url },
186             bugtracker => { value => \&url },
187             repository => { value => \&url },
188             ':key' => { value => \&string, name => \&resource },
189             }
190             },
191              
192             # additional user defined key/value pairs
193             # note we can only validate the key name, as the structure is user defined
194             ':key' => { name => \&keyword, value => \&anything },
195             },
196              
197             # v1.2 is misleading, it seems to assume that a number of fields where created
198             # within v1.1, when they were created within v1.2. This may have been an
199             # original mistake, and that a v1.1 was retro fitted into the timeline, when
200             # v1.2 was originally slated as v1.1. But I could be wrong ;)
201             '1.2' => {
202             # 'header' => { mandatory => 1, value => \&header },
203             'meta-spec' => { mandatory => 1, 'map' => { version => { mandatory => 1, value => \&version},
204             url => { mandatory => 1, value => \&urlspec } } },
205              
206             'name' => { mandatory => 1, value => \&string },
207             'version' => { mandatory => 1, value => \&version },
208             'license' => { mandatory => 1, value => \&license },
209             'generated_by' => { mandatory => 1, value => \&string },
210             'author' => { mandatory => 1, list => { value => \&string } },
211             'abstract' => { mandatory => 1, value => \&string },
212              
213             'distribution_type' => { value => \&string },
214             'dynamic_config' => { value => \&boolean },
215              
216             'keywords' => { list => { value => \&string } },
217              
218             'private' => $no_index_1_2,
219             '$no_index' => $no_index_1_2,
220              
221             'requires' => $module_map1,
222             'recommends' => $module_map1,
223             'build_requires' => $module_map1,
224             'conflicts' => $module_map2,
225              
226             'optional_features' => {
227             'map' => {
228             ':key' => { name => \&identifier,
229             'map' => { description => { value => \&string },
230             requires_packages => { value => \&string },
231             requires_os => { value => \&string },
232             excludes_os => { value => \&string },
233             requires => $module_map1,
234             recommends => $module_map1,
235             build_requires => $module_map1,
236             conflicts => $module_map2,
237             }
238             }
239             }
240             },
241              
242             'provides' => {
243             'map' => { ':key' => { name => \&module,
244             'map' => { file => { mandatory => 1, value => \&file },
245             version => { value => \&version } } } }
246             },
247              
248             'resources' => {
249             'map' => { license => { value => \&url },
250             homepage => { value => \&url },
251             bugtracker => { value => \&url },
252             repository => { value => \&url },
253             ':key' => { value => \&string, name => \&resource },
254             }
255             },
256              
257             # additional user defined key/value pairs
258             # note we can only validate the key name, as the structure is user defined
259             ':key' => { name => \&keyword, value => \&anything },
260             },
261              
262             # note that the 1.1 spec doesn't specify optional or mandatory fields, what
263             # appears below is assumed from later specifications.
264             '1.1' => {
265             # 'header' => { mandatory => 1, value => \&header },
266             'name' => { mandatory => 1, value => \&string },
267             'version' => { mandatory => 1, value => \&version },
268             'license' => { mandatory => 1, value => \&license },
269             'license_uri' => { mandatory => 0, value => \&url },
270             'generated_by' => { mandatory => 1, value => \&string },
271              
272             'distribution_type' => { value => \&string },
273             'dynamic_config' => { value => \&boolean },
274              
275             'private' => $no_index_1_1,
276              
277             'requires' => $module_map1,
278             'recommends' => $module_map1,
279             'build_requires' => $module_map1,
280             'conflicts' => $module_map2,
281              
282             # additional user defined key/value pairs
283             # note we can only validate the key name, as the structure is user defined
284             ':key' => { name => \&keyword, value => \&anything },
285             },
286              
287             # note that the 1.0 spec doesn't specify optional or mandatory fields, what
288             # appears below is assumed from later specifications.
289             '1.0' => {
290             # 'header' => { mandatory => 1, value => \&header },
291             'name' => { mandatory => 1, value => \&string },
292             'version' => { mandatory => 1, value => \&version },
293             'license' => { mandatory => 1, value => \&license },
294             'generated_by' => { mandatory => 1, value => \&string },
295              
296             'distribution_type' => { value => \&string },
297             'dynamic_config' => { value => \&boolean },
298              
299             'requires' => $module_map1,
300             'recommends' => $module_map1,
301             'build_requires' => $module_map1,
302             'conflicts' => $module_map2,
303              
304             # additional user defined key/value pairs
305             # note we can only validate the key name, as the structure is user defined
306             ':key' => { name => \&keyword, value => \&anything },
307             },
308             );
309              
310             #############################################################################
311             #Code #
312             #############################################################################
313              
314             =head1 CLASS CONSTRUCTOR
315              
316             =over
317              
318             =item * new( data => $data [, spec => $version] )
319              
320             The constructor must be passed a valid data structure.
321              
322             Optionally you may also provide a specification version. This version is then
323             use to ensure that the given data structure meets the respective
324             specification definition. If no version is provided the module will attempt to
325             deduce the appropriate specification version from the data structure itself.
326              
327             =back
328              
329             =cut
330              
331             sub new {
332 45     45 1 36080 my ($class,%hash) = @_;
333              
334             # create an attributes hash
335 45         126 my $atts = {
336             'spec' => $hash{spec},
337             'data' => $hash{data},
338             };
339              
340             # create the object
341 45         156 my $self = bless $atts, $class;
342             }
343              
344             =head1 METHODS
345              
346             =head2 Main Methods
347              
348             =over
349              
350             =item * parse()
351              
352             Using the given data structure provided with the constructor, attempts to
353             parse and validate according to the appropriate specification definition.
354              
355             Returns 1 if any errors found, otherwise returns 0.
356              
357             =item * errors()
358              
359             Returns a list of the errors found during parsing.
360              
361             =back
362              
363             =cut
364              
365             sub parse {
366 44     44 1 114 my $self = shift;
367 44         82 my $data = $self->{data};
368              
369 44 100       134 unless($self->{spec}) {
370 21 100 66     126 $self->{spec} = $data->{'meta-spec'} && $data->{'meta-spec'}{'version'} ? $data->{'meta-spec'}{'version'} : '1.0';
371             }
372              
373 44         106 $self->check_map($definitions{$self->{spec}},$data);
374 44 100       190 return defined $self->{errors} ? 1 : 0;
375             }
376              
377             sub errors {
378 41     41 1 128 my $self = shift;
379 41 100       109 return () unless($self->{errors});
380 23         18 return @{$self->{errors}};
  23         76  
381             }
382              
383             =head2 Check Methods
384              
385             =over
386              
387             =item * check_map($spec,$data)
388              
389             Checks whether a map (or hash) part of the data structure conforms to the
390             appropriate specification definition.
391              
392             =item * check_list($spec,$data)
393              
394             Checks whether a list (or array) part of the data structure conforms to
395             the appropriate specification definition.
396              
397             =back
398              
399             =cut
400              
401             sub check_map {
402 350     350 1 668 my ($self,$spec,$data) = @_;
403              
404 350 100       707 if(ref($spec) ne 'HASH') {
405 1         3 $self->_error( "Unknown META.yml specification, cannot validate." );
406 1         1 return;
407             }
408              
409 349 100       570 if(ref($data) ne 'HASH') {
410 3         9 $self->_error( "Expected a map structure from data string or file." );
411 3         6 return;
412             }
413              
414 346         782 for my $key (keys %$spec) {
415 1350 100       2625 next unless($spec->{$key}->{mandatory});
416 419 100       802 next if(defined $data->{$key});
417 4         4 push @{$self->{stack}}, $key;
  4         10  
418 4         12 $self->_error( "Missing mandatory field, '$key'" );
419 4         4 pop @{$self->{stack}};
  4         8  
420             }
421              
422 346         919 for my $key (keys %$data) {
423 1219         987 push @{$self->{stack}}, $key;
  1219         2170  
424 1219 100       2042 if($spec->{$key}) {
    50          
425 783 100       1370 if($spec->{$key}{value}) {
    100          
    50          
426 480         817 $spec->{$key}{value}->($self,$key,$data->{$key});
427             } elsif($spec->{$key}{'map'}) {
428 234         484 $self->check_map($spec->{$key}{'map'},$data->{$key});
429             } elsif($spec->{$key}{'list'}) {
430 69         147 $self->check_list($spec->{$key}{'list'},$data->{$key});
431             }
432              
433             } elsif ($spec->{':key'}) {
434 436         693 $spec->{':key'}{name}->($self,$key,$key);
435 436 100       833 if($spec->{':key'}{value}) {
    50          
    0          
436 366         627 $spec->{':key'}{value}->($self,$key,$data->{$key});
437             } elsif($spec->{':key'}{'map'}) {
438 70         167 $self->check_map($spec->{':key'}{'map'},$data->{$key});
439             } elsif($spec->{':key'}{'list'}) {
440 0         0 $self->check_list($spec->{':key'}{'list'},$data->{$key});
441             }
442              
443             } else {
444 0         0 $self->_error( "Unknown key, '$key', found in map structure" );
445             }
446 1218         1036 pop @{$self->{stack}};
  1218         2085  
447             }
448             }
449              
450             sub check_list {
451 71     71 1 662 my ($self,$spec,$data) = @_;
452              
453 71 100       151 if(ref($data) ne 'ARRAY') {
454 3         9 $self->_error( "Expected a list structure" );
455 3         6 return;
456             }
457              
458 68 50       116 if(defined $spec->{mandatory}) {
459 0 0       0 if(!defined $data->[0]) {
460 0         0 $self->_error( "Missing entries from mandatory list" );
461             }
462             }
463              
464 68         102 for my $value (@$data) {
465 99         84 push @{$self->{stack}}, $value;
  99         133  
466 99 50       165 if(defined $spec->{value}) {
    0          
    0          
    0          
467 99         169 $spec->{value}->($self,'list',$value);
468             } elsif(defined $spec->{'map'}) {
469 0         0 $self->check_map($spec->{'map'},$value);
470             } elsif(defined $spec->{'list'}) {
471 0         0 $self->check_list($spec->{'list'},$value);
472              
473             } elsif ($spec->{':key'}) {
474 0         0 $self->check_map($spec,$value);
475              
476             } else {
477 0         0 $self->_error( "Unknown value type, '$value', found in list structure" );
478             }
479 97         75 pop @{$self->{stack}};
  97         210  
480             }
481             }
482              
483             =head2 Validator Methods
484              
485             =over
486              
487             =item * header($self,$key,$value)
488              
489             Validates that the YAML header is valid.
490              
491             Note: No longer used as we now read the YAML data structure, not the file.
492              
493             =item * url($self,$key,$value)
494              
495             Validates that a given value is in an acceptable URL format
496              
497             =item * urlspec($self,$key,$value)
498              
499             Validates that the URL to a META.yml specification is a known one.
500              
501             =item * string_or_undef($self,$key,$value)
502              
503             Validates that the value is either a string or an undef value. Bit of a
504             catchall function for parts of the data structure that are completely user
505             defined.
506              
507             =item * string($self,$key,$value)
508              
509             Validates that a string exists for the given key.
510              
511             =item * file($self,$key,$value)
512              
513             Validate that a file is passed for the given key. This may be made more
514             thorough in the future. For now it acts like \&string.
515              
516             =item * exversion($self,$key,$value)
517              
518             Validates a list of versions, e.g. '<= 5, >=2, ==3, !=4, >1, <6, 0'.
519              
520             =item * version($self,$key,$value)
521              
522             Validates a single version string. Versions of the type '5.8.8' and '0.00_00'
523             are both valid. A leading 'v' like 'v1.2.3' is also valid.
524              
525             =item * boolean($self,$key,$value)
526              
527             Validates for a boolean value. Currently these values are '1', '0', 'true',
528             'false', however the latter 2 may be removed.
529              
530             =item * license($self,$key,$value)
531              
532             Validates that a value is given for the license. Returns 1 if an known license
533             type, or 2 if a value is given but the license type is not a recommended one.
534              
535             =item * resource($self,$key,$value)
536              
537             Validates that the given key is in CamelCase, to indicate a user defined
538             keyword.
539              
540             =item * keyword($self,$key,$value)
541              
542             Validates that key is in an acceptable format for the META.yml specification,
543             i.e. any in the character class [-_a-z].
544              
545             For user defined keys, although not explicitly stated in the specifications
546             (v1.0 - v1.4), the convention is to precede the key with a pattern matching
547             qr{\Ax_}i. Following this any character from the character class [-_a-zA-Z]
548             can be used. This clarification has been added to v2.0 of the specification.
549              
550             =item * identifier($self,$key,$value)
551              
552             Validates that key is in an acceptable format for the META.yml specification,
553             for an identifier, i.e. any that matches the regular expression
554             qr/[a-z][a-z_]/i.
555              
556             =item * module($self,$key,$value)
557              
558             Validates that a given key is in an acceptable module name format, e.g.
559             'Test::CPAN::Meta::YAML::Version'.
560              
561             =item * anything($self,$key,$value)
562              
563             Usually reserved for user defined structures, allowing them to be considered
564             valid without a need for a specification definition for the structure.
565              
566             =back
567              
568             =cut
569              
570             sub header {
571 4     4 1 11 my ($self,$key,$value) = @_;
572 4 100       9 if(defined $value) {
573 3 100 100     23 return 1 if($value && $value =~ /^--- #YAML:1.0/);
574             }
575 3         6 $self->_error( "file does not have a valid YAML header." );
576 3         10 return 0;
577             }
578              
579             sub _uri_split {
580 28     28   163 return $_[0] =~ m,(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?,;
581             }
582              
583             sub url {
584 30     30 1 41 my ($self,$key,$value) = @_;
585 30 100       46 if($value) {
586 28         46 my ($scheme, $auth, $path, $query, $frag) = _uri_split($value);
587              
588 28 100       60 unless ( $scheme ) {
589 2         9 $self->_error( "'$value' for '$key' does not have a URL scheme" );
590 2         7 return 0;
591             }
592 26 100       44 unless ( $auth ) {
593 4         18 $self->_error( "'$value' for '$key' does not have a URL authority" );
594 4         12 return 0;
595             }
596 22         38 return 1;
597             } else {
598 2         3 $value = '';
599             }
600 2         8 $self->_error( "'$value' for '$key' is not a valid URL." );
601 2         7 return 0;
602             }
603              
604             sub urlspec {
605 40     40 1 53 my ($self,$key,$value) = @_;
606 40 100       121 if(defined $value) {
607 39 100 100     201 return 1 if($value && $known_specs{$self->{spec}} eq $value);
608 8 100 100     38 if($value && $known_urls{$value}) {
609 6         15 $self->_error( 'META.yml specification URL does not match version' );
610 6         11 return 0;
611             }
612             }
613 3         400 $self->_error( 'Unknown META.yml specification' );
614 3         10 return 0;
615             }
616              
617             sub string {
618 271     271 1 305 my ($self,$key,$value) = @_;
619 271 100       479 if(defined $value) {
620 270 100 100     708 return 1 if($value || $value =~ /^0$/);
621             }
622 2         5 $self->_error( "value is an undefined string" );
623 2         4 return 0;
624             }
625              
626             sub string_or_undef {
627 4     4 1 6 my ($self,$key,$value) = @_;
628 4 100       11 return 1 unless(defined $value);
629 3 100 100     19 return 1 if($value || $value =~ /^0$/);
630 1         4 $self->_error( "No string defined for '$key'" );
631 1         3 return 0;
632             }
633              
634             sub file {
635 69     69 1 79 my ($self,$key,$value) = @_;
636 69 100       158 return 1 if(defined $value);
637 1         7 $self->_error( "No file defined for '$key'" );
638 1         5 return 0;
639             }
640              
641             sub exversion {
642 301     301 1 348 my ($self,$key,$value) = @_;
643 301 100 100     1138 if(defined $value && ($value || $value =~ /0/)) {
      66        
644 298         253 my $pass = 1;
645 298 100       630 for(split(",",$value)) { $self->version($key,$_) or ($pass = 0); }
  303         451  
646 298         461 return $pass;
647             }
648 3 100       7 $value = '' unless(defined $value);
649 3         10 $self->_error( "'$value' for '$key' is not a valid version." );
650 3         10 return 0;
651             }
652              
653             sub version {
654 459     459 1 534 my ($self,$key,$value) = @_;
655 459 100       588 if(defined $value) {
656 457 100 100     972 return 0 unless($value || $value =~ /0/);
657 456 100       2627 return 1 if($value =~ /^\s*((<|<=|>=|>|!=|==)\s*)?v?\d+((\.\d+((_|\.)\d+)?)?)/);
658             } else {
659 2         3 $value = '';
660             }
661 6         25 $self->_error( "'$value' for '$key' is not a valid version." );
662 6         14 return 0;
663             }
664              
665             sub boolean {
666 8     8 1 17 my ($self,$key,$value) = @_;
667 8 100       14 if(defined $value) {
668 6 100       31 return 1 if($value =~ /^(0|1|true|false)$/);
669             } else {
670 2         3 $value = '';
671             }
672 4         14 $self->_error( "'$value' for '$key' is not a boolean value." );
673 4         14 return 0;
674             }
675              
676             my %licenses = (
677             'perl' => 'http://dev.perl.org/licenses/',
678             'gpl' => 'http://www.opensource.org/licenses/gpl-license.php',
679             'apache' => 'http://apache.org/licenses/LICENSE-2.0',
680             'artistic' => 'http://opensource.org/licenses/artistic-license.php',
681             'artistic2' => 'http://opensource.org/licenses/artistic-license-2.0.php',
682             'artistic-2.0' => 'http://opensource.org/licenses/artistic-license-2.0.php',
683             'lgpl' => 'http://www.opensource.org/licenses/lgpl-license.phpt',
684             'bsd' => 'http://www.opensource.org/licenses/bsd-license.php',
685             'gpl' => 'http://www.opensource.org/licenses/gpl-license.php',
686             'mit' => 'http://opensource.org/licenses/mit-license.php',
687             'mozilla' => 'http://opensource.org/licenses/mozilla1.1.php',
688             'open_source' => undef,
689             'unrestricted' => undef,
690             'restrictive' => undef,
691             'unknown' => undef,
692             );
693              
694             sub license {
695 61     61 1 4319 my ($self,$key,$value) = @_;
696 61 100       96 if(defined $value) {
697 60 100 100     315 return 1 if($value && exists $licenses{$value});
698 4 100       12 return 2 if($value);
699             } else {
700 1         2 $value = '';
701             }
702 2         8 $self->_error( "License '$value' is unknown" );
703 2         7 return 0;
704             }
705              
706             sub resource {
707 7     7 1 11 my ($self,$key) = @_;
708 7 100       13 if(defined $key) {
709             # a valid user defined key should be alphabetic
710             # and contain at least one capital case letter.
711 6 100 66     57 return 1 if($key && $key =~ /^[a-z]+$/i && $key =~ /[A-Z]/);
      100        
712             } else {
713 1         1 $key = '';
714             }
715 3         8 $self->_error( "Resource '$key' must be in CamelCase." );
716 3         8 return 0;
717             }
718              
719             sub keyword {
720 80     80 1 2663 my ($self,$key) = @_;
721 80 100       114 if(defined $key) {
722 79 100 100     528 return 1 if($key && $key =~ /^([a-z][-_a-z]*)$/); # spec defined
723 6 100 100     29 return 1 if($key && $key =~ /^x_([a-z][-_a-z]*)$/i); # user defined
724             } else {
725 1         2 $key = '';
726             }
727 6         16 $self->_error( "Key '$key' is not a legal keyword." );
728 6         20 return 0;
729             }
730              
731             sub identifier {
732 13     13 1 2630 my ($self,$key) = @_;
733 13 100       27 if(defined $key) {
734 12 100 100     93 return 1 if($key && $key =~ /^([a-z][_a-z]+)$/i); # spec 2.0 defined
735             } else {
736 1         2 $key = '';
737             }
738 5         15 $self->_error( "Key '$key' is not a legal identifier." );
739 5         13 return 0;
740             }
741              
742             sub module {
743 370     370 1 383 my ($self,$key) = @_;
744 370 100       526 if(defined $key) {
745 369 100 100     2377 return 1 if($key && $key =~ /^[A-Za-z0-9_]+(::[A-Za-z0-9_]+)*$/);
746             } else {
747 1         2 $key = '';
748             }
749 9         89 $self->_error( "Key '$key' is not a legal module name." );
750 9         18 return 0;
751             }
752              
753 69     69 1 71 sub anything { return 1 }
754              
755             sub _error {
756 73     73   83 my $self = shift;
757 73         71 my $mess = shift;
758              
759 73 100       159 $mess .= ' ('.join(' -> ',@{$self->{stack}}).')' if($self->{stack});
  25         83  
760 73         132 $mess .= " [Validation: $self->{spec}]";
761              
762 73         65 push @{$self->{errors}}, $mess;
  73         179  
763             }
764              
765             q( This release is sponsored by Made In Love: Hand made gifts for your loved ones, friends or even a treat for your own home - http://madeinlove.co.uk );
766              
767             __END__