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   40226 use warnings;
  10         16  
  10         368  
4 10     10   48 use strict;
  10         15  
  10         329  
5              
6 10     10   46 use vars qw($VERSION);
  10         14  
  10         34736  
7             $VERSION = '0.24';
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 32450 my ($class,%hash) = @_;
333              
334             # create an attributes hash
335 45         140 my $atts = {
336             'spec' => $hash{spec},
337             'data' => $hash{data},
338             };
339              
340             # create the object
341 45         155 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 103 my $self = shift;
367 44         83 my $data = $self->{data};
368              
369 44 100       100 unless($self->{spec}) {
370 21 100 66     127 $self->{spec} = $data->{'meta-spec'} && $data->{'meta-spec'}{'version'} ? $data->{'meta-spec'}{'version'} : '1.0';
371             }
372              
373 44         112 $self->check_map($definitions{$self->{spec}},$data);
374 44 100       201 return defined $self->{errors} ? 1 : 0;
375             }
376              
377             sub errors {
378 41     41 1 166 my $self = shift;
379 41 100       106 return () unless($self->{errors});
380 23         24 return @{$self->{errors}};
  23         81  
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 775 my ($self,$spec,$data) = @_;
403              
404 350 100       635 if(ref($spec) ne 'HASH') {
405 1         4 $self->_error( "Unknown META.yml specification, cannot validate." );
406 1         2 return;
407             }
408              
409 349 100       504 if(ref($data) ne 'HASH') {
410 3         10 $self->_error( "Expected a map structure from data string or file." );
411 3         4 return;
412             }
413              
414 346         715 for my $key (keys %$spec) {
415 1350 100       2341 next unless($spec->{$key}->{mandatory});
416 419 100       736 next if(defined $data->{$key});
417 4         7 push @{$self->{stack}}, $key;
  4         10  
418 4         15 $self->_error( "Missing mandatory field, '$key'" );
419 4         5 pop @{$self->{stack}};
  4         8  
420             }
421              
422 346         753 for my $key (keys %$data) {
423 1219         922 push @{$self->{stack}}, $key;
  1219         1530  
424 1219 100       2275 if($spec->{$key}) {
    50          
425 783 100       1332 if($spec->{$key}{value}) {
    100          
    50          
426 480         782 $spec->{$key}{value}->($self,$key,$data->{$key});
427             } elsif($spec->{$key}{'map'}) {
428 234         432 $self->check_map($spec->{$key}{'map'},$data->{$key});
429             } elsif($spec->{$key}{'list'}) {
430 69         134 $self->check_list($spec->{$key}{'list'},$data->{$key});
431             }
432              
433             } elsif ($spec->{':key'}) {
434 436         647 $spec->{':key'}{name}->($self,$key,$key);
435 436 100       705 if($spec->{':key'}{value}) {
    50          
    0          
436 366         573 $spec->{':key'}{value}->($self,$key,$data->{$key});
437             } elsif($spec->{':key'}{'map'}) {
438 70         152 $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         928 pop @{$self->{stack}};
  1218         1832  
447             }
448             }
449              
450             sub check_list {
451 71     71 1 1002 my ($self,$spec,$data) = @_;
452              
453 71 100       160 if(ref($data) ne 'ARRAY') {
454 3         10 $self->_error( "Expected a list structure" );
455 3         6 return;
456             }
457              
458 68 50       125 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         90 for my $value (@$data) {
465 99         84 push @{$self->{stack}}, $value;
  99         133  
466 99 50       158 if(defined $spec->{value}) {
    0          
    0          
    0          
467 99         168 $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         73 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 15 my ($self,$key,$value) = @_;
572 4 100       13 if(defined $value) {
573 3 100 100     30 return 1 if($value && $value =~ /^--- #YAML:1.0/);
574             }
575 3         11 $self->_error( "file does not have a valid YAML header." );
576 3         14 return 0;
577             }
578              
579             sub _uri_split {
580 28     28   149 return $_[0] =~ m,(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?,;
581             }
582              
583             sub url {
584 30     30 1 45 my ($self,$key,$value) = @_;
585 30 100       47 if($value) {
586 28         49 my ($scheme, $auth, $path, $query, $frag) = _uri_split($value);
587              
588 28 100       60 unless ( $scheme ) {
589 2         13 $self->_error( "'$value' for '$key' does not have a URL scheme" );
590 2         9 return 0;
591             }
592 26 100       41 unless ( $auth ) {
593 4         22 $self->_error( "'$value' for '$key' does not have a URL authority" );
594 4         15 return 0;
595             }
596 22         37 return 1;
597             } else {
598 2         5 $value = '';
599             }
600 2         14 $self->_error( "'$value' for '$key' is not a valid URL." );
601 2         11 return 0;
602             }
603              
604             sub urlspec {
605 40     40 1 53 my ($self,$key,$value) = @_;
606 40 100       79 if(defined $value) {
607 39 100 100     227 return 1 if($value && $known_specs{$self->{spec}} eq $value);
608 8 100 100     44 if($value && $known_urls{$value}) {
609 6         19 $self->_error( 'META.yml specification URL does not match version' );
610 6         550 return 0;
611             }
612             }
613 3         8 $self->_error( 'Unknown META.yml specification' );
614 3         12 return 0;
615             }
616              
617             sub string {
618 271     271 1 292 my ($self,$key,$value) = @_;
619 271 100       454 if(defined $value) {
620 270 100 100     607 return 1 if($value || $value =~ /^0$/);
621             }
622 2         5 $self->_error( "value is an undefined string" );
623 2         8 return 0;
624             }
625              
626             sub string_or_undef {
627 4     4 1 10 my ($self,$key,$value) = @_;
628 4 100       16 return 1 unless(defined $value);
629 3 100 100     26 return 1 if($value || $value =~ /^0$/);
630 1         6 $self->_error( "No string defined for '$key'" );
631 1         4 return 0;
632             }
633              
634             sub file {
635 69     69 1 140 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         4 return 0;
639             }
640              
641             sub exversion {
642 301     301 1 309 my ($self,$key,$value) = @_;
643 301 100 100     957 if(defined $value && ($value || $value =~ /0/)) {
      66        
644 298         273 my $pass = 1;
645 298 100       537 for(split(",",$value)) { $self->version($key,$_) or ($pass = 0); }
  303         410  
646 298         426 return $pass;
647             }
648 3 100       9 $value = '' unless(defined $value);
649 3         14 $self->_error( "'$value' for '$key' is not a valid version." );
650 3         10 return 0;
651             }
652              
653             sub version {
654 459     459 1 516 my ($self,$key,$value) = @_;
655 459 100       564 if(defined $value) {
656 457 100 100     946 return 0 unless($value || $value =~ /0/);
657 456 100       2406 return 1 if($value =~ /^\s*((<|<=|>=|>|!=|==)\s*)?v?\d+((\.\d+((_|\.)\d+)?)?)/);
658             } else {
659 2         3 $value = '';
660             }
661 6         29 $self->_error( "'$value' for '$key' is not a valid version." );
662 6         21 return 0;
663             }
664              
665             sub boolean {
666 8     8 1 21 my ($self,$key,$value) = @_;
667 8 100       20 if(defined $value) {
668 6 100       42 return 1 if($value =~ /^(0|1|true|false)$/);
669             } else {
670 2         5 $value = '';
671             }
672 4         19 $self->_error( "'$value' for '$key' is not a boolean value." );
673 4         17 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 6455 my ($self,$key,$value) = @_;
696 61 100       105 if(defined $value) {
697 60 100 100     329 return 1 if($value && exists $licenses{$value});
698 4 100       18 return 2 if($value);
699             } else {
700 1         3 $value = '';
701             }
702 2         12 $self->_error( "License '$value' is unknown" );
703 2         10 return 0;
704             }
705              
706             sub resource {
707 7     7 1 18 my ($self,$key) = @_;
708 7 100       17 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     81 return 1 if($key && $key =~ /^[a-z]+$/i && $key =~ /[A-Z]/);
      100        
712             } else {
713 1         3 $key = '';
714             }
715 3         15 $self->_error( "Resource '$key' must be in CamelCase." );
716 3         13 return 0;
717             }
718              
719             sub keyword {
720 80     80 1 4034 my ($self,$key) = @_;
721 80 100       128 if(defined $key) {
722 79 100 100     488 return 1 if($key && $key =~ /^([a-z][-_a-z]*)$/); # spec defined
723 6 100 100     39 return 1 if($key && $key =~ /^x_([a-z][-_a-z]*)$/i); # user defined
724             } else {
725 1         2 $key = '';
726             }
727 6         25 $self->_error( "Key '$key' is not a legal keyword." );
728 6         27 return 0;
729             }
730              
731             sub identifier {
732 13     13 1 3961 my ($self,$key) = @_;
733 13 100       35 if(defined $key) {
734 12 100 100     121 return 1 if($key && $key =~ /^([a-z][_a-z]+)$/i); # spec 2.0 defined
735             } else {
736 1         3 $key = '';
737             }
738 5         19 $self->_error( "Key '$key' is not a legal identifier." );
739 5         23 return 0;
740             }
741              
742             sub module {
743 370     370 1 345 my ($self,$key) = @_;
744 370 100       493 if(defined $key) {
745 369 100 100     2140 return 1 if($key && $key =~ /^[A-Za-z0-9_]+(::[A-Za-z0-9_]+)*$/);
746             } else {
747 1         2 $key = '';
748             }
749 9         31 $self->_error( "Key '$key' is not a legal module name." );
750 9         24 return 0;
751             }
752              
753 69     69 1 72 sub anything { return 1 }
754              
755             sub _error {
756 73     73   100 my $self = shift;
757 73         81 my $mess = shift;
758              
759 73 100       224 $mess .= ' ('.join(' -> ',@{$self->{stack}}).')' if($self->{stack});
  25         79  
760 73         203 $mess .= " [Validation: $self->{spec}]";
761              
762 73         83 push @{$self->{errors}}, $mess;
  73         202  
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__