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