File Coverage

blib/lib/YAML/PP/Schema/Include.pm
Criterion Covered Total %
statement 80 86 93.0
branch 25 30 83.3
condition 2 4 50.0
subroutine 14 15 93.3
pod 0 9 0.0
total 121 144 84.0


line stmt bran cond sub pod time code
1 1     1   459 use strict;
  1         2  
  1         30  
2 1     1   5 use warnings;
  1         2  
  1         54  
3             package YAML::PP::Schema::Include;
4              
5             our $VERSION = '0.036_002'; # TRIAL VERSION
6              
7 1     1   5 use Carp qw/ croak /;
  1         2  
  1         43  
8 1     1   6 use Scalar::Util qw/ weaken /;
  1         2  
  1         39  
9 1     1   5 use File::Basename qw/ dirname /;
  1         2  
  1         895  
10              
11             sub new {
12 4     4 0 13161 my ($class, %args) = @_;
13              
14 4         11 my $paths = delete $args{paths};
15 4 100       13 if (defined $paths) {
16 2 50       7 unless (ref $paths eq 'ARRAY') {
17 2         6 $paths = [$paths];
18             }
19             }
20             else {
21 2         4 $paths = [];
22             }
23 4   50     18 my $allow_absolute = $args{allow_absolute} || 0;
24 4   50     19 my $loader = $args{loader} || \&default_loader;
25              
26 4         17 my $self = bless {
27             paths => $paths,
28             allow_absolute => $allow_absolute,
29             last_includes => [],
30             cached => {},
31             loader => $loader,
32             }, $class;
33 4         13 return $self;
34             }
35              
36             sub init {
37 0     0 0 0 my ($self) = @_;
38 0         0 $self->{last_includes} = [];
39 0         0 $self->{cached} = [];
40             }
41              
42 11     11 0 20 sub paths { $_[0]->{paths} }
43 11     11 0 18 sub allow_absolute { $_[0]->{allow_absolute} }
44             sub yp {
45 15     15 0 48 my ($self, $yp) = @_;
46 15 100       35 if (@_ == 2) {
47 4         9 $self->{yp} = $yp;
48 4         13 weaken $self->{yp};
49 4         10 return $yp;
50             }
51 11         28 return $self->{yp};
52             }
53              
54             sub register {
55 4     4 0 10 my ($self, %args) = @_;
56 4         9 my $schema = $args{schema};
57              
58             $schema->add_resolver(
59             tag => '!include',
60 4     11   20 match => [ all => sub { $self->include(@_) } ],
  11         51  
61             implicit => 0,
62             );
63             }
64              
65             sub include {
66 11     11 0 22 my ($self, $constructor, $event) = @_;
67 11         24 my $yp = $self->yp;
68 11         23 my $search_paths = $self->paths;
69 11         19 my $allow_absolute = $self->allow_absolute;
70              
71 11         19 my $relative = not @$search_paths;
72 11 100       22 if ($relative) {
73 6         10 my $last_includes = $self->{last_includes};
74 6 100       16 if (@$last_includes) {
75 3         8 $search_paths = [ $last_includes->[-1] ];
76             }
77             else {
78             # we are in the top-level file and need to look into
79             # the original YAML::PP instance
80 3         13 my $filename = $yp->loader->filename;
81 3         143 $search_paths = [dirname $filename];
82             }
83             }
84 11         23 my $filename = $event->{value};
85              
86 11         13 my $fullpath;
87 11 100       86 if (File::Spec->file_name_is_absolute($filename)) {
88 1 50       4 unless ($allow_absolute) {
89 1         87 croak "Absolute filenames not allowed";
90             }
91 0         0 $fullpath = $filename;
92             }
93             else {
94 10         70 my @paths = File::Spec->splitdir($filename);
95 10 50       24 unless ($allow_absolute) {
96             # if absolute paths are not allowed, we also may not use upwards ..
97 10         66 @paths = File::Spec->no_upwards(@paths);
98             }
99 10         23 for my $candidate (@$search_paths) {
100 10         116 my $test = File::Spec->catfile( $candidate, @paths );
101 10 100       245 if (-e $test) {
102 9         27 $fullpath = $test;
103 9         23 last;
104             }
105             }
106 10 100       243 croak "File '$filename' not found" unless defined $fullpath;
107             }
108              
109 9 100       45 if ($self->{cached}->{ $fullpath }++) {
110 1         106 croak "Circular include '$fullpath'";
111             }
112 8 100       19 if ($relative) {
113 5         7 push @{ $self->{last_includes} }, dirname $fullpath;
  5         197  
114             }
115              
116             # We need a new object because we are still in the parsing and
117             # constructing process
118 8         45 my $clone = $yp->clone;
119 8         24 my ($data) = $self->loader->($clone, $fullpath);
120              
121 6 100       18 if ($relative) {
122 3         5 pop @{ $self->{last_includes} };
  3         6  
123             }
124 6 50       21 unless (--$self->{cached}->{ $fullpath }) {
125 6         17 delete $self->{cached}->{ $fullpath };
126             }
127 6         27 return $data;
128             }
129              
130             sub loader {
131 8     8 0 12 my ($self, $code) = @_;
132 8 50       25 if (@_ == 2) {
133 0         0 $self->{loader} = $code;
134 0         0 return $code;
135             }
136 8         18 return $self->{loader};
137             }
138             sub default_loader {
139 8     8 0 17 my ($yp, $filename) = @_;
140 8         26 $yp->load_file($filename);
141             }
142              
143             1;
144              
145             __END__