File Coverage

blib/lib/Pod/Elemental/Transformer/Stenciller.pm
Criterion Covered Total %
statement 68 71 95.7
branch 9 14 64.2
condition 1 3 33.3
subroutine 15 15 100.0
pod 0 2 0.0
total 93 105 88.5


line stmt bran cond sub pod time code
1 1     1   1164726 use 5.10.1;
  1         12  
2 1     1   6 use strict;
  1         2  
  1         23  
3 1     1   5 use warnings;
  1         2  
  1         64  
4              
5             package Pod::Elemental::Transformer::Stenciller;
6              
7             # ABSTRACT: Injects content from textfiles transformed with Stenciller
8             our $AUTHORITY = 'cpan:CSSON'; # AUTHORITY
9             our $VERSION = '0.0301';
10              
11 1     1   5 use Moose;
  1         2  
  1         8  
12 1     1   6798 use MooseX::AttributeDocumented;
  1         7106  
  1         4  
13             with qw/Pod::Elemental::Transformer Stenciller::Utils/;
14              
15 1     1   56822 use namespace::autoclean;
  1         3  
  1         13  
16 1     1   794 use Types::Standard -types;
  1         69617  
  1         14  
17 1     1   4861 use Types::Path::Tiny qw/Dir/;
  1         33320  
  1         13  
18 1     1   1052 use Types::Stenciller qw/Stenciller/;
  1         5763  
  1         12  
19 1     1   467 use Carp qw/carp croak/;
  1         2  
  1         66  
20 1     1   558 use Module::Load qw/load/;
  1         1106  
  1         7  
21 1     1   64 use Path::Tiny;
  1         2  
  1         54  
22 1     1   539 use Stenciller;
  1         571044  
  1         679  
23              
24             has directory => (
25             is => 'ro',
26             isa => Dir,
27             coerce => 1,
28             required => 1,
29             documentation => 'Path to directory where the stencil files are.'
30             );
31             has settings => (
32             is => 'ro',
33             isa => HashRef,
34             traits => ['Hash'],
35             default => sub { +{} },
36             handles => {
37             get_setting => 'get',
38             set_setting => 'set',
39             all_settings => 'elements',
40             },
41             documentation_default => '{ }',
42             documentation_order => 0,
43             documentation => 'If a plugin takes more attributes..',
44             );
45             has plugins => (
46             is => 'ro',
47             isa => HashRef,
48             default => sub { +{} },
49             documentation_default => '{ }',
50             documentation_order => 0,
51             init_arg => undef,
52             traits => ['Hash'],
53             handles => {
54             get_plugin => 'get',
55             set_plugin => 'set',
56             },
57             documentation_order => 0,
58             );
59             has stencillers => (
60             is => 'ro',
61             isa => HashRef,
62             traits => ['Hash'],
63             init_arg => undef,
64             handles => {
65             get_stenciller_for_filename => 'get',
66             set_stenciller_for_filename => 'set',
67             },
68             documentation_order => 0,
69             );
70              
71             around BUILDARGS => sub {
72             my $next = shift;
73             my $class = shift;
74             my @args = @_;
75              
76             my $args = ref $args[0] eq 'HASH' ? $args[0] : { @args };
77              
78             $class->$next(
79             directory => delete $args->{'directory'},
80             settings => $args
81             );
82             };
83              
84             sub transform_node {
85 1     1 0 8075 my $self = shift;
86 1         2 my $main_node = shift;
87              
88             NODE:
89 1         2 foreach my $node (@{ $main_node->children }) {
  1         25  
90 5         261 my $content = $node->content;
91 5         42 my $start = substr($content, 0, 11, '');
92 5 100       16 next NODE if $start ne ':stenciller';
93              
94 2         13 $content =~ s{^\h+}{}; # remove leading whitespace
95 2 50       10 next if $content !~ m{([^\h\v]+)}; # the next sequence of non-space is the wanted plugin name
96              
97 2         6 my $wanted_plugin = $1;
98 2         9 my $plugin_name = $self->ensure_plugin($wanted_plugin);
99              
100 2         19 (undef, my($filename, $possible_hash)) = split /\h+/ => $content, 3;
101 2         7 chomp $filename;
102 2 50 33     38 my $node_settings = defined $possible_hash && $possible_hash =~ m{\{.*\}} ? $self->eval_to_hashref($possible_hash, $filename) : {};
103              
104 2         216 my $stenciller = $self->get_stenciller_for_filename($filename);
105              
106 2 100       13 if(!Stenciller->check($stenciller)) {
107 1         54 $stenciller = Stenciller::->new(filepath => path($self->directory)->child($filename));
108 1 50       14239 if(!$stenciller->has_stencils) {
109 0         0 carp sprintf '! no stencils in %s/%s - skipping', $self->directory, $filename;
110 0         0 return;
111             }
112              
113 1         100 $self->set_stenciller_for_filename($filename => $stenciller);
114             }
115              
116 2         102 my $transformed_content = $stenciller->transform(plugin_name => $plugin_name,
117             constructor_args => $self->settings,
118             transform_args => { %$node_settings, require_in_extra => { key => 'to_pod', value => 1, default => 1 } },
119             );
120 2         3072 $transformed_content =~ s{[\v\h]+$}{};
121 2         102 $node->content($transformed_content);
122              
123             }
124             }
125             sub ensure_plugin {
126 2     2 0 5 my $self = shift;
127 2         4 my $plugin_name = shift;
128              
129 2 50       79 return $self->get_plugin($plugin_name) if $self->get_plugin($plugin_name);
130              
131 2         14 my $plugin_class = sprintf 'Stenciller::Plugin::%s', $plugin_name;
132 2         9 load($plugin_class);
133              
134 2 50       513014 if(!$plugin_class->does('Stenciller::Transformer')) {
135 0         0 croak("[$plugin_name] doesn't do the Stenciller::Transformer role. Quitting.");
136             }
137 2         705 $self->set_plugin($plugin_name => $plugin_name);
138 2         6 return $plugin_name;
139             }
140              
141             __PACKAGE__->meta->make_immutable;
142              
143             1;
144              
145             __END__
146              
147             =pod
148              
149             =encoding UTF-8
150              
151             =head1 NAME
152              
153             Pod::Elemental::Transformer::Stenciller - Injects content from textfiles transformed with Stenciller
154              
155              
156              
157             =begin html
158              
159             <p>
160             <img src="https://img.shields.io/badge/perl-5.14+-blue.svg" alt="Requires Perl 5.14+" />
161             <img src="https://img.shields.io/badge/coverage-88.6%25-orange.svg" alt="coverage 88.6%" />
162             <a href="https://github.com/Csson/p5-Pod-Elemental-Transformer-Stenciller/actions?query=workflow%3Amakefile-test"><img src="https://img.shields.io/github/workflow/status/Csson/p5-Pod-Elemental-Transformer-Stenciller/makefile-test" alt="Build status at Github" /></a>
163             </p>
164              
165             =end html
166              
167             =head1 VERSION
168              
169             Version 0.0301, released 2021-06-30.
170              
171              
172              
173             =head1 SYNOPSIS
174              
175             # in weaver.ini
176             [-Transformer / Stenciller]
177             transformer = Stenciller
178             directory = path/to/stencildir
179              
180             =head1 DESCRIPTION
181              
182             This transformer uses a special command in pod files to inject content from elsewhere via a L<Stenciller> transformer plugin.
183              
184             =head2 Example
185              
186             1. Start with the C<weaver.ini> from the L</"synopsis">.
187              
188             2. Add a textfile, in C<path/to/stencildir/file-with-stencils.stencil>:
189              
190             == stencil { to_pod => 1 } ==
191              
192             Header text
193              
194             --input--
195              
196             Input text
197              
198             --end input--
199              
200             Between text
201              
202             --output--
203              
204             Output text
205              
206             --end output--
207              
208             Footer text
209              
210             3. Add a Perl module:
211              
212             package A::Test::Module;
213              
214             1;
215              
216             __END__
217              
218             =pod
219              
220             =head1 NAME
221              
222             =head1 DESCRIPTION
223              
224             :stenciller ToUnparsedText file-with-stencils.stencil
225              
226             The last line in the Perl module will result in the following:
227              
228             =over 4
229              
230             =item *
231              
232             The textfile is parsed with L<Stenciller>
233              
234             =item *
235              
236             The textfile is then transformed using the L<Stenciller::Plugin::ToUnparsedText> plugin.
237              
238             =item *
239              
240             The ':stenciller ...' line in the pod is replaced with the transformed content.
241              
242             =back
243              
244             =head2 Pod hash
245              
246             It is possible to filter stencils by index with an optional hash in the pod:
247              
248             :stenciller ToUnparsedText 1-test.stencil { stencils => [0, 2..4] }
249              
250             This will only include the stencils with index 0, 2, 3 and 4 from C<file-with-stencils.stencil>.
251              
252             =head2 Stencil hash
253              
254             This module checks for the C<to_pod> key in the stencil hash. If it has a true value (or doesn't exist) it is included in the transformation.
255              
256             However, any stencil excluded by the L</"Pod hash"> is already disregarded. It is probably least confusing to choose B<one> of these places to do all filtering.
257              
258             =head1 ATTRIBUTES
259              
260              
261             =head2 directory
262              
263             =begin HTML
264              
265             <table cellpadding="0" cellspacing="0">
266             <tr>
267             <td style="padding-right: 6px; padding-left: 6px; border-right: 1px solid #b8b8b8; white-space: nowrap;"><a href="https://metacpan.org/pod/Types::Path::Tiny#Dir">Dir</a></td>
268             <td style="padding-right: 6px; padding-left: 6px; border-right: 1px solid #b8b8b8; white-space: nowrap;">required</td>
269             <td style="padding-left: 6px; padding-right: 6px; white-space: nowrap;">read-only</td>
270             </tr>
271             </table>
272              
273             <p>Path to directory where the stencil files are.</p>
274              
275             =end HTML
276              
277             =begin markdown
278              
279             <table cellpadding="0" cellspacing="0">
280             <tr>
281             <td style="padding-right: 6px; padding-left: 6px; border-right: 1px solid #b8b8b8; white-space: nowrap;"><a href="https://metacpan.org/pod/Types::Path::Tiny#Dir">Dir</a></td>
282             <td style="padding-right: 6px; padding-left: 6px; border-right: 1px solid #b8b8b8; white-space: nowrap;">required</td>
283             <td style="padding-left: 6px; padding-right: 6px; white-space: nowrap;">read-only</td>
284             </tr>
285             </table>
286              
287             <p>Path to directory where the stencil files are.</p>
288              
289             =end markdown
290              
291             =head1 SEE ALSO
292              
293             =over 4
294              
295             =item *
296              
297             L<Stenciller>
298              
299             =item *
300              
301             L<Stenciller::Plugin::ToUnparsedText>
302              
303             =item *
304              
305             L<Pod::Weaver>
306              
307             =back
308              
309             =head1 SOURCE
310              
311             L<https://github.com/Csson/p5-Pod-Elemental-Transformer-Stenciller>
312              
313             =head1 HOMEPAGE
314              
315             L<https://metacpan.org/release/Pod-Elemental-Transformer-Stenciller>
316              
317             =head1 AUTHOR
318              
319             Erik Carlsson <info@code301.com>
320              
321             =head1 COPYRIGHT AND LICENSE
322              
323             This software is copyright (c) 2021 by Erik Carlsson.
324              
325             This is free software; you can redistribute it and/or modify it under
326             the same terms as the Perl 5 programming language system itself.
327              
328             =cut