File Coverage

blib/lib/HTML/Chunks.pm
Criterion Covered Total %
statement 6 128 4.6
branch 0 48 0.0
condition 0 20 0.0
subroutine 2 23 8.7
pod 10 21 47.6
total 18 240 7.5


line stmt bran cond sub pod time code
1             package HTML::Chunks;
2              
3 1     1   1004 use strict;
  1         1  
  1         129  
4              
5             our $VERSION = '1.55';
6              
7 1     1   6 use constant DATA_REGEX => qr/##[\w\.]+##/;
  1         2  
  1         1950  
8              
9             sub new
10             {
11 0     0 1   my $self = bless {}, shift;
12              
13 0           $self->init(@_);
14 0           return $self;
15             }
16              
17             sub init
18             {
19 0     0 0   my $self = shift;
20              
21 0           $self->{crush} = 1;
22 0           $self->{cascade} = 1;
23 0           $self->{dataStack} = [];
24 0           $self->{chunk} = {};
25 0           $self->{default} = undef;
26            
27 0 0         $self->read(@_) if @_;
28             }
29              
30             sub readChunkFile
31             {
32 0     0 1   my $self = shift;
33              
34 0           foreach my $file (@_) {
35 0 0         if (my $fh = $self->getFilehandle($file)) {
36 0           my @chunkStack;
37            
38 0           while (my $line = <$fh>) {
39 0           $self->parse(\@chunkStack, \$line);
40             }
41 0           close $fh;
42             }
43             else {
44 0           warn "Can't open file [$file]\n";
45             }
46             }
47             }
48              
49             sub readSimpleFile
50             {
51 0     0 1   my $self = shift;
52              
53 0           foreach my $file (@_) {
54 0 0         if (my $fh = $self->getFilehandle($file)) {
55 0           while (<$fh>) {
56 0 0 0       next if /^#/ or !/\S/;
57            
58 0           s/[\r\n]//g;
59 0           s/^\s+|\s+$//g;
60            
61 0           my ($name, $chunk) = split(/\s+/, $_, 2);
62 0 0 0       if ($name && $chunk) {
63 0           $self->addNamedChunk($name, $chunk);
64             }
65             }
66 0           close $fh;
67             }
68             }
69             }
70              
71             sub getFilehandle
72             {
73 0     0 0   my $self = shift;
74 0           my ($file) = @_;
75            
76 0 0         return $file if ref $file; # already a filehandle
77 0 0         return open (CHUNKIN, $file) ? \*CHUNKIN : undef;
78             }
79            
80             sub addChunk
81             {
82 0     0 1   my $self = shift;
83              
84 0           foreach my $chunk (@_) {
85 0 0         next unless $chunk;
86            
87             # allow a ref to be passed for efficiency
88 0 0         my $ref = ref $chunk ? $chunk : \$chunk;
89 0           $self->parse([], $ref);
90             }
91             }
92              
93             sub addNamedChunk
94             {
95 0     0 1   my $self = shift;
96 0           my ($name, $chunk) = @_;
97            
98 0 0 0       return unless ($name && $chunk);
99            
100             # allow a ref to be passed for efficiency
101 0 0         my $ref = ref $chunk ? $chunk : \$chunk;
102 0           $self->{chunk}{$name} = undef;
103 0           $self->parse([ \$self->{chunk}{$name} ], $ref);
104             }
105              
106             sub parse
107             {
108 0     0 0   my $self = shift;
109 0           my ($chunkStack, $data) = @_;
110 0           my $chunk = $chunkStack->[-1];
111            
112             # allow a ref to be passed for efficiency
113 0 0         my $dataRef = ref $data ? $data : \$data;
114              
115 0           foreach (split(/()/, $$dataRef)) {
116 0 0 0       if (//i) {
    0 0        
    0 0        
117 0           $self->{chunk}{$1} = undef;
118 0           push @{$chunkStack}, $chunk = \$self->{chunk}{$1};
  0            
119             }
120             elsif ($chunk && //i) {
121 0 0         $$chunk =~ s/\s{2,}/\n/sg if $self->{crush}; # crush whitespace
122 0           pop @{$chunkStack};
  0            
123 0           $chunk = $chunkStack->[-1];
124             }
125             elsif ($chunk && ($_ || !$self->{crush})) {
126 0           $$chunk .= $_;
127             }
128             }
129             }
130              
131             sub output
132             {
133 0     0 1   my $self = shift;
134 0           my $name = shift;
135              
136 0           $self->outputAsChunk(\$self->{chunk}{$name}, @_);
137             }
138              
139             sub outputAsChunk
140             {
141 0     0 0   my $self = shift;
142 0           my $chunk = shift;
143 0           my $data = shift;
144              
145 0 0         if ($chunk) {
146 0 0         my $chunkRef = ref $chunk ? $chunk : \$chunk;
147 0   0       $data ||= {};
148            
149 0           push @{$self->{dataStack}}, $data;
  0            
150 0           $self->outputBasicChunk($chunkRef, @_);
151 0           pop @{$self->{dataStack}};
  0            
152             }
153             }
154              
155             # basic chunk output including data substitution, using data already
156             # on the data stack.
157             sub outputBasicChunk
158             {
159 0     0 0   my $self = shift;
160 0           my $chunk = shift;
161 0 0         my $chunkRef = ref $chunk ? $chunk : \$chunk;
162 0           my $data_regex = $self->DATA_REGEX;
163              
164 0           foreach my $piece (split(/(|$data_regex)/, $$chunkRef)) {
165 0 0         if ($piece =~ /($data_regex)/) {
166 0           $self->outputData(substr($1, 2, -2), @_);
167             }
168             else {
169 0           print $piece;
170             }
171             }
172             }
173              
174             sub outputData
175             {
176 0     0 0   my $self = shift;
177 0           my $name = shift;
178 0           my $value = $self->getDataValue($name);
179            
180 0 0         if (ref $value eq 'CODE') {
181 0           &{$value}($self, $name, @_);
  0            
182             }
183             else {
184 0           print $value;
185             }
186             }
187              
188             sub getDataValue
189             {
190 0     0 0   my ($self, $name) = (shift, shift);
191 0           my $value;
192 0 0         my $last = $self->{cascade} ? 0 : $#{$self->{dataStack}};
  0            
193            
194 0           for (my $ndx = $#{$self->{dataStack}}; $ndx >= $last; $ndx--) {
  0            
195 0 0         if (exists($self->{dataStack}[$ndx]{$name})) {
196 0           $value = $self->{dataStack}[$ndx]{$name};
197 0           last;
198             }
199             }
200            
201 0 0         if ($value eq '') {
202 0           return $self->{default};
203             }
204             else {
205 0           return $value;
206             }
207             }
208              
209             sub getChunkNames
210             {
211 0     0 1   my $self = shift;
212 0           return keys %{$self->{chunk}};
  0            
213             }
214              
215             sub getChunk
216             {
217 0     0 1   my $self = shift;
218 0           my ($name) = @_;
219            
220 0           return $self->{chunk}{$name};
221             }
222              
223             sub getChunkHash
224             {
225 0     0 0   my $self = shift;
226 0           return { %{$self->{chunk}} };
  0            
227             }
228              
229             sub setCrush
230             {
231 0     0 1   my $self = shift;
232 0           my $old = $self->{crush};
233 0           ($self->{crush}) = (@_);
234 0           return $old;
235             }
236              
237             sub setCascade
238             {
239 0     0 1   my $self = shift;
240 0           my $old = $self->{cascade};
241 0           ($self->{cascade}) = (@_);
242 0           return $old;
243             }
244              
245             sub setDefaultDataValue
246             {
247 0     0 0   my $self = shift;
248 0           ($self->{default}) = (@_);
249             }
250              
251             # legacy wrappers for backward compatibility
252              
253             sub crush
254             {
255 0     0 0   my $self = shift;
256 0           return $self->setCrush(@_);
257             }
258              
259             sub read
260             {
261 0     0 0   my $self = shift;
262 0           $self->readChunkFile(@_);
263             }
264              
265             1;
266              
267             __END__