File Coverage

blib/lib/Text/FillIn.pm
Criterion Covered Total %
statement 110 163 67.4
branch 27 54 50.0
condition 0 3 0.0
subroutine 21 29 72.4
pod 11 17 64.7
total 169 266 63.5


line stmt bran cond sub pod time code
1             package Text::FillIn;
2 1     1   634 use Carp;
  1         1  
  1         82  
3 1     1   657 use FileHandle;
  1         11318  
  1         6  
4 1     1   403 use strict;
  1         6  
  1         31  
5 1     1   5 use vars qw($VERSION %DEFAULT);
  1         1  
  1         1597  
6             $VERSION = '0.05';
7              
8             # Set a bunch of defaults
9             %DEFAULT = (
10             'path' => ['.'],
11             '$hook' => 'find_value',
12             '&hook' => 'run_function',
13             'Ldelim' => '[[',
14             'Rdelim' => ']]',
15             'text' => '',
16             'properties' => {},
17             'object' => undef,
18             );
19              
20             sub new {
21 24     24 1 57338 my $package = shift;
22 24         107 my $text = shift;
23            
24 24         416 my $self = {
25             %DEFAULT,
26             'text' => $text,
27             };
28              
29             # Copy the special structures so we don't share their memory
30 24         53 $self->{'properties'} = { %{$self->{'properties'}} };
  24         92  
31 24         46 $self->{'path'} = [ @{$self->{'path'}} ];
  24         84  
32            
33 24         229 return bless ($self, $package);
34             }
35              
36             sub get_file {
37 0     0 1 0 my $self = shift;
38 0         0 my $file = shift;
39            
40 0 0       0 if ($file eq 'null') {
41 0         0 $self->{'text'} = '';
42 0         0 return;
43             }
44            
45             # Find out what file to open:
46 0         0 my $realfile;
47 0 0       0 if ($file =~ /^\//) {
48 0         0 $realfile = $file;
49             } else {
50 0         0 foreach my $dir ($self->path()) {
51 0 0       0 if ( -f "$dir/$file" ) {
52 0         0 $realfile = "$dir/$file";
53 0         0 last;
54             }
55             }
56             }
57              
58 0 0 0     0 unless ($realfile and -f $realfile) {
59 0         0 warn ("Can't find file '$file' in (@{[$self->path()]})");
  0         0  
60 0         0 return 0;
61             }
62              
63 0         0 my $fh = new FileHandle($realfile);
64 0 0       0 unless ( defined $fh ) {
65 0         0 warn ("Can't open $realfile: $!");
66 0         0 $self->{'text'} = '';
67 0         0 return 0;
68             }
69            
70 0         0 $self->{'text'} = join('', $fh->getlines );
71 0         0 return 1;
72             }
73              
74 113     113 1 134 sub Ldelim { my $s = shift; $s->_prop('Ldelim', @_) }
  113         250  
75 112     112 1 171 sub Rdelim { my $s = shift; $s->_prop('Rdelim', @_) }
  112         217  
76 0     0 1 0 sub text { my $s = shift; $s->_prop('text', @_) }
  0         0  
77 39     39 1 133 sub object { my $s = shift; $s->_prop('object', @_) }
  39         254  
78              
79             sub hook {
80 38     38 1 103 my $self = shift;
81 38         48 my $char = shift;
82 38         193 return $self->_prop($char.'hook', @_);
83             }
84              
85             sub path {
86 0     0 1 0 my $self = shift;
87 0 0       0 return @{ (@_ ? $self->_prop('path', [@_]) : $self->_prop('path')) };
  0         0  
88             }
89              
90             sub property {
91 0     0 1 0 my $self = shift;
92 0         0 my $prop = shift;
93            
94             # Which SV we should get or set - this object only, or the default
95 0 0       0 my $get_set = (ref $self ? \($self->{'properties'}{$prop}) : \($DEFAULT{'properties'}{$prop}));
96            
97 0 0       0 if (@_) {
98             # Set the property
99 0         0 $$get_set = shift;
100             }
101 0         0 return $$get_set;
102             }
103              
104              
105             sub interpret {
106 14     14 1 37 my $self = shift;
107 14         186 $self->_interpret_engine('collect');
108             }
109              
110             sub interpret_and_print {
111 10     10 1 3611 my $self = shift;
112 10         29 $self->_interpret_engine('print');
113             }
114              
115             # Deprecated - use text()
116             sub set_text {
117 0     0 0 0 my $self = shift;
118 0         0 my $text = shift;
119            
120 0         0 $self->{'text'} = $text;
121             }
122              
123             # Deprecated - use text()
124             sub get_text {
125 0     0 0 0 my $self = shift;
126            
127 0         0 return $self->{'text'};
128             }
129              
130             # Deprecated - use property()
131             sub get_property {
132 0     0 0 0 my $self = shift;
133 0         0 my $prop_name = shift;
134            
135 0         0 return $self->{'properties'}->{$prop_name};
136             }
137              
138             # Deprecated - use property()
139             sub set_property {
140 0     0 0 0 my $self = shift;
141 0         0 my $prop_name = shift;
142 0         0 my $prop_val = shift;
143            
144 0         0 $self->{'properties'}->{$prop_name} = $prop_val;
145             }
146              
147              
148              
149             ############################# Private functions
150              
151             sub _prop {
152 302     302   326 my $self = shift;
153 302         364 my $prop = shift;
154            
155             # Which SV we should get or set - this object only, or the default
156 302 100       901 my $get_set = (ref $self ? \($self->{$prop}) : \($DEFAULT{$prop}));
157            
158 302 100       713 if (@_) {
159             # Set the property
160 6         20 $$get_set = shift;
161             }
162 302         1212 return $$get_set;
163             }
164              
165             sub _deal_with {
166 48     48   107 my ($text, $style, $outref) = @_;
167 48 100       138 if ($style eq 'print') {
    50          
168 20         119 print $text;
169             } elsif ($style eq 'collect') {
170 28         42 ${$outref} .= $text;
  28         72  
171             }
172             }
173              
174             sub _interpret_engine {
175              
176 24     24   36 my $self = shift;
177 24         62 my $style = shift;
178 24         28 my ($first_right, $first_left, $last_left, $out_text, $save);
179 24         34 my $debug = 0;
180 24         81 my $text = $self->{'text'}; # Duplicates memory, I'll clean up later
181            
182 24         61 my ($ld, $rd) = ($self->Ldelim(), $self->Rdelim());
183 24 50       58 warn "Delimiters are $ld and $rd" if $debug;
184              
185 24         31 while (1) {
186              
187 86 50       344 warn ("interpreting '$text'") if $debug;
188             # Shave off any leading plain text before the first real [[
189 86         89 my ($prelength, $pretext);
190 86         190 $first_left = &_real_index($text, $ld);
191 86 50       267 warn ("first left is at $first_left") if $debug;
192 86 100       572 if ( $first_left == -1 ) {
    100          
193             # No more to do, just spit out the text
194 24         67 $self->_unquote(\$text);
195 24         90 &_deal_with($text, $style, \$out_text);
196 24         49 last;
197            
198             } elsif ($first_left > 0) { # There's a real [[ here
199 24         77 $pretext = substr($text, 0, $first_left);
200 24         69 $self->_unquote(\$pretext);
201 24         169 &_deal_with($pretext, $style, \$out_text);
202 24         54 substr($text, 0, $first_left) = '';
203 24         46 next;
204             }
205            
206             # There's now a real [[ at position 0.
207             # Find the first right delimiter and fill in before it:
208 38         71 $first_right = &_real_index($text, $rd);
209 38 50       88 warn ("first right is at $first_right") if $debug;
210 38         143 $last_left = &_real_index(substr($text, 0, $first_right), $ld, 1);
211 38 50       98 warn ("last left is at $last_left") if $debug;
212            
213 38 50       86 if ($first_right == -1) { # Something's amiss, abort
214 0         0 warn ("Problem interpreting text " . substr($text, 0, $first_right));
215 0         0 &_deal_with($text, $style, \$out_text);
216 0         0 last;
217             }
218             # Fill in the text in between the first right delimiter and the last left delimiter before it:
219 38         173 substr($text, $last_left, $first_right - $last_left + length($rd)) =
220             $self->_do_interpret(substr($text, $last_left, $first_right - $last_left + length($rd)));
221             }
222 24         374 return $out_text;
223             }
224              
225             sub _real_index {
226             # Finds the first occurrence of $exp in $text before
227             # position $before that doesn't follow a backslash
228            
229 162     162   325 my $text = shift;
230 162         318 my $exp = shift;
231 162         179 my $last = shift;
232            
233 162 100       324 if ($last) {
234 38 50       436 if ($text =~ / (.*)(^|[^\\]) \Q$exp/sx) {
235 38         116 return(length($1) + length($2));
236             } else {
237 0         0 return -1;
238             }
239             } else {
240 124 100       3118 if ($text =~ / (.*?)(^|[^\\]) \Q$exp/sx) {
241 100         970 return (length($1) + length($2));
242             } else {
243 24         55 return -1;
244             }
245             }
246             }
247              
248             sub _unquote {
249 48     48   55 my $self = shift;
250 48         54 my $textref = shift;
251            
252 48         103 my ($ldx, $rdx) = map {quotemeta} ($self->Ldelim(), $self->Rdelim());
  96         266  
253 48         66 ${$textref} =~ s/ \\( $ldx | $rdx ) /$1/xgs;
  48         414  
254             }
255              
256             sub _do_interpret {
257 38     38   51 my $self = shift;
258 38         93 my $string = shift;
259            
260 38         80 my ($ldx, $rdx) = map {quotemeta} ($self->Ldelim(), $self->Rdelim());
  76         211  
261            
262 38 50       463 unless ($string =~ /^ $ldx \s* ([\W]) (.*?) \s* $rdx $/sx ) {
263             # Looks like we weren't meant to see this - but we can't interpret it again either
264 0         0 carp ("Can't interpret template chunk '$string'");
265 0         0 return;
266             }
267 38         91 my ($char, $guts) = ($1, $2);
268            
269 38         50 my ($hook, $object);
270 38 50       109 if (defined ($hook = $self->hook($char))) {
271 1     1   6 no strict('refs'); # Allow symbolic name substitution for a little while
  1         2  
  1         210  
272 38 100       98 if (defined ($object = $self->object())) {
273 1         6 return $object->$hook($guts, $char);
274             } else {
275 37         45 return &{$hook}($guts, $char);
  37         149  
276             }
277             } else {
278 0         0 croak ("No interpret hook defined for type '$1'");
279             }
280             }
281              
282              
283             ############################ Sample hook functions ##########################
284              
285              
286 31     31 0 256 sub find_value { $main::TVars{ $_[0] } }
287              
288             sub run_function {
289             # Usage: $result = &run_function("some_function(param1,param2,param3)");
290 6     6 0 14 my $text = shift;
291 6 50       76 my ($function_name, $args) = $text =~ /(\w+)\((.*)\)/
292             or die ("Can't understand function call '$text'");
293 1     1   5 no strict('refs'); # Allow symbolic name substitution for a little while
  1         2  
  1         89  
294 6         23 return &{"TExport::$function_name"}( split(/,/, $args) );
  6         46  
295             }
296              
297              
298             1;
299              
300             __END__