File Coverage

blib/lib/Module/Setup.pm
Criterion Covered Total %
statement 346 347 99.7
branch 125 128 97.6
condition 58 59 98.3
subroutine 52 52 100.0
pod 0 24 0.0
total 581 610 95.2


line stmt bran cond sub pod time code
1             package Module::Setup;
2              
3 47     47   293 use strict;
  47         94  
  47         1759  
4 47     47   255 use warnings;
  47         97  
  47         1507  
5 47     47   1970 use 5.008001;
  47         196  
  47         3664  
6             our $VERSION = '0.09';
7              
8 47     47   264 use Carp ();
  47         93  
  47         949  
9 47     47   75139 use Class::Trigger;
  47         93938  
  47         322  
10 47     47   2871 use Cwd ();
  47         137  
  47         1452  
11 47     47   96845 use ExtUtils::MakeMaker qw(prompt);
  47         6581880  
  47         4354  
12 47     47   83063 use File::HomeDir;
  47         516548  
  47         6157  
13 47     47   473 use File::Path;
  47         99  
  47         3001  
14 47     47   2615 use File::Temp;
  47         31862  
  47         3786  
15 47     47   80730 use Getopt::Long;
  47         898813  
  47         337  
16 47     47   82770 use Pod::Usage;
  47         3662066  
  47         8421  
17              
18 47     47   36142 use Module::Setup::Devel;
  47         205  
  47         1668  
19 47     47   316 use Module::Setup::Distribute;
  47         97  
  47         1017  
20 47     47   29703 use Module::Setup::Path;
  47         136  
  47         2561  
21 47     47   286 use Module::Setup::Path::Flavor;
  47         94  
  47         1114  
22 47     47   245 use Module::Setup::Path::Template;
  47         81  
  47         279713  
23              
24             our $HAS_TERM;
25              
26 266     266 0 1093 sub argv { shift->{argv} }
27 161     161 0 804 sub config { shift->{config} }
28 1367     1367 0 12360 sub options { shift->{options} }
29 2036     2036 0 26404 sub base_dir { shift->{base_dir} }
30 575     575 0 10532 sub distribute { shift->{distribute} }
31 5     5 0 49 sub plugins_stash { shift->{plugins_stash} }
32              
33             sub new {
34 119     119 0 1090 my($class, %args) = @_;
35              
36 119   100     754 $args{options} ||= +{};
37 119   100     583 $args{argv} ||= +[];
38              
39 119         1208 my $self = bless { %args }, $class;
40 119         2524 $self->{_current_dir} = Cwd::getcwd;
41 119         666 $self->{plugins_stash} = +{};
42              
43 119         570 $self;
44             }
45              
46             sub DESTROY {
47 89     89   237 my $self = shift;
48 89 100       12217 chdir $self->{_current_dir} unless $self->{_current_dir} eq Cwd::getcwd;
49             }
50              
51             sub _setup_options_pod2usage {
52 3     3   1015 pod2usage(1);
53             }
54             sub _setup_options_version {
55 1     1   921 print "module-setup v$VERSION\n";
56 1         5 exit 1;
57             }
58              
59             sub setup_options {
60 17     17 0 39 my($self, %args) = @_;
61 17         36 $Module::Setup::HAS_TERM = 1;
62              
63 17 100       57 _setup_options_pod2usage unless @ARGV;
64              
65 17         64 my $options = {};
66 17 100       365 GetOptions(
67             'init' => \($options->{init}),
68             'pack' => \($options->{pack}),
69             'direct' => \($options->{direct}),
70             'flavor|flavour=s' => \($options->{flavor}),
71             'flavor-class|flavour-class=s' => \($options->{flavor_class}),
72             'additional=s' => \($options->{additional}),
73             'without-additional' => \($options->{without_additional}),
74             'executable' => \($options->{executable}),
75             'plugin=s@' => \($options->{plugins}),
76             'target' => \($options->{target}),
77             'module-setup-dir' => \($options->{module_setup_dir}),
78             'devel' => \($options->{devel}),
79             'test' => \($options->{test}),
80             version => \&_setup_options_version,
81             help => \&_setup_options_pod2usage,
82             ) or _setup_options_pod2usage;
83              
84 17         17877 $self->{options} = $options;
85 17         185 $self->{argv} = [ @ARGV ];
86 17         60 $self;
87             }
88              
89              
90             sub _clear_triggers {
91 146     146   327 my $self = shift;
92             # reset triggers # this is bad hack
93 146         1050 delete $self->{__triggers};
94 146         628 delete $self->{_class_trigger_results};
95             }
96              
97             sub _load_argv {
98 174     174   438 my($self, $name, $default) = @_;
99              
100 174 100       261 $self->options->{$name} = @{ $self->argv } ? shift @{ $self->argv } : undef;
  174         530  
  92         280  
101 174 100 100     556 if (!$self->options->{$name} && defined $default) {
102 84 100       457 $self->options->{$name} = ref($default) eq 'CODE' ? $default->() : $default;
103             }
104 174         466 $self->options->{$name};
105             }
106             sub _load_argv_module {
107 70     70   159 my $self = shift;
108 70         313 $self->_load_argv( module => '' );
109 70 100       198 Carp::croak "module name is required" unless $self->options->{module};
110 69         320 $self->options->{module};
111             }
112             sub _load_argv_flavor {
113 69     69   154 my $self = shift;
114 69     65   569 $self->_load_argv( flavor => sub { $self->select_flavor } );
  65         513  
115 69 100       467 Carp::croak "flavor name is required" unless $self->options->{flavor};
116 68         214 $self->options->{flavor};
117             }
118              
119             sub setup_base_dir {
120 104     104 0 363 my $self = shift;
121              
122 104         327 my $path;
123 104 100       313 if ($self->options->{direct}) {
124 4         19 $path = File::Temp->newdir;
125             } else {
126 100   100     298 $path = $self->options->{module_setup_dir} || $ENV{MODULE_SETUP_DIR} || Module::Setup::Path::Dir->new(File::HomeDir->my_home, '.module-setup');
127             }
128 104 100       2613 die 'module_setup directory was not able to be discovered.' unless $path;
129              
130 103         1394 $self->{base_dir} = Module::Setup::Path->new($path);
131 103 100       549 $self->base_dir->init_directories unless $self->base_dir->is_initialized;
132             }
133              
134             sub run {
135 100     100 0 236 my $self = shift;
136 100         776 my $options = $self->options;
137 100         672 $self->_clear_triggers;
138              
139 100   100     855 $options->{flavor_class} ||= 'Default';
140 100 50       365 return Module::Setup::Devel->new($self)->run if $options->{devel};
141              
142 100         621 $self->setup_base_dir;
143              
144 100 100 100     7740 if ($options->{init} || (!$options->{pack} && $options->{additional})) {
      66        
145 30         460 $self->_load_argv( flavor => 'default' );
146 30         170 return $self->create_flavor;
147             }
148              
149 70         367 $self->_load_argv_module;
150 69         319 $self->_load_argv_flavor;
151 68         213 $self->base_dir->set_flavor($options->{flavor});
152              
153 68 100 100     563 if ($options->{additional} && !-d $self->base_dir->flavor->additional->path_to($options->{additional})) {
154 1         232 Carp::croak "additional template is no exist: $options->{additional}";
155             }
156              
157 67 100       339 return $self->pack_flavor if $options->{pack};
158              
159 56 100       235 unless ($self->base_dir->flavor->is_dir) {
160 26 100       178 return unless $self->create_flavor;
161             }
162              
163 53         75131 $self->load_config;
164 53         294 $self->load_plugins;
165              
166             # create skeleton
167 53         328 $self->create_skeleton;
168 53         258 $self->call_trigger( 'after_create_skeleton' );
169              
170             # test
171 53         2945 chdir $self->distribute->dist_path;
172 53         310 $self->call_trigger( 'check_skeleton_directory' );
173 46         9470 $self->call_trigger( 'finalize_create_skeleton' );
174 46         3462 chdir $self->{_current_dir};
175              
176 46         220 $self->call_trigger( 'finish_of_run' );
177 44         2651 $self;
178             }
179              
180              
181             sub load_config {
182 53     53 0 133 my $self = shift;
183 53         225 my $options = $self->options;
184              
185 53   100     422 my $option_plugins = delete $options->{plugins} || [];
186 53         221 my $config = $self->base_dir->flavor->config->load;
187 53         339 $config = +{
188             plugins => [],
189 53         794 %{ $config },
190 53         385399 %{ $options },
191             };
192 53         280 push @{ $config->{plugins} }, @{ $option_plugins };
  53         191  
  53         185  
193              
194 53         307 $self->{config} = $config;
195             }
196              
197             sub plugin_collect {
198 100     100 0 203 my $self = shift;
199              
200 100         223 my %loaded_local_plugin;
201 100         397 for my $local_plugin ( $self->base_dir->global_plugins->collect, $self->base_dir->flavor->plugins->collect ) {
202 3         34 $local_plugin->require;
203 3 100       31 if ($local_plugin->package->isa('Module::Setup::Plugin')) {
204 2         29 $loaded_local_plugin{$local_plugin->package} = $local_plugin;
205             }
206             }
207 100         1150 %loaded_local_plugin;
208             }
209              
210             sub load_plugins {
211 100     100 0 306 my $self = shift;
212              
213 100         637 my %loaded_local_plugin = $self->plugin_collect;
214              
215 100   100     845 $self->{loaded_plugin} ||= +{};
216 100         222 for my $plugin (@{ $self->config->{plugins} }) {
  100         450  
217 363         654 my $pkg;
218 363         819 my $config = +{};
219 363 100       1035 if (ref($plugin)) {
220 4 100       15 if (ref($plugin) eq 'HASH') {
221 2         7 $pkg = $plugin->{module};
222 2         8 $config = $plugin->{config};
223             } else {
224 2         8 next;
225             }
226             } else {
227 359         686 $pkg = $plugin;
228             }
229 361 100       1851 $pkg = "Module::Setup::Plugin::$pkg" unless $pkg =~ s/^\+//;
230              
231 361 100       3338 unless ($loaded_local_plugin{$pkg}) {
232 359         36808 eval "require $pkg"; ## no critic
233 359 100       4212 Carp::croak $@ if $@;
234             }
235 360         3999 $self->{loaded_plugin}->{$pkg} = $pkg->new( context => $self, config => $config );
236             }
237             }
238              
239             sub write_file {
240 883     883 0 1792 my($self, $opts) = @_;
241 883         1584 my $path = $opts->{dist_path};
242              
243 883 100       4332 if (-e $path) {
244 58         4221 my $ans = $self->dialog("$path exists. Override? [yN] ", 'n');
245 58 100       851 return if $ans !~ /[Yy]/;
246             } else {
247 825         83866 $path->dir->mkpath;
248             }
249              
250 837         437197 my $template;
251 837 100       3586 if ($opts->{is_binary}) {
252 1         246 $template = pack 'H*', $opts->{template};
253             } else {
254 836         1817 $template = $opts->{template};
255             }
256              
257 837         4037 $self->log("Creating $path");
258 837         3610 my $out = $path->openw;
259 837         9600 $out->print($template);
260 837         12943 $out->close;
261              
262 837 100       93622 chmod oct($opts->{chmod}), $path if $opts->{chmod};
263             }
264              
265             sub install_flavor {
266 427     427 0 660 my($self, $tmpl) = @_;
267              
268 427         1142 my $flavor = $self->base_dir->flavor;
269 427         1899 my $template_path = $flavor->template;
270 427 100       1134 if (exists $tmpl->{additional}) {
271 21         70 $template_path = Module::Setup::Path::Template->new($flavor->additional->path, $tmpl->{additional});
272 21         68 $template_path->path->mkpath;
273             }
274              
275 427         673 my $path;
276 427 100 100     3007 if (exists $tmpl->{file} && $tmpl->{file}) {
    100 100        
    100 100        
      100        
277 403         2499 $path = $template_path->path_to(split '/', $tmpl->{file});
278             } elsif (exists $tmpl->{dir} && $tmpl->{dir}) {
279 9         53 return Module::Setup::Path::Dir->new( $template_path->path, split('/', $tmpl->{dir}) )->mkpath;
280             } elsif (exists $tmpl->{plugin} && $tmpl->{plugin} && !exists $tmpl->{additional}) {
281 3         16 $path = $flavor->plugins->path_to(split '/', $tmpl->{plugin});
282             } else {
283 12         79 return;
284             }
285              
286 406         2976 $self->write_file(+{
287             dist_path => $path,
288 406         1153 %{ $tmpl },
289             });
290             }
291              
292             sub _load_flavor_class {
293 56     56   132 my($self, $class) = @_;
294 56 100       379 $class = "Module::Setup::Flavor::$class" unless $class =~ s/^\+//;
295 56 100       8471 eval " require $class "; Carp::croak $@ if $@; ## no critic
  56         1469  
296 55         229 $class;
297             }
298              
299             sub create_flavor {
300 56     56 0 125 my $self = shift;
301              
302 56         194 my $options = $self->options;
303 56         157 my $name = $options->{flavor};
304 56         273 my $flavor_class = $self->_load_flavor_class($options->{flavor_class});
305              
306 55         510 $self->base_dir->set_flavor($name);
307 55 100 100     407 Carp::croak "create flavor: $name exists " if $self->base_dir->flavor->is_exists && !exists $options->{additional};
308 54         837 my $flavor = $flavor_class->new;
309 54 100       459 return unless $flavor->setup_flavor($self);
310              
311 52         422 my @template = $flavor->loader;
312 52         262 my $config = +{};
313 52         152 my $additional_config = +{};
314 52 100       299 if ($options->{additional}) {
315 5         26 $additional_config = $self->base_dir->flavor->additional->config->load;
316             }
317 52         8620 for my $tmpl (@template) {
318 466 100 100     2044 if (exists $tmpl->{config} && ref($tmpl->{config}) eq 'HASH') {
319 39         290 $config = $tmpl->{config};
320             } else {
321 427         537 my $additional;
322 427 100       1546 if (exists $tmpl->{additional}) {
    100          
323 6         17 $additional = $tmpl->{additional};
324             } elsif ($options->{additional}) {
325 15         36 $additional = $options->{additional};
326             }
327 427 100       883 local $tmpl->{additional} = $additional if $additional; ## no critic;
328 427 100       886 if ($additional) {
329 21         83 $additional_config->{$additional} = +{
330             class => $flavor_class,
331             };
332             }
333 427         1391 $self->install_flavor($tmpl);
334             }
335             }
336 52         208 $config->{class} = $flavor_class;
337              
338 52         244 $self->base_dir->flavor->additional->path->mkpath;
339 52         306 $self->base_dir->flavor->additional->config->dump($additional_config);
340              
341 52 100       531081 if ($options->{additional}) {
342 5         66 $flavor->setup_additional($self, $config);
343 5         91 return 1;
344             }
345              
346 47         338 $self->base_dir->flavor->plugins->path->mkpath;
347 47         268 $self->base_dir->flavor->template->path->mkpath;
348              
349 47 100 100     437 if (exists $options->{plugins} && $options->{plugins} && @{ $options->{plugins} }) {
  10   100     67  
350 9   100     59 $config->{plugins} ||= [];
351 9         17 push @{ $config->{plugins} }, @{ delete $options->{plugins} };
  9         22  
  9         48  
352             }
353 47   100     306 $config->{plugins} ||= [];
354              
355 47         725 $flavor->setup_config($self, $config);
356              
357             # load plugins
358 47         196 local $self->{config} = +{
359 47         711 %{ $config },
360 47         124 %{ $options },
361             plugins => $config->{plugins},
362             };
363 47         299 $self->load_plugins;
364              
365 46         640 $self->call_trigger( before_dump_config => $config );
366              
367 46         1625 $self->_clear_triggers;
368              
369 46         228 $self->base_dir->flavor->config->dump($config);
370             }
371              
372             sub create_skeleton {
373 53     53 0 126 my $self = shift;
374 53         214 my $config = $self->config;
375              
376 53         695 $self->{distribute} = Module::Setup::Distribute->new(
377             $config->{module},
378             target => $config->{target},
379             );
380 53         316 $self->call_trigger( 'after_setup_module_attribute' );
381 53         3018 $self->distribute->dist_path->mkpath;
382              
383 53         380 my $template_vars = {
384             module => $self->distribute->module,
385             dist => $self->distribute->dist_name,
386             module_path => $self->distribute->module_path,
387 53         383 module_unix_path => join('/', @{ $self->distribute->package }),
388             config => $config,
389             distribute => $self->distribute,
390             localtime => scalar localtime,
391 53         265 moniker => $self->distribute->package->[ scalar(@{ $self->distribute->package })-1 ],
392             };
393 53         356 $self->call_trigger( after_setup_template_vars => $template_vars);
394 53         3529 $self->{distribute}->set_template_vars($template_vars);
395              
396 53         239 for my $path ($self->base_dir->flavor->template->find_files) {
397 465         3543 $self->{distribute}->install_template($self, $path);
398             }
399 53         10088 $self->call_trigger( 'append_template_file' );
400              
401 53         1880 return $template_vars;
402             }
403              
404             sub _collect_flavor_files {
405 26     26   80 my($self, $template, $path_name, $type) = @_;
406              
407 26         115 my $base_path = $type->path;
408 26         174 for my $file ($type->find_files) {
409 101 100       559 my @path = $file->is_dir ? $file->dir_list : ($file->dir->dir_list, $file->basename);
410 101         7230 while ($path[0] eq '.') { shift @path };
  48         142  
411              
412 101 100       301 if ($file->is_dir) {
413 5         21 push @{ $template }, +{
  5         38  
414             dir => join('/', @path),
415             };
416             } else {
417 96         559 my $body = $type->path_to($file)->slurp;
418 96         6014 my $tmpl = +{
419             $path_name => join('/', @path),
420             template => $body,
421             };
422 96 100       389 if (-B $type->path_to($file)) {
423 1         293 $tmpl->{template} = unpack 'H*', $body;
424 1         6 $tmpl->{is_binary} = 1;
425             }
426 96         23181 push @{ $template }, $tmpl;
  96         2580  
427             }
428             }
429             }
430              
431             sub pack_flavor {
432 11     11 0 23 my $self = shift;
433 11         48 my $config = $self->options;
434 11         41 my $module = $config->{module};
435 11         30 my $flavor = $config->{flavor};
436              
437 11         23 my $template = [];
438 11 50       66 my $flavor_dir = exists $config->{flavor_dir} ? Module::Setup::Path::Flavor->new( $config->{flavor_dir} ) : $self->base_dir->flavor;
439 11         63 $self->_collect_flavor_files($template, file => $flavor_dir->template);
440 11         805 $self->_collect_flavor_files($template, plugin => $flavor_dir->plugins);
441 11         2297 push @{ $template }, +{
  11         84  
442             config => YAML::LoadFile($flavor_dir->config->path),
443             };
444              
445 11 100       59316 unless ($config->{without_additional}) {
446 10 100       55 $template = [] if $config->{additional};
447 10         103 for my $additional ( $flavor_dir->additional->path->children ) {
448 19 100       1625 next unless $additional->is_dir;
449 6         40 my $name = $additional->dir_list(-1);
450 6 100 100     114 next if $config->{additional} && $name ne $config->{additional};
451 4         18 my $base_path = Module::Setup::Path::Template->new($flavor_dir->additional->path, $name);
452              
453 4         13 my $templates = [];
454 4         20 $self->_collect_flavor_files($templates, file => $base_path);
455 4 100       43 if ($config->{additional}) {
456 2         6 push @{ $template }, @{ $templates };
  2         4  
  2         22  
457             } else {
458 2         4 push @{ $template }, map { $_->{additional} = $name; $_ } @{ $templates };
  2         3  
  4         9  
  4         20  
  2         5  
459             }
460             }
461             }
462              
463 11         127 my $executable_code = '';
464 11 50       56 if ($config->{executable}) {
465 0         0 $executable_code = <
466             #!/bin/env perl
467             package main;
468             use strict;
469             use warnings;
470             use Module::Setup;
471              
472             my \$msetup = Module::Setup->new;
473             \$msetup->setup_options;
474             \$msetup->options->{direct} = 1;
475             \$msetup->options->{flavor_class} = '+$config->{module}';
476             {
477             no warnings 'redefine';
478             *Module::Setup::_load_flavor_class = sub { '$config->{module}' };
479             \$msetup->run;
480             }
481             EXECUTABLE__
482             }
483              
484 11         29 my $eq = '=';
485 11         22 my $yaml = YAML::Dump(@{ $template });
  11         71  
486 11         130609 $self->stdout(<
487             $executable_code
488             package $module;
489             use strict;
490             use warnings;
491             use base 'Module::Setup::Flavor';
492             1;
493              
494             ${eq}head1
495              
496             $module - pack from $flavor
497              
498             ${eq}head1 SYNOPSIS
499              
500             $ module-setup --init --flavor-class=+$module new_flavor
501              
502             ${eq}cut
503              
504             \__DATA__
505              
506             $yaml
507             FLAVOR__
508             }
509              
510             sub select_flavor {
511 61     61 0 131 my $self = shift;
512 61 100       185 return 'default' if $self->options->{direct};
513 57 100       212 return 'default' if $self->base_dir->flavors->path->children == 0;
514              
515 36         9891 my @flavors;
516 36         155 for my $flavor ( $self->base_dir->flavors->path->children ) {
517 39 100       2672 next unless $flavor->is_dir;
518 38         443 my $name = $flavor->dir_list(-1);
519 38 100       1046 ($name eq 'default') ? unshift @flavors, $name : push @flavors, $name;
520             }
521 36 100       525 return $flavors[0] if @flavors == 1;
522              
523 1         2 my $num = 1;
524 1         2 my $message;
525 1         3 for my $flavor (@flavors) {
526 3         12 $message .= sprintf "[%d]: %s\n", $num++, $flavor;
527             }
528              
529 1         3 my $selected;
530             $self->dialog( "${message}Select flavor:", 1, sub {
531 5     5   7 my($self, $ret) = @_;
532 5 100       20 return unless $ret =~ /^[0-9]+$/;
533 2         8 $selected = $flavors[ $ret - 1 ];
534 1         8 } );
535 1         8 $self->log("You chose flavor: $selected");
536 1         22 return $selected;
537             }
538              
539             sub stdout {
540 2     2 0 17 my($self, $msg) = @_;
541 2 100       12 print STDOUT "$msg\n" if $HAS_TERM;
542             }
543             sub log {
544 1345     1345 0 30344 my($self, $msg) = @_;
545 1345 100       4910 print STDERR "$msg\n" if $HAS_TERM;
546             }
547             sub dialog {
548 89     89 0 192 my($self, $msg, $default, $validator_callback) = @_;
549 89 100       550 return $default unless $HAS_TERM;
550 4         7 while (1) {
551 9         25 my $ret = prompt($msg, $default);
552 9 100 100     76 return $ret unless $validator_callback && ref($validator_callback) eq 'CODE';
553 7 100       15 return $ret if $validator_callback->($self, $ret);
554             }
555             }
556              
557             sub system {
558 2     2 0 39 my($self, @args) = @_;
559 2         11490 CORE::system(@args);
560             }
561              
562             sub shell {
563 2     2 0 12 my($self, $cmd) = @_;
564 2         24063 `$cmd`;
565             }
566              
567             1;
568             __END__