File Coverage

blib/lib/XML/Writer/Nest.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package XML::Writer::Nest;
2              
3             our $VERSION = '1.0';
4              
5 1     1   24965 use Moose;
  0            
  0            
6             has 'tag' => (isa => 'Str', is => 'ro', required => 1);
7             has 'attr' => (isa => 'ArrayRef[Maybe[Str]]', is => 'ro', default => sub { [] } ); # hashref wont preserve order!
8             has 'writer' => (isa => 'XML::Writer', is => 'ro', required => 1);
9              
10              
11             use XML::Writer;
12              
13             sub nest {
14             my($self, $tag, @attr)=@_;
15              
16             my @nattr;
17              
18             if (scalar @attr and ref $attr[0] eq 'ARRAY') {
19             @nattr = @{$attr[0]};
20             } else {
21             @nattr = @attr;
22             }
23              
24             XML::Writer::Nest->new(tag => $tag, attr => \@nattr, writer => $self->writer);
25             }
26            
27            
28              
29             sub BUILD {
30             my($self)=@_;
31              
32             my @attr = defined($self->attr) ? @{$self->attr} : () ;
33              
34             #warn "Writer" . $self->writer;
35            
36             $self->writer->startTag($self->tag, @attr);
37              
38             $self;
39             }
40            
41             sub DEMOLISH {
42             my($self)=@_;
43              
44             $self->writer->endTag();
45             }
46              
47              
48              
49             =head1 NAME
50              
51             XML::Writer::Nest - dataElement() for when you need to embed elements, not data
52              
53              
54              
55              
56             =head1 SYNOPSIS
57              
58             use XML::Writer::Nest;
59              
60             my $writer = new XML::Writer;
61              
62             { my $level1 = XML::Writer::Nest->new(tag => 'level1', attr => [ hee => 'haw', fee => 'fi' ], writer => $writer );
63              
64             { my $level2 = $level1->nest(level2 => [ attr1 => 3 ] ); # or call the class conc. again.
65            
66             { my $level3 = $level2->nest('level3');
67              
68             } # endTag created automatically
69              
70             } # endTag created automatically
71              
72             } # endTag created automatically
73              
74             Vanilla L<XML::Writer> would not have indentation and you would have to manually close your start tags:
75              
76             $writer->startTag("level1");
77             $writer->startTag("level2");
78             $writer->startTag("level3");
79             $writer->endTag();
80             $writer->endTag();
81             $writer->endTag();
82              
83              
84              
85              
86             =head1 DESCRIPTION
87              
88             When nesting XML elements with XML::Writer, you have to manually close your startTags.
89             Also, you dont necessarily have any visual feedback via indentation for each level
90             of tag nesting.
91              
92             C<< XML::Writer::Nest >> solves both of those problems.
93              
94             =head2 XML::Generator
95              
96             L<XML::Generator|XML::Generator> solves this problem a different way. But
97             I dont see an easy way to make use of object-oriented dispatch to specialize
98             and generalize XML production with it.
99              
100             My current module and Moose's C<inner> work just fine together.
101              
102              
103             =head1 API
104              
105             There is a class-level constructor and an object-level constructor. The class level constructor
106             requires 3 arguments (tag, attributes, and C<XML::Writer> instance). The object-level
107             constructor only requires tag and attribute arguments - it passes along the
108             C<XML::Writer> instance.
109              
110             NOTE: This module operates based on lexical scope. So both object and class level construction
111             are done right after creating a new lexical scope with braces.
112              
113             =head2 Class-based constructor
114              
115             { my $xml_nest = XML::Writer::Nest->new(tag => 'tagname', attr => \@attr, writer => $xml_writer);
116              
117             # add some additional things for this nest level via $xml_writer->api_calls
118             } # when $xml_nest goes out of scope, it calls $xml_writer->endTag automatically
119              
120             =head2 Object-based constructor
121              
122             { my $xml_nest2 = $xml_nest->nest(tagname => \@attr);
123              
124             # add some additional things for this nest level via $xml_writer->api_calls
125             } # when $xml_nest2 goes out of scope, it calls $xml_writer->endTag automatically
126              
127             { my $xml_nest2 = $xml_nest->nest(tagname => @attr);
128              
129             # add some additional things for this nest level via $xml_writer->api_calls
130             } # when $xml_nest2 goes out of scope, it calls $xml_writer->endTag automatically
131              
132             Please note: the object-level constructor will B<either> an arrayref or array of attributes.
133             The class-based constructor will take B<only> an B<arrayref> of attributes.
134              
135              
136             =head1 DISCUSSION
137              
138             =head2 Caveat emptor
139              
140             If you wish to nest elements at the same level ("sibling elements"), then you must brace each:
141              
142             #!/usr/bin/perl
143              
144             use strict;
145             use XML::Writer::Nest;
146              
147             my $output;
148             my $writer = new XML::Writer(OUTPUT => $output);
149             my $main = new XML::Writer::Nest(tag => 'main', writer => $writer);
150            
151             {
152             my $head = $main->nest('head');
153            
154             }
155             {
156             my $body = $main->nest('body');
157             }
158              
159             print STDOUT $output . "\n\n";
160              
161              
162             =head2 XML::Generator
163              
164             L<XML::Generator|XML::Generator> is another module which allows for automatic creation of closing tags based
165             on behavior of the Perl programming language.
166              
167             From what I can see, one is not able to leverage object-oriented re-use of parts of the XML generation by
168             delegating specialized aspects of the rendering to subclasses.
169              
170             Concretely, Moose's augment function has demonstrated a way of allowing generic and specific aspects of
171             XML generation to co-operate.
172              
173             Therefore, I like Moose in combination with XML::Writer for object-oriented XML production. However,
174             the automatic creation of closing XML tags by XML::Generator is quite attractive. Not only that, but the
175             automatic source-code indentation is especially handy when you are creating highly nested XML.
176              
177             Another thing I don't like about XML::Generator is that you must use one highly nested function call to produce the
178             output document. I prefer brace-levels and a series of calls to the XML::Writer interface.
179              
180             =head2 Practical Comparison
181              
182             Let's take the synopsis example from XML::Generator and write it in all 3 approaches. First let's take a look at
183             the desired XML output.
184              
185             <foo xmlns:qux="http://qux.com/">
186             <bar baz="3">
187             <bam />
188             </bar>
189             <qux:bar>Hey there, world</qux:bar>
190             </foo>
191              
192             =head3 XML::Generator
193              
194             use XML::Generator ':pretty';
195              
196             print foo(bar({ baz => 3 }, bam()),
197             bar([ 'qux' => 'http://qux.com/' ],
198             "Hey there, world"));
199              
200              
201              
202              
203             =head2
204              
205             =head1 AUTHOR
206              
207             Terrence Brannon, C<< <metaperl at gmail.com> >>
208              
209             =head1 SEE ALSO
210              
211             =head2 "Constructive Use of Destructors"
212              
213             L<http://www.metaperl.org/publications>
214              
215             This talk to the Columbus, OH Perl mongers discusses XML::Writer::Nest in detail.
216              
217              
218             =head1 BUGS
219              
220             Please report any bugs or feature requests to C<bug-xml-writer-nest at rt.cpan.org>, or through
221             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=XML-Writer-Nest>. I will be notified, and then you'll
222             automatically be notified of progress on your bug as I make changes.
223              
224              
225              
226              
227             =head1 SUPPORT
228              
229             You can find documentation for this module with the perldoc command.
230              
231             perldoc XML::Writer::Nest
232              
233              
234             You can also look for information at:
235              
236             =over 4
237              
238             =item * RT: CPAN's request tracker
239              
240             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=XML-Writer-Nest>
241              
242             =item * AnnoCPAN: Annotated CPAN documentation
243              
244             L<http://annocpan.org/dist/XML-Writer-Nest>
245              
246             =item * CPAN Ratings
247              
248             L<http://cpanratings.perl.org/d/XML-Writer-Nest>
249              
250             =item * Search CPAN
251              
252             L<http://search.cpan.org/dist/XML-Writer-Nest/>
253              
254             =back
255              
256              
257             =head1 ACKNOWLEDGEMENTS
258              
259             Many thanks to #moose, especially Jeese Luehrs (doy)!, matt trout, doy, and confound.
260              
261              
262              
263             =head1 COPYRIGHT & LICENSE
264              
265             Copyright 2009 Terrence Brannon, all rights reserved.
266              
267             This program is free software; you can redistribute it and/or modify it
268             under the same terms as Perl itself.
269              
270              
271             =cut
272              
273             1; # End of XML::Writer::Nest