File Coverage

blib/lib/App/Scaffolder/Template.pm
Criterion Covered Total %
statement 92 102 90.2
branch 26 38 68.4
condition 16 35 45.7
subroutine 15 15 100.0
pod 8 8 100.0
total 157 198 79.2


line stmt bran cond sub pod time code
1             package App::Scaffolder::Template;
2             {
3             $App::Scaffolder::Template::VERSION = '0.002000';
4             }
5              
6             # ABSTRACT: Represent a template for App::Scaffolder
7              
8 3     3   142951 use strict;
  3         5  
  3         100  
9 3     3   18 use warnings;
  3         5  
  3         92  
10              
11 3     3   15 use Carp;
  3         4  
  3         198  
12 3     3   17 use Scalar::Util qw(blessed);
  3         5  
  3         139  
13 3     3   16 use Path::Class::File;
  3         5  
  3         72  
14 3     3   13 use File::Spec;
  3         5  
  3         3886  
15              
16              
17             sub new {
18 16     16 1 21621 my ($class, $arg_ref) = @_;
19              
20 16         80 my $name = $arg_ref->{name};
21 16 50 33     109 if (! defined $name || $name eq '') {
22 0         0 croak("Required 'name' parameter not passed");
23             }
24              
25 16         34 my $path = $arg_ref->{path};
26 16 50 33     104 unless (defined $path && ref $path eq 'ARRAY') {
27 0         0 croak("Required 'path' parameter not passed or not an array reference");
28             }
29              
30 16         57 my $self = {
31             name => $name,
32             path => $path,
33             };
34              
35 16         109 return bless($self, $class);
36             }
37              
38              
39              
40             sub add_path_entry {
41 1     1 1 3 my ($self, $dir) = @_;
42              
43 1 50 33     16 unless (blessed $dir && $dir->isa('Path::Class::Dir')) {
44 0         0 croak("Required 'dir' parameter not passed or not a 'Path::Class::Dir' instance");
45             }
46 1         2 push @{$self->{path}}, $dir;
  1         3  
47 1         4 return;
48             }
49              
50              
51              
52             sub get_path {
53 16     16 1 39 my ($self) = @_;
54 16         62 return $self->{path};
55             }
56              
57              
58              
59             sub get_name {
60 2     2 1 408 my ($self) = @_;
61 2         17 return $self->{name};
62             }
63              
64              
65              
66             sub process {
67 11     11 1 13616 my ($self, $arg_ref) = @_;
68              
69 11 50 33     109 unless (defined $arg_ref && ref $arg_ref eq 'HASH') {
70 0         0 croak("No parameters passed or not a hash reference");
71             }
72 11         26 my $target = $arg_ref->{target};
73 11 50 33     138 unless (blessed $target && $target->isa('Path::Class::Dir')) {
74 0         0 croak("Required 'target' parameter not passed or not a 'Path::Class::Dir' instance");
75             }
76 11         29 my $variables = $arg_ref->{variables};
77 11 50 33     65 unless (defined $variables && ref $variables eq 'HASH') {
78 0         0 croak("Required 'variables' parameter not passed or not a hash reference");
79             }
80              
81 11         16 my @created_files;
82 11         21 for my $file (values %{$self->get_template_files()}) {
  11         39  
83 15         64 my $rel_target = $self->replace_file_path_variables(
84             $file->{rel_target},
85             $variables
86             );
87 14         53 my $target_dir = $target->subdir($rel_target->parent());
88 14 100       1428 unless (-d $target_dir) {
89 2 50       113 $target_dir->mkpath()
90             or confess("Unable to create target directory $target_dir");
91             }
92              
93 14         1002 my $output_file = $target_dir->file(
94             $rel_target->basename()
95             );
96 14 100 100     1265 if (-e $output_file && ! $arg_ref->{overwrite}) {
97 2         96 croak(
98             "File " . $output_file . " exists - need to pass 'overwrite' "
99             . "parameter to overwrite files"
100             );
101             }
102 12         730 $output_file->openw()->write($self->get_content_for(
103             $file->{source}, $variables
104             ));
105 12         941 push @created_files, $output_file;
106             }
107              
108 8         22390 return @created_files;
109             }
110              
111              
112              
113             sub get_content_for {
114 14     14 1 3841 my ($self, $file, $variables) = @_;
115              
116 14 50 33     144 unless (blessed $file && $file->isa('Path::Class::File')) {
117 0         0 croak("Required 'file' parameter not passed or not a 'Path::Class::File' instance");
118             }
119 14   100     64 $variables ||= {};
120              
121 14         27 my $content = '';
122 14 100       43 if ($file =~ m{\.tt$}x) {
123 6         1831 require Template;
124 6         23162 my $template = Template->new({
125             ABSOLUTE => 1,
126             });
127 6 50       32820 $template->process($file->stringify(), $variables, \$content)
128             or confess $template->error();
129             }
130             else {
131 8         354 $content = $file->slurp();
132             }
133 14         57739 return $content;
134             }
135              
136              
137              
138             sub replace_file_path_variables {
139 26     26 1 7555 my ($self, $path, $variables) = @_;
140              
141 26 50 33     301 unless (blessed $path && $path->isa('Path::Class::File')) {
142 0         0 croak("Required 'path' parameter not passed or not a 'Path::Class::File' instance");
143             }
144              
145 26 50 33     155 if (! defined $variables || ref $variables ne 'HASH') {
146 0         0 croak("Required 'variables' parameter not passed or not a hash reference");
147             }
148              
149 26         34 my $orig_path = $path;
150 26         544 my @orig_parts = File::Spec->splitdir($orig_path);
151              
152 26         535 while ($path =~ m{___([^_/\\]+)___}x) {
153 15 50       415 if (defined $variables->{$1}) {
154 15         36 $path =~ s{___([^_/\\]+)___}{$variables->{$1}}gx;
155 15         912 $path = Path::Class::File->new($path);
156             }
157             else {
158 0         0 croak("Unreplaceable filename variable $1 found");
159             }
160             }
161 26         1494 my @parts = File::Spec->splitdir($path);
162 26 100 66     677 if (scalar @parts > scalar File::Spec->no_upwards(@parts)
163             || scalar @parts < scalar @orig_parts) {
164 4         11 croak("Potential directory traversal detected in path '$path'");
165             }
166 22         72 return $path;
167             }
168              
169              
170              
171             sub get_template_files {
172 13     13 1 31 my ($self) = @_;
173 13         28 my $file = {};
174 13         22 for my $path_entry (map {$_->absolute()} @{$self->get_path()}) {
  14         113  
  13         39  
175             $path_entry->recurse(callback => sub {
176 42     42   9917 my ($child) = @_;
177 42 100       111 unless ($child->is_dir()) {
178 24         167 my $rel_path = $child->relative($path_entry);
179 24         4403 my $key = $rel_path->stringify();
180 24 100       345 if ($key =~ s{\.tt$}{}x) {
181 10         33 my ($filename) = $rel_path->basename() =~ m{(.*)\.tt}x;
182 10         82 $rel_path = $rel_path->parent()->file($filename)->cleanup();
183             }
184 24 100       2092 unless (exists $file->{$key}) {
185 23         145 $file->{$key} = {
186             rel_target => $rel_path,
187             source => $child,
188             };
189             }
190             }
191 14         637 });
192             }
193 13         9459 return $file;
194             }
195              
196              
197             1;
198              
199             __END__