File Coverage

blib/lib/Class/DBI/ToSax.pm
Criterion Covered Total %
statement 85 85 100.0
branch 27 32 84.3
condition 18 27 66.6
subroutine 11 11 100.0
pod 1 3 33.3
total 142 158 89.8


line stmt bran cond sub pod time code
1             package Class::DBI::ToSax;
2             # @(#) $Id: ToSax.pm,v 1.25 2003/10/14 15:11:01 dom Exp $
3              
4             # There's a bug in UNIVERSAL::isa() in 5.6.0 :(
5 2     2   46581 use 5.006001;
  2         7  
  2         59  
6 2     2   9 use strict;
  2         5  
  2         48  
7 2     2   9 use warnings;
  2         6  
  2         94  
8              
9             our $VERSION = '0.10';
10              
11 2     2   8 use base qw( Class::Data::Inheritable );
  2         4  
  2         1633  
12              
13 2     2   673 use Carp qw( croak );
  2         3  
  2         137  
14 2     2   1548 use NEXT;
  2         9844  
  2         1809  
15              
16             sub _emit_sax_value {
17 28     28   523 my $self = shift;
18 28         67 my ( $handler, $col, $val, %opt ) = @_;
19 28 100 66     131 if ( ref( $val ) && $val->can( 'to_sax' ) ) {
20             # Record ourselves for our children.
21 14   100     51 $opt{ _ancestors } ||= [];
22 14         15 push @{ $opt{ _ancestors } }, $self;
  14         27  
23 14         48 $val->to_sax( $handler, %opt );
24             } else {
25 14         47 my $data = {
26             LocalName => $col,
27             Name => $col,
28             NamespaceURI => '',
29             Prefix => '',
30             };
31 14         58 $handler->start_element( $data );
32 14 100 66     806 $val = '' if !defined $val || length $val == 0;
33 14         53 $handler->characters( { Data => $val } );
34 14         90 $handler->end_element( $data );
35             }
36             }
37              
38             our %seen;
39             sub to_sax {
40 23     23 1 6431 my $self = shift;
41 23         33 my $class = ref $self;
42 23         49 my ( $handler, %opt ) = @_;
43 23 50 33     185 croak "usage: to_sax(handler,opt)\n"
      33        
44             unless $handler && ref $handler && $handler->can( 'start_element' );
45              
46             # NB: Hack alert! Calling this in array context /should/ work
47             # correctly in all versions of Class::DBI, whether before or after
48             # MCPK support was added.
49 23         56 my @pk = $self->primary_column;
50             # Avoid a warning with an undef id. In reality, this should never
51             # happen, but I've got non-database-backed objects that get
52             # created without and id. So I have to be careful here.
53 23 50       309 my $id = join '/', map { defined $_ ? $_ : '' } $self->get( @pk );
  24         176  
54 23         73 my $table = $class->table;
55 23 50       148 my $toplevel = $opt{ notoplevel } ? 0 : !scalar %seen;
56 23   66     71 my $wrapper = delete $opt{ wrapper } || $self->table;
57              
58             # Ensure that we never have the same class twice in the call stack.
59 23 100       139 return if $seen{ "$table-$id" };
60 21         51 local %seen = %seen;
61 21         44 $seen{ "$table-$id" }++;
62              
63 21 100       103 $handler->start_document( {} ) if $toplevel;
64 21         2289 my $table_data = {
65             Name => $wrapper,
66             LocalName => $wrapper,
67             NamespaceURI => '',
68             Prefix => '',
69             Attributes => {
70             '{}id' => {
71             LocalName => 'id',
72             Name => 'id',
73             NamespaceURI => '',
74             Prefix => '',
75             Value => $id,
76             },
77             },
78             };
79 21         90 $handler->start_element( $table_data );
80              
81 21 100 100     1253 if ( $toplevel || $self->_stop_recursion( %opt ) ) {
82 14 100       21 my %has_a = map { $_ => 1 } @{ $self->_has_a_methods || [] };
  9         119  
  14         45  
83 14         61 my %pk = map { $_ => 1 } @pk;
  15         33  
84 14   100     34 my @plain = grep { !$pk{ $_ } && !$has_a{ $_ } } $self->columns;
  37         254  
85              
86 14         47 foreach my $col ( sort @plain ) {
87 13         43 $self->_emit_sax_value( $handler, $col, $self->$col, %opt );
88             }
89              
90 14         1177 foreach my $col ( sort keys %has_a ) {
91 9         26 $self->_emit_sax_value( $handler, $col, $self->$col, %opt,
92             wrapper => $col );
93             }
94              
95 14 100       52 foreach my $col ( sort @{ $self->_has_many_methods || [] } ) {
  14         53  
96             $self->_emit_sax_value( $handler, $col, $_, %opt )
97 7         59 foreach $self->$col;
98             }
99             }
100              
101 21         322 $handler->end_element( $table_data );
102 21 100       1002 $handler->end_document( {} ) if $toplevel;
103             }
104              
105             # If this function returns true, we won't recurse into this object,
106             # leaving just a reference to the object. There are a number of ways in
107             # which to take this decision...
108             sub _stop_recursion {
109 12     12   15 my $self = shift;
110 12         24 my ( %opt ) = @_;
111 12 100       40 return 1 unless exists $opt{ norecurse };
112              
113 8         12 my $norecurse = $opt{ norecurse };
114 8 100       28 if ( !ref $norecurse ) {
    100          
    50          
115             # A simple true scalar will stop all recursion.
116 2         11 return !$norecurse;
117             } elsif ( ref $norecurse eq 'HASH' ) {
118             # If the hash entry for this table is true, stop the recursion.
119 2         5 return !$norecurse->{ $self->table };
120             } elsif ( ref $norecurse eq 'CODE' ) {
121             # If we've been given a lambda, punt the decision to it. Note
122             # that the return code is the reverse of what actually happens,
123             # in order to make it similiar to the hash ref case.
124 4 50       6 my @ancestors = @{ $opt{ _ancestors } || [] };
  4         10  
125 4         11 return !$norecurse->( @ancestors, $self );
126             }
127             }
128              
129             # Override has_many() so that we can capture the method name.
130             __PACKAGE__->mk_classdata( '_has_many_methods' );
131             sub has_many {
132 1     1 0 153 my $class = shift;
133 1         2 my ( $method ) = @_;
134 1   50     8 my $method_list = $class->_has_many_methods || [];
135 1         12 push @$method_list, $method;
136 1         3 $class->_has_many_methods( $method_list );
137 1         34 return $class->NEXT::has_many( @_ );
138             }
139              
140             # Ditto for has_a relationships.
141             __PACKAGE__->mk_classdata( '_has_a_methods' );
142             sub has_a {
143 2     2 0 4922 my $class = shift;
144 2         3 my ( $method ) = @_;
145 2   50     13 my $method_list = $class->_has_a_methods || [];
146 2         19 push @$method_list, $method;
147 2         16 $class->_has_a_methods( $method_list );
148 2         55 return $class->NEXT::has_a( @_ );
149             }
150              
151             1;
152             __END__