File Coverage

blib/lib/String/BufferStack.pm
Criterion Covered Total %
statement 140 152 92.1
branch 46 64 71.8
condition 30 48 62.5
subroutine 21 24 87.5
pod 20 20 100.0
total 257 308 83.4


line stmt bran cond sub pod time code
1             package String::BufferStack;
2              
3 5     5   14956 use strict;
  5         12  
  5         251  
4 5     5   29 use warnings;
  5         10  
  5         176  
5 5     5   36 use Carp;
  5         8  
  5         14722  
6              
7             our $VERSION; $VERSION = "1.16";
8              
9             =head1 NAME
10              
11             String::BufferStack - Nested buffers for templating systems
12              
13             =head1 SYNOPSIS
14              
15             my $stack = String::BufferStack->new;
16             $stack->push( filter => sub {return uc shift} );
17             $stack->append("content");
18             $stack->flush_output;
19              
20             =head1 DESCRIPTION
21              
22             C provides a framework for storing nested
23             buffers. By default, all of the buffers flow directly to the output
24             method, but individual levels of the stack can apply filters, or store
25             their output in a scalar reference.
26              
27             =head1 METHODS
28              
29             =head2 new PARAMHASH
30              
31             Creates a new buffer stack and returns it. Possible arguments include:
32              
33             =over
34              
35             =item prealoc
36              
37             Preallocate this many bytes in the output buffer. This can reduce
38             reallocations, and thus speed up appends.
39              
40             =item out_method
41              
42             The method to call when output trickles down to the bottom-most buffer
43             and is flushed via L. The default C prints
44             the content to C. This method will always be called with
45             non-undef, non-zero length content.
46              
47             =item use_length
48              
49             Calculate length of each buffer as it is built. This imposes a
50             significant runtime cost, so should be avoided if at all possible.
51             Defaults to off.
52              
53             =back
54              
55             =cut
56              
57             sub new {
58 6     6 1 6312 my $class = shift;
59 6         20 my %args = @_;
60 6   50     82 my $output = " "x($args{prealloc} || 0);
61 6         11 $output = '';
62             return bless {
63             stack => [],
64             top => undef,
65             output => \$output,
66 1     1   7 out_method => $args{out_method} || sub { print STDOUT @_ },
67 6   100     176 pre_appends => {},
68             use_length => $args{use_length},
69             }, $class;
70             }
71              
72             =head2 push PARAMHASH
73              
74             Pushes a new frame onto the buffer stack. By default, the output from
75             this new frame connects to the input of the previous frame. There are
76             a number of possible options:
77              
78             =over
79              
80             =item buffer
81              
82             A string reference, into which the output from this stack frame will
83             appear. By default, this is the input buffer of the previous frame.
84              
85             =item private
86              
87             If a true value is passed for C, it creates a private string
88             reference, and uses that as the buffer -- this is purely for
89             convenience. That is, the following blocks are equivilent:
90              
91             my $buffer = "";
92             $stack->push( buffer => \$buffer );
93             # ...
94             $stack->pop;
95             print $buffer;
96              
97             $stack->push( private => 1 );
98             # ...
99             print $stack->pop;
100              
101             =item pre_append
102              
103             A callback, which will be called with a reference to the
104             C object, and the arguments to append, whenever
105             this stack frame has anything appended to the input buffer, directly
106             or indirectly.
107              
108             Within the context of the pre-append callback, L,
109             L, and L function on the frame the
110             pre-append is attached to, not the topmost trame. Using L
111             within the pre-append callback is not suggested; use
112             L instead. L can be used to alter or
113             remove the pre-append callback itself -- this is not uncommon, in
114             the case where the first append is the only one which needs be watched
115             for, for instance.
116              
117             =item filter
118              
119             A callback, used to process data which is appended to the stack frame.
120             By default, filters are lazy, being called only when a frame is
121             popped. They can be forced at any time by calling L,
122             however.
123              
124             =back
125              
126             =cut
127              
128             sub push {
129 19     19 1 44 my $self = shift;
130 19 100       117 my $frame = {
131             buffer => $self->{top} ? $self->{top}{pre_filter} : $self->{output},
132             @_
133             };
134 19         40 my $filter = "";
135 19         67 my $buffer = "";
136 19 50       70 $frame->{buffer} = \$buffer if delete $frame->{private};
137 19 0 33     124 $frame->{length} = (defined ${$frame->{buffer}}) ? CORE::length(${$frame->{buffer}}) : 0
  0 50       0  
  0         0  
138             if $self->{use_length} or $frame->{use_length};
139 19 100       77 $frame->{pre_filter} = $frame->{filter} ? \$filter : $frame->{buffer};
140 19         34 $self->{top} = $frame;
141 19         1444 local $self->{local_frame} = $frame;
142 19 100       78 $self->set_pre_append(delete $frame->{pre_append}) if defined $frame->{pre_append};
143 19         26 CORE::push(@{$self->{stack}}, $frame);
  19         77  
144             }
145              
146             =head2 depth
147              
148             Returns the current depth of the stack. This starts at 0, when no
149             frames have been pushed, and increases by one for each frame pushed.
150              
151             =cut
152              
153             sub depth {
154 14     14 1 22 my $self = shift;
155 14         19 return scalar @{$self->{stack}};
  14         67  
156             }
157              
158             =head2 append STRING [, STRING, ...]
159              
160             Appends the given strings to the input side of the topmost buffer.
161             This will call all pre-append hooks attached to it, as well. Note
162             that if the frame has a filter, the filter will not immediately run,
163             but will be delayed until the frame is L'd, or L
164             is called.
165              
166             When called with no frames on the stack, appends the stringins
167             directly to the L.
168              
169             =cut
170              
171             sub append {
172 49     49 1 9406 my $self = shift;
173 49   66     654 my $frame = $self->{local_frame} || $self->{top};
174 49 100       123 if ($frame) {
175 31         61 my $ref = $frame->{pre_filter};
176 31 100 100     169 if (exists $self->{pre_appends}{$frame->{buffer}} and not $frame->{filter}) {
177             # This is an append to the output buffer, signal all pre_append hooks for it
178 8         12 for my $frame (@{$self->{pre_appends}{$frame->{buffer}}}) {
  8         30  
179 11 50       36 die unless $frame->{pre_append};
180 11         21 local $self->{local_frame} = $frame;
181 11         34 $frame->{pre_append}->($self, @_);
182             }
183             }
184 31         101 for (@_) {
185 31 100       162 $$ref .= $_ if defined;
186             }
187             } else {
188 18         36 my $ref = $self->{output};
189 18         39 for (@_) {
190 18 50       91 $$ref .= $_ if defined;
191             }
192             }
193             }
194              
195             =head2 direct_append STRING [, STRING, ...]
196              
197             Similar to L, but appends the strings to the output side of
198             the frame, skipping pre-append callbacks and filters.
199              
200             When called with no frames on the stack, appends the strings
201             directly to the L.
202              
203             =cut
204              
205             sub direct_append {
206 2     2 1 9 my $self = shift;
207 2   33     9 my $frame = $self->{local_frame} || $self->{top};
208 2 50       7 my $ref = $frame ? $frame->{buffer} : $self->{output};
209 2         27 for (@_) {
210 2 50       21 $$ref .= $_ if defined;
211             }
212             }
213              
214             =head2 pop
215              
216             Removes the topmost frame on the stack, flushing the topmost filters
217             in the process. Returns the output buffer of the frame -- note that
218             this may not contain only strings appended in the current frame, but
219             also those from before, as a speed optimization. That is:
220              
221             $stack->append("one");
222             $stack->push;
223             $stack->append(" two");
224             $stack->pop; # returns "one two"
225              
226             This operation is a no-op if there are no frames on the stack.
227              
228             =cut
229              
230             sub pop {
231 20     20 1 888 my $self = shift;
232 20 100       80 return unless $self->{top};
233 18         183 $self->filter;
234 18         22 my $frame = CORE::pop(@{$self->{stack}});
  18         46  
235 18         50 local $self->{local_frame} = $frame;
236 18         4437 $self->set_pre_append(undef);
237 18 100       29 $self->{top} = @{$self->{stack}} ? $self->{stack}[-1] : undef;
  18         1339  
238 18         577 return ${$frame->{buffer}};
  18         446  
239             }
240              
241             =head2 set_pre_append CALLBACK
242              
243             Alters the pre-append callback on the topmost frame. The callback
244             will be called before text is appended to the input buffer of the
245             frame, and will be passed the C and the arguments
246             to L.
247              
248             =cut
249              
250             sub set_pre_append {
251 29     29 1 67 my $self = shift;
252 29         40 my $hook = shift;
253 29   33     91 my $frame = $self->{local_frame} || $self->{top};
254 29 50       69 return unless $frame;
255 29 100 66     220 if ($hook and not $frame->{pre_append}) {
    100 66        
256 7         11 CORE::push(@{$self->{pre_appends}{$frame->{buffer}}}, $frame);
  7         41  
257             } elsif (not $hook and $frame->{pre_append}) {
258 9         159 $self->{pre_appends}{ $frame->{buffer} }
259 7         14 = [ grep { $_ ne $frame } @{ $self->{pre_appends}{ $frame->{buffer} } } ];
  7         26  
260 7         37 delete $self->{pre_appends}{ $frame->{buffer} }
261 7 100       16 unless @{ $self->{pre_appends}{ $frame->{buffer} } };
262             }
263 29         93 $frame->{pre_append} = $hook;
264             }
265              
266             =head2 set_filter FILTER
267              
268             Alters the filter on the topmost frame. Doing this flushes the
269             filters on the topmost frame.
270              
271             =cut
272              
273             sub set_filter {
274 4     4 1 9 my $self = shift;
275 4         6 my $filter = shift;
276 4 50       11 return unless $self->{top};
277 4         10 $self->filter;
278 4 100 100     45 if (defined $self->{top}{filter} and not defined $filter) {
    100 100        
279             # Removing a filter, flush, then in = out
280 1         54 $self->{top}{pre_filter} = $self->{top}{buffer};
281             } elsif (not defined $self->{top}{filter} and defined $filter) {
282             # Adding a filter, add a pre_filter stage
283 1         2 my $pre_filter = "";
284 1         3 $self->{top}{pre_filter} = \$pre_filter;
285             }
286 4         15 $self->{top}{filter} = $filter;
287             }
288              
289             =head2 filter
290              
291             Filters the topmost stack frame, if it has outstanding unfiltered
292             data. This will propagate content to lower frames, possibly calling
293             their pre-append hooks.
294              
295             =cut
296              
297             sub filter {
298 37     37 1 79 my $self = shift;
299 37   100     164 my $frame = shift || $self->{top};
300 37 100 100     276 return unless $frame and $frame->{filter} and CORE::length(${$frame->{pre_filter}});
  22   100     161  
301              
302             # We remove the input before we shell out to the filter, so we
303             # don't get into infinite loops.
304 13         8630 my $input = ${$frame->{pre_filter}};
  13         35325  
305 13         362 ${$frame->{pre_filter}} = '';
  13         40  
306 13         65 my $output = $frame->{filter}->($input);
307 13 100       123 if (exists $self->{pre_appends}{$frame->{buffer}}) {
308 1         2 for my $frame (@{$self->{pre_appends}{$frame->{buffer}}}) {
  1         4  
309 1         3 local $self->{local_frame} = $frame;
310 1         4 $frame->{pre_append}->($self, $output);
311             }
312             }
313 13         21 ${$frame->{buffer}} .= $output;
  13         67  
314             }
315              
316             =head2 flush
317              
318             If there are no frames on the stack, calls L.
319             Otherwise, calls L.
320              
321             =cut
322              
323             sub flush {
324 9     9 1 574 my $self = shift;
325             # Flushing with no stack flushes the output
326 9 100       28 return $self->flush_output unless $self->depth;
327             # Otherwise it just flushes the filters
328 3         11 $self->flush_filters;
329             }
330              
331             =head2 flush_filters
332              
333             Flushes all filters. This does not flush output from the output
334             buffer; see L.
335              
336             =cut
337              
338             sub flush_filters {
339 18     18 1 35 my $self = shift;
340             # Push content through filters -- reverse so the top one is first
341 18         21 for my $frame (reverse @{$self->{stack}}) {
  18         108  
342 13         39 $self->filter($frame);
343             }
344             }
345              
346             =head2 buffer
347              
348             Returns the contents of the output buffer of the topmost frame; if
349             there are no frames, returns the output buffer.
350              
351             =cut
352              
353             sub buffer {
354 59     59 1 125 my $self = shift;
355 59 100       1362 return $self->{top} ? ${$self->{top}{buffer}} : ${$self->{output}};
  33         182  
  26         138  
356             }
357              
358             =head2 buffer_ref
359              
360             Returns a reference to the output buffer of the topmost frame; if
361             there are no frames, returns a reference to the output buffer. Note
362             that adjusting this skips pre-append and filter hooks.
363              
364             =cut
365              
366             sub buffer_ref {
367 0     0 1 0 my $self = shift;
368 0 0       0 return $self->{top} ? $self->{top}{buffer} : $self->{output};
369             }
370              
371             =head2 length
372              
373             If C was enabled in the buffer stack's constructor,
374             returns the number of characters appended to the current frame; if
375             there are no frames, returns the length of the output buffer.
376              
377             If C was not enabled, warns and returns 0.
378              
379             =cut
380              
381             sub length {
382 0     0 1 0 my $self = shift;
383 0 0 0     0 carp("String::BufferStack object didn't enable use_length") and return 0
      0        
      0        
384             unless $self->{use_length} or ($self->{top} and $self->{top}{use_length});
385 0 0       0 return $self->{top} ? CORE::length(${$self->{top}{buffer}}) - $self->{top}{length} : CORE::length(${$self->{output}});
  0         0  
  0         0  
386             }
387              
388              
389             =head2 flush_output
390              
391             Flushes all filters using L, then flushes output from
392             the output buffer, using the configured L.
393              
394             =cut
395              
396             sub flush_output {
397 7     7 1 10 my $self = shift;
398 7         15 $self->flush_filters;
399              
400             # Look at what we have at the end
401 7 100       11 return unless CORE::length(${$self->{output}});
  7         26  
402 5         8 $self->{out_method}->(${$self->{output}});
  5         87  
403 5         19 ${$self->{output}} = "";
  5         11  
404 5         13 return "";
405             }
406              
407             =head2 output_buffer
408              
409             Returns the pending output buffer, which sits below all existing
410             frames.
411              
412             =cut
413              
414             sub output_buffer {
415 39     39 1 71 my $self = shift;
416 39         49 return ${$self->{output}};
  39         763  
417             }
418              
419             =head2 output_buffer_ref
420              
421             Returns a reference to the pending output buffer, allowing you to
422             modify it.
423              
424             =cut
425              
426             sub output_buffer_ref {
427 4     4 1 7 my $self = shift;
428 4         19 return $self->{output};
429             }
430              
431             =head2 clear
432              
433             Clears I buffers in the stack, including the output buffer.
434              
435             =cut
436              
437             sub clear {
438 10     10 1 23 my $self = shift;
439 10         17 ${$self->{output}} = "";
  10         29  
440 10         19 ${$_->{pre_filter}} = ${$_->{buffer}} = "" for @{$self->{stack}};
  10         35  
  1         3  
  1         3  
441 10         23 return "";
442             }
443              
444             =head2 clear_top
445              
446             Clears the topmost buffer in the stack; if there are no frames on the
447             stack, clears the output buffer.
448              
449             =cut
450              
451             sub clear_top {
452 2     2 1 5 my $self = shift;
453 2 100       9 if ($self->{top}) {
454 1         2 ${$self->{top}{pre_filter}} = ${$self->{top}{buffer}} = "";
  1         2  
  1         2  
455             } else {
456 1         623 ${$self->{output}} = "";
  1         4  
457             }
458 2         5 return "";
459             }
460              
461             =head2 out_method [CALLBACK]
462              
463             Gets or sets the output method callback, which is given content from
464             the pending output buffer, which sits below all frames.
465              
466             =cut
467              
468             sub out_method {
469 0     0 1   my $self = shift;
470 0 0         $self->{out_method} = shift if @_;
471 0           return $self->{out_method};
472             }
473              
474             =head1 SEE ALSO
475              
476             Many concepts were originally taken from L's internal
477             buffer stack.
478              
479             =head1 AUTHORS
480              
481             Alex Vandiver C<< alexmv@bestpractical.com >>
482              
483             =head1 LICENSE
484              
485             Copyright 2008-2009, Best Practical Solutions.
486              
487             This package is distributed under the same terms as Perl itself.
488              
489             =cut
490              
491              
492             1;