Template-Resolver

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
    - 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
 
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

MANIFEST  view on Meta::CPAN

12
13
14
15
16
17
18
19
20
21
22
23
24
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

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
    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

136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
=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

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
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 0.291 second using v1.01-cache-2.11-cpan-55f5a4728d2 )