use strict; use warnings; use utf8; package Amon2::Setup::Flavor; use Text::Xslate; use File::Spec; use File::Basename; use File::Path (); use Amon2; use Plack::Util (); use Carp (); my $xslate = Text::Xslate->new( syntax => 'TTerse', type => 'text', tag_start => '<%', tag_end => '%>', 'module' => [ 'Text::Xslate::Bridge::TT2Like' ], ); sub infof { my $caller = do { my $x; for (1..10) { $x = caller($_); last if $x ne __PACKAGE__; } $x; }; $caller =~ s/^Amon2::Setup:://; print "[$caller] "; @_==1 ? print(@_) : printf(@_); print "\n"; } sub new { my $class = shift; my %args = @_ ==1 ? %{$_[0]} : @_; $args{amon2_version} = $Amon2::VERSION; for (qw/module/) { die "Missing mandatory parameter $_" unless exists $args{$_}; } $args{module} =~ s!-!::!g; # $module = "Foo::Bar" # $dist = "Foo-Bar" # $path = "Foo/Bar" my @pkg = split /::/, $args{module}; $args{dist} = join "-", @pkg; $args{path} = join "/", @pkg; bless { %args }, $class; } sub run { die "This is abstract base method" } sub mkpath { my ($self, $path) = @_; Carp::croak("path should not be ref") if ref $path; infof("mkpath: $path"); File::Path::mkpath($path); } sub render_string { my $self = shift; my $template = shift; my %args = @_==1 ? %{$_[0]} : @_; return $xslate->render_string($template, {%$self, %args}); } sub write_file { my ($self, $filename, $template) = (shift, shift, shift); Carp::croak("filename should not be reference") if ref $filename; $filename =~ s/<<([^>]+)>>/$self->{lc($1)} or die "$1 is not defined. But you want to use $1 in filename."/ge; my $content = $self->render_string($template, @_); $self->write_file_raw($filename, $content); } sub write_file_raw { my ($self, $filename, $content) = @_; Carp::croak("filename should not be reference") if ref $filename; infof("writing $filename"); my $dirname = dirname($filename); File::Path::mkpath($dirname) if $dirname; open my $ofh, '>:utf8', $filename or die "Cannot open file: $filename: $!"; print {$ofh} $content; close $ofh; } sub load_asset { my ($self, $asset) = @_; my $klass = Plack::Util::load_class($asset, 'Amon2::Setup::Asset'); my $require_newline = $self->{tags} ? 1 : 0; $self->{tags} .= $klass->tags; $self->{tags} .= "\n" if $require_newline; # $klass->run($self); } sub write_asset { my ($self, $asset, $base) = @_; $asset || die "Missing asset name"; $base ||= 'static/'; my $klass = Plack::Util::load_class($asset, 'Amon2::Setup::Asset'); my $files = $klass->files; while (my ($fname, $content) = each %$files) { $self->write_file_raw("$base/$fname", $content); } } 1; __END__ =head1 NAME Amon2::Setup::Flavor - Abstract base class for flavors. =head1 DESCRIPTION This is an abstract base class for flavors. But you don't need to inherit this class. Amon2 uses duck typing. You should implement only C<< Class->run >> method. In Amon2, flavor means setup script. =head1 METHODS This class provides some useful methods to write setup script. =over 4 =item $flavor->init() Hook point to initialize module directory. =item $flavor->mkpath($dir) same as C<< `mkdir -p $dir` >>. =item $flavor->write_file($fnametmpl, $template) C<< $fnametmpl >> will be replace with the parameters. Generate file using L. For more details, read the source Luke! Or please write docs... =item $flavor->write_file_raw($fname, $content) Write C<< $content >> to the C<< $fname >>. =back