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