File Coverage

blib/lib/XML/Spice.pm
Criterion Covered Total %
statement 37 105 35.2
branch 3 40 7.5
condition 0 9 0.0
subroutine 10 17 58.8
pod 0 1 0.0
total 50 172 29.0


line stmt bran cond sub pod time code
1             package XML::Spice;
2             $XML::Spice::VERSION = '0.04';
3             require 5.008;
4              
5 2     2   43119 use warnings;
  2         5  
  2         59  
6 2     2   11 use strict;
  2         3  
  2         61  
7              
8 2     2   11 use Carp;
  2         6  
  2         361  
9              
10             our $PRETTY_PRINT = 0;
11              
12             sub import {
13 3     3   827 my ($pkg, @args) = @_;
14              
15 3         7 my $them = caller();
16              
17 3 100       12 if (@args) {
18 1         3 for my $arg (@args) {
19 3 50       14 croak "Invalid element name '$arg'" if $arg !~ m/^[A-Za-z]\w*$/;
20              
21             {
22 2     2   11 no strict "refs";
  2         3  
  2         165  
  3         4  
23 3     0   10 *{$them."::".$arg} = sub { x($arg, @_) };
  3         17  
  0         0  
24             }
25             }
26             }
27              
28             else {
29 2     2   10 no strict "refs";
  2         4  
  2         420  
30 2         4 *{$them."::x"} = \&x;
  2         24  
31             }
32             }
33              
34             sub x {
35 0     0 0   my ($tag, @args) = @_;
36              
37 0           my $chunk = {
38             tag => $tag,
39             attrs => {},
40             };
41              
42 0           for my $arg (@args) {
43 0 0         if (ref $arg eq "HASH") {
44 0           for my $key (keys %$arg) {
45 0 0         if (!defined $arg->{$key}) {
46 0           delete $chunk->{attrs}->{$key};
47             } else {
48 0           $chunk->{attrs}->{$key} = "".$arg->{$key};
49             }
50             }
51             }
52              
53             else {
54 0           push @{$chunk->{sub}}, $arg;
  0            
55             }
56             }
57              
58 0           return bless $chunk, "XML::Spice::Chunk";
59             }
60              
61              
62             package # hide from PAUSE
63             XML::Spice::Chunk;
64              
65 2     2   10 use warnings;
  2         5  
  2         59  
66 2     2   10 use strict;
  2         3  
  2         47  
67              
68 2     2   7 use Carp;
  2         3  
  2         148  
69              
70             use overload
71 2     2   3082 '""' => \&_xml;
  2         2055  
  2         13  
72              
73             my $TIDY_LOADED;
74              
75             sub _xml {
76 0     0     my ($chunk) = @_;
77              
78 0 0 0       if ($PRETTY_PRINT && !defined $TIDY_LOADED) {
79 0           eval { require XML::Tidy::Tiny };
  0            
80 0 0         if ($@) {
81 0           carp "Couldn't load XML::Tidy::Tiny: $@";
82 0           $TIDY_LOADED = 0;
83             }
84             else {
85 0           $TIDY_LOADED = 1;
86             }
87             }
88              
89 0   0       my $WANT_PRETTY = $PRETTY_PRINT && $TIDY_LOADED;
90              
91 0 0 0       return $chunk->{cached} if exists $chunk->{cached} && !$WANT_PRETTY;
92              
93             sub _escape_attr {
94 0     0     my ($val) = @_;
95 0           $val =~ s/'/'/g;
96 0           return $val;
97             }
98              
99             sub _escape_cdata {
100 0     0     my ($val) = @_;
101 0           $val =~ s/&/&/g;
102 0           $val =~ s/
103 0           $val =~ s/>/>/g;
104 0           $val =~ s/"/"/g;
105 0           $val =~ s/([^\x20-\x7E])/'&#' . ord($1) . ';'/ge;
  0            
106 0           return $val;
107             }
108              
109             sub _serialise {
110 0     0     my ($chunk, @things) = @_;
111              
112 0           my $xml = '';
113              
114 0           for my $thing (@things) {
115 0 0         next if ! defined $thing;
116              
117 0 0         if (ref $thing eq "CODE") {
118 0           $thing = &{$thing};
  0            
119              
120 0 0         if (ref $thing eq "HASH") {
121 0           for my $key (keys %$thing) {
122 0 0         if (!defined $thing->{$key}) {
123 0           delete $chunk->{attrs}->{$key};
124             } else {
125 0           $chunk->{attrs}->{$key} = "".$thing->{$key};
126             }
127             }
128 0           undef $thing;
129             }
130              
131 0           redo;
132             }
133              
134 0 0         if (ref $thing eq "ARRAY") {
    0          
135 0           $xml .= $chunk->_serialise(@$thing);
136             }
137              
138             elsif (ref $thing eq "XML::Spice::Chunk") {
139 0           $xml .= $thing->_xml;
140             }
141              
142             else {
143 0 0         next if $thing eq "";
144 0           $xml .= _escape_cdata($thing);
145             }
146             }
147              
148 0           return $xml;
149             }
150              
151 0 0         my $subxml = $chunk->_serialise(@{$chunk->{sub}}) if exists $chunk->{sub};
  0            
152              
153 0           my $xml = "<" . $chunk->{tag};
154              
155 0           for my $attr (keys %{$chunk->{attrs}}) {
  0            
156 0           $xml .= " $attr='" . _escape_attr($chunk->{attrs}->{$attr}) . "'";
157             }
158              
159 0 0         if (!defined $subxml) {
160 0           $xml .= "/>";
161 0 0         $chunk->{cached} = $xml unless $WANT_PRETTY;
162 0 0         return $WANT_PRETTY ? XML::Tidy::Tiny::xml_tidy($xml) : $xml;
163             }
164              
165 0           $xml .= ">" . $subxml . "{tag} . ">";
166              
167 0 0         $chunk->{cached} = $xml unless $WANT_PRETTY;
168 0 0         return $WANT_PRETTY ? XML::Tidy::Tiny::xml_tidy($xml) : $xml;
169             }
170              
171             sub forget {
172 0     0     my ($chunk) = @_;
173              
174 0           delete $chunk->{cached};
175             }
176              
177             1;
178              
179             __END__