Changeset 21998 for lang/perl/MooseX-DOM

Show
Ignore:
Timestamp:
10/24/08 00:34:02 (5 years ago)
Author:
daisuke
Message:

merge the rewrite branch

Location:
lang/perl/MooseX-DOM/trunk
Files:
7 added
6 removed
4 modified

Legend:

Unmodified
Added
Removed
  • lang/perl/MooseX-DOM/trunk/Changes

    r17482 r21998  
    11Changes 
    22======= 
     3 
     40.00999_01 - 23 Oct 2008 
     5  * Major rewrite. Beware! Backwards incompatible! 
     6    Everything is changed, except for the core idea. 
     7    This release should make it really easy to create DOM based objects. 
    38 
    490.00004 - 12 Aug 2008 
  • lang/perl/MooseX-DOM/trunk/Makefile.PL

    r17353 r21998  
    55all_from('lib/MooseX/DOM.pm'); 
    66 
    7 requires 'Class::Inspector'; 
    87requires 'Moose'; 
    9 requires 'XML::LibXML', 1.63; 
    10 requires 'XML::LibXML::XPathContext'; 
     8requires 'XML::LibXML'; 
    119 
    1210test_requires 'Test::More'; 
    1311test_requires 'Test::UseAllModules'; 
    1412 
    15 auto_include; 
     13auto_include_deps; 
    1614WriteAll; 
     15 
  • lang/perl/MooseX-DOM/trunk/lib/MooseX/DOM.pm

    r17482 r21998  
    33package MooseX::DOM; 
    44use strict; 
    5 use Moose::Util; 
    6 use Carp (); 
     5use warnings; 
     6use 5.008; 
     7use MooseX::DOM::Meta::Class; 
    78 
    89our $AUTHORITY = 'cpan:DMAKI'; 
    9 our $VERSION   = '0.00004'; 
    10  
    11 BEGIN { 
    12     my $engine = $ENV{MOOSEX_DOM_ENGINE} || 'MooseX::DOM::LibXML'; 
    13     Class::MOP::load_class( $engine ); 
    14  
    15     constant->import(ENGINE => $engine); 
    16 } 
     10our $VERSION   = '0.00999'; 
    1711 
    1812sub import { 
    1913    my $class = shift; 
    20     my $caller = caller(0); 
    21  
    22     return if $caller eq 'main'; 
    23  
    24     # if $caller is already meta-fied. 
    25     if ( $caller->can('meta') ) { 
    26         Carp::confess "You already have 'meta' initialized. you need to 'use MooseX::DOM' /instead/ of 'use Moose'"; 
    27     } 
    28  
    29     my $engine = &ENGINE; 
    30     $engine->init_meta( $caller ); 
    31     Moose::Util::apply_all_roles($caller->meta, $engine); 
    32     Moose->import( { into => $caller }, @_ ); 
    33  
    34     my $exporter = join('::', $engine, 'export_dsl'); 
    35     goto &$exporter; 
     14    my $caller = caller(); 
     15 
     16    my $backend = 'MooseX::DOM::LibXML'; 
     17    Class::MOP::load_class($backend); 
     18 
     19    Moose::Util::MetaRole::apply_metaclass_roles( 
     20        for_class => $caller, 
     21        metaclass_roles => [ 'MooseX::DOM::Meta::Class' ] 
     22    ); 
     23    $backend->setup($caller); 
     24    $class->export_keywords($caller); 
    3625} 
    3726 
    3827sub unimport { 
    3928    my $class = shift; 
    40  
    41     my $caller = caller(0); 
    42     Moose->unimport( { into => $caller }, @_ ); 
    43  
    44     my $engine = &ENGINE; 
    45     my $unexporter = join('::', $engine, 'unexport_dsl' ); 
    46     goto &$unexporter; 
    47 } 
    48  
    49      
    50  
     29    my $caller = caller(); 
     30 
     31    $class->unexport_keywords($caller); 
     32} 
     33 
     34sub export_keywords { 
     35    my ($class, $caller) = @_; 
     36 
     37    my $exporter = Sub::Exporter::build_exporter({ 
     38        into => $caller, 
     39        groups => { default => [ ':all' ] }, 
     40        exports => [ 
     41            dom_nodes      => sub { $class->build_dom_nodes($caller) }, 
     42            dom_fetchnodes => sub { $class->build_dom_fetchnodes($caller) }, 
     43            dom_to_class   => sub { $class->build_dom_to_class($caller) }, 
     44            dom_value      => sub { $class->build_dom_value($caller) }, 
     45        ] 
     46    }); 
     47    $exporter->($class); 
     48} 
     49 
     50sub unexport_keywords { 
     51    my ($class, $caller) = @_; 
     52    my @keywords = qw(dom_nodes dom_fetchnodes dom_to_class dom_value); 
     53 
     54    { no strict 'refs'; 
     55        foreach my $name (@keywords) { 
     56            if ( defined &{ $caller . '::' . $name }) { 
     57                delete ${ $caller . '::' }{$name}; 
     58            } 
     59        } 
     60    } 
     61} 
     62 
     63sub build_dom_value { 
     64    my ($class, $caller) = @_; 
     65 
     66    return sub { 
     67        my $name = shift; 
     68        my $args = { @_ == 1 ? (fetch => {xpath => $_[0]}) : @_ }; 
     69 
     70        my $fetch = $args->{fetch}; 
     71        my $fetch_xpath = $fetch->{xpath} || $name; 
     72        my $meta = $caller->meta; 
     73        $meta->add_method( 
     74            $name, 
     75            Moose::Meta::Method->wrap( 
     76                package_name => $caller, 
     77                name         => $name, 
     78                body         => sub { 
     79                    my $self = shift; 
     80                    $self->dom_root->findvalue($fetch_xpath); 
     81                } 
     82            ) 
     83        ); 
     84    }; 
     85} 
     86 
     87sub build_dom_nodes { 
     88    my ($class, $caller) = @_; 
     89 
     90    return sub { 
     91        my $name = shift; 
     92        my $args = { @_ == 1 ? (fetch => $_[0]) : @_ }; 
     93 
     94        $args->{into} = $caller; 
     95        my @methods = ( 
     96            $class->build_dom_nodes_accessor($name, $args), 
     97            $class->build_dom_nodes_appender($name, $args), 
     98        ); 
     99 
     100        my $meta = $caller->meta; 
     101        foreach my $method (@methods) { 
     102            $meta->add_method($method->{name}, $method->{code}); 
     103        } 
     104    } 
     105} 
     106 
     107sub build_dom_nodes_appender { 
     108    my ($class, $name, $args) = @_; 
     109 
     110    # I can't figure out this one automatically (I think).  
     111    # just expect a code, and if I can't find it, not methods are 
     112    # returned to the callee 
     113    my $config = ref $args->{append} eq 'HASH' ? $args->{append} :  
     114        { code => $args->{append} }; 
     115    my $method = $config->{name} || "add_$name"; 
     116    my $code = $config->{code}; 
     117    my $ret; 
     118    if ($code) { 
     119        $ret = { 
     120            $method, 
     121            Moose::Meta::Method->wrap( 
     122                package_name => $args->{into}, 
     123                name         => $method, 
     124                body         => $code 
     125            ) 
     126        }; 
     127    } 
     128    return $ret ? $ret : (); 
     129} 
     130 
     131sub build_dom_nodes_accessor { 
     132    my ($class, $name, $args) = @_; 
     133 
     134    my $fetch = $args->{fetch}; 
     135    my $store = $args->{store}; 
     136 
     137    if (! ref $fetch) { 
     138        my $xpath = $fetch; 
     139        $fetch = sub { shift->dom_root->findnodes($xpath) }; 
     140    } 
     141 
     142    my $code = <<"    EOSUB"; 
     143        sub { 
     144            my \$self = shift; 
     145            my \@ret = \$fetch->(\$self); 
     146    EOSUB 
     147    if ($store) { 
     148        $code .= <<"        EOSUB"; 
     149            if (\@_) { 
     150                \$store->(\$self, \@_); 
     151            } 
     152        EOSUB 
     153    } 
     154 
     155    $code .= <<"    EOSUB"; 
     156            return \@ret; 
     157        } 
     158    EOSUB 
     159    my $cv = eval $code; Carp::confess($@) if $@; 
     160 
     161    return { 
     162        name => $name, 
     163        code => Moose::Meta::Method->wrap( 
     164            package_name => $args->{into}, 
     165            name         => $name, 
     166            body         => $cv, 
     167        ) 
     168    }; 
     169} 
     170 
     171sub build_dom_fetchnodes { 
     172    my ($class, $caller) = @_; 
     173 
     174    return sub { 
     175        my $args = {@_ == 1 ? (xpath => $_[0]) : @_}; 
     176        my $filter = $args->{filter}; 
     177        my $xpath  = $args->{xpath}; 
     178        return $filter ? 
     179            sub { 
     180                my $self = shift; 
     181                return $filter->($self->dom_root->findnodes($xpath)); 
     182            } : 
     183            sub { 
     184                my $self = shift; 
     185                return $self->dom_root->findnodes($xpath); 
     186            } 
     187    }; 
     188} 
     189 
     190sub build_dom_to_class { 
     191    my ($class, $caller) = @_; 
     192 
     193    return sub { 
     194        my $args = {@_ == 1 ? (to_class => $_[0]) : @_}; 
     195        my $to_class = $args->{to_class}; 
     196        Class::MOP::load_class($to_class); 
     197        return sub { 
     198            map { $to_class->new($_) } @_; 
     199        } 
     200    } 
     201} 
    512021; 
    52203 
     
    55206=head1 NAME 
    56207 
    57 MooseX::DOM - Simplistic Object XML Mapper 
     208MooseX::DOM - Easily Create DOM Based Objects 
    58209 
    59210=head1 SYNOPSIS 
    60211 
    61   package MyObject; 
    62   use MooseX::DOM; 
    63   
    64   has_dom_child 'title'; 
    65  
    66   no Moose; 
    67   no MoooseX::DOM; 
    68  
    69   my $obj = MyObject->new(node => <<EOXML); 
    70   <feed> 
    71     <title>Foo</title> 
    72   </feed> 
    73   EOXML 
    74  
    75   print $obj->title(), "\n"; # Foo 
    76   $obj->title('Bar'); 
    77   print $obj->title(), "\n"; # Bar 
     212    package RSS; 
     213    use Moose; 
     214    use MooseX::DOM; 
     215 
     216    dom_value 'version' => '@version'; 
     217    dom_nodes 'items' => ( 
     218        fetch => dom_fetchnodes( 
     219            xpath => 'channel/item', 
     220            filter => dom_to_class('RSS::Item') 
     221        ) 
     222    ); 
     223 
     224    # or, easy way (just get some DOM nodes) 
     225    # dom_nodes 'items' => 'channel/items'; 
     226 
     227    # or, create your own way to fetch the nodes 
     228    # dom_nodes 'items' => ( 
     229    #     fetch => sub { ... } 
     230    # ); 
     231 
     232    no Moose; 
     233    no MooseX::DOM; 
     234 
     235    package RSS::Item; 
     236    use Moose; 
     237    use MooseX::DOM; 
     238 
     239    dom_value 'title'; 
     240    dom_value 'description'; 
     241    dom_value 'link'; 
     242 
     243    no Moose; 
     244    no MooseX::DOM; 
     245 
     246    sub BUILDARGS { 
     247        my $class = shift; 
     248        my $args  = {@_ == 1? (dom_root => $_[0]) : @_}; 
     249        return $args; 
     250    } 
     251 
     252    package main; 
     253 
     254    # parse_file() is automatically created for you. 
     255    my $rss = RSS->parse_file('rss.xml'); 
     256    foreach my $item ($rss->items) { 
     257        print "item link  = ", $item->link, "\n"; 
     258        print "item title = ", $item->title, "\n"; 
     259    } 
    78260 
    79261=head1 DESCRIPTION 
    80262 
    81 This module is intended to be used in conjunction with other modules 
    82 that encapsulate XML data (for example, XML feeds). 
    83  
    84 =head1 DECLARATION 
    85  
    86 =head2 has_dom_root $name[, %opts] 
    87  
    88 Specifies that the given XML have the specified tag. This specification is 
    89 also used when creating new root node for creating the underlying XML 
    90  
    91   has_dom_root $name => ( 
    92     # attributes => { ... } 
    93   ); 
    94  
    95 =head2 has_dom_attr $name[, %opts] 
    96  
    97 Specifies that the object should contain an attribute by the given name 
    98  
    99 =head2 has_dom_child $name[, %opts] 
    100  
    101 Specifies that the object should contain a single child by the given name. 
    102 Will generate accessor that can handle set/get 
    103  
    104   has_dom_child 'foo'; 
    105  
    106   $obj->foo(); # get the value of child element foo 
    107   $obj->foo("bar"); # set the value of child element foo to bar 
    108  
    109 %opts may contain C<namespace>, C<tag>, and C<filter> 
    110  
    111 Specifying C<namespace> forces MooseX::DOM to look for tags in a specific 
    112 namespace uri. 
    113  
    114 Specifying C<tag> allows MooseX::DOM to look for the tag name given in C<tag> 
    115 while making the generated method name as C<$name> 
    116  
    117 The optional C<filter> parameter should be a subroutine that takes the object  
    118 itself as the first parameter, and the DOM node(s) as the rest of the  
    119 parameters.  You are allowed to transform the node as you like. By default,  
    120 a filter that converts the node to its text content is used. 
    121  
    122   has_dom_child 'foo' => ( 
    123     filter => sub { 
    124       my ($self, $node) = @_; 
    125       # return whatever you want to return, perhaps transforming $node 
    126     } 
    127   ); 
    128  
    129 The optional C<create> parameter should be a subroutine that does the 
    130 does the actual insertion of the new node, given the arguments. 
    131 By default it expects a list of text argument, and creates a child node 
    132 with those arguments. 
    133  
    134   has_dom_child 'foo' => ( 
    135     create => sub { 
    136       my($self, %args) = @_; 
    137       # keys in %args: 
    138       #   child 
    139       #   namespace 
    140       #   tag 
    141       #   value 
    142     } 
    143   ); 
    144  
    145 =head2 has_dom_children  
    146  
    147 Specifies that the object should contain possibly multiple children by the 
    148 given name 
    149  
    150   has_dom_children 'foo'; 
    151  
    152   $obj->foo(); # Returns a list of values for each child element foo 
    153   $obj->foo(qw(1 2 3)); # Discards old values of foo, and create new nodes 
    154  
    155 %opts may contain C<namespace>, C<tag>, C<filter>, and C<create> 
    156  
    157 The optional C<namespace> parameter forces MooseX::DOM to look for tags in a  
    158 specific namespace uri. 
    159  
    160 The optional C<tag> parameter allows MooseX::DOM to look for the tag name given  
    161 in C<tag> while making the generated method name as C<$name> 
    162  
    163 The optional C<filter> parameter should be a subroutine that takes the object  
    164 itself as the first parameter, and the DOM node(s) as the rest of the  
    165 parameters.  You are allowed to transform the node as you like. By default,  
    166 a filter that converts the node to its text content is used. 
    167  
    168   has_dom_children 'foo' => ( 
    169     filter => sub { 
    170       my ($self, @nodes) = @_; 
    171       # return whatever you want to return, perhaps transforming @nodes 
    172     } 
    173   ); 
    174  
    175 The optional C<create> parameter should be a subroutine that does the 
    176 does the actual insertion of the new nodes, given the arguments. 
    177 By default it expects a list of text arguments, and creates child nodes 
    178 with those arguments. 
    179  
    180   has_dom_children 'foo' => ( 
    181     create => sub { 
    182       my($self, %args) = @_; 
    183       # keys in %args: 
    184       #   children 
    185       #   namespace 
    186       #   tag 
    187       #   values 
    188     } 
    189   ); 
    190  
    191 =head2 has_dom_content $name 
    192  
    193 If your node only contains text data (that is, your root node does not have any 
    194 subsequent element nodes as its child), you can access the text data directly 
    195 with this declaration 
     263MooseX::DOM is a tool that allows you to define classes that are based on 
     264XML DOM. 
     265 
     266=head1 PROVIDED DSL 
     267 
     268The following DSL is provided upon calling C<MooseX::DOM>. When  
     269C<no MooseX::DOM> is used, these functions are removed from your namespace. 
     270 
     271=head2 dom_nodes $name => %spec 
     272 
     273Declares that a method named $name should be built, using the given spec. 
     274Returns a list of nodes, or what the filter argument trasnlates them to. 
     275 
     276If %spec is omitted, $name is taken to be the xpath to fetch. 
     277 
     278=head2 dom_value $name => %spec 
     279 
     280Declares that a method named $name should be built, using the given spec. 
     281Returns the result of the fetch, whatever that may be. 
     282 
     283If %spec is omitted, $name is taken to be the xpath to fetch. 
     284 
     285=head2 dom_fetchnodes %spec 
     286 
     287Creates a closure that fetches some nodes 
     288 
     289=head2 dom_to_class %spec 
     290 
     291Creates a closure that transforms nodes to something else, typically an object. 
     292 
     293=head1 PROVIDED METHODS 
     294 
     295The following methods are built onto your class automatically. 
     296 
     297=head2 parse_file 
     298 
     299=head2 parse_string 
     300 
     301=head2 parse_fh 
     302 
     303These methods allow you to parse a piece of XML, and build a MooseX::DOM 
     304object based on it. 
     305 
     306=head2 dom_findnodes($xpath) 
     307 
     308Does a DOM XPath lookup. Returns a plain DOM object. 
     309 
     310=head2 dom_findvalue($xpath) 
     311 
     312Does a DOM XPath lookup. Returns whatever value the XPath results to. 
    196313 
    197314=head1 AUTHOR 
  • lang/perl/MooseX-DOM/trunk/lib/MooseX/DOM/LibXML.pm

    r17756 r21998  
    22 
    33package MooseX::DOM::LibXML; 
    4 use Moose::Role; 
    5 use MooseX::DOM::LibXML::ContextNode; 
    6 use MooseX::DOM::Meta::LibXML; 
    7 use MooseX::DOM::Meta::LibXML::Attribute::Content; 
     4# I want to use MooseX::Singleton, but it generates some warnings, 
     5# so thi is a plain moose class with package level global 
     6use Moose; 
     7use MooseX::DOM::LibXML::XPathContext; 
    88 
    9 use constant DEFAULT_NAMESPACE_PREFIX => "#default"; 
    10  
    11 has 'node' => ( 
     9has 'dom_root_attr' => ( 
    1210    is => 'rw', 
    13     isa => 'MooseX::DOM::LibXML::ContextNode', 
    14     coerce => 1, 
     11    isa => 'Moose::Meta::Attribute', 
     12    lazy    => 1, 
     13    builder => 'build_dom_root_attr' 
    1514); 
    1615 
    17 has 'namespaces' => ( 
    18     is => 'rw', 
    19     isa => 'HashRef', 
    20     required => 1, 
    21     default => sub { +{} } 
    22 ); 
     16my @METHODS = qw(parse_file parse_string dom_findvalue dom_findnodes); 
     17foreach my $method (@METHODS) { 
     18    my $attr    = "${method}_method"; 
     19    my $builder = "build_${method}_method"; 
    2320 
    24 no Moose; 
    25  
    26 sub BUILD { 
    27     my $self = shift; 
    28     $self->node->register_namespaces( $self->namespaces ); 
    29     $self; 
    30 } 
    31  
    32 sub init_meta { 
    33     # Only MooseX::DOM knows the true caller, so we expect it to 
    34     # provide us with one 
    35     my ($class, $caller) = @_; 
    36  
    37     Moose::init_meta(  
    38         $caller,  
    39         undef, 
    40         'MooseX::DOM::Meta::LibXML' 
     21    has $attr=> ( 
     22        is => 'rw', 
     23        isa => 'Moose::Meta::Method', 
     24        lazy    => 1, 
     25        builder => $builder 
    4126    ); 
    4227} 
    4328 
    44 BOOTSTRAP: { 
    45     my $subname = sub { join('::', $_[1] || __PACKAGE__, $_[0]) }; 
    46     my $subassign = sub { 
    47         no strict 'refs'; 
    48         *{$_[0]} = Class::MOP::subname($_[0], $_[1]); 
    49     }; 
     29__PACKAGE__->meta->make_immutable; 
     30     
     31no Moose; 
    5032 
    51     # Used to convert element node to its text content 
    52     my $textfilter = sub { 
    53         my $self = shift; 
    54         return map { blessed $_ && $_->can('textContent') ? $_->textContent : $_ } @_; 
    55     }; 
     33our $INSTANCE; 
    5634 
    57     # Used only in has_dom_children, to create a list of element nodes from 
    58     # list of text 
    59     my $text2elements = Class::MOP::subname($subname->('text2elements') => sub { 
    60         my($self, %args) = @_; 
     35sub setup { 
     36    my ($class, $caller) = @_; 
    6137 
    62         my $values = $args{values}; 
    63         my $namespace = $args{namespace} || ''; 
    64         my $tag = $args{tag}; 
     38    my $meta = $caller->meta; 
     39    my $self = ($INSTANCE ||= $class->new()); 
     40    $self->add_attributes($meta); 
     41    $self->add_methods($meta); 
     42    $meta->backend($self); 
     43} 
    6544 
    66         my $node = $self->node; 
     45sub add_attributes { 
     46    my ($self, $meta) = @_; 
    6747 
    68         my $nsuri = $self->namespaces->{ $namespace }; 
    69         my $document = $node->ownerDocument; 
    70         my @children; 
    71         foreach my $data (@$values) { 
    72             my $child = ($nsuri) ? 
    73                 $document->createElementNS($nsuri, $tag) : 
    74                 $document->createElement($tag) 
    75             ; 
    76             $child->appendTextNode($data); 
    77             push @children, $child; 
    78             $node->appendChild($child); 
    79         } 
    80         return @children; 
    81     }); 
     48    $meta->add_attribute( $self->dom_root_attr ); 
     49} 
    8250 
    83     my %exports = ( 
    84         has_dom_root => sub { 
    85             return Class::MOP::subname($subname->('has_dom_root') => sub ($;%) { 
    86                 my $caller = caller(); 
    87                 my ($tag, %args) = @_; 
    88                 # tag => $tag 
    89                 # attributes => { attr1 => $val1, attr2 => $val2 } 
     51sub add_methods { 
     52    my ($self, $meta) = @_; 
    9053 
    91                 # Find the meta attribute for 'node', and add type constraints 
    92                  
    93                 $tag = $args{tag} if $args{tag}; 
    94                 my $attrs = $args{attributes}; 
    95  
    96                 my $meta = $caller->meta; 
    97                 my $node_attr = $meta->get_attribute('node'); 
    98                 return () unless $node_attr; 
    99  
    100  
    101                 $meta->dom_root( { tag => $tag, attributes => $attrs } ); 
    102  
    103                 # This needs to be done here so that the /applied/ class 
    104                 # can use it instead of this class, which is a role 
    105                 $meta->add_around_method_modifier(new => sub { 
    106                     my $next = shift; 
    107                     my $self = $next->(@_); 
    108                     $self->meta->assert_root_node($self); 
    109                     return $self; 
    110                 }); 
    111                 $meta->add_after_method_modifier(node => sub { 
    112                     my $self = shift; 
    113                     if (@_) { 
    114                         $self->meta->assert_root($self, @_); 
    115                     } 
    116                 }); 
    117             }); 
    118         }, 
    119         has_dom_content => sub { 
    120             return Class::MOP::subname($subname->('has_dom_content') => sub ($;%) { 
    121                 my ($name, %args) = @_; 
    122                 my $caller = caller(); 
    123                 my $meta = $caller->meta; 
    124                 my $metaclass = 'MooseX::DOM::Meta::LibXML::Attribute::Content'; 
    125                 my $attr = Moose::Meta::Attribute->interpolate_class_and_new( 
    126                     $name, 
    127                     metaclass => $metaclass, 
    128                     is        => 'rw', 
    129                     tag       => $name, 
    130                     accessor  => $name, 
    131                     %args, 
    132                 ); 
    133                 $meta->add_attribute( $attr ); 
    134             }); 
    135         }, 
    136         has_dom_attr => sub { 
    137             return Class::MOP::subname($subname->('has_dom_attr') => sub ($;%) { 
    138                 my $caller = caller(); 
    139                 my ($name, %args) = @_; 
    140  
    141                 my $meta = $caller->meta; 
    142                 my $metaclass = 'MooseX::DOM::Meta::LibXML::Attribute::Attribute'; 
    143      
    144                 my $attr = Moose::Meta::Attribute->interpolate_class_and_new( 
    145                     $name, 
    146                     metaclass => $metaclass, 
    147                     is        => 'rw', 
    148                     accessor  => $name, 
    149                     %args, 
    150                 ); 
    151                 $meta->add_attribute( $attr ); 
    152             }); 
    153         }, 
    154         has_dom_children => sub { 
    155             return Class::MOP::subname($subname->('has_dom_children') => sub ($;%) { 
    156                 my ($name, %args) = @_; 
    157                 my $caller = caller(); 
    158                 my $meta = $caller->meta; 
    159                 my $metaclass = 'MooseX::DOM::Meta::LibXML::Attribute::Element'; 
    160                 my $attr = Moose::Meta::Attribute->interpolate_class_and_new( 
    161                     $name, 
    162                     metaclass => $metaclass, 
    163                     is        => 'rw', 
    164                     accessor  => $name, 
    165                     tag       => $name, 
    166                     create    => $text2elements, 
    167                     filter    => $textfilter, 
    168                     %args, 
    169                 ); 
    170                 $meta->add_attribute( $attr ); 
    171             }); 
    172         }, 
    173         has_dom_child => sub { 
    174             return Class::MOP::subname($subname->('has_dom_child') => sub ($;%) { 
    175                 my ($name, %args) = @_; 
    176                 my $caller = caller(); 
    177                 my $meta = $caller->meta; 
    178                 my $metaclass = 'MooseX::DOM::Meta::LibXML::Attribute::Element'; 
    179                 my $attr = Moose::Meta::Attribute->interpolate_class_and_new( 
    180                     $name, 
    181                     metaclass => $metaclass, 
    182                     is        => 'rw', 
    183                     accessor  => $name, 
    184                     tag       => $name, 
    185                     create    => $text2elements, 
    186                     filter    => $textfilter, 
    187                     single    => 1, 
    188                     %args, 
    189                 ); 
    190                 $meta->add_attribute( $attr ); 
    191             }); 
    192         } 
    193     ); 
    194  
    195     my $export = Sub::Exporter::build_exporter({ 
    196         exports => \%exports, 
    197         groups  => { default => [ ':all' ] } 
    198     }); 
    199     sub export_dsl { 
    200         goto &$export if $export; 
    201     } 
    202  
    203     sub unexport_dsl { 
    204         no strict 'refs'; 
    205         my $class = caller(); 
    206  
    207         # loop through the exports ... 
    208         foreach my $name ( keys %exports ) { 
    209  
    210             # if we find one ... 
    211             if ( defined &{ $class . '::' . $name } ) { 
    212                 my $keyword = \&{ $class . '::' . $name }; 
    213  
    214                 # make sure it is from Moose 
    215                 my ($pkg_name) = Class::MOP::get_code_info($keyword); 
    216                 next if $pkg_name ne __PACKAGE__; 
    217  
    218                 # and if it is from Moose then undef the slot 
    219                 delete ${ $class . '::' }{$name}; 
    220             } 
    221         } 
     54    foreach my $method (@METHODS) { 
     55        my $attr = "${method}_method"; 
     56        $meta->add_method($method => $self->$attr); 
    22257    } 
    22358} 
    22459 
    225 sub from_xml { 
    226     my $class = shift; 
    227     return $class->new(node => XML::LibXML->new->parse_string($_[0])->documentElement); 
    228 } 
    229 sub from_file { 
    230     my $class = shift; 
    231     return $class->new(node => XML::LibXML->new->parse_file($_[0])->documentElement); 
     60sub build_dom_root_attr { 
     61    Moose::Meta::Attribute->new( 
     62        dom_root => ( 
     63            is     => 'rw', 
     64            isa    => 'MooseX::DOM::LibXML::XPathContext', 
     65            coerce => 1, 
     66        ) 
     67    ) 
    23268} 
    23369 
    234 sub as_xml { 
     70sub build_parse_file_method { 
    23571    my $self = shift; 
    236     $self->node->toString(1); 
     72    return Moose::Meta::Method->wrap( 
     73        package_name => Scalar::Util::blessed $self, 
     74        name         => 'parse_file', 
     75        body         => sub { 
     76            my ($class, $file) = @_; 
     77            $class->new(dom_root =>  XML::LibXML->new->parse_file($file)); 
     78        } 
     79    ); 
    23780} 
    23881 
     82sub build_parse_string_method { 
     83    my $self = shift; 
     84    return Moose::Meta::Method->wrap( 
     85        package_name => Scalar::Util::blessed $self, 
     86        name         => 'parse_string', 
     87        body         => sub { 
     88            my ($class, $string) = @_; 
     89            $class->new(dom_root =>  XML::LibXML->new->parse_string($string)); 
     90        } 
     91    ); 
     92} 
     93 
     94sub build_dom_findvalue_method { 
     95    my $self = shift; 
     96    return Moose::Meta::Method->wrap( 
     97        package_name => Scalar::Util::blessed $self, 
     98        name         => 'dom_findvalue', 
     99        body         => sub { 
     100            my ($self, $args) = @_; 
     101            if (! ref $args) { 
     102                $args = { xpath => $args }; 
     103            } 
     104            $self->dom_root->findvalue($args->{xpath}); 
     105        } 
     106    ); 
     107} 
     108 
     109sub build_dom_findnodes_method { 
     110    my $self = shift; 
     111    return Moose::Meta::Method->wrap( 
     112        package_name => Scalar::Util::blessed $self, 
     113        name         => 'dom_findnodes', 
     114        body         => sub { 
     115            my ($self, $args) = @_; 
     116            if (! ref $args) { 
     117                $args = { xpath => $args }; 
     118            } 
     119            $self->dom_root->findnodes($args->{xpath}); 
     120        } 
     121    ); 
     122} 
    239123 
    2401241; 
    241