File Coverage

blib/lib/Nephia/Setup.pm
Criterion Covered Total %
statement 186 193 96.3
branch 23 34 67.6
condition 5 11 45.4
subroutine 39 41 95.1
pod 16 17 94.1
total 269 296 90.8


line stmt bran cond sub pod time code
1             package Nephia::Setup;
2 2     2   91250 use strict;
  2         4  
  2         68  
3 2     2   10 use warnings;
  2         4  
  2         63  
4 2     2   2299 use Archive::Extract;
  2         432744  
  2         69  
5 2     2   19 use Carp;
  2         4  
  2         158  
6 2     2   1716 use Data::Section::Simple;
  2         1128  
  2         89  
7 2     2   12 use File::Basename 'fileparse';
  2         4  
  2         89  
8 2     2   2263 use File::Fetch;
  2         44736  
  2         73  
9 2     2   21 use File::Spec;
  2         5  
  2         48  
10 2     2   14 use File::Temp 'tempdir';
  2         3  
  2         110  
11 2     2   33 use Module::Load ();
  2         4  
  2         31  
12 2     2   1318 use Nephia::Chain;
  2         6  
  2         54  
13 2     2   1108 use Nephia::Context;
  2         6  
  2         49  
14 2     2   1058 use Nephia::MetaTemplate;
  2         7  
  2         58  
15 2     2   2048 use URI;
  2         10137  
  2         4012  
16              
17             our $NEXT;
18              
19             sub new {
20 3     3 0 5320 my ($class, %opts) = @_;
21 3         11 $opts{nest} = 0;
22 3   33     15 $opts{approot} ||= $class->_resolve_approot($opts{appname});
23 3   33     26 $opts{classfile} ||= $class->_resolve_classfile($opts{appname});
24 3         29 $opts{action_chain} = Nephia::Chain->new(namespace => 'Nephia::Setup::Action');
25 3   100     16 $opts{plugins} ||= [];
26 3   33     25 $opts{deps} ||= $class->_build_deps;
27 3 50       31 $opts{meta_tmpl} = Nephia::MetaTemplate->new($opts{meta_tmpl} ? %{$opts{meta_tmpl}} : ());
  0         0  
28 3         23 my $self = bless {%opts}, $class;
29 3         17 $self->_load_plugins;
30 3         14 return $self
31             }
32              
33             sub _resolve_approot {
34 1     1   4 my ($class, $appname) = @_;
35 1         3 return ['.', $class->_normalize_appname($appname)];
36             }
37              
38             sub _normalize_appname {
39 2     2   5 my ($class, $appname) = @_;
40 2         2 my $rtn = $appname;
41 2         7 $rtn =~ s|\:\:|\-|g;
42 2         14 return $rtn;
43             }
44              
45             sub _resolve_classfile {
46 3     3   8 my ($class, $appname) = @_;
47 3         26 return ['lib', split('::', $appname.'.pm')];
48             }
49              
50             sub _build_deps {
51             {
52 3     3   24 requires => ['Nephia' => 0],
53             test => {
54             requires => ['Test::More' => 0],
55             },
56             };
57             }
58              
59             sub _deparse_deps {
60 4     4   7 my $nest_level = shift;
61 4 100       15 my $nest = $nest_level > 0 ? join('', map{' '} 1 .. $nest_level*4) : '';
  8         16  
62 4         11 my %val = @_;
63 4         5 my $data = "";
64 4         13 for my $key (keys %val) {
65 6         10 my $v = $val{$key};
66 6 100       21 if (ref($v) eq 'ARRAY') {
    50          
67 4         9 my @mods = @$v;
68 4         12 while (@mods) {
69 4         9 my $name = shift(@mods);
70 4         6 my $version = shift(@mods);
71 4         25 $data .= "$nest$key '$name' => $version;\n";
72             }
73             }
74             elsif (ref($v) eq 'HASH') {
75 2         5 $data .= "on '$key' => sub {\n";
76 2         11 $data .= &_deparse_deps($nest_level + 1, %$v);
77 2         76 $data .= "};\n";
78             }
79             }
80 4         17 return $data;
81             }
82              
83             sub appname {
84 7     7 1 1887 my $self = shift;
85 7         53 return $self->{appname};
86             }
87              
88             sub approot {
89 33     33 1 1324 my $self = shift;
90 33 50       772 return ref($self->{approot}) eq 'ARRAY' ? @{$self->{approot}} : ( $self->{approot} );
  0         0  
91             }
92              
93             sub classfile {
94 1     1 1 3 my $self = shift;
95 1         2 return @{$self->{classfile}};
  1         8  
96             }
97              
98             sub action_chain {
99 16     16 1 27 my $self = shift;
100 16 100       122 return wantarray ? $self->{action_chain}->as_array : $self->{action_chain};
101             }
102              
103             sub deps {
104 3     3 1 775 my $self = shift;
105 3         21 return $self->{deps};
106             }
107              
108             sub meta_tmpl {
109 1     1 1 3 my $self = shift;
110 1         5 return $self->{meta_tmpl};
111             }
112              
113             sub makepath {
114 8     8 1 2549 my ($self, @in_path) = @_;
115 8         27 my $path = File::Spec->catdir($self->approot, @in_path);
116 8         21 my $level = 0;
117 8         273 while ( ! -d $path ) {
118 7         25 my $_path = File::Spec->catdir($self->approot, @in_path[0..$level]);
119 7 100       134 unless (-d $_path) {
120 6         76 $self->diag("Create directory %s", $_path);
121 6 50       647 mkdir $_path or $self->stop("could not create path %s - %s", $path, $!);
122             }
123 7         189 $level++;
124             }
125             }
126              
127             sub spew {
128 5     5 1 122 my $self = shift;
129 5         16 my $data = pop;
130 5         10 my $filename = pop;
131 5         16 my @in_path = @_;
132 5         33 my $path = File::Spec->catfile($self->approot, @in_path, $filename);
133 5         34 $self->makepath( @in_path );
134 5 50       139 if (-e $path) {
135 0         0 return;
136             }
137 5         29 $self->diag('Create file %s', $path);
138 5 50       634 open my $fh, '>', $path or $self->stop("could not open file %s - %s", $path, $!);
139 5         788 print $fh $data;
140 5         379 close $fh;
141             }
142              
143             sub process_template {
144 3     3 1 1958 my ($self, $data) = @_;
145 3         18 local $NEXT = '\{\{$NEXT\}\}'; ### for minilla friendly
146 3         50 while (my ($code) = $data =~ /\{\{(.*?)\}\}/) {
147 3         376 my $replace = eval "$code";
148 3 50       16 $self->stop($@) if $@;
149 3         29 $data =~ s/\{\{(.*?)\}\}/$replace/x;
150             }
151 3         8 $data =~ s/\\\{/{/g;
152 3         7 $data =~ s/\\\}/}/g;
153 3         11 $data =~ s/\:\:\:/=/g;
154 3         13 return $data;
155             }
156              
157             sub do_task {
158 3     3 1 4331 my $self = shift;
159 3         14 $self->diag("\033[44m\033[1;36mBegin to setup %s\033[0m", $self->appname);
160             my $context = Nephia::Context->new(
161 0     0   0 data_section => sub { Data::Section::Simple->new($_[0]) },
162 3         43 );
163 3         9 $self->{nest}++;
164 3         14 for my $action ( $self->action_chain ) {
165 4         15 my $name = ref($action);
166 4         26 $self->diag("\033[1;34m[Action]\033[0m \033[0;35m%s\033[0m - provided by \033[0;32m%s\033[0m", $name, $self->action_chain->from($name));
167 4         16 $self->{nest}++;
168 4         26 $context = $action->($self, $context);
169 4         37 $self->{nest}--;
170 4         16 $self->diag("Done.");
171             }
172 3         10 $self->{nest}--;
173 3         10 $self->diag("\033[44m\033[1;36mSetup finished.\033[0m");
174             }
175              
176             sub diag {
177 31     31 1 3352 my ($self, $str, @params) = @_;
178 31         105 my $spaces = $self->_spaces_for_nest;
179 31         2700 printf STDERR $spaces.$str."\n", @params;
180             }
181              
182             sub stop {
183 0     0 1 0 my ($self, $str, @params) = @_;
184 0         0 my $spaces = $self->_spaces_for_nest;
185 0         0 croak( sprintf($spaces."\033[41m\033[1;33m[! SETUP STOPPED !]\033[0m \033[1;31m".$str."\033[0m", @params) );
186             }
187              
188             sub _spaces_for_nest {
189 33     33   3062 my $self = shift;
190 33         60 my $spaces = '';
191 33 100       227 if ($self->{nest}) {
192 21         219 $spaces .= ' ' for 1 .. $self->{nest} * 2;
193             }
194 33         90 return $spaces;
195             }
196              
197             sub _load_plugins {
198 3     3   6 my $self = shift;
199 3         6 for my $plugin_name ( @{$self->{plugins}} ) {
  3         20  
200 2         7 $self->_load_plugin($plugin_name);
201             }
202             }
203              
204             sub _load_plugin {
205 3     3   7 my ($self, $plugin_name) = @_;
206 3         10 my $plugin_class = $self->_plugin_name_normalize($plugin_name);
207 3 50       48 Module::Load::load($plugin_class) unless $plugin_class->can('new');
208 3         14 my $plugin = $plugin_class->new(setup => $self);
209 3         12 $plugin->fix_setup;
210 3         19 for my $bundle ($plugin->bundle) {
211 1         7 $self->diag("\033[1;36m[bundle]\033[0m \033[0;35m%s\033[0m for \033[0;32m%s\033[0m", $self->_plugin_name_normalize($bundle), $plugin_class);
212 1         7 $self->_load_plugin($bundle);
213             }
214 3         26 return $plugin;
215             }
216              
217             sub _plugin_name_normalize {
218 6     6   10 my ($self, $plugin_name) = @_;
219 6 100       27 my $plugin_class = $plugin_name =~ /^Nephia::Setup::Plugin::/ ? $plugin_name : 'Nephia::Setup::Plugin::'.$plugin_name;
220 6         20 return $plugin_class;
221             }
222              
223             sub cpanfile {
224 2     2 1 2229 my $self = shift;
225 2         3 &_deparse_deps(0, %{$self->deps});
  2         7  
226             }
227              
228             sub assets {
229 2     2 1 11 my ($self, $url, @in_path) = @_;
230 2         9 my $path = File::Spec->catfile($self->approot, @in_path);
231 2 50       66 unless ( -e $path ) {
232 2         8 $self->diag('Fetching content from url %s', $url);
233 2         24 my $fetcher = File::Fetch->new( uri => $url );
234 2         32928 my $content ;
235 2 50       79 $fetcher->fetch(to => \$content) or $self->stop('Could not fetch url %s : %s', $url, $!);
236 2         946034 $self->spew(@in_path, $content);
237             }
238             }
239              
240             sub extract_archive {
241 1     1 1 6 my ($self, $archive_file, @extract_to) = @_;
242 1         6 my $path = File::Spec->catdir($self->approot, @extract_to);
243              
244 1         16 $self->makepath(@extract_to);
245              
246 1         7 $self->diag('Extract Archive %s into %s', $archive_file, $path);
247 1         33 my $archive = Archive::Extract->new(archive => $archive_file);
248 1         385 $archive->extract(to => $path);
249              
250 1         1926035 $self->diag('Cleanup Archive %s', $archive_file);
251 1         99707 unlink $archive_file;
252             }
253              
254             sub assets_archive {
255 1     1 1 15 my ($self, $url, @in_path) = @_;
256 1         5 my $path = File::Spec->catdir($self->approot, @in_path);
257 1 50       39 unless ( -d $path ) {
258 1         4 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
259              
260 1         13 my ($filename) = fileparse( URI->new($url)->path );
261 1         182 $self->assets( $url, $filename );
262 1         16 my $archive_file = File::Spec->catfile($self->approot, $filename);
263              
264 1         8 $self->extract_archive( $archive_file, @in_path );
265             }
266             }
267              
268             1;
269              
270             __END__