File Coverage

blib/lib/HTML/Widget/Plugin/Select.pm
Criterion Covered Total %
statement 60 60 100.0
branch 27 28 96.4
condition 8 9 88.8
subroutine 12 12 100.0
pod 6 6 100.0
total 113 115 98.2


line stmt bran cond sub pod time code
1 15     15   9192 use strict;
  15         30  
  15         378  
2 15     15   73 use warnings;
  15         23  
  15         746  
3             package HTML::Widget::Plugin::Select;
4             # ABSTRACT: a widget for selection from a list
5             $HTML::Widget::Plugin::Select::VERSION = '0.204';
6 15     15   76 use parent 'HTML::Widget::Plugin';
  15         26  
  15         120  
7              
8             #pod =head1 SYNOPSIS
9             #pod
10             #pod $widget_factory->select({
11             #pod id => 'the-selector', # if no name attr given, defaults to id value
12             #pod value => 10,
13             #pod options => [
14             #pod [ 0 => "Zero" ],
15             #pod [ 5 => "Five" ],
16             #pod [ 10 => "Ten" ],
17             #pod ],
18             #pod });
19             #pod
20             #pod =head1 DESCRIPTION
21             #pod
22             #pod This plugin provides a select-from-list widget.
23             #pod
24             #pod The C attribute may be used to add a default class to every
25             #pod produced input. This class cannot be overridden.
26             #pod
27             #pod my $plugin = HTML::Widget::Factory::Input->new({
28             #pod default_classes => [ qw(foo bar) ],
29             #pod });
30             #pod
31             #pod =head1 METHODS
32             #pod
33             #pod =head2 C< provided_widgets >
34             #pod
35             #pod This plugin provides the following widgets: select
36             #pod
37             #pod =cut
38              
39 17     17 1 49 sub provided_widgets { qw(select) }
40              
41             #pod =head2 C< select >
42             #pod
43             #pod This method returns a select-from-list widget.
44             #pod
45             #pod In addition to the generic L attributes, the following
46             #pod are valid arguments:
47             #pod
48             #pod =over
49             #pod
50             #pod =item disabled
51             #pod
52             #pod If true, this option indicates that the select widget can't be changed by the
53             #pod user.
54             #pod
55             #pod =item ignore_invalid
56             #pod
57             #pod If this is given and true, an invalid value is ignored instead of throwing an
58             #pod exception.
59             #pod
60             #pod =item options
61             #pod
62             #pod This may be an arrayref of arrayrefs, each containing a value/name/option
63             #pod tuple, or it may be a hashref of values and names.
64             #pod
65             #pod Use the array form if you need multiple entries for a single value or if order
66             #pod is important, or to provide per-select-option options. The only valid option
67             #pod is C.
68             #pod
69             #pod =item value
70             #pod
71             #pod If this argument is given, the option with this value will be pre-selected in
72             #pod the widget's initial state.
73             #pod
74             #pod An exception will be thrown if more or less than one of the provided options
75             #pod has this value.
76             #pod
77             #pod =back
78             #pod
79             #pod =cut
80              
81 15     15   1296 use HTML::Element;
  15         28  
  15         91  
82              
83 33     33   117 sub _attribute_args { qw(disabled) }
84 82     82   229 sub _boolean_args { qw(disabled) }
85              
86             sub select { ## no critic Builtin
87 11     11 1 19 my ($self, $factory, $arg) = @_;
88              
89 11         25 $self->build($factory, $arg);
90             }
91              
92             #pod =head2 C< build >
93             #pod
94             #pod my $widget = $class->build($factory, \%arg)
95             #pod
96             #pod This method does the actual construction of the widget based on the args set up
97             #pod in the exported widget-constructing call. It's here for subclasses to exploit.
98             #pod
99             #pod =cut
100              
101             sub build {
102 15     15 1 23 my ($self, $factory, $arg) = @_;
103 15 100       45 $arg->{attr}{name} = $arg->{attr}{id} unless $arg->{attr}{name};
104              
105 15         54 my $widget = HTML::Element->new('select');
106              
107 15         326 my @options;
108 15 100       41 if (ref $arg->{options} eq 'HASH') {
109 1         2 @options = map { [ $_, $arg->{options}{$_} ] } keys %{ $arg->{options} };
  3         9  
  1         4  
110             } else {
111 14         18 @options = @{ $arg->{options} };
  14         40  
112             Carp::croak "undefined value passed to select widget"
113 14 100 100     21 if grep { not(defined $_) or ref $_ and not defined $_->[0] } @options;
  56 100       496  
114             }
115              
116 13 100       55 $self->validate_value($arg->{value}, \@options) unless $arg->{ignore_invalid};
117              
118 10         22 for my $entry (@options) {
119 44 100       609 my ($value, $name, $opt_arg) = (ref $entry) ? @$entry : ($entry) x 2;
120 44         110 my $option = $self->make_option($factory, $value, $name, $arg, $opt_arg);
121 44         106 $widget->push_content($option);
122             }
123              
124 10         134 $widget->attr($_ => $arg->{attr}{$_}) for keys %{ $arg->{attr} };
  10         58  
125 10         261 return $widget->as_XML;
126             }
127              
128             #pod =head2 C< make_option >
129             #pod
130             #pod my $option = $class->make_option($factory, $value, $name, $arg, $opt_arg);
131             #pod
132             #pod This method constructs the HTML::Element option element that will represent one
133             #pod of the options that may be put into the select box. This method is likely to
134             #pod be refactored in the future, and its arguments may change.
135             #pod
136             #pod =cut
137              
138             sub make_option {
139 32     32 1 55 my ($self, $factory, $value, $name, $arg, $opt_arg) = @_;
140              
141 32         89 my $option = HTML::Element->new('option', value => $value);
142 32         788 $option->push_content($name);
143 32 50 66     413 $option->attr(disabled => 'disabled') if $opt_arg && $opt_arg->{disabled};
144             $option->attr(selected => 'selected')
145 32 100 100     137 if defined $arg->{value} and $arg->{value} eq $value;
146              
147 32         98 return $option;
148             }
149              
150             #pod =head2 C< validate_value >
151             #pod
152             #pod This method checks whether the given value option is valid. See C>
153             #pod for an explanation of its default rules.
154             #pod
155             #pod =cut
156              
157             sub validate_value {
158 7     7 1 15 my ($class, $value, $options) = @_;
159              
160 7 100       12 my @options = map { ref $_ ? $_->[0] : $_ } @$options;
  32         78  
161             # maybe this should be configurable?
162 7 100       20 if ($value) {
163 4         7 my $matches = grep { $value eq $_ } @options;
  11         21  
164              
165 4 100       18 if (not $matches) {
    100          
166             Carp::croak "provided value '$value' not in given options: "
167 1         5 . join(q{ }, map { "'$_'" } @options);
  1         211  
168             } elsif ($matches > 1) {
169 1         125 Carp::croak "provided value '$matches' matches more than one option";
170             }
171             }
172             }
173              
174             sub rewrite_arg {
175 15     15 1 33 my ($self, $arg, @rest) = @_;
176              
177 15         57 $arg = $self->SUPER::rewrite_arg($arg, @rest);
178              
179 15 100       41 if ($self->{default_classes}) {
180 2         3 my $class = join q{ }, @{ $self->{default_classes} };
  2         8  
181             $arg->{attr}{class} = defined $arg->{attr}{class}
182 2 100       9 ? "$class $arg->{attr}{class}"
183             : $class;
184             }
185              
186 15         41 return $arg;
187             }
188              
189              
190             1;
191              
192             __END__