Template-Resolver
view release on metacpan or search on metacpan
- Resolved https://rt.cpan.org/Ticket/Display.html?id=116437 Fails with perls < 5.8.9
- Updated tests to use Log::Any instead of Log::Log4perl
1.12 2016-07-23
- Added option for supplying additional transforms to the resolver constructor
1.11 2016-07-01
- Removed double slash from path in destination file
1.10 2016-07-01
- Added the ability to supply a resolver to the overlay method to augment the
default behavior
1.09 2016-03-31
- Remove file if it exists before processing template to ensure proper
permissions are propagated.
1.08 2016-03-31
- Added O_TRUNC to sysopen to ensure file is truncated if it already exists
during template resolution.
1.07 2016-03-31
- Fixed overlay to maintain file permissions. Will use permission from existing
files, both base and template, augmented by the current umask.
1.06 2016-03-23
- Fixed: Overlay fails if template is in a directory that does not exist in base
(https://github.com/lucastheisen/template-resolver/issues/1)
1.05 2016-01-25
- Added overlays
1.04 2016-01-23
- Add support for any blessed entity to be treated as if it were a hashref
1.03 2016-01-22
- Added support for multi-line placeholder
1.02 2015-12-05
- Fixed deprecated regex syntax
lib/Template/Resolver.pm
lib/Template/Transformer.pm
t/Overlay.t
t/Resolver.t
t/author-critic.t
t/author-pod-coverage.t
t/author-pod-syntax.t
t/base/a.txt
t/base/c.txt
t/base/subdir/b.txt
t/overlay1/subdir/b.txt
t/overlay2/a.txt
weaver.ini
lib/Template/Overlay.pm view on Meta::CPAN
return bless( {}, shift )->_init(@_);
}
sub _init {
my ( $self, $base, $resolver, %options ) = @_;
$self->{base} = File::Spec->rel2abs($base);
$self->{resolver} = $resolver;
$self->{key} = $options{key};
$logger->debug( 'new overlay [', $self->{base}, ']' );
return $self;
}
sub _overlay_files {
my ( $self, $overlays ) = @_;
my %overlay_files = ();
foreach my $overlay ( ref($overlays) eq 'ARRAY' ? @$overlays : ($overlays) ) {
$overlay = File::Spec->rel2abs($overlay);
my $base_path_length = length($overlay);
find(
sub {
if ( -f $File::Find::name && $_ !~ /~$/ && $_ !~ /^\..+\.swp$/ ) {
my $relative = _relative_path( $File::Find::name, $base_path_length );
$overlay_files{$relative} = $File::Find::name;
}
},
$overlay
);
}
return %overlay_files;
}
sub overlay {
my ( $self, $overlays, %options ) = @_;
my %overlay_files = $self->_overlay_files($overlays);
my $destination = $self->{base};
if ( $options{to} && $options{to} ne $self->{base} ) {
$destination = File::Spec->rel2abs( $options{to} );
my $base_path_length = length( File::Spec->rel2abs( $self->{base} ) );
find(
sub {
my $relative = _relative_path( $File::Find::name, $base_path_length );
if ( -d $File::Find::name ) {
make_path( File::Spec->catdir( $destination, $relative ) );
}
if ( -f $File::Find::name ) {
my $template = delete( $overlay_files{$relative} );
my $file = File::Spec->catfile( $destination, $relative );
if ($template) {
$self->_resolve( $template, $file, $options{resolver} );
}
else {
copy( $_, $file );
}
}
},
$self->{base}
);
}
foreach my $relative ( keys(%overlay_files) ) {
my $file = File::Spec->catfile( $destination, $relative );
make_path( ( File::Spec->splitpath($file) )[1] );
$self->_resolve( $overlay_files{$relative}, $file, $options{resolver} );
}
}
sub _relative_path {
my ( $path, $base_path_length ) = @_;
return
length($path) == $base_path_length
? ''
: substr( $File::Find::name, $base_path_length + 1 );
}
lib/Template/Overlay.pm view on Meta::CPAN
=head1 VERSION
version 1.16
=head1 SYNOPSIS
use Template::Overlay;
use Template::Resolver;
my $overlay_me = Template::Overlay->new(
'/path/to/base/folder',
Template->Resolver->new($entity),
key => 'REPLACEME');
$overlay_me->overlay(
['/path/to/template/base','/path/to/another/template/base'],
to => '/path/to/processed');
=head1 DESCRIPTION
This provides the ability ot overlay a set of files with a set of resolved templates.
It uses L<Template::Resolver> to resolve each file.
=head1 CONSTRUCTORS
=head2 new($base, $resolver, [%options])
Creates a new overlay processor for the files in C<$base> using C<$resolver> to process
the template files. The available options are:
=over 4
=item key
The template key used by C<Template::Resolver-E<lt>resolve>.
=back
=head1 METHODS
=head2 overlay($overlays, [%options])
Overlays the C<$base> directory (specified in the constructor) with the resolved
templates from the directories in C<$overlays>. C<$overlays> can be either a path,
or an array reference containing paths. If multiple C<$overlays> contain the same
template, the last one in the array will take precedence. The available options are:
=over 4
=item resolver
A callback, that if specified, will be called for each template file found. It will
be called with two arguments: the first is the path to the template file, the second
is the path to the destination file. If the callback returns a I<falsey> value,
then it is assumed that the supplied callbac decided not to process this file and
processing will proceed as normal. Otherwise, it is assumed that the callback
handled processing of the file, so the default processing will be skipped.
=item to
If specified, the files in C<$base> will not be not be modified. Rather, they will
be copied to the path specified by C<$to> and the overlays will be processed on top
of that directory.
=back
=head1 AUTHOR
Lucas Theisen <lucastheisen@pastdev.com>
=head1 COPYRIGHT AND LICENSE
t/Overlay.t view on Meta::CPAN
my $test_dir = dirname( File::Spec->rel2abs($0) );
sub test_dir {
return File::Spec->catdir( $test_dir, @_ );
}
sub test_file {
return File::Spec->catfile( $test_dir, @_ );
}
sub overlay {
my ( $config, $overlays, $no_base, %options ) = @_;
my $dir = File::Temp->newdir(); #returns object
Template::Overlay->new(
$no_base ? $dir->dirname() : test_dir('base'),
Template::Resolver->new($config),
key => 'T'
)->overlay( $overlays, to => $dir->dirname(), %options );
my %results = ();
find(
sub {
if ( -f $File::Find::name && $File::Find::name =~ /^\Q$dir\E\/(.*)$/ ) {
$results{$1} = do { local ( @ARGV, $/ ) = $_; <> };
}
},
$dir
);
return \%results;
t/Overlay.t view on Meta::CPAN
sub slurp {
my ($file) = @_;
return do { local ( @ARGV, $/ ) = $file; <> };
}
my $config = {
what => { this => { 'is' => 'im not sure' } },
todays => { random => { thought => 'something awesome' } }
};
my $results = overlay( $config, test_dir('overlay1') );
like( $results->{'a.txt'}, qr/This is a test\.(?:\r|\n|\r\n)/, 'overlay1 a.txt' );
like(
$results->{'subdir/b.txt'},
qr/Random thought for today is: something awesome(?:\r|\n|\r\n)/,
'overlay1 subdir/b.txt'
);
like( $results->{'c.txt'}, qr/Another file full of nonsense\.(?:\r|\n|\r\n)/, 'overlay1 c.txt' );
$config = {
what => { this => { 'is' => 'im not sure' } },
todays => { random => { thought => 'something awesome' } }
};
$results = overlay( $config, test_dir('overlay2') );
like( $results->{'a.txt'}, qr/This is a im not sure\.(?:\r|\n|\r\n)/, 'overlay2 a.txt' );
like(
$results->{'subdir/b.txt'},
qr/Random thought for today is: fumanchu\.(?:\r|\n|\r\n)/,
'overlay2 subdir/b.txt'
);
like( $results->{'c.txt'}, qr/Another file full of nonsense\.(?:\r|\n|\r\n)/, 'overlay2 c.txt' );
$config = {
what => { this => { 'is' => 'im not sure' } },
todays => { random => { thought => 'something awesome' } }
};
$results = overlay( $config, [ test_dir('overlay1'), test_dir('overlay2') ] );
like( $results->{'a.txt'}, qr/This is a im not sure\.(?:\r|\n|\r\n)/, 'overlay1,overlay2 a.txt' );
like(
$results->{'subdir/b.txt'},
qr/Random thought for today is: something awesome(?:\r|\n|\r\n)/,
'overlay1,overlay2 subdir/b.txt'
);
like(
$results->{'c.txt'},
qr/Another file full of nonsense\.(?:\r|\n|\r\n)/,
'overlay1,overlay2 c.txt'
);
$config = {
what => { this => { 'is' => 'im not sure' } },
todays => { random => { thought => 'something awesome' } }
};
$results = overlay( $config, [ test_dir('overlay2'), test_dir('overlay1') ] );
like( $results->{'a.txt'}, qr/This is a im not sure\.(?:\r|\n|\r\n)/, 'overlay2,overlay1 a.txt' );
like(
$results->{'subdir/b.txt'},
qr/Random thought for today is: something awesome(?:\r|\n|\r\n)/,
'overlay2,overlay1 subdir/b.txt'
);
like(
$results->{'c.txt'},
qr/Another file full of nonsense\.(?:\r|\n|\r\n)/,
'overlay2,overlay1 c.txt'
);
$results = overlay( $config, test_dir('overlay1'), 1 );
like(
$results->{'subdir/b.txt'},
qr/Random thought for today is: something awesome(?:\r|\n|\r\n)/,
'overlay1 subdir/b.txt no base'
);
{
my $callback_called;
$results = overlay(
$config,
test_dir('overlay1'),
1,
resolver => sub {
my ( $template, $file ) = @_;
$callback_called = 1;
spurt( "foo", $file );
return 1;
}
);
ok( $callback_called, 'callback called, processing stopped' );
is( $results->{'subdir/b.txt'}, 'foo', 'callback overlay1 subdir/b.txt no base' );
$callback_called = 0;
$results = overlay(
$config,
test_dir('overlay1'),
1,
resolver => sub {
my ( $template, $file ) = @_;
$callback_called = 1;
return 0;
}
);
ok( $callback_called, 'callback called, processing proceeded' );
like(
$results->{'subdir/b.txt'},
qr/Random thought for today is: something awesome(?:\r|\n|\r\n)/,
'callback override overlay1 subdir/b.txt no base'
);
}
{
my $temp_dir = File::Temp->newdir();
my $base_dir = File::Spec->catdir( $temp_dir, 'base' );
mkdir($base_dir);
my $template_dir = File::Spec->catdir( $temp_dir, 'template' );
mkdir($template_dir);
my $overlay_dir = File::Spec->catdir( $temp_dir, 'overlay' );
mkdir($overlay_dir);
my $base_file = File::Spec->catfile( $base_dir, 'file.txt' );
spurt( "foo", $base_file );
chmod( 0644, $base_file );
my $template_file = File::Spec->catfile( $template_dir, 'file.txt' );
spurt( "bar", $template_file );
chmod( 0755, $template_file );
my $old_umask = umask(0027);
eval {
Template::Overlay->new( $base_dir, Template::Resolver->new( {} ) )
->overlay( $template_dir, to => $overlay_dir );
};
my $error = $@;
umask($old_umask);
ok( !$error, 'permission overlay' );
my $overlay_file = File::Spec->catfile( $overlay_dir, 'file.txt' );
is( "100750",
sprintf( '%04o', ( stat($overlay_file) )[2] ),
'mode set correctly when found in both base and template'
);
}
( run in 1.101 second using v1.01-cache-2.11-cpan-39bf76dae61 )