File Coverage

blib/lib/Module/Setup.pm
Criterion Covered Total %
statement 346 347 99.7
branch 125 128 97.6
condition 53 59 89.8
subroutine 52 52 100.0
pod 0 23 0.0
total 576 609 94.5


line stmt bran cond sub pod time code
1             package Module::Setup;
2              
3 41     41   197 use strict;
  41         62  
  41         1262  
4 41     41   186 use warnings;
  41         61  
  41         1199  
5 41     41   970 use 5.008001;
  41         123  
6             our $VERSION = '0.06_01';
7              
8 41     41   185 use Carp ();
  41         53  
  41         769  
9 41     41   20764 use Class::Trigger;
  41         52282  
  41         620  
10 41     41   1926 use Cwd ();
  41         56  
  41         898  
11 41     41   31691 use ExtUtils::MakeMaker qw(prompt);
  41         4349432  
  41         3140  
12 41     41   20225 use File::HomeDir;
  41         211454  
  41         3008  
13 41     41   281 use File::Path;
  41         77  
  41         1951  
14 41     41   971 use File::Temp;
  41         24314  
  41         2765  
15 41     41   29889 use Getopt::Long;
  41         426236  
  41         208  
16 41     41   7890 use Path::Class;
  41         13983  
  41         2302  
17 41     41   27458 use Pod::Usage;
  41         1512624  
  41         6404  
18              
19 41     41   22085 use Module::Setup::Devel;
  41         140  
  41         1414  
20 41     41   240 use Module::Setup::Distribute;
  41         68  
  41         990  
21 41     41   16250 use Module::Setup::Path;
  41         75  
  41         1109  
22 41     41   206 use Module::Setup::Path::Flavor;
  41         55  
  41         766  
23 41     41   161 use Module::Setup::Path::Template;
  41         54  
  41         146036  
24              
25             our $HAS_TERM;
26              
27 247     247 0 742 sub argv { shift->{argv} }
28 142     142 0 465 sub config { shift->{config} }
29 1261     1261 0 8197 sub options { shift->{options} }
30 1851     1851 0 8915 sub base_dir { shift->{base_dir} }
31 416     416 0 7172 sub distribute { shift->{distribute} }
32 5     5 0 30 sub plugins_stash { shift->{plugins_stash} }
33              
34             sub new {
35 112     112 0 891 my($class, %args) = @_;
36              
37 112   100     449 $args{options} ||= +{};
38 112   100     698 $args{argv} ||= +[];
39 112         730 $args{_current_dir} = Cwd::getcwd;
40              
41 112         509 my $self = bless { %args }, $class;
42 112         618 $self->{_current_dir} = Cwd::getcwd;
43 112         265 $self->{plugins_stash} = +{};
44              
45 112         403 $self;
46             }
47              
48             sub DESTROY {
49 85     85   152 my $self = shift;
50 85 100       5887 chdir $self->{_current_dir} unless $self->{_current_dir} eq Cwd::getcwd;
51             }
52              
53             sub _setup_options_pod2usage {
54 3     3   632 pod2usage(1);
55             }
56             sub _setup_options_version {
57 1     1   534 print "module-setup v$VERSION\n";
58 1         4 exit 1;
59             }
60              
61             sub setup_options {
62 17     17 0 22 my($self, %args) = @_;
63 17         20 $Module::Setup::HAS_TERM = 1;
64              
65 17 100       32 _setup_options_pod2usage unless @ARGV;
66              
67 17         37 my $options = {};
68             GetOptions(
69             'init' => \($options->{init}),
70             'pack' => \($options->{pack}),
71             'direct' => \($options->{direct}),
72             'flavor|flavour=s' => \($options->{flavor}),
73             'flavor-class|flavour-class=s' => \($options->{flavor_class}),
74             'additional=s' => \($options->{additional}),
75             'without-additional' => \($options->{without_additional}),
76             'executable' => \($options->{executable}),
77             'plugin=s@' => \($options->{plugins}),
78             'target' => \($options->{target}),
79             'module-setup-dir' => \($options->{module_setup_dir}),
80             'devel' => \($options->{devel}),
81 17 100       158 'test' => \($options->{test}),
82             version => \&_setup_options_version,
83             help => \&_setup_options_pod2usage,
84             ) or _setup_options_pod2usage;
85              
86 17         8515 $self->{options} = $options;
87 17         30 $self->{argv} = [ @ARGV ];
88 17         31 $self;
89             }
90              
91              
92             sub _clear_triggers {
93 133     133   233 my $self = shift;
94             # reset triggers # this is bad hack
95 133         358 delete $self->{__triggers};
96 133         258 delete $self->{_class_trigger_results};
97             }
98              
99             sub _load_argv {
100 161     161   320 my($self, $name, $default) = @_;
101              
102 161 100       166 $self->options->{$name} = @{ $self->argv } ? shift @{ $self->argv } : undef;
  161         359  
  86         191  
103 161 100 100     316 if (!$self->options->{$name} && defined $default) {
104 77 100       393 $self->options->{$name} = ref($default) eq 'CODE' ? $default->() : $default;
105             }
106 161         350 $self->options->{$name};
107             }
108             sub _load_argv_module {
109 64     64   117 my $self = shift;
110 64         232 $self->_load_argv( module => '' );
111 64 100       144 Carp::croak "module name is required" unless $self->options->{module};
112 63         149 $self->options->{module};
113             }
114             sub _load_argv_flavor {
115 63     63   101 my $self = shift;
116 63     59   402 $self->_load_argv( flavor => sub { $self->select_flavor } );
  59         236  
117 63 100       274 Carp::croak "flavor name is required" unless $self->options->{flavor};
118 62         144 $self->options->{flavor};
119             }
120              
121             sub setup_base_dir {
122 97     97 0 284 my $self = shift;
123              
124 97         125 my $path;
125 97 100       225 if ($self->options->{direct}) {
126 1         4 $path = File::Temp->newdir;
127             } else {
128 96   100     206 $path = $self->options->{module_setup_dir} || $ENV{MODULE_SETUP_DIR} || Path::Class::Dir->new(File::HomeDir->my_home, '.module-setup');
129             }
130 97 100       1034 die 'module_setup directory was not able to be discovered.' unless $path;
131              
132 96         1028 $self->{base_dir} = Module::Setup::Path->new($path);
133 96 100       335 $self->base_dir->init_directories unless $self->base_dir->is_initialized;
134             }
135              
136             sub run {
137 93     93 0 178 my $self = shift;
138 93         310 my $options = $self->options;
139 93         311 $self->_clear_triggers;
140              
141 93   100     522 $options->{flavor_class} ||= 'Default';
142 93 50       290 return Module::Setup::Devel->new($self)->run if $options->{devel};
143              
144 93         327 $self->setup_base_dir;
145              
146 93 100 66     6955 if ($options->{init} || (!$options->{pack} && $options->{additional})) {
      66        
147 29         119 $self->_load_argv( flavor => 'default' );
148 29         111 return $self->create_flavor;
149             }
150              
151 64         263 $self->_load_argv_module;
152 63         195 $self->_load_argv_flavor;
153 62         151 $self->base_dir->set_flavor($options->{flavor});
154              
155 62 100 100     354 if ($options->{additional} && !-d $self->base_dir->flavor->additional->path_to($options->{additional})) {
156 1         189 Carp::croak "additional template is no exist: $options->{additional}";
157             }
158              
159 61 100       286 return $self->pack_flavor if $options->{pack};
160              
161 50 100       150 unless ($self->base_dir->flavor->is_dir) {
162 21 100       553 return unless $self->create_flavor;
163             }
164              
165 47         40964 $self->load_config;
166 47         176 $self->load_plugins;
167              
168             # create skeleton
169 47         216 $self->create_skeleton;
170 47         284 $self->call_trigger( 'after_create_skeleton' );
171              
172             # test
173 47         1635 chdir $self->distribute->dist_path;
174 47         1297 $self->call_trigger( 'check_skeleton_directory' );
175 40         4557 $self->call_trigger( 'finalize_create_skeleton' );
176 40         1798 chdir $self->{_current_dir};
177              
178 40         143 $self->call_trigger( 'finish_of_run' );
179 38         1307 $self;
180             }
181              
182              
183             sub load_config {
184 47     47 0 112 my $self = shift;
185 47         137 my $options = $self->options;
186              
187 47   100     301 my $option_plugins = delete $options->{plugins} || [];
188 47         137 my $config = $self->base_dir->flavor->config->load;
189             $config = +{
190             plugins => [],
191 47         251 %{ $config },
192 47         146250 %{ $options },
  47         432  
193             };
194 47         190 push @{ $config->{plugins} }, @{ $option_plugins };
  47         135  
  47         89  
195              
196 47         141 $self->{config} = $config;
197             }
198              
199             sub plugin_collect {
200 88     88 0 145 my $self = shift;
201              
202 88         138 my %loaded_local_plugin;
203 88         246 for my $local_plugin ( $self->base_dir->global_plugins->collect, $self->base_dir->flavor->plugins->collect ) {
204 3         28 $local_plugin->require;
205 3 100       24 if ($local_plugin->package->isa('Module::Setup::Plugin')) {
206 2         20 $loaded_local_plugin{$local_plugin->package} = $local_plugin;
207             }
208             }
209 88         781 %loaded_local_plugin;
210             }
211              
212             sub load_plugins {
213 88     88 0 156 my $self = shift;
214              
215 88         331 my %loaded_local_plugin = $self->plugin_collect;
216              
217 88   100     641 $self->{loaded_plugin} ||= +{};
218 88         145 for my $plugin (@{ $self->config->{plugins} }) {
  88         323  
219 322         370 my $pkg;
220 322         475 my $config = +{};
221 322 100       680 if (ref($plugin)) {
222 4 100       13 if (ref($plugin) eq 'HASH') {
223 2         4 $pkg = $plugin->{module};
224 2         4 $config = $plugin->{config};
225             } else {
226 2         6 next;
227             }
228             } else {
229 318         461 $pkg = $plugin;
230             }
231 320 100       1435 $pkg = "Module::Setup::Plugin::$pkg" unless $pkg =~ s/^\+//;
232              
233 320 100       837 unless ($loaded_local_plugin{$pkg}) {
234 318         19548 eval "require $pkg"; ## no critic
235 318 100       2072 Carp::croak $@ if $@;
236             }
237 319         1788 $self->{loaded_plugin}->{$pkg} = $pkg->new( context => $self, config => $config );
238             }
239             }
240              
241             sub write_file {
242 810     810 0 1167 my($self, $opts) = @_;
243 810         1051 my $path = $opts->{dist_path};
244              
245 810 100       2815 if (-e $path) {
246 58         3279 my $ans = $self->dialog("$path exists. Override? [yN] ", 'n');
247 58 100       898 return if $ans !~ /[Yy]/;
248             } else {
249 752         33100 $path->dir->mkpath;
250             }
251              
252 764         65871 my $template;
253 764 100       1564 if ($opts->{is_binary}) {
254 1         260 $template = pack 'H*', $opts->{template};
255             } else {
256 763         1120 $template = $opts->{template};
257             }
258              
259 764         2561 $self->log("Creating $path");
260 764         2176 my $out = $path->openw;
261 764         125218 $out->print($template);
262 764         7583 $out->close;
263              
264 764 100       31891 chmod oct($opts->{chmod}), $path if $opts->{chmod};
265             }
266              
267             sub install_flavor {
268 391     391 0 517 my($self, $tmpl) = @_;
269              
270 391         761 my $flavor = $self->base_dir->flavor;
271 391         1109 my $template_path = $flavor->template;
272 391 100       801 if (exists $tmpl->{additional}) {
273 21         51 $template_path = Module::Setup::Path::Template->new($flavor->additional->path, $tmpl->{additional});
274 21         55 $template_path->path->mkpath;
275             }
276              
277 391         43994 my $path;
278 391 100 66     1668 if (exists $tmpl->{file} && $tmpl->{file}) {
    100 66        
    100 66        
      100        
279 367         1943 $path = $template_path->path_to(split '/', $tmpl->{file});
280             } elsif (exists $tmpl->{dir} && $tmpl->{dir}) {
281 9         47 return Module::Setup::Path::Dir->new( $template_path->path, split('/', $tmpl->{dir}) )->mkpath;
282             } elsif (exists $tmpl->{plugin} && $tmpl->{plugin} && !exists $tmpl->{additional}) {
283 3         15 $path = $flavor->plugins->path_to(split '/', $tmpl->{plugin});
284             } else {
285 12         49 return;
286             }
287              
288             $self->write_file(+{
289             dist_path => $path,
290 370         551 %{ $tmpl },
  370         1990  
291             });
292             }
293              
294             sub _load_flavor_class {
295 50     50   98 my($self, $class) = @_;
296 50 100       313 $class = "Module::Setup::Flavor::$class" unless $class =~ s/^\+//;
297 50 100       4009 eval " require $class "; Carp::croak $@ if $@; ## no critic
  50         974  
298 49         150 $class;
299             }
300              
301             sub create_flavor {
302 50     50 0 94 my $self = shift;
303              
304 50         132 my $options = $self->options;
305 50         107 my $name = $options->{flavor};
306 50         194 my $flavor_class = $self->_load_flavor_class($options->{flavor_class});
307              
308 49         184 $self->base_dir->set_flavor($name);
309 49 100 100     229 Carp::croak "create flavor: $name exists " if $self->base_dir->flavor->is_exists && !exists $options->{additional};
310 48         1666 my $flavor = $flavor_class->new;
311 48 100       257 return unless $flavor->setup_flavor($self);
312              
313 46         249 my @template = $flavor->loader;
314 46         136 my $config = +{};
315 46         97 my $additional_config = +{};
316 46 100       227 if ($options->{additional}) {
317 5         19 $additional_config = $self->base_dir->flavor->additional->config->load;
318             }
319 46         6211 for my $tmpl (@template) {
320 427 100 100     4468 if (exists $tmpl->{config} && ref($tmpl->{config}) eq 'HASH') {
321 36         134 $config = $tmpl->{config};
322             } else {
323 391         355 my $additional;
324 391 100       1048 if (exists $tmpl->{additional}) {
    100          
325 6         12 $additional = $tmpl->{additional};
326             } elsif ($options->{additional}) {
327 15         25 $additional = $options->{additional};
328             }
329 391 100       669 local $tmpl->{additional} = $additional if $additional; ## no critic;
330 391 100       661 if ($additional) {
331 21         69 $additional_config->{$additional} = +{
332             class => $flavor_class,
333             };
334             }
335 391         879 $self->install_flavor($tmpl);
336             }
337             }
338 46         637 $config->{class} = $flavor_class;
339              
340 46         224 $self->base_dir->flavor->additional->path->mkpath;
341 46         5906 $self->base_dir->flavor->additional->config->dump($additional_config);
342              
343 46 100       234593 if ($options->{additional}) {
344 5         63 $flavor->setup_additional($self, $config);
345 5         71 return 1;
346             }
347              
348 41         201 $self->base_dir->flavor->plugins->path->mkpath;
349 41         7030 $self->base_dir->flavor->template->path->mkpath;
350              
351 41 100 66     1990 if (exists $options->{plugins} && $options->{plugins} && @{ $options->{plugins} }) {
  7   100     40  
352 6   100     26 $config->{plugins} ||= [];
353 6         9 push @{ $config->{plugins} }, @{ delete $options->{plugins} };
  6         16  
  6         15  
354             }
355 41   100     196 $config->{plugins} ||= [];
356              
357 41         540 $flavor->setup_config($self, $config);
358              
359             # load plugins
360             local $self->{config} = +{
361 41         160 %{ $config },
362 41         410 %{ $options },
363             plugins => $config->{plugins},
364 41         73 };
365 41         231 $self->load_plugins;
366              
367 40         236 $self->call_trigger( before_dump_config => $config );
368              
369 40         1055 $self->_clear_triggers;
370              
371 40         136 $self->base_dir->flavor->config->dump($config);
372             }
373              
374             sub create_skeleton {
375 47     47 0 92 my $self = shift;
376 47         138 my $config = $self->config;
377              
378             $self->{distribute} = Module::Setup::Distribute->new(
379             $config->{module},
380             target => $config->{target},
381 47         480 );
382 47         208 $self->call_trigger( 'after_setup_module_attribute' );
383 47         2058 $self->distribute->dist_path->mkpath;
384              
385             my $template_vars = {
386             module => $self->distribute->module,
387             dist => $self->distribute->dist_name,
388             module_path => $self->distribute->module_path,
389 47         8575 module_unix_path => join('/', @{ $self->distribute->package }),
  47         116  
390             config => $config,
391             distribute => $self->distribute,
392             localtime => scalar localtime,
393             };
394 47         231 $self->call_trigger( after_setup_template_vars => $template_vars);
395 47         1895 $self->{distribute}->set_template_vars($template_vars);
396              
397 47         172 for my $path ($self->base_dir->flavor->template->find_files) {
398 429         21647 $self->{distribute}->install_template($self, $path);
399             }
400 47         4137 $self->call_trigger( 'append_template_file' );
401              
402 47         899 return $template_vars;
403             }
404              
405             sub _collect_flavor_files {
406 26     26   56 my($self, $template, $path_name, $type) = @_;
407              
408 26         82 my $base_path = $type->path;
409 26         131 for my $file ($type->find_files) {
410 101 100       424 my @path = $file->is_dir ? $file->dir_list : ($file->dir->dir_list, $file->basename);
411 101         3506 while ($path[0] eq '.') { shift @path };
  48         118  
412              
413 101 100       218 if ($file->is_dir) {
414 5         16 push @{ $template }, +{
  5         31  
415             dir => join('/', @path),
416             };
417             } else {
418 96         419 my $body = $type->path_to($file)->slurp;
419 96         16797 my $tmpl = +{
420             $path_name => join('/', @path),
421             template => $body,
422             };
423 96 100       308 if (-B $type->path_to($file)) {
424 1         158 $tmpl->{template} = unpack 'H*', $body;
425 1         5 $tmpl->{is_binary} = 1;
426             }
427 96         5996 push @{ $template }, $tmpl;
  96         419  
428             }
429             }
430             }
431              
432             sub pack_flavor {
433 11     11 0 22 my $self = shift;
434 11         31 my $config = $self->options;
435 11         24 my $module = $config->{module};
436 11         24 my $flavor = $config->{flavor};
437              
438 11         136 my $template = [];
439 11 50       54 my $flavor_dir = exists $config->{flavor_dir} ? Module::Setup::Path::Flavor->new( $config->{flavor_dir} ) : $self->base_dir->flavor;
440 11         45 $self->_collect_flavor_files($template, file => $flavor_dir->template);
441 11         239 $self->_collect_flavor_files($template, plugin => $flavor_dir->plugins);
442 11         1757 push @{ $template }, +{
  11         84  
443             config => YAML::LoadFile($flavor_dir->config->path),
444             };
445              
446 11 100       35552 unless ($config->{without_additional}) {
447 10 100       48 $template = [] if $config->{additional};
448 10         77 for my $additional ( $flavor_dir->additional->path->children ) {
449 19 100       4422 next unless $additional->is_dir;
450 6         30 my $name = $additional->dir_list(-1);
451 6 100 100     86 next if $config->{additional} && $name ne $config->{additional};
452 4         19 my $base_path = Module::Setup::Path::Template->new($flavor_dir->additional->path, $name);
453              
454 4         9 my $templates = [];
455 4         16 $self->_collect_flavor_files($templates, file => $base_path);
456 4 100       30 if ($config->{additional}) {
457 2         3 push @{ $template }, @{ $templates };
  2         2  
  2         10  
458             } else {
459 2         3 push @{ $template }, map { $_->{additional} = $name; $_ } @{ $templates };
  2         6  
  4         8  
  4         21  
  2         5  
460             }
461             }
462             }
463              
464 11         118 my $executable_code = '';
465 11 50       46 if ($config->{executable}) {
466 0         0 $executable_code = <
467             #!/bin/env perl
468             package main;
469             use strict;
470             use warnings;
471             use Module::Setup;
472              
473             my \$msetup = Module::Setup->new;
474             \$msetup->setup_options;
475             \$msetup->options->{direct} = 1;
476             \$msetup->options->{flavor_class} = '+$config->{module}';
477             {
478             no warnings 'redefine';
479             *Module::Setup::_load_flavor_class = sub { '$config->{module}' };
480             \$msetup->run;
481             }
482             EXECUTABLE__
483             }
484              
485 11         25 my $eq = '=';
486 11         18 my $yaml = YAML::Dump(@{ $template });
  11         59  
487 11         80091 $self->stdout(<
488             $executable_code
489             package $module;
490             use strict;
491             use warnings;
492             use base 'Module::Setup::Flavor';
493             1;
494              
495             ${eq}head1
496              
497             $module - pack from $flavor
498              
499             ${eq}head1 SYNOPSIS
500              
501             $ module-setup --init --flavor-class=+$module new_flavor
502              
503             ${eq}cut
504              
505             \__DATA__
506              
507             $yaml
508             FLAVOR__
509             }
510              
511             sub select_flavor {
512 55     55 0 94 my $self = shift;
513 55 100       148 return 'default' if $self->options->{direct};
514 54 100       149 return 'default' if $self->base_dir->flavors->path->children == 0;
515              
516 35         12429 my @flavors;
517 35         247 for my $flavor ( $self->base_dir->flavors->path->children ) {
518 38 100       9450 next unless $flavor->is_dir;
519 37         288 my $name = $flavor->dir_list(-1);
520 37 100       590 ($name eq 'default') ? unshift @flavors, $name : push @flavors, $name;
521             }
522 35 100       254 return $flavors[0] if @flavors == 1;
523              
524 1         2 my $num = 1;
525 1         108 my $message;
526 1         2 for my $flavor (@flavors) {
527 3         10 $message .= sprintf "[%d]: %s\n", $num++, $flavor;
528             }
529              
530 1         2 my $selected;
531             $self->dialog( "${message}Select flavor:", 1, sub {
532 5     5   5 my($self, $ret) = @_;
533 5 100       22 return unless $ret =~ /^[0-9]+$/;
534 2         12 $selected = $flavors[ $ret - 1 ];
535 1         8 } );
536 1         9 $self->log("You chose flavor: $selected");
537 1         13 return $selected;
538             }
539              
540             sub stdout {
541 2     2 0 15 my($self, $msg) = @_;
542 2 100       10 print STDOUT "$msg\n" if $HAS_TERM;
543             }
544             sub log {
545 1215     1215 0 30703 my($self, $msg) = @_;
546 1215 100       3315 print STDERR "$msg\n" if $HAS_TERM;
547             }
548             sub dialog {
549 84     84 0 129 my($self, $msg, $default, $validator_callback) = @_;
550 84 100       384 return $default unless $HAS_TERM;
551 4         5 while (1) {
552 9         19 my $ret = prompt($msg, $default);
553 9 100 100     63 return $ret unless $validator_callback && ref($validator_callback) eq 'CODE';
554 7 100       12 return $ret if $validator_callback->($self, $ret);
555             }
556             }
557              
558             sub system {
559 2     2 0 35 my($self, @args) = @_;
560 2         8048 CORE::system(@args);
561             }
562              
563             1;
564             __END__