| 1 | package Module::Setup; |
|---|
| 2 | |
|---|
| 3 | use strict; |
|---|
| 4 | use warnings; |
|---|
| 5 | our $VERSION = '0.03'; |
|---|
| 6 | |
|---|
| 7 | use Carp (); |
|---|
| 8 | use Class::Trigger; |
|---|
| 9 | use ExtUtils::MakeMaker qw(prompt); |
|---|
| 10 | use Fcntl qw( :mode ); |
|---|
| 11 | use File::Basename; |
|---|
| 12 | use File::Find::Rule; |
|---|
| 13 | use File::Path; |
|---|
| 14 | use File::Temp; |
|---|
| 15 | use Getopt::Long; |
|---|
| 16 | use Module::Collect; |
|---|
| 17 | use Path::Class; |
|---|
| 18 | use Pod::Usage; |
|---|
| 19 | use YAML (); |
|---|
| 20 | |
|---|
| 21 | our $HAS_TERM; |
|---|
| 22 | |
|---|
| 23 | sub new { |
|---|
| 24 | my $class = shift; |
|---|
| 25 | bless {}, $class; |
|---|
| 26 | } |
|---|
| 27 | |
|---|
| 28 | sub log { |
|---|
| 29 | my($self, $msg) = @_; |
|---|
| 30 | print STDERR "$msg\n" if $HAS_TERM; |
|---|
| 31 | } |
|---|
| 32 | sub dialog { |
|---|
| 33 | my($self, $msg, $default) = @_; |
|---|
| 34 | return $default unless $HAS_TERM; |
|---|
| 35 | prompt($msg, $default); |
|---|
| 36 | } |
|---|
| 37 | |
|---|
| 38 | sub _clear_triggers { |
|---|
| 39 | my $self = shift; |
|---|
| 40 | # reset triggers # this is bad hack |
|---|
| 41 | delete $self->{__triggers}; |
|---|
| 42 | delete $self->{_class_trigger_results}; |
|---|
| 43 | } |
|---|
| 44 | |
|---|
| 45 | sub run { |
|---|
| 46 | my($self, $options, $argv) = @_; |
|---|
| 47 | $self->_clear_triggers; |
|---|
| 48 | |
|---|
| 49 | $options ||= $self->setup_options; |
|---|
| 50 | my @argv = defined $argv && ref($argv) eq 'ARRAY' ? @{ $argv } : @ARGV; |
|---|
| 51 | |
|---|
| 52 | $options->{flavor_class} ||= 'Default'; |
|---|
| 53 | |
|---|
| 54 | # create flavor |
|---|
| 55 | if ($options->{init}) { |
|---|
| 56 | $options->{flavor} = shift @argv if @argv; |
|---|
| 57 | $options->{flavor} ||= 'default'; |
|---|
| 58 | return $self->create_flavor($options); |
|---|
| 59 | } |
|---|
| 60 | |
|---|
| 61 | # create module |
|---|
| 62 | $options->{module} = shift @argv; |
|---|
| 63 | $options->{flavor} = shift @argv if @argv; |
|---|
| 64 | $options->{flavor} ||= $self->select_flavor; |
|---|
| 65 | |
|---|
| 66 | if ($options->{pack}) { |
|---|
| 67 | #pack flavor template |
|---|
| 68 | return $self->pack_flavor($options); |
|---|
| 69 | } |
|---|
| 70 | |
|---|
| 71 | |
|---|
| 72 | $self->{module_setup_dir} ||= $options->{module_setup_dir}; |
|---|
| 73 | $self->{module_setup_dir} = File::Temp->newdir if $options->{direct}; |
|---|
| 74 | |
|---|
| 75 | unless ( -d $self->module_setup_dir('flavors') && -d $self->module_setup_dir('flavors', $options->{flavor}) ) { |
|---|
| 76 | # setup the module-setup directory |
|---|
| 77 | $self->create_flavor($options); |
|---|
| 78 | $self->_create_directory( dir => $self->module_setup_dir('plugins') ); |
|---|
| 79 | } |
|---|
| 80 | |
|---|
| 81 | my $config = $self->load_config($options); |
|---|
| 82 | $self->load_plugins($config); |
|---|
| 83 | |
|---|
| 84 | # create skeleton |
|---|
| 85 | my $attributes = $self->create_skeleton($config); |
|---|
| 86 | return unless $attributes; |
|---|
| 87 | $self->call_trigger( after_create_skeleton => $attributes ); |
|---|
| 88 | |
|---|
| 89 | # test |
|---|
| 90 | chdir Path::Class::Dir->new( @{ $attributes->{module_attribute}->{dist_path} } ); |
|---|
| 91 | $self->call_trigger( check_skeleton_directory => $attributes ); |
|---|
| 92 | |
|---|
| 93 | $self->call_trigger( finalize_create_skeleton => $attributes ); |
|---|
| 94 | } |
|---|
| 95 | |
|---|
| 96 | sub setup_options { |
|---|
| 97 | my $self = shift; |
|---|
| 98 | |
|---|
| 99 | pod2usage(2) unless @ARGV; |
|---|
| 100 | |
|---|
| 101 | my $options = {}; |
|---|
| 102 | GetOptions( |
|---|
| 103 | 'init' => \($options->{init}), |
|---|
| 104 | 'pack' => \($options->{pack}), |
|---|
| 105 | 'direct' => \($options->{direct}), |
|---|
| 106 | 'flavor=s' => \($options->{flavor}), |
|---|
| 107 | 'flavor-class=s' => \($options->{flavor_class}), |
|---|
| 108 | 'plugin=s@' => \($options->{plugins}), |
|---|
| 109 | 'target' => \($options->{target}), |
|---|
| 110 | module_setup_dir => \($options->{module_setup_dir}), |
|---|
| 111 | version => sub { |
|---|
| 112 | print "module-setup v$VERSION\n"; |
|---|
| 113 | exit 1; |
|---|
| 114 | }, |
|---|
| 115 | help => sub { pod2usage(1); }, |
|---|
| 116 | ) or pod2usage(2); |
|---|
| 117 | |
|---|
| 118 | $options; |
|---|
| 119 | } |
|---|
| 120 | |
|---|
| 121 | sub load_config { |
|---|
| 122 | my($self, $options) = @_; |
|---|
| 123 | |
|---|
| 124 | $options->{plugins} ||= []; |
|---|
| 125 | my @option_plugins = @{ delete $options->{plugins} }; |
|---|
| 126 | |
|---|
| 127 | my $config = YAML::LoadFile( $self->module_setup_dir('flavors', $options->{flavor}, 'config.yaml') ); |
|---|
| 128 | $config = +{ |
|---|
| 129 | %{ $config }, |
|---|
| 130 | %{ $options }, |
|---|
| 131 | }; |
|---|
| 132 | |
|---|
| 133 | $config->{plugins} ||= []; |
|---|
| 134 | push @{ $config->{plugins} }, @option_plugins; |
|---|
| 135 | |
|---|
| 136 | $config; |
|---|
| 137 | } |
|---|
| 138 | |
|---|
| 139 | sub plugin_collect { |
|---|
| 140 | my($self, $config) = @_; |
|---|
| 141 | |
|---|
| 142 | my @local_plugins; |
|---|
| 143 | push @local_plugins, @{ Module::Collect->new( path => $self->module_setup_dir('plugins') )->modules }; |
|---|
| 144 | push @local_plugins, @{ Module::Collect->new( path => $self->module_setup_dir('flavors', $config->{flavor}, 'plugins') )->modules }; |
|---|
| 145 | my %loaded_local_plugin; |
|---|
| 146 | for my $local_plugin (@local_plugins) { |
|---|
| 147 | $local_plugin->require; |
|---|
| 148 | if ($local_plugin->package->isa('Module::Setup::Plugin')) { |
|---|
| 149 | $loaded_local_plugin{$local_plugin->package} = $local_plugin; |
|---|
| 150 | } |
|---|
| 151 | } |
|---|
| 152 | %loaded_local_plugin; |
|---|
| 153 | } |
|---|
| 154 | |
|---|
| 155 | sub load_plugins { |
|---|
| 156 | my($self, $config) = @_; |
|---|
| 157 | |
|---|
| 158 | my %loaded_local_plugin = $self->plugin_collect($config); |
|---|
| 159 | |
|---|
| 160 | my %loaded_plugin; |
|---|
| 161 | for my $plugin (@{ $config->{plugins} }) { |
|---|
| 162 | my $pkg; |
|---|
| 163 | my $config = +{}; |
|---|
| 164 | if (ref($plugin)) { |
|---|
| 165 | if (ref($plugin) eq 'HASH') { |
|---|
| 166 | $pkg = $plugin->{module}; |
|---|
| 167 | $config = $plugin->{config}; |
|---|
| 168 | } else { |
|---|
| 169 | next; |
|---|
| 170 | } |
|---|
| 171 | } else { |
|---|
| 172 | $pkg = $plugin; |
|---|
| 173 | } |
|---|
| 174 | $pkg = "Module::Setup::Plugin::$pkg" unless $pkg =~ s/^\+//; |
|---|
| 175 | |
|---|
| 176 | unless ($loaded_local_plugin{$pkg}) { |
|---|
| 177 | eval "require $pkg"; ## no critic |
|---|
| 178 | Carp::croak $@ if $@; |
|---|
| 179 | } |
|---|
| 180 | $loaded_plugin{$pkg} = $pkg->new( context => $self, config => $config ); |
|---|
| 181 | } |
|---|
| 182 | } |
|---|
| 183 | |
|---|
| 184 | sub module_setup_dir { |
|---|
| 185 | my($self, @path) = @_; |
|---|
| 186 | my $base = $self->{module_setup_dir} || $ENV{MODULE_SETUP_DIR} || do { |
|---|
| 187 | eval { require File::HomeDir }; |
|---|
| 188 | my $home = $@ ? $ENV{HOME} : File::HomeDir->my_home; |
|---|
| 189 | Path::Class::Dir->new( $home, '.module-setup' ); |
|---|
| 190 | }; |
|---|
| 191 | |
|---|
| 192 | if (@path) { |
|---|
| 193 | my $new_base = Path::Class::Dir->new( $base, @path ); |
|---|
| 194 | $new_base = Path::Class::File->new( $base, @path ) unless -d $base; |
|---|
| 195 | $base = $new_base; |
|---|
| 196 | } |
|---|
| 197 | $base; |
|---|
| 198 | } |
|---|
| 199 | |
|---|
| 200 | sub create_directory { |
|---|
| 201 | my $self = shift; |
|---|
| 202 | $self->_create_directory(@_); |
|---|
| 203 | } |
|---|
| 204 | sub _create_directory { |
|---|
| 205 | my($self, %opts) = @_; |
|---|
| 206 | my $dir = $opts{dir} || File::Basename::dirname($opts{file}); |
|---|
| 207 | unless (-e $dir) { |
|---|
| 208 | $self->log("Creating directory $dir"); |
|---|
| 209 | $dir = $dir->stringify if ref($dir) && $dir->can('stringify'); |
|---|
| 210 | File::Path::mkpath($dir, 0, 0777); ## no critic |
|---|
| 211 | } |
|---|
| 212 | } |
|---|
| 213 | |
|---|
| 214 | sub write_file { |
|---|
| 215 | my($self, $opts) = @_; |
|---|
| 216 | my $path = $opts->{dist_path}; |
|---|
| 217 | |
|---|
| 218 | if (-e $path) { |
|---|
| 219 | my $ans = $self->dialog("$path exists. Override? [yN] ", 'n'); |
|---|
| 220 | return if $ans !~ /[Yy]/; |
|---|
| 221 | } |
|---|
| 222 | |
|---|
| 223 | $self->_create_directory( file => $path ); |
|---|
| 224 | |
|---|
| 225 | $self->log("Creating $path"); |
|---|
| 226 | open my $out, ">", $path or die "$path: $!"; |
|---|
| 227 | print $out $opts->{template}; |
|---|
| 228 | close $out; |
|---|
| 229 | |
|---|
| 230 | chmod oct($opts->{chmod}), $path if $opts->{chmod}; |
|---|
| 231 | } |
|---|
| 232 | |
|---|
| 233 | sub install_flavor { |
|---|
| 234 | my($self, $name, $tmpl) = @_; |
|---|
| 235 | |
|---|
| 236 | my $path = (exists $tmpl->{plugin} && $tmpl->{plugin}) ? |
|---|
| 237 | $self->module_setup_dir( 'flavors', $name, 'plugins', $tmpl->{plugin} ) : |
|---|
| 238 | $self->module_setup_dir( 'flavors', $name, 'template', $tmpl->{file} ); |
|---|
| 239 | $self->write_file(+{ |
|---|
| 240 | dist_path => $path, |
|---|
| 241 | %{ $tmpl }, |
|---|
| 242 | }); |
|---|
| 243 | } |
|---|
| 244 | |
|---|
| 245 | sub write_template { |
|---|
| 246 | my($self, $options) = @_; |
|---|
| 247 | |
|---|
| 248 | $self->call_trigger( template_process => $options ); |
|---|
| 249 | $options->{template} = delete $options->{content} unless $options->{template}; |
|---|
| 250 | $options->{dist_path} =~ s/____var-(.+)-var____/$options->{vars}->{$1} || $options->{vars}->{config}->{$1}/eg; |
|---|
| 251 | |
|---|
| 252 | push @{ $self->{install_files} }, $options->{dist_path}; |
|---|
| 253 | $self->write_file($options); |
|---|
| 254 | } |
|---|
| 255 | |
|---|
| 256 | sub install_template { |
|---|
| 257 | my($self, $base, $path, $vars, $module_attribute) = @_; |
|---|
| 258 | |
|---|
| 259 | my $src = Path::Class::File->new($base, $path); |
|---|
| 260 | my $dist = Path::Class::File->new(@{ $module_attribute->{dist_path} }, $path); |
|---|
| 261 | |
|---|
| 262 | my $mode = ( stat $src )[2]; |
|---|
| 263 | $mode = sprintf "%03o", S_IMODE($mode); |
|---|
| 264 | |
|---|
| 265 | open my $fh, '<', $src or die "$src: $!"; |
|---|
| 266 | my $template = do { local $/; <$fh> }; |
|---|
| 267 | close $fh; |
|---|
| 268 | |
|---|
| 269 | my $options = { |
|---|
| 270 | dist_path => $dist, |
|---|
| 271 | template => $template, |
|---|
| 272 | chmod => $mode, |
|---|
| 273 | vars => $vars, |
|---|
| 274 | content => undef, |
|---|
| 275 | }; |
|---|
| 276 | $self->write_template($options); |
|---|
| 277 | } |
|---|
| 278 | |
|---|
| 279 | sub create_flavor { |
|---|
| 280 | my($self, $options) = @_; |
|---|
| 281 | $options ||= +{}; |
|---|
| 282 | my $name = $options->{flavor}; |
|---|
| 283 | my $class = $options->{flavor_class}; |
|---|
| 284 | |
|---|
| 285 | $class = "Module::Setup::Flavor::$class" unless $class =~ s/^\+//; |
|---|
| 286 | |
|---|
| 287 | Carp::croak "create flavor: $name exists " if -d $self->module_setup_dir('flavors', $name); |
|---|
| 288 | eval " require $class "; Carp::croak $@ if $@; ## no critic |
|---|
| 289 | |
|---|
| 290 | my @template = $class->loader; |
|---|
| 291 | my $config = +{}; |
|---|
| 292 | for my $tmpl (@template) { |
|---|
| 293 | if (exists $tmpl->{config} && ref($tmpl->{config}) eq 'HASH') { |
|---|
| 294 | $config = $tmpl->{config}; |
|---|
| 295 | } else { |
|---|
| 296 | $self->install_flavor($name, $tmpl); |
|---|
| 297 | } |
|---|
| 298 | } |
|---|
| 299 | |
|---|
| 300 | # plugins |
|---|
| 301 | $self->_create_directory( dir => $self->module_setup_dir('flavors', $name, 'plugins') ); |
|---|
| 302 | |
|---|
| 303 | if (exists $options->{plugins} && $options->{plugins} && @{ $options->{plugins} }) { |
|---|
| 304 | $config->{plugins} ||= []; |
|---|
| 305 | push @{ $config->{plugins} }, @{ $options->{plugins} }; |
|---|
| 306 | } |
|---|
| 307 | $config->{plugins} ||= []; |
|---|
| 308 | |
|---|
| 309 | # load plugins |
|---|
| 310 | $self->load_plugins(+{ |
|---|
| 311 | %{ $config }, |
|---|
| 312 | %{ $options }, |
|---|
| 313 | plugins => $config->{plugins}, |
|---|
| 314 | }); |
|---|
| 315 | |
|---|
| 316 | $self->call_trigger( befor_dump_config => $config ); |
|---|
| 317 | |
|---|
| 318 | $self->_clear_triggers; |
|---|
| 319 | |
|---|
| 320 | # save config |
|---|
| 321 | YAML::DumpFile($self->module_setup_dir('flavors', $name, 'config.yaml'), $config); |
|---|
| 322 | } |
|---|
| 323 | |
|---|
| 324 | sub _find_flavor_template { |
|---|
| 325 | my($self, $config) = @_; |
|---|
| 326 | my $module = $config->{module}; |
|---|
| 327 | my $flavor = $config->{flavor}; |
|---|
| 328 | |
|---|
| 329 | Carp::croak "module name is required" unless $module; |
|---|
| 330 | |
|---|
| 331 | my $flavor_path = $self->module_setup_dir('flavors', $flavor); |
|---|
| 332 | Carp::croak "No such flavor: $flavor" unless -d $flavor_path; |
|---|
| 333 | |
|---|
| 334 | my @files = File::Find::Rule->new->file->relative->in( $self->module_setup_dir('flavors', $flavor, 'template') ); |
|---|
| 335 | Carp::croak "No such flavor template files: $flavor" unless @files; |
|---|
| 336 | @files; |
|---|
| 337 | } |
|---|
| 338 | |
|---|
| 339 | sub create_skeleton { |
|---|
| 340 | my($self, $config) = @_; |
|---|
| 341 | $config ||= +{}; |
|---|
| 342 | $self->{install_files} = []; |
|---|
| 343 | |
|---|
| 344 | my @files = $self->_find_flavor_template($config); |
|---|
| 345 | |
|---|
| 346 | my $module = $config->{module}; |
|---|
| 347 | my $flavor = $config->{flavor}; |
|---|
| 348 | |
|---|
| 349 | my @pkg = split /::/, $module; |
|---|
| 350 | my $module_attribute = +{ |
|---|
| 351 | module => $module, |
|---|
| 352 | package => \@pkg, |
|---|
| 353 | dist_name => join('-', @pkg), |
|---|
| 354 | dist_path => [ join('-', @pkg) ], |
|---|
| 355 | }; |
|---|
| 356 | if (exists $config->{target} && $config->{target}) { |
|---|
| 357 | unshift @{ $module_attribute->{dist_path} }, $config->{target}; |
|---|
| 358 | } |
|---|
| 359 | $self->call_trigger( after_setup_module_attribute => $module_attribute); |
|---|
| 360 | |
|---|
| 361 | $self->create_directory( dir => Path::Class::Dir->new(@{ $module_attribute->{dist_path} }) ); |
|---|
| 362 | |
|---|
| 363 | my $template_vars = { |
|---|
| 364 | module => $module_attribute->{module}, |
|---|
| 365 | dist => $module_attribute->{dist_name}, |
|---|
| 366 | module_path => join('/', @{ $module_attribute->{package} }), |
|---|
| 367 | config => $config, |
|---|
| 368 | localtime => scalar localtime, |
|---|
| 369 | }; |
|---|
| 370 | $self->call_trigger( after_setup_template_vars => $template_vars); |
|---|
| 371 | |
|---|
| 372 | my $base = $self->module_setup_dir('flavors', $flavor, 'template'); |
|---|
| 373 | for my $path (@files) { |
|---|
| 374 | $self->install_template($base, $path, $template_vars, $module_attribute); |
|---|
| 375 | } |
|---|
| 376 | $self->call_trigger( append_template_file => $template_vars, $module_attribute); |
|---|
| 377 | |
|---|
| 378 | return +{ |
|---|
| 379 | module_attribute => $module_attribute, |
|---|
| 380 | template_vars => $template_vars, |
|---|
| 381 | install_files => $self->{install_files}, |
|---|
| 382 | }; |
|---|
| 383 | } |
|---|
| 384 | |
|---|
| 385 | sub pack_flavor { |
|---|
| 386 | my($self, $config) = @_; |
|---|
| 387 | my $module = $config->{module}; |
|---|
| 388 | my $flavor = $config->{flavor}; |
|---|
| 389 | |
|---|
| 390 | my @template_files = $self->_find_flavor_template($config); |
|---|
| 391 | |
|---|
| 392 | my @plugin_files = File::Find::Rule->new->file->relative->in( $self->module_setup_dir('flavors', $flavor, 'plugins') ); |
|---|
| 393 | |
|---|
| 394 | my @template; |
|---|
| 395 | for my $conf ( |
|---|
| 396 | { type => 'template', files => \@template_files }, |
|---|
| 397 | { type => 'plugins' , files => \@plugin_files }, |
|---|
| 398 | { type => 'config' , files =>['config.yaml'] }, |
|---|
| 399 | ) { |
|---|
| 400 | my $base_path; |
|---|
| 401 | if ($conf->{type} eq 'config') { |
|---|
| 402 | $base_path = $self->module_setup_dir('flavors', $flavor); |
|---|
| 403 | } else { |
|---|
| 404 | $base_path = $self->module_setup_dir('flavors', $flavor, $conf->{type}); |
|---|
| 405 | } |
|---|
| 406 | |
|---|
| 407 | for my $file (@{ $conf->{files} }) { |
|---|
| 408 | my $path = Path::Class::File->new($base_path, $file); |
|---|
| 409 | |
|---|
| 410 | if ($conf->{type} eq 'config') { |
|---|
| 411 | my $data = YAML::LoadFile($path); |
|---|
| 412 | push @template, +{ |
|---|
| 413 | config => $data |
|---|
| 414 | }; |
|---|
| 415 | } else { |
|---|
| 416 | open my $fh, '<', $path or die "$path: $!"; |
|---|
| 417 | my $data = do { local $/; <$fh> }; |
|---|
| 418 | close $fh; |
|---|
| 419 | my $path_name = $conf->{type} eq 'template' ? 'file' : 'plugin'; |
|---|
| 420 | push @template, +{ |
|---|
| 421 | $path_name => $file, |
|---|
| 422 | template => $data, |
|---|
| 423 | }; |
|---|
| 424 | } |
|---|
| 425 | } |
|---|
| 426 | } |
|---|
| 427 | |
|---|
| 428 | my $eq = '='; |
|---|
| 429 | my $yaml = YAML::Dump(@template); |
|---|
| 430 | print <<END; |
|---|
| 431 | package $module; |
|---|
| 432 | use strict; |
|---|
| 433 | use warnings; |
|---|
| 434 | use base 'Module::Setup::Flavor'; |
|---|
| 435 | 1; |
|---|
| 436 | |
|---|
| 437 | ${eq}head1 |
|---|
| 438 | |
|---|
| 439 | $module - pack from $flavor |
|---|
| 440 | |
|---|
| 441 | ${eq}head1 SYNOPSIS |
|---|
| 442 | |
|---|
| 443 | $ module-setup --init --flavor-class=+$module new_flavor |
|---|
| 444 | |
|---|
| 445 | ${eq}cut |
|---|
| 446 | |
|---|
| 447 | __DATA__ |
|---|
| 448 | |
|---|
| 449 | $yaml |
|---|
| 450 | END |
|---|
| 451 | } |
|---|
| 452 | |
|---|
| 453 | sub select_flavor { |
|---|
| 454 | my $self = shift; |
|---|
| 455 | return 'default' unless -d $self->module_setup_dir('flavors'); |
|---|
| 456 | |
|---|
| 457 | my $num = 1; |
|---|
| 458 | my @flavors; |
|---|
| 459 | for my $flavor ( $self->module_setup_dir('flavors')->children ) { |
|---|
| 460 | next unless $flavor->is_dir; |
|---|
| 461 | |
|---|
| 462 | my $name = $flavor->dir_list(-1); |
|---|
| 463 | push @flavors, $name; |
|---|
| 464 | |
|---|
| 465 | $self->log( sprintf "[%d]: %s", $num++, $name ); |
|---|
| 466 | } |
|---|
| 467 | |
|---|
| 468 | my $selected = $self->dialog( 'Select flavor:', 1 ) || 1; |
|---|
| 469 | $flavors[ $selected - 1 ] || 'default'; |
|---|
| 470 | } |
|---|
| 471 | |
|---|
| 472 | 1; |
|---|
| 473 | __END__ |
|---|
| 474 | |
|---|
| 475 | =head1 NAME |
|---|
| 476 | |
|---|
| 477 | Module::Setup - a simple module maker "yet another Module::Start(?:er)?" |
|---|
| 478 | |
|---|
| 479 | =head1 SYNOPSIS |
|---|
| 480 | |
|---|
| 481 | simply use |
|---|
| 482 | |
|---|
| 483 | $ module-setup Foo::Bar |
|---|
| 484 | |
|---|
| 485 | make flavor |
|---|
| 486 | |
|---|
| 487 | $ module-setup --init catalyst-action # create a "catalyst actions" flavor |
|---|
| 488 | |
|---|
| 489 | edit for flavor |
|---|
| 490 | |
|---|
| 491 | $ cd ~/.module-setup/flavor/catalyst-action/template && some files edit for catalyst action templates |
|---|
| 492 | |
|---|
| 493 | use flavor |
|---|
| 494 | |
|---|
| 495 | $ module-setup Foo catalyst-action # create to Catalyst::Action::Foo module |
|---|
| 496 | |
|---|
| 497 | redistribute pack for flavor |
|---|
| 498 | |
|---|
| 499 | $ module-setup --pack MyFlavorCatalystAction catalyst-action > MyFlavorCatalystAction.pm |
|---|
| 500 | |
|---|
| 501 | using redistributed flavor |
|---|
| 502 | |
|---|
| 503 | $ module-setup --direct --flavor-class=+MyFlavorCatalystAction New::Class |
|---|
| 504 | |
|---|
| 505 | importing redistributed flavor |
|---|
| 506 | |
|---|
| 507 | $ module-setup --init --flavor-class=+MyFlavorCatalystAction new_flavor |
|---|
| 508 | |
|---|
| 509 | for git |
|---|
| 510 | |
|---|
| 511 | $ module-setup --plugin=VC::Git Foo::Bar # or edit your ~/.module-setup/flavor/foo/config.yaml |
|---|
| 512 | |
|---|
| 513 | |
|---|
| 514 | =head1 DESCRIPTION |
|---|
| 515 | |
|---|
| 516 | Module::Setup is very simply module start kit. |
|---|
| 517 | |
|---|
| 518 | When the module-setup command is executed first, a necessary template for ~/.module-setup directory is copied. |
|---|
| 519 | |
|---|
| 520 | =head1 What's difference Module::Setup and Module::Starter? |
|---|
| 521 | |
|---|
| 522 | L<Module::Starter> is very useful module. However customize of module template is complex. |
|---|
| 523 | |
|---|
| 524 | If L<Module::Sterter::PBP> is used, do you solve it? |
|---|
| 525 | |
|---|
| 526 | Yes, but switch of two or more templates is complex. |
|---|
| 527 | |
|---|
| 528 | If Module::Setup is used, switch of template flavor is easy. |
|---|
| 529 | |
|---|
| 530 | flavor customized uniquely becomes the form which can be redistributed by "module-setup --pack". |
|---|
| 531 | |
|---|
| 532 | if incorporating Module::Setup in your application, you can make Helper which is well alike of Catalyst::Helper. |
|---|
| 533 | |
|---|
| 534 | =head1 Example For Incorporating |
|---|
| 535 | |
|---|
| 536 | use Module::Setup; |
|---|
| 537 | my $pmsetup = Module::Setup->new; |
|---|
| 538 | local $ENV{MODULE_SETUP_DIR} = '/tmp/module-setup'; # dont use ~/.module-setup directory |
|---|
| 539 | my $options = { |
|---|
| 540 | # see setup_options method |
|---|
| 541 | }; |
|---|
| 542 | $pmsetup->run($options, [qw/ New::Module foo_flavor /]); # create New::Module module with foo_flavor flavor |
|---|
| 543 | |
|---|
| 544 | =head1 AUTHOR |
|---|
| 545 | |
|---|
| 546 | Kazuhiro Osawa E<lt>ko@yappo.ne.jpE<gt> |
|---|
| 547 | |
|---|
| 548 | =head1 SEE ALSO |
|---|
| 549 | |
|---|
| 550 | L<Module::Setup::Plugin>, L<module-setup> |
|---|
| 551 | |
|---|
| 552 | this module's base code is pmsetup written by Tatsuhiko Miyagawa. |
|---|
| 553 | |
|---|
| 554 | some pmsetup scripts are in a L<http://svn.coderepos.org/share/lang/perl/misc/pmsetup> |
|---|
| 555 | |
|---|
| 556 | =head1 REPOSITORY |
|---|
| 557 | |
|---|
| 558 | svn co http://svn.coderepos.org/share/lang/perl/Module-Setup/trunk Module-Setup |
|---|
| 559 | |
|---|
| 560 | Module::Setup is Subversion repository is hosted at L<http://coderepos.org/share/>. |
|---|
| 561 | patches and collaborators are welcome. |
|---|
| 562 | |
|---|
| 563 | =head1 LICENSE |
|---|
| 564 | |
|---|
| 565 | This library is free software; you can redistribute it and/or modify |
|---|
| 566 | it under the same terms as Perl itself. |
|---|
| 567 | |
|---|
| 568 | =cut |
|---|