The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
NAME
    XML::LibXML::Augment - extend XML::LibXML::{Attr,Element,Document} on a
    per-namespace/element basis

SYNOPSIS
     {
       package Local::Element::Bar;
   
       use 5.010;
       use strict;
       use warnings;
       use XML::LibXML::Augment -names => ['{http://example.com/}bar'];
   
       sub tellJoke
       {
         say q{A man walked into a bar.};
         say q{"Ouch," he said.};
         say q{It was an iron bar.};
       }
     }
 
     {
       package main;
   
       use 5.010;
       use strict;
       use warnings;
       use XML::LibXML::Augment;
   
       my $doc = XML::LibXML->load_xml(string => <<'XML');
       <foo xmlns="http://example.com/">
         <bar baz="1" />
       </foo>
       XML
   
       XML::LibXML::Augment->upgrade($doc);
       $doc->findnodes('//*[@baz]')->shift->tellJoke;
     }

DESCRIPTION
    XML::LibXML is super-awesome. However, I don't know about you, but
    sometimes I wish it had some domain-specific knowledge. For example, if I
    have an XML::LibXML::Element which represents an HTML `<form>` element,
    why can't it have a `submit` method?

    OK, so I can subclass XML::LibXML::Element, but then I call `childNodes`
    on my subclass, and get back plain, non-subclassed objects, and I'm back
    where I first began.

    XML::LibXML::Augment is the package I've been meaning to write for quite
    some time, to take care of all those issues.

    The magic is in the import method. You write a package which imports
    XML::LibXML::Augment with particular settings. Then, once you've parsed
    the document, you call `XML::LibXML::Augment->upgrade` on the root node.

  `import %args`
    Currently three options are supported. Each has a leading hyphen.

   `-names => \@list`
    Each item on the list is the name of an element you want to override. If
    the element is in a namespace, use Clark notation:

     {http://www.example.com/namespace}localname

    If the element is not in a namespace, then leave out the curly braces.
    Because of clashes (see "Conflict resolution" in CAVEATS below) it seems
    like a bad idea to try to augment non-namespaced elements based entirely
    on their localname. But, hey, you want to do it? It's your funeral.

    You can use "*" as the localname to indicate that you wish to subclass all
    elements in a particular namespace. (Again, see "Conflict resolution".)

    Yes, this is a list, because it might make sense to use the same package
    to cover, say, HTML `<a>`, `<link>` and `<area>` elements.

   `-type => $type`
    $type can be either 'Element', 'Attr' or 'Document', but defaults to
    'Element'. This indicates what sort of thing you're subclassing. Only
    elements, attributes and documents are supported. (Document subclassing is
    based on the namespace and localname of its root element.)

    Elements, attributes and documents are pairwise disjoint classes, so you
    cannot (for example) subclass elements and attributes in the same package.

   `-isa => \@packages`
    Normally the import routine will automatically establish your package as a
    subclass of XML::LibXML::Augment::Attr or XML::LibXML::Augment::Element by
    monkeying around with your @ISA.

    There are times when you want more precise control over @ISA though, such
    as inheritance heirarchies:

      Local::HTML::TableHeader
         -> Local::HTML::TableCell
            -> Local::HTML::Element
               -> XML::LibXML::Augment::Element

    In this case, you probably want to use the automatic subclassing for
    Local::HTML::Element, but not for the other two classes, which would work
    better with explicit subclassing:

     {
       package Local::HTML::TableCell;
       use XML::LibXML::Augment
         -names => ['{http://www.w3.org/1999/xhtml}td'],
         -isa   => ['Local::HTML::Element'];
     }

  `upgrade(@things)`
    This is a function, not a method. It's not exported, so call it with its
    full name:

     XML::LibXML::Augment::upgrade(@things);

    Upgrades the things in-place, skipping over things that cannot be
    upgraded, and returns the things as a list. You can of course call it like
    this:

     @upgraded = XML::LibXML::Augment->upgrade(@things);

    But bear in mind that because of the way Perl method calls work, this is
    effectively the same as:

     @upgraded = (
       'XML::LibXML::Augment',
       XML::LibXML::Augment::upgrade(@things),
       );

    What is upgrading? Things that are not blessed objects are not upgradable.
    Blessed objects that XML::LibXML::Augment can find an appropriate subclass
    for are reblessed into that package (e.g. XML::LibXML::Comment is
    reblessed into XML::LibXML::Augment::Comment). The nodes in
    XML::LibXML::NodeLists are reblessed.

  `rebless($thing)`
    This is basically a single-argument version `upgrade` but designed to be
    called as a class method, and doesn't recurse into nodelists.

      my $upgraded = XML::LibXML::Augment->rebless($element);

    Note that $element is actually upgraded in-place.

      refaddr($upgraded) == refaddr($element); # true

  `ideal_class_for_object($object)`
    Calculates the class that `rebless` would bless the object into, but
    doesn't actually do the reblessing.

  `make_class(@superclasses)`
    Constructs a new class that is a subclass of the given classes. Call this
    as a class method. Returns the class name. This is a method used
    internally by XML::LibXML::Augment, documented in case anybody else wants
    to use it.

  `BLESS`
    XML::LibXML::Augment doesn't actually have a method called `BLESS`, but
    your package can do.

    Inspired by Moose's `BUILD` method, your package's `BLESS` method will be
    called (if it exists) just after an XML::LibXML node is reblessed into
    your package. Unlike Moose's `BUILD` method, the inheritance chain isn't
    automatically walked. It is your package's responsibility to call
    `SUPER::BLESS` if required.

    Do bear in mind that XML::LibXML's node objects are little more than
    pointers to the "real" XML nodes that live on the C side of the XS divide.
    As such, XML::LibXML can (and frequently does) destroy and re-instantiate
    the pointers willy-nilly. This may limit the usefulness of `BLESS`.

HOW DOES IT WORK?
    Mostly just careful use of inheritance.

CAVEATS
  Conflict resolution
    Only one class can handle any given element. If two different modules want
    to subclass, say, XHTML `<form>` elements, there can be only one winner.
    So, which one wins? Neither. XML::LibXML::Augment creates a brand,
    spanking new class which inherits from both, and that new class wins. This
    will usually work, but may trip up sometimes. The `joke.pl` example
    bundled with the XML-LibXML-Augment release gives a demonstration of this
    feature.

    Note that packages which use an element wildcard, for instance:

     package Local::HTML::Element;
     use XML::LibXML::Augment -names => ['{http://www.w3.org/1999/xhtml}*'];

    are treated purely as fallbacks. If there exists a non-wildcard class to
    handle an element, then the wildcard class will be ignored altogether - it
    won't be included in the funky on-the-fly class generation described
    above.

    For these reasons, wildcards are perhaps best avoided. It's usually better
    to do something like:

     package Local::HTML::Element;
     our @ELEMENTS;
     BEGIN {
       @ELEMENTS = map { "{http://www.w3.org/1999/xhtml}$_" }
         qw/a area b col colgroup ... u/;
     }
     use XML::LibXML::Augment -names => \@ELEMENTS;

  XML::LibXML::XPathContext
    We don't touch XML::LibXML::XPathContext. Results from calling, e.g.
    `findnodes` on an XPath context will return plain old XML::LibXML nodes.
    (You can of course upgrade them.) That doesn't render XPath completely
    unusable though - XML::LibXML::Node also has a `findnodes` method, which
    will return upgraded objects.

  Subclassing all elements (or all attributes)
    XML::LibXML::Augment requires you to specify the namespace URI and
    localname of the elements/attributes you wish to subclass. If you want to
    provide additional methods to all XML::LibXML::Elements, then perhaps
    XML::LibXML::Augment is not for you. Try:

     sub XML::LibXML::Element::myAwesomeMethod {
       ...
     }

    If you think adding methods to other peoples' classes is evil, then go
    write some Java and quit complaining.

BUGS
    Please report any bugs to
    <http://rt.cpan.org/Dist/Display.html?Queue=XML-LibXML-Augment>.

SEE ALSO
    XML::LibXML.

AUTHOR
    Toby Inkster <tobyink@cpan.org>.

COPYRIGHT AND LICENCE
    This software is copyright (c) 2012, 2014 by Toby Inkster.

    This is free software; you can redistribute it and/or modify it under the
    same terms as the Perl 5 programming language system itself.

DISCLAIMER OF WARRANTIES
    THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
    WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
    MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.