File Coverage

blib/lib/XML/Tag.pm
Criterion Covered Total %
statement 51 59 86.4
branch 16 24 66.6
condition 6 7 85.7
subroutine 9 9 100.0
pod 2 3 66.6
total 84 102 82.3


line stmt bran cond sub pod time code
1             #! /usr/bin/perl
2             package XML::Tag;
3 4     4   124239 use strict;
  4         9  
  4         187  
4 4     4   23 use warnings;
  4         7  
  4         268  
5             our $VERSION = '0.4';
6              
7             # ABSTRACT: lib to build builders for xml content
8              
9             sub import {
10 4     4   29 shift;
11 4         14 my ( $caller ) = caller;
12 4     4   20 no strict 'refs';
  4         18  
  4         1885  
13 4         8 my @tags = do {
14 4 50       18 if (@_) {@_}
  0         0  
15 4         11 else { qw< tag ns as_xml > }
16             };
17 4         10 for (@tags) { *{"$caller\::$_"} = \&{$_} }
  12         13  
  12         1850  
  12         25  
18             }
19              
20             sub tag {
21 7     7 1 853 my ( $tag, $code, $attrs ) = @_;
22 7 50       22 my %attr = $attrs ? %$attrs : ();
23 7 50       19 my @data = $code ? $code->() : ();
24              
25             # TODO: what if blessed ?
26 7         31 while (my $ref = ref $data[0] ) {
27 0 0       0 $ref eq 'HASH' or die "$ref cant hold xml attributes";
28 0         0 my $news = shift @data;
29 0         0 while ( my ( $k, $v ) = each %$news ) { push @{ $attr{$k} }, $v; }
  0         0  
  0         0  
30             }
31              
32 0 0       0 my @content =
33             ( '<'
34             , $tag
35             , ( keys %attr
36             ? ( map {
37             # yeah: i know that this code can lead to stuttering xml like
38             # class="foo foo foo bar"
39             # frankly ? i don't care :-)
40 7 50       31 ' '
    100          
41             , $_
42             , '='
43 0         0 , ( map {ref $_ ? qq{"@$_"} : qq("$_") } $attr{$_} )
44             } keys %attr )
45             : ()
46             )
47             , ( @data
48             ? ( '>', @data, '')
49             : '/>'
50             )
51             );
52              
53 7 100       14 if (wantarray) { @content }
  4         34  
54 3         13 else { join '', @content }
55              
56             }
57              
58             sub ns {
59 4     4 1 20931 my ( $ns, $pkg ) = do {
60 4         7 my $first = shift;
61 4 100 100     7 if ( ref $first ) { map {$_||=''} @$first }
  3         8  
  4         14  
62 1         2 else { $first, $first } # xml ns = perl package
63             };
64 4   66     23 $pkg ||= caller;
65 4 100       11 $ns and $ns.=':'; # add namespace separator
66              
67 4   100     23 $_//='' for $ns, $pkg;
68              
69 4         7 for my $spec ( @_ ) {
70 10         11 my ( $sub, $tag ) = do {
71 10 100       15 if ( ref $spec ) { @$spec }
  3         4  
72 7         12 else { $spec, $spec }
73             };
74 4     4   26 no strict 'refs';
  4         7  
  4         1304  
75 10     3   1696 *{"${pkg}::$sub"} = sub (&) { tag "$ns$tag", @_ }
  3         24  
76 10         45 }
77             }
78              
79             sub as_xml (_);
80             sub as_xml (_) {
81 2     2 0 12 my $entry = shift;
82 2         2 my @render;
83 2         8 while ( my ($tag,$v) = each %$entry ) {
84 3 100       16 push @render
85             , "<$tag>"
86             , ( ref $v ? as_xml $v : $v )
87             , ""
88             };
89 2         8 join '', @render;
90             }
91              
92             1;
93              
94             =head1 XML::Tag, a simple XML builder
95              
96             Builders are a set of helpers to generate the tag content. I see 3 major gains
97             using this strategy over templating systems:
98              
99             =over 2
100              
101             =item *
102              
103             keep the power of perl in your hands (don't abuse it and respect at least
104             an MVC separation)
105              
106             =item *
107              
108             don't be WYSIWYG. When i write code, i need indentations and line feeds
109             to make things readable. All those extra stuff must disapear in the final
110             result because they are useless and anoying when you manage to control spaces
111             with CSS.
112              
113             =item *
114              
115             stay confident about the quality of generated code: as long as they
116             compiles, the helpers render bug free xml (WARNING: the quality of all PCDATA,
117             attribute values and schemas is *your* job)
118              
119             =back
120              
121             L
122             or see it in action:
123              
124             To render this text on C.
125              
126             my personal homepage
127              
128             you can use directly the C function from XML::Tag
129              
130             use XML::Tag;
131             use Modern::Perl;
132              
133             print ''
134             , tag html => sub {
135             tag head => sub {
136             tag title => sub { +{lang => 'fr'}, "my personal homepage" }
137             }
138             }
139              
140             you can use the C function from XML::Tag to generate the helpers
141              
142             use XML::Tag;
143             use Modern::Perl;
144              
145             BEGIN {
146             ns '' # use the default namespace
147             , qw< html head title >
148             }
149              
150             print '', html {
151             head {
152             title { +{lang => 'fr'}, "my personal homepage" }
153             }
154             }
155              
156             you can even use a ready to use set of helpers
157              
158             use XML::Tag::html5;
159             print '', html {
160             head {
161             title { +{lang => 'fr'}, "my personal homepage" }
162             }
163             }
164              
165             =head2 XML::Tag functions
166              
167             =head3 tag
168              
169             =head3 ns
170              
171             =head3 tag $name, $content, $attrs
172              
173             the parameters of tag are
174              
175             =over 2
176              
177             =item *
178              
179             $name: the name of the tag
180              
181             =item *
182              
183             $content:
184              
185             a sub returning th
186              
187             * content sub
188             * a hashref with the list of default attributes for the tag
189              
190             =item *
191              
192             $name ???
193              
194             =back
195              
196             perl -MXML::Tag -E '
197             print "($_)" for tag title => sub { "content" }, +{qw(class test)};
198             '
199              
200             (<)(title)( )(class)(=)("test")(>)(content)()
201              
202              
203             tag title => sub { "content" }, +{qw(class test)}
204             tag title => sub { +{qw(class test)}, "content" }
205              
206             use XML::Tag;
207             print for tag title => sub { "content" }, +{qw(class test)};
208              
209              
210             use XML::Tag;
211             print for tag title => sub { "content" }, +{qw(class test)};
212              
213             use XML::Tag;
214             tag title => sub { "content" }, +{qw(class test)}
215             tag title => sub { +{qw(class test)}, "content" }
216              
217              
218             the content sub returns a list, the first elements of the lists are
219              
220             use Modern::Perl;
221             use XML::Tag;
222              
223             sub foo (&) { tag foo => @_, {qw< isa foo >} }
224              
225             print foo{
226             + {qw< class bar id bang >}
227             , {qw< style text-align:center >}
228             , "this is "
229             , "the content"
230             };
231              
232             =head2 how to build tag list
233              
234             extract_elements () {
235             xmlstarlet sel -T -t -m '//xs:element/@name' -v . -n "$@"
236             }
237              
238             schema=http://dublincore.org/schemas/xmls/simpledc20021212.xsd
239             curl -ls "$schema" | extract_elements
240