File Coverage

blib/lib/HTML/Widget/Plugin/Struct.pm
Criterion Covered Total %
statement 51 51 100.0
branch 13 22 59.0
condition 7 15 46.6
subroutine 11 11 100.0
pod 2 2 100.0
total 84 101 83.1


line stmt bran cond sub pod time code
1 2     2   111235 use strict;
  2         3  
  2         60  
2 2     2   10 use warnings;
  2         5  
  2         98  
3             package HTML::Widget::Plugin::Struct;
4             # ABSTRACT: dump data structures for CGI::Expand expansion
5             $HTML::Widget::Plugin::Struct::VERSION = '0.005';
6 2     2   1474 use parent qw(HTML::Widget::Plugin);
  2         539  
  2         12  
7              
8             #pod =head1 DESCRIPTION
9             #pod
10             #pod This plugin provides a means to dump a (somewhat) complex Perl data structure
11             #pod to hidden widgets which can then be reconstructed by L.
12             #pod
13             #pod =cut
14              
15 2     2   12226 use Scalar::Util ();
  2         5  
  2         1673  
16              
17             #pod =method provided_widgets
18             #pod
19             #pod This plugin provides the following widgets: struct
20             #pod
21             #pod =cut
22              
23 2     2 1 32497 sub provided_widgets { qw(struct) }
24              
25             #pod =method struct
26             #pod
27             #pod C is the only widget provided by this plugin. It accepts four
28             #pod arguments:
29             #pod
30             #pod * name - the base name for the widget (required, will default to id if given)
31             #pod * id - the base id for the widget (optional)
32             #pod * class - a class to apply to each element generated (optional)
33             #pod * value - the structure to represent
34             #pod
35             #pod The value can be an arbitrarily deep structure built from simple scalars, hash
36             #pod references, and array references. The inclusion of any other kind of data will
37             #pod cause an exception to be raised.
38             #pod
39             #pod References which appear twice will be treated as multiple occurances of
40             #pod identical structures. It won't be possible to tell that they were originally
41             #pod references to the same datum. Any circularity in the structure will cause an
42             #pod exception to be raised.
43             #pod
44             #pod =cut
45              
46             sub struct {
47 5     5 1 15681 my ($self, $factory, $arg) = @_;
48              
49 5 50 33     28 $arg->{attr}{name} = $arg->{attr}{id}
50             if ! defined $arg->{attr}{name} and defined $arg->{attr}{id};
51              
52 5 50 33     46 Carp::croak "no name provided for struct widget" unless
53             defined $arg->{attr}{name} and length $arg->{attr}{name};
54              
55 5 50       16 return unless defined $arg->{value};
56              
57 5         8 my $ref_stack = [];
58              
59 5         16 $self->_build_struct($factory, $arg, $ref_stack);
60             }
61              
62             my %DUMPER_FOR = (
63             '' => '_build_scalar_struct',
64             HASH => '_build_hash_struct',
65             ARRAY => '_build_array_struct',
66             );
67              
68             sub _build_struct {
69 24     24   38 my ($self, $factory, $arg, $ref_stack) = @_;
70              
71 24 50       87 return '' unless defined $arg->{value};
72              
73 5         205 Carp::croak "looping data structure detected while dumping struct"
74             if ref $arg->{value}
75 24 100 100     80 and grep { $_ == Scalar::Util::refaddr($arg->{value}) } @$ref_stack;
76              
77 23         57 $self->_assert_value_ok($arg->{value});
78              
79 23         59 my $method = $DUMPER_FOR{ ref $arg->{value} };
80              
81 23         64 return $self->$method($factory, $arg, $ref_stack);
82             }
83              
84             sub _build_scalar_struct {
85 16     16   23 my ($self, $factory, $arg) = @_;
86              
87 16         125 return $factory->hidden({
88             name => $arg->{attr}{name},
89             id => $arg->{attr}{id},
90             value => $arg->{value},
91             class => $arg->{attr}{class},
92             });
93             }
94              
95             sub _build_hash_struct {
96 3     3   5 my ($self, $factory, $arg, $ref_stack) = @_;
97              
98 3   33     10 my $has_id = defined $arg->{attr}{id} && length $arg->{attr}{id};
99              
100 3         5 my $widget = '';
101 3         9 push @$ref_stack, Scalar::Util::refaddr($arg->{value});
102 3         4 for my $key (keys %{ $arg->{value} }) {
  3         8  
103 5 50       677 $widget .= $self->_build_struct(
104             $factory,
105             {
106             value => $arg->{value}{$key},
107             attr => {
108             ($has_id ? (id => "$arg->{attr}{id}.$key") : ()),
109             name => "$arg->{attr}{name}.$key",
110             class => $arg->{attr}{class},
111             },
112             },
113             $ref_stack,
114             );
115             }
116 3         897 pop @$ref_stack;
117 3         17 return $widget;
118             }
119              
120             sub _build_array_struct {
121 4     4   9 my ($self, $factory, $arg, $ref_stack) = @_;
122              
123 4   33     17 my $has_id = defined $arg->{attr}{id} && length $arg->{attr}{id};
124              
125 4         9 my $widget = '';
126 4         16 push @$ref_stack, Scalar::Util::refaddr($arg->{value});
127 4         8 for my $index (0 .. $#{ $arg->{value} }) {
  4         21  
128 14 50       2967 next unless defined $arg->{value}[$index];
129 14 50       105 $widget .= $self->_build_struct(
130             $factory,
131             {
132             value => $arg->{value}[$index],
133             attr => {
134             name => "$arg->{attr}{name}.$index",
135             ($has_id ? (id => "$arg->{attr}{id}.$index") : ()),
136             class => $arg->{attr}{class},
137             },
138             },
139             $ref_stack,
140             );
141             }
142 3         275 pop @$ref_stack;
143 3         18 return $widget;
144             }
145              
146             sub _assert_value_ok {
147 23     23   27 my ($self, $value) = @_;
148              
149 23 100       60 return unless length (my $ref = ref $value);
150 7 50       32 Carp::croak "can't widgetize objects" if Scalar::Util::blessed($value);
151 7 50       26 Carp::croak "can't serialize $ref references" unless $DUMPER_FOR{ $ref };
152             }
153              
154             #pod =head1 TODO
155             #pod
156             #pod =for :list
157             #pod * improve the test suite
158             #pod
159             #pod =cut
160              
161             1;
162              
163             __END__