File Coverage

blib/lib/PRANG/Graph.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1              
2             package PRANG::Graph;
3             $PRANG::Graph::VERSION = '0.19';
4 7     7   2499753 use Moose::Role;
  7         25656  
  7         30  
5 7     7   33592 use Moose::Meta::TypeConstraint;
  7         17  
  7         123  
6              
7 7     7   2197 use PRANG;
  7         16  
  7         153  
8 7     7   2044 use PRANG::Graph::Context;
  7         22  
  7         274  
9              
10 7     7   2910 use PRANG::Graph::Node;
  7         19  
  7         182  
11 7     7   2277 use PRANG::Graph::Element;
  0            
  0            
12             use PRANG::Graph::Text;
13             use PRANG::Graph::Seq;
14             use PRANG::Graph::Choice;
15             use PRANG::Graph::Quantity;
16              
17             use PRANG::Graph::Meta::Attr;
18             use PRANG::Graph::Meta::Element;
19             use MooseX::Params::Validate;
20              
21             use PRANG::Marshaller;
22              
23             use Moose::Exporter;
24              
25             sub has_attr {
26             my ( $meta, $name, %options ) = @_;
27             my $traits_a = $options{traits} ||= [];
28             push @$traits_a, "PRANG::Attr";
29             $meta->add_attribute(
30             $name,
31             %options,
32             );
33             }
34              
35             sub has_element {
36             my ( $meta, $name, %options ) = @_;
37             my $traits_a = $options{traits} ||= [];
38             push @$traits_a, "PRANG::Element";
39             $meta->add_attribute(
40             $name,
41             %options,
42             );
43             }
44              
45             Moose::Exporter->setup_import_methods(
46             with_meta => [qw(has_attr has_element)],
47             class_metaroles => {
48             class => [qw(PRANG::Graph::Meta::Class)],
49             },
50             );
51              
52              
53             requires 'xmlns';
54             requires 'root_element';
55              
56             sub marshaller { #returns PRANG::Marshaller {
57             my $inv = shift;
58            
59             if ( ref $inv ) {
60             $inv = ref $inv;
61             }
62             PRANG::Marshaller->get($inv);
63             }
64              
65             sub parse {
66             my $class = shift;
67            
68             my ( $xml, $lax ) = pos_validated_list(
69             \@_,
70             { isa => 'Str' },
71             { isa => 'Bool', optional => 1, default => 0 },
72             );
73              
74             my $instance = $class->marshaller->parse( xml => $xml, lax => $lax );
75             return $instance;
76             }
77              
78             sub parse_file {
79             my $class = shift;
80             my ( $filename, $lax ) = pos_validated_list(
81             \@_,
82             { isa => 'Str' },
83             { isa => 'Bool', optional => 1, default => 0 },
84             );
85            
86             my $instance = $class->marshaller->parse( filename => $filename, lax => $lax );
87             return $instance;
88             }
89              
90             sub parse_fh() {
91             my $class = shift;
92             my ( $fh, $lax ) = pos_validated_list(
93             \@_,
94             { isa => 'GlobRef' },
95             { isa => 'Bool', optional => 1, default => 0 },
96             );
97            
98             my $instance = $class->marshaller->parse( fh => $fh, lax => $lax );
99             return $instance;
100             }
101              
102             sub from_dom {
103             my $class = shift;
104             my ( $dom, $lax ) = pos_validated_list(
105             \@_,
106             { isa => 'XML::LibXML::Document' },
107             { isa => 'Bool', optional => 1, default => 0 },
108             );
109              
110             my $instance = $class->marshaller->from_dom( dom => $dom, lax => $lax );
111             return $instance;
112             }
113              
114             sub from_root_node {
115             my $class = shift;
116             my ( $root_node, $lax ) = pos_validated_list(
117             \@_,
118             { isa => 'XML::LibXML::Node' },
119             { isa => 'Bool', optional => 1, default => 0 },
120             );
121              
122             my $instance = $class->marshaller->from_root_node( root_node => $root_node, lax => $lax );
123             return $instance;
124             }
125              
126             sub to_xml() {
127             my $self = shift;
128             my ( $format ) = pos_validated_list(
129             \@_,
130             { isa => 'Int', default => 0 },
131             );
132            
133             my $marshaller = $self->marshaller;
134             $marshaller->to_xml( $self, $format );
135             }
136              
137             1;
138              
139             =head1 NAME
140              
141             PRANG::Graph - XML mapping by peppering Moose attributes
142              
143             =head1 SYNOPSIS
144              
145             # declaring a /language/
146             package My::XML::Language;
147             use Moose;
148             use PRANG::Graph;
149             sub xmlns { "some:urn" }
150             sub root_element { "Root" }
151             with 'PRANG::Graph';
152             has_element "data" =>
153             is => "ro",
154             isa => "My::XML::Language::Node",
155             ;
156              
157             # declaring a /node/ in a language
158             package My::XML::Language::Node;
159             use Moose;
160             use PRANG::Graph;
161             has_attr "count" =>
162             is => "ro",
163             isa => "Num",
164             ;
165             has_element "text" =>
166             is => "ro",
167             isa => "Str",
168             xml_nodeName => "",
169             ;
170              
171             package main;
172             # example document for the above.
173             my $xml = q[<Root xmlns="some:urn"><data count="2">blah</data></Root>];
174              
175             # loading XML to data structures
176             my $parsed = My::XML::Language->parse($xml);
177              
178             # alternatives
179             $parsed = My::XML::Language->parse_file($filename);
180             $parsed = My::XML::Language->parse_fh($fh);
181             $parsed = My::XML::Language->from_dom($dom); # $dom isa XML::LibXML::Document
182              
183             # converting back to XML
184             print $parsed->to_xml;
185              
186             =head1 DESCRIPTION
187              
188             PRANG::Graph allows you to mark attributes on your L<Moose> classes as
189             corresponding to XML attributes and child elements. This allows your
190             class structure to function as an I<XML graph> (a generalized form of
191             an specification for the shape of an XML document; ie, what nodes and
192             attributes are allowed at which point).
193              
194             =head2 PRANG::Graph for document types
195              
196             If a class implements a document type, that is, it is a valid root
197             node for your language, it B<must> both C<use> and implement the
198             C<PRANG::Graph> role, and implement the required functions C<xmlns>
199             and C<root_element>;
200              
201             package XML::Language;
202             use Moose;
203             use PRANG::Graph;
204             sub xmlns { }
205             sub root_element { "rootNode" }
206             with 'PRANG::Graph';
207              
208             If no URL is returned by the C<xmlns> function, XML namespaces are not
209             permitted in the input documents.
210              
211             =head2 PRANG::Graph for element types
212              
213             If a class implements an element, that is, a node which appears
214             somewhere other than the root node of your language, it B<must> C<use>
215             the C<PRANG::Graph> package. Well, actually, it must apply the PRANG
216             class trait, but using the package will also define the useful
217             functions C<has_attr> and C<has_element>, so most users will want
218             that.
219              
220             The minimum is;
221              
222             package XML::Language::SomeElement;
223             use PRANG::Graph;
224              
225             Note, the name of the node is not defined in the class itself, it is
226             defined in the class which includes it, using the C<has_element>
227             method. See L<PRANG::Graph::Meta::Element> for more information.
228              
229             =head2 PRANG::Graph for multi-root document types
230              
231             If your language has multiple valid root elements, then you must
232             define one document type for each valid root element. These must all
233             implement a particular role, which should bundle the C<PRANG::Graph>
234             role. The common role should not define C<root_element>, but probably
235             should define C<xmlns> in most cases:
236              
237             eg,
238              
239             package XML::Language::Family;
240             use Moose::Role;
241             sub xmlns { }
242             with 'PRANG::Graph';
243              
244             package XML::Language::One;
245             use Moose;
246             use PRANG::Graph;
247             sub root_element { "one" }
248             with 'XML::Language::Family';
249              
250             package XML::Language::Two;
251             use Moose;
252             use PRANG::Graph;
253             sub root_element { "two" }
254             with 'XML::Language::Family';
255              
256             =head2 PRANG::Graph for plug-in element types
257              
258             Normally, the details on the node name of elements is in the class
259             which includes those elements, not the target classes. However, it is
260             also possible to refer to roles instead of classes. The first time
261             the parser encounters this, it sees which loaded classes implement
262             that role, and then builds the map from element name to class.
263              
264             The classes should implement the C<PRANG::Graph> role, effectively
265             defining a document type.
266              
267             The effect of specifying a role as a type is to immediate search all
268             I<loaded packages> to see which consume the specified role.
269              
270             Given the example in the previous section, if you used:
271              
272             has_element 'some_attr' =>
273             is => "ro",
274             isa => "XML::Language::Family",
275             ;
276              
277             It would be the same as:
278              
279             has_element 'some_attr' =>
280             is => "ro",
281             isa => "XML::Language::One|XML::Language::Two",
282             xml_nodeName => {
283             one => "XML::Language::One",
284             two => "XML::Language::Two",
285             },
286             ;
287              
288             Otherwise, the requirements are the same as for regular element
289             definition as described in L<PRANG::Graph::Meta::Element>. In
290             particular, two types cannot share the same C<xmlns> I<and>
291             C<root_element>; that would be ambiguous.
292              
293             =head1 EXPORTS
294              
295             These exports are delivered to the class which says C<use
296             PRANG::Graph;>
297              
298             =head2 has_attr
299              
300             This is the same as declaring an attribute using the regular Moose
301             C<has> keyword, but adds the C<PRANG::Attr> trait, declared in
302             L<PRANG::Graph::Meta::Attr> - the attribute will behave just like a
303             regular Moose attribute, but the marshalling machinery will convert it
304             to and from a particular XML attribute (or even multiple attributes).
305             See L<PRANG::Graph::Meta::Attr> for more information.
306              
307             =head2 has_element
308              
309             Same as C<has_attr>, except it adds the C<PRANG::Element> trait,
310             indicating to the marshalling machinery to emit an XML node or
311             sequence of nodes. See L<PRANG::Graph::Meta::Element>.
312              
313             =head1 METHODS
314              
315             These methods are defined in, or required classes which implement the
316             C<PRANG::Graph> I<role>. In general, that means they are a document
317             type.
318              
319             =head2 B<parse($class: Str $xml, Bool $lax) returns Object>
320              
321             Parse an XML string according to the PRANG Graph and return the built
322             object. Throws exceptions on error.
323              
324             By example, this is:
325              
326             my $object = $class->parse($xml);
327            
328             The $lax param tells PRANG to use 'lax' parsing mode. This ignores
329             any extra elements or attributes in the XML that haven't been
330             specified in your definition classes. This is useful if you're
331             parsing XML from a trusted source, but that source may introduce
332             new elements and attributes. Using lax mode, your parser will
333             be forwards compatible for any additions.
334              
335             Without lax mode, any attributes or elements not defined in your
336             classes will cause PRANG to throw an exception.
337              
338             =head2 B<to_xml(PRANG::Graph $object: Int $format = 0) returns Str>
339              
340             Converts an object to an XML string. The returned XML will include
341             the XML declaration and so on.
342             Output will be indented by LibXML2 if the C<format> parameter is set
343             to 1. The default is 0 (do not indent). See
344             L<XML::LibXML::Document/toString> for other valid values.
345              
346             By example, this is:
347              
348             my $xml = $object->to_xml;
349              
350             =head2 B<xmlns(Moose::Meta::Class $class:) returns Maybe[Str]>
351              
352             This is a B<required class method> which returns the XML namespace of
353             this document type. This is used for emitting, and when parsing the
354             input namespace must generally be either unset or match this XML
355             namespace.
356              
357             If you are not using namespaces, just define the method in your class,
358             and return a false value.
359              
360             =head2 B<encoding() returns Maybe[Str]>
361              
362             Class method that defines the encoding for this class when emitting XML.
363             Defaults to 'UTF-8'.
364              
365             =head2 B<root_element(Moose::Meta::Class $class:) returns Str>
366              
367             This is a B<required class method> which returns the XML name of the
368             element which corresponds to this document type.
369              
370             =head1 HOW IT WORKS
371              
372             These details are not important for regular use of PRANG, however if
373             you can understand this you will grok the module much more quickly.
374              
375             This class applies a I<trait> to your classes' metaclass. This means,
376             that when you C<use PRANG::Graph>, there is an implied;
377              
378             use Moose -traits => ["PRANG"];
379              
380             Which is something like;
381              
382             PRANG::Graph::Meta::Class->meta->apply(__PACKAGE__->meta);
383              
384             That sets up the metaclass to be capable of being used by the
385             marshalling machinery. This machinery expects Moose attributes which
386             have the C<PRANG::Element> or C<PRANG::Attr> traits applied to connect
387             XML attributes and elements to object attributes. These are in turn
388             implemented by the L<PRANG::Graph::Meta::Attr> and
389             L<PRANG::Graph::Meta::Element> classes.
390              
391             Applying the L<PRANG::Graph> role happens separately, and delivers a
392             separate set of super-powers. It is roughly equivalent to;
393              
394             PRANG::Graph->meta->apply(__PACKAGE__);
395              
396             So, the key difference between these two aspects are the source
397             package, and the destination meta-object; in one, it is the class, in
398             the other, the metaclass.
399              
400             =head1 SEE ALSO
401              
402             L<PRANG>, L<PRANG::Graph::Meta::Class>, L<PRANG::Graph::Meta::Attr>,
403             L<PRANG::Graph::Meta::Element>, L<PRANG::Graph::Node>
404              
405             =head1 AUTHOR AND LICENCE
406              
407             Development commissioned by NZ Registry Services, and carried out by
408             Catalyst IT - L<http://www.catalyst.net.nz/>
409              
410             Copyright 2009, 2010, NZ Registry Services. This module is licensed
411             under the Artistic License v2.0, which permits relicensing under other
412             Free Software licenses.
413              
414             =cut
415