File Coverage

blib/lib/HTML/Tested/Value.pm
Criterion Covered Total %
statement 124 136 91.1
branch 51 64 79.6
condition 27 38 71.0
subroutine 34 37 91.8
pod 11 19 57.8
total 247 294 84.0


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             HTML::Tested::Value - Base class for most HTML::Tested widgets.
4              
5             =head1 DESCRIPTION
6              
7             This class provides the most basic HTML::Tested widget - simple value to be
8             output in the template.
9              
10             =head1 METHODS
11              
12             =cut
13              
14 20     20   1838 use strict;
  20         41  
  20         2021  
15 20     20   113 use warnings FATAL => 'all';
  20         58  
  20         1052  
16              
17             package HTML::Tested::Value;
18 20     20   25053 use HTML::Entities;
  20         283850  
  20         3215  
19 20     20   45529 use HTML::Tested::Seal;
  20         70  
  20         698  
20 20     20   132 use Carp;
  20         250  
  20         2618  
21 20     20   126 use Data::Dumper;
  20         388  
  20         63876  
22              
23             sub setup_datetime_option {
24 0     0 0 0 my ($self, $dto, $opts) = @_;
25 0   0     0 $opts ||= $self->options;
26 0         0 eval "use DateTime::Format::Strptime";
27 0 0       0 confess "Unable to use DateTime::Format::Strptime: $@" if $@;
28 0 0       0 $dto = { pattern => $dto } unless ref($dto);
29 0         0 $opts->{is_datetime} = DateTime::Format::Strptime->new($dto);
30 0         0 $self->compile;
31             }
32              
33             =head2 $class->new($parent, $name, %opts)
34              
35             Creates new L named C<$name> at parent class C<$parent>.
36              
37             C<%opts> is a hash containing various options changing behaviour of this widget.
38              
39             See OPTIONS section for description of available options.
40              
41             =cut
42             sub new {
43 106     106 1 387 my ($class, $parent, $name, %opts) = @_;
44 106         871 my $self = bless({ name => $name, _options => \%opts
45             , constraints => [], validators => [] }, $class);
46 106   100     671 my $cs = $opts{constraints} || [];
47 106         337 $self->push_constraint($_) for @$cs;
48              
49 106         3947 my $dto = $self->options->{is_datetime};
50 106 50       327 $self->setup_datetime_option($dto) if $dto;
51 106         407 return $self;
52             }
53              
54             sub _get_option {
55 1143     1143   2897 my ($self, $caller, $wname, $opname) = @_;
56 1143 100 66     6530 if ($caller && ref($caller)) {
57 513         1090 my $n = "__ht__$wname\_$opname";
58 513 100       1970 return $caller->{$n} if exists $caller->{$n};
59             }
60 1132         3794 return $self->options->{$opname};
61             }
62              
63             =head2 $widget->name
64              
65             Returns the name of the widget.
66              
67             =cut
68 942     942 1 5765 sub name { return shift()->{name}; }
69              
70             =head2 $widget->options
71              
72             Returns hash of options assigned to this widget. See OPTIONS section for
73             description of available options.
74              
75             =cut
76 1710     1710 1 9860 sub options { return shift()->{_options}; }
77              
78             =head2 $widget->value_to_string($name, $val, $caller, $stash)
79              
80             This function is called from C to return final string which will be
81             rendered into stash. For HTML::Tested::Value it simply returns $val.
82              
83             C<$caller> is the object calling this function. C<$stash> is read-only hash of
84             the values accummulated so far.
85              
86             =cut
87             sub value_to_string {
88 88     88 1 282 my ($self, $name, $val) = @_;
89 88         251 return $val;
90             }
91              
92             =head2 $widget->encode_value($val)
93              
94             Uses HTML::Entities to encode $val.
95              
96             =cut
97             sub encode_value {
98 258     258 1 483 my ($self, $val) = @_;
99 258 50       1849 confess ref($self) . "->" . $self->name . ": Non scalar value $val\n"
100             . Dumper($val) if ref($val);
101 258         1671 return encode_entities($val, '<>&"' . "'");
102             }
103              
104             sub get_default_value {
105 38     38 0 82 my ($self, $caller, $n) = @_;
106 38   66     316 my $func = $caller->{"__$n\_defval"} || $self->{__defval};
107 38         121 return $func->($self, $n, $caller);
108             }
109              
110             =head2 $widget->get_value($caller, $id)
111              
112             It is called from $widget->render to get the value to render. If the value
113             is C C will be used to get default value for the
114             widget.
115              
116             =cut
117             sub get_value {
118 219     219 1 558 my ($self, $caller, $id, $n) = @_;
119 219   100     2082 return $caller->{$n} // $self->get_default_value($caller, $n);
120             }
121              
122             =head2 $widget->seal_value($value, $caller)
123              
124             If C option is used, this function is called from $widget->render to
125             seal the value before putting it to stash. See HTML::Tested::Seal for sealing
126             description.
127              
128             This function maintains cache of sealed values in caller. Thus promising that
129             the same value will map to the same id during request.
130              
131             =cut
132             sub seal_value {
133 58     58 1 113 my ($self, $val, $caller) = @_;
134 58         389 return HTML::Tested::Seal->instance->encrypt($val);
135             }
136              
137             sub transform_value {
138 161     161 0 321 my ($self, $caller, $val, $n) = @_;
139 161   66     921 my $func = $caller->{"__$n\_transform"} || $self->{__transform};
140 161         434 return $func->($self, $val, $caller, $n);
141             }
142              
143             sub prepare_value {
144 202     202 0 464 my ($self, $caller, $id, $n) = @_;
145 202         698 my $val = $self->get_value($caller, $id, $n);
146 202 100       670 return undef unless defined($val);
147 197         1555 return $self->transform_value($caller, $val, $n);
148             }
149              
150             sub _render_i {
151 206     206   1171 my ($self, $caller, $stash, $id, $n) = @_;
152 206         731 my $val = $self->prepare_value($caller, $id, $n);
153 206 100       22261 return unless defined($val);
154 201         863 return $self->value_to_string($id, $val, $caller, $stash);
155             }
156              
157             =head2 $widget->render($caller, $stash, $id, $name)
158              
159             Renders widget into $stash. For HTML::Tested::Value it essentially means
160             assigning $stash->{ $name } with $widget->get_value.
161              
162             =cut
163             sub render {
164 210     210 1 443 my ($self, $caller, $stash, $id, $n) = @_;
165 210   66     2291 my $func = $caller->{"__$n\_render"} || $self->{__render};
166 210         576 my $res = $func->($self, $caller, $stash, $id, $n);
167 210 100       4147 $stash->{$n} = $res if defined($res);
168             }
169              
170 87     87 0 919 sub bless_from_tree { return $_[1]; }
171              
172             =head2 $widget->push_constraint($constraint)
173              
174             C<$constraint> should be ARRAY reference with the following format:
175              
176             [ TYPE, OP, COOKIE ]
177              
178             where C is type of the constraint, C is the operation to be done on
179             the constraint and cookie is optional method for the application to recognize
180             specific constraint.
181              
182             Available types are:
183              
184             =over
185              
186             =item C
187              
188             With OP being regexp string (or C value) (e.g. [ regexp => '\d+' ] or [
189             regexp => qr/\d+/ ]).
190              
191             =item C
192              
193             Ensures that the value is defined. C doesn't matter here
194             (e.g. [ defined => '' ]).
195              
196             =item C
197              
198             Any user defined constraint - second parameter should be function to call.
199             It gets the value and the caller as the arguments.
200              
201             For example [ 'my_foo' => sub { my ($v, $caller) = @_; return is_ok? } ].
202              
203             =back
204              
205             =cut
206             sub push_constraint {
207 9     9 1 33 my ($self, $c) = @_;
208 9         15 my $func;
209 9         13 push @{ $self->{constraints} }, $c;
  9         24  
210 9 50 33     59 confess "Constraint should be of [ TYPE, OP ] format"
211             unless ($c && ref($c) eq 'ARRAY');
212 9 100       33 if ($c->[0] eq 'regexp') {
    100          
    50          
213 6         12 my $rexp = $c->[1];
214             $func = sub {
215 19     19   24 my $v = shift;
216 19 100       242 return defined($v) ? $v =~ /$rexp/ : undef;
217 6         30 };
218             } elsif ($c->[0] eq 'defined') {
219 1     3   6 $func = sub { return defined($_[0]); };
  3         13  
220             } elsif ($c->[1]) {
221 2         5 $func = $c->[1];
222             } else {
223 0         0 confess "Unknown type " . $c->[0] . " found!\n";
224             }
225 9 50       26 push @{ $self->{validators} }, $func if $func;
  9         33  
226             }
227              
228             =head2 $widget->validate($value, $caller)
229              
230             Validate value returning list of failed constraints in the format specified
231             above.
232              
233             I.e. the C<$value> is "constraint-clean" when C returns empty list.
234              
235             Validate is disabled if C widget option is set.
236              
237             =cut
238             sub validate {
239 33     33 1 124 my ($self, $caller) = @_;
240 33         70 my $n = $self->name;
241 33         119 my $val = $caller->$n;
242 33 100       419 return () if $caller->ht_get_widget_option($n, "no_validate");
243 31 100 100     132 return ([ $n, 'integer' ]) if (defined($val)
      100        
244             && $caller->ht_get_widget_option($n, "is_integer")
245             && $val !~ /^\d+$/);
246 29         46 my $vs = $self->{validators};
247 29         35 my @res;
248 29         89 for (my $i = 0; $i < @$vs; $i++) {
249 28 100       107 next if $vs->[$i]->($val, $caller);
250 12         50 push @res, [ $n, @{ $self->{constraints}->[$i] } ];
  12         63  
251             }
252 29         201 return @res;
253             }
254              
255             sub unseal_value {
256 4     4 0 9 my ($self, $val, $caller) = @_;
257 4         30 return HTML::Tested::Seal->instance->decrypt($val);
258             }
259              
260 1     1 0 11 sub merge_one_value { shift()->absorb_one_value(@_); }
261              
262             =head2 $widget->absorb_one_value($parent, $val, @path)
263              
264             Parses C<$val> and puts the result into C<$parent> object. C<@path> is used for
265             widgets aggregating other widgets (such as C).
266              
267             =cut
268             sub absorb_one_value {
269 128     128 1 250 my ($self, $root, $val, @path) = @_;
270 128 100       280 return if $self->options->{is_trusted};
271 127 100       246 $val = $self->unseal_value($val, $root)
272             if $self->options->{"is_sealed"};
273 127         317 my $dtfs = $self->options->{"is_datetime"};
274 127 50       526 $val = $dtfs->parse_datetime($val) if $dtfs;
275 127 100 100     845 $root->{ $self->name } = (defined($val) && $val eq ""
276             && !$self->options->{keep_empty_string}) ? undef : $val;
277             }
278              
279             sub _set_callback {
280 354     354   725 my ($self, $caller, $n, $what, $func) = @_;
281 354 100 66     1788 my $obj = ($caller && ref($caller)) ? $caller : $self;
282 354 100 66     1598 my $key = ($caller && ref($caller)) ? "__$n\_$what" : "__$what";
283 354         1184 $obj->{$key} = $func;
284             }
285              
286             sub _trans_datetime {
287 0     0   0 my ($self, $dtfs, $val, $caller, $n) = @_;
288 0 0       0 return $dtfs->format_datetime($val) if $val;
289             }
290              
291             sub compile {
292 118     118 0 265 my ($self, $caller) = @_;
293 118         310 my $n = $self->name;
294 118         617 my $trans = $self->can('encode_value');
295 118         528 my $func = $self->can('_render_i');
296 118     18   611 my $defval = sub { return '' };
  18         79  
297 118 100       521 if ($self->_get_option($caller, $n, 'is_disabled')) {
    50          
    100          
    100          
298 3         7 $func = $defval;
299             } elsif (my $dtfs = $self->_get_option($caller, $n, "is_datetime")) {
300 0     0   0 $trans = sub { return shift()->_trans_datetime($dtfs, @_); };
  0         0  
301             } elsif ($self->_get_option($caller, $n, "is_sealed")) {
302             $trans = sub {
303 42     42   145 my $this = shift;
304 42         128 my $val = shift;
305 42         284 $val = $this->seal_value($val, @_);
306 42         10319 return $this->encode_value($val, @_);
307 16         81 };
308             } elsif ($self->_get_option($caller, $n, "is_trusted")) {
309 3     6   17 $trans = sub { return $_[1]; };
  6         30  
310             }
311              
312 118         348 my $dval = $self->_get_option($caller, $n, "default_value");
313 118 100       1705 if (defined($dval)) {
    100          
314 16 100   18   100 $defval = ref($dval) eq 'CODE' ? $dval : sub { return $dval; };
  18         85  
315             } elsif ($self->_get_option($caller, $n, "skip_undef")) {
316 3     5   16 $defval = sub { return undef; };
  5         25  
317             }
318              
319 118         1480 $self->_set_callback($caller, $n, 'render', $func);
320 118         333 $self->_set_callback($caller, $n, 'transform', $trans);
321 118         312 $self->_set_callback($caller, $n, 'defval', $defval);
322             }
323              
324             1;
325              
326             =head1 OPTIONS
327              
328             Options can be used to customize widget behaviour. Each widget is free to
329             define its own options. They can be set per class or per object using
330             C. The options can be retrieved using
331             C.
332              
333             C defines the following options:
334              
335             =over
336              
337             =item is_sealed
338              
339             The widget value is encrypted before rendering it. The value is decrypted from
340             the request parameters in transparent fashion.
341              
342             =item is_disabled
343              
344             The widget is disabled: it is rendered as blank value.
345              
346             =item default_value
347              
348             Default value for the widget. It is rendered if current widget value is
349             C.
350              
351             =item skip_undef
352              
353             Normally, if widget value is C, the widget is rendered as blank value.
354             When this option is set the widget will not appear in the stash at all.
355              
356             =item constraints
357              
358             Array reference containing widget value constraints. See C
359             documentation for the individual entry format.
360              
361             =item is_trusted
362              
363             Do not perform the escaping of special characters on the value. Improperly
364             setting this option may result in XSS security breach.
365              
366             =item is_integer
367              
368             Ensures that the value is integer.
369              
370             =back
371              
372             =head1 AUTHOR
373              
374             Boris Sukholitko (boriss@gmail.com)
375            
376             =head1 COPYRIGHT
377              
378             This program is free software; you can redistribute
379             it and/or modify it under the same terms as Perl itself.
380              
381             The full text of the license can be found in the
382             LICENSE file included with this module.
383              
384              
385             =head1 SEE ALSO
386              
387             HTML::Tested
388              
389             =cut
390