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   28120 use warnings;
  10         12  
  10         249  
4 10     10   34 use strict;
  10         8  
  10         223  
5              
6 10     10   31 use vars qw($VERSION);
  10         10  
  10         24081  
7             $VERSION = '0.25';
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 33085 my ($class,%hash) = @_;
333              
334             # create an attributes hash
335 45         119 my $atts = {
336             'spec' => $hash{spec},
337             'data' => $hash{data},
338             };
339              
340             # create the object
341 45         137 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 96 my $self = shift;
367 44         66 my $data = $self->{data};
368              
369 44 100       85 unless($self->{spec}) {
370 21 100 66     119 $self->{spec} = $data->{'meta-spec'} && $data->{'meta-spec'}{'version'} ? $data->{'meta-spec'}{'version'} : '1.0';
371             }
372              
373 44         94 $self->check_map($definitions{$self->{spec}},$data);
374 44 100       158 return defined $self->{errors} ? 1 : 0;
375             }
376              
377             sub errors {
378 41     41 1 110 my $self = shift;
379 41 100       90 return () unless($self->{errors});
380 23         19 return @{$self->{errors}};
  23         64  
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 562 my ($self,$spec,$data) = @_;
403              
404 350 100       553 if(ref($spec) ne 'HASH') {
405 1         2 $self->_error( "Unknown META.yml specification, cannot validate." );
406 1         2 return;
407             }
408              
409 349 100       419 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         573 for my $key (keys %$spec) {
415 1350 100       2399 next unless($spec->{$key}->{mandatory});
416 419 100       657 next if(defined $data->{$key});
417 4         4 push @{$self->{stack}}, $key;
  4         6  
418 4         10 $self->_error( "Missing mandatory field, '$key'" );
419 4         4 pop @{$self->{stack}};
  4         5  
420             }
421              
422 346         675 for my $key (keys %$data) {
423 1219         728 push @{$self->{stack}}, $key;
  1219         1674  
424 1219 100       1623 if($spec->{$key}) {
    50          
425 783 100       1123 if($spec->{$key}{value}) {
    100          
    50          
426 480         626 $spec->{$key}{value}->($self,$key,$data->{$key});
427             } elsif($spec->{$key}{'map'}) {
428 234         356 $self->check_map($spec->{$key}{'map'},$data->{$key});
429             } elsif($spec->{$key}{'list'}) {
430 69         106 $self->check_list($spec->{$key}{'list'},$data->{$key});
431             }
432              
433             } elsif ($spec->{':key'}) {
434 436         552 $spec->{':key'}{name}->($self,$key,$key);
435 436 100       605 if($spec->{':key'}{value}) {
    50          
    0          
436 366         477 $spec->{':key'}{value}->($self,$key,$data->{$key});
437             } elsif($spec->{':key'}{'map'}) {
438 70         112 $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         814 pop @{$self->{stack}};
  1218         1555  
447             }
448             }
449              
450             sub check_list {
451 71     71 1 576 my ($self,$spec,$data) = @_;
452              
453 71 100       116 if(ref($data) ne 'ARRAY') {
454 3         7 $self->_error( "Expected a list structure" );
455 3         4 return;
456             }
457              
458 68 50       99 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         77 for my $value (@$data) {
465 99         60 push @{$self->{stack}}, $value;
  99         105  
466 99 50       125 if(defined $spec->{value}) {
    0          
    0          
    0          
467 99         122 $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         62 pop @{$self->{stack}};
  97         160  
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 10 my ($self,$key,$value) = @_;
572 4 100       9 if(defined $value) {
573 3 100 100     19 return 1 if($value && $value =~ /^--- #YAML:1.0/);
574             }
575 3         6 $self->_error( "file does not have a valid YAML header." );
576 3         8 return 0;
577             }
578              
579             sub _uri_split {
580 28     28   139 return $_[0] =~ m,(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?,;
581             }
582              
583             sub url {
584 30     30 1 39 my ($self,$key,$value) = @_;
585 30 100       41 if($value) {
586 28         37 my ($scheme, $auth, $path, $query, $frag) = _uri_split($value);
587              
588 28 100       53 unless ( $scheme ) {
589 2         9 $self->_error( "'$value' for '$key' does not have a URL scheme" );
590 2         6 return 0;
591             }
592 26 100       38 unless ( $auth ) {
593 4         19 $self->_error( "'$value' for '$key' does not have a URL authority" );
594 4         10 return 0;
595             }
596 22         29 return 1;
597             } else {
598 2         3 $value = '';
599             }
600 2         7 $self->_error( "'$value' for '$key' is not a valid URL." );
601 2         6 return 0;
602             }
603              
604             sub urlspec {
605 40     40 1 42 my ($self,$key,$value) = @_;
606 40 100       70 if(defined $value) {
607 39 100 100     179 return 1 if($value && $known_specs{$self->{spec}} eq $value);
608 8 100 100     31 if($value && $known_urls{$value}) {
609 6         13 $self->_error( 'META.yml specification URL does not match version' );
610 6         342 return 0;
611             }
612             }
613 3         4 $self->_error( 'Unknown META.yml specification' );
614 3         8 return 0;
615             }
616              
617             sub string {
618 271     271 1 251 my ($self,$key,$value) = @_;
619 271 100       333 if(defined $value) {
620 270 100 100     512 return 1 if($value || $value =~ /^0$/);
621             }
622 2         4 $self->_error( "value is an undefined string" );
623 2         5 return 0;
624             }
625              
626             sub string_or_undef {
627 4     4 1 5 my ($self,$key,$value) = @_;
628 4 100       11 return 1 unless(defined $value);
629 3 100 100     18 return 1 if($value || $value =~ /^0$/);
630 1         3 $self->_error( "No string defined for '$key'" );
631 1         3 return 0;
632             }
633              
634             sub file {
635 69     69 1 68 my ($self,$key,$value) = @_;
636 69 100       125 return 1 if(defined $value);
637 1         4 $self->_error( "No file defined for '$key'" );
638 1         3 return 0;
639             }
640              
641             sub exversion {
642 301     301 1 263 my ($self,$key,$value) = @_;
643 301 100 100     793 if(defined $value && ($value || $value =~ /0/)) {
      66        
644 298         252 my $pass = 1;
645 298 100       441 for(split(",",$value)) { $self->version($key,$_) or ($pass = 0); }
  303         327  
646 298         352 return $pass;
647             }
648 3 100       6 $value = '' unless(defined $value);
649 3         8 $self->_error( "'$value' for '$key' is not a valid version." );
650 3         6 return 0;
651             }
652              
653             sub version {
654 459     459 1 392 my ($self,$key,$value) = @_;
655 459 100       484 if(defined $value) {
656 457 100 100     799 return 0 unless($value || $value =~ /0/);
657 456 100       2074 return 1 if($value =~ /^\s*((<|<=|>=|>|!=|==)\s*)?v?\d+((\.\d+((_|\.)\d+)?)?)/);
658             } else {
659 2         2 $value = '';
660             }
661 6         20 $self->_error( "'$value' for '$key' is not a valid version." );
662 6         11 return 0;
663             }
664              
665             sub boolean {
666 8     8 1 11 my ($self,$key,$value) = @_;
667 8 100       16 if(defined $value) {
668 6 100       26 return 1 if($value =~ /^(0|1|true|false)$/);
669             } else {
670 2         2 $value = '';
671             }
672 4         11 $self->_error( "'$value' for '$key' is not a boolean value." );
673 4         12 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 3805 my ($self,$key,$value) = @_;
696 61 100       81 if(defined $value) {
697 60 100 100     264 return 1 if($value && exists $licenses{$value});
698 4 100       11 return 2 if($value);
699             } else {
700 1         2 $value = '';
701             }
702 2         6 $self->_error( "License '$value' is unknown" );
703 2         6 return 0;
704             }
705              
706             sub resource {
707 7     7 1 10 my ($self,$key) = @_;
708 7 100       10 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     49 return 1 if($key && $key =~ /^[a-z]+$/i && $key =~ /[A-Z]/);
      100        
712             } else {
713 1         2 $key = '';
714             }
715 3         9 $self->_error( "Resource '$key' must be in CamelCase." );
716 3         8 return 0;
717             }
718              
719             sub keyword {
720 80     80 1 2290 my ($self,$key) = @_;
721 80 100       99 if(defined $key) {
722 79 100 100     389 return 1 if($key && $key =~ /^([a-z][-_a-z]*)$/); # spec defined
723 6 100 100     22 return 1 if($key && $key =~ /^x_([a-z][-_a-z]*)$/i); # user defined
724             } else {
725 1         1 $key = '';
726             }
727 6         14 $self->_error( "Key '$key' is not a legal keyword." );
728 6         18 return 0;
729             }
730              
731             sub identifier {
732 13     13 1 2311 my ($self,$key) = @_;
733 13 100       21 if(defined $key) {
734 12 100 100     87 return 1 if($key && $key =~ /^([a-z][_a-z]+)$/i); # spec 2.0 defined
735             } else {
736 1         2 $key = '';
737             }
738 5         31 $self->_error( "Key '$key' is not a legal identifier." );
739 5         14 return 0;
740             }
741              
742             sub module {
743 370     370 1 295 my ($self,$key) = @_;
744 370 100       422 if(defined $key) {
745 369 100 100     1776 return 1 if($key && $key =~ /^[A-Za-z0-9_]+(::[A-Za-z0-9_]+)*$/);
746             } else {
747 1         2 $key = '';
748             }
749 9         28 $self->_error( "Key '$key' is not a legal module name." );
750 9         12 return 0;
751             }
752              
753 69     69 1 59 sub anything { return 1 }
754              
755             sub _error {
756 73     73   72 my $self = shift;
757 73         58 my $mess = shift;
758              
759 73 100       130 $mess .= ' ('.join(' -> ',@{$self->{stack}}).')' if($self->{stack});
  25         68  
760 73         116 $mess .= " [Validation: $self->{spec}]";
761              
762 73         54 push @{$self->{errors}}, $mess;
  73         127  
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__