File Coverage

blib/lib/Rose/HTML/Form/Field/DateTime/Range.pm
Criterion Covered Total %
statement 84 97 86.6
branch 25 34 73.5
condition 8 18 44.4
subroutine 23 26 88.4
pod 11 18 61.1
total 151 193 78.2


line stmt bran cond sub pod time code
1              
2             use strict;
3 2     2   777  
  2         4  
  2         58  
4             use Rose::HTML::Object::Errors qw(:date);
5 2     2   10  
  2         4  
  2         12  
6             use Rose::HTML::Object::Messages
7             qw(FIELD_ERROR_LABEL_MINIMUM_DATE FIELD_ERROR_LABEL_MAXIMUM_DATE);
8 2     2   11  
  2         3  
  2         9  
9             use Rose::HTML::Form::Field::DateTime::StartDate;
10 2     2   479 use Rose::HTML::Form::Field::DateTime::EndDate;
  2         4  
  2         18  
11 2     2   11  
  2         4  
  2         12  
12             use base 'Rose::HTML::Form::Field::Compound';
13 2     2   8  
  2         4  
  2         947  
14             use Rose::Object::MakeMethods::Generic
15             (
16             'scalar --get_set_init' =>
17 2         13 [
18             'range_separator_regex',
19             'min_prefix_html',
20             'max_prefix_html',
21             'separator_html',
22             ]
23             );
24 2     2   13  
  2         4  
25             our $VERSION = '0.606';
26              
27             {
28             my($self) = shift;
29              
30 1     1 1 3 $self->add_fields
31             (
32 1         14 min =>
33             {
34             type => 'datetime start',
35             error_label_id => FIELD_ERROR_LABEL_MINIMUM_DATE,
36             size => 21,
37             maxlength => 25,
38             },
39              
40             max =>
41             {
42             type => 'datetime end',
43             error_label_id => FIELD_ERROR_LABEL_MAXIMUM_DATE,
44             size => 21,
45             maxlength => 25,
46             },
47             );
48             }
49              
50             {
51             my($self) = shift;
52              
53             if(@_)
54 0     0 0 0 {
55             $self->field('min')->size(@_);
56 0 0       0 $self->field('max')->size(@_);
57             }
58 0         0  
59 0         0 return $self->field('min')->size(@_);
60             }
61              
62 0         0 {
63             my($self) = shift;
64              
65             if(@_)
66             {
67 0     0 0 0 $self->field('min')->output_format(@_);
68             $self->field('max')->output_format(@_);
69 0 0       0 }
70              
71 0         0 return $self->field('min')->output_format(@_);
72 0         0 }
73              
74              
75 0         0 {
76             my($self) = shift;
77              
78 1     1 0 4 if(@_)
79 1     1 0 18 {
80             $self->invalidate_output_value;
81             return $self->{'range_separator'} = shift;
82             }
83 6     6 1 11  
84             return (defined $self->{'range_separator'}) ? $self->{'range_separator'} :
85 6 100       13 ($self->{'range_separator'} = $self->init_range_separator);
86             }
87 1         18  
88 1         3 {
89             my($self, $value) = @_;
90             return $value unless(ref $value && @$value == 2);
91             return join($self->range_separator,
92 5 100       16 map { $_->strftime('%Y-%m-%d %H:%M:%S') } @$value);
93             }
94              
95             {
96             my($self) = shift;
97 3     3 1 8 return join($self->range_separator, map { defined($_) ? $_ : '' }
98 3 50 33     12 map { $self->field($_)->output_value }
99             qw(min max));
100 3         7 }
  6         212  
101              
102             {
103             my($self, $value) = @_;
104              
105 2     2 1 5 return undef unless(defined $value);
106 4 50       14  
107 2         4 my($min, $max);
  4         10  
108              
109             if(ref $value eq 'ARRAY' && @$value == 2)
110             {
111             ($min, $max) = @$value;
112             }
113 17     17 1 30 elsif(!ref $value)
114             {
115 17 100       37 ($min, $max) = split($self->range_separator_regex, $value, 2);
116             }
117 12         18 else
118             {
119 12 100 66     37 Carp::croak ref($self), " can't handle the input value '$value'";
    50          
120             }
121 2         5  
122             my $min_date = $self->field('min')->inflate_value($min) || $min;
123             my $max_date = $self->field('max')->inflate_value($max) || $max;
124              
125 10         24 return
126             {
127             min => $min_date,
128             max => $max_date,
129 0         0 };
130             }
131              
132 12   33     126 {
133 12   33     78 my($self, $value) = @_;
134              
135             if(ref $value eq 'ARRAY')
136             {
137 12         100 $self->subfield_input_value(min => $value->[0]);
138             $self->subfield_input_value(max => $value->[1]);
139              
140             #$self->field('min')->_set_input_value($value->[0]);
141             #$self->field('max')->_set_input_value($value->[1]);
142              
143             return [ $self->field('min')->internal_value, $self->field('max')->internal_value ];
144 7     7 1 16 }
145             else
146 7 100       18 {
147             my $values = $self->decompose_value($value);
148 2         9 return [ $values->{'min'}, $values->{'max'} ];
149 2         7 }
150             }
151              
152              
153             {
154 2         8 my($self) = shift;
155              
156             return '<span class="date-range">' .
157             $self->field('min')->html_label . $self->min_prefix_html . $self->field('min')->html_field . $self->separator_html .
158 5         10 $self->field('max')->html_label . $self->max_prefix_html . $self->field('max')->html_field .
159 5         20 '</span>';
160             }
161              
162             {
163 1     1 0 10 my($self) = shift;
164 1     1 0 9  
165 1     1 0 11 return '<span class="date-range">' .
166             $self->field('min')->xhtml_label . $self->min_prefix_html . $self->field('min')->xhtml_field . $self->separator_html .
167             $self->field('max')->xhtml_label . $self->max_prefix_html . $self->field('max')->xhtml_field .
168             '</span>';
169 1     1 1 1685 }
170              
171 1         5 {
172             my($self) = shift;
173              
174             return '<table class="date-range">' .
175             '<tr><td class="min">' .
176             $self->field('min')->html_label . $self->min_prefix_html . $self->field('min')->html . '</td><td>' . $self->separator_html . '</td><td class="max">' .
177             $self->field('max')->html_label . $self->max_prefix_html . $self->field('max')->html . '</td></tr>' .
178             ($self->has_errors ? '<tr><td colspan="3">' . $self->html_errors . '</td></tr>' : '') .
179 0     0 1 0 '</table>';
180             }
181 0         0  
182             {
183             my($self) = shift;
184              
185             return '<table class="date-range">' .
186             '<tr><td class="min">' .
187             $self->field('min')->xhtml_label . $self->min_prefix_html . $self->field('min')->xhtml . '</td><td>' . $self->separator_html . '</td><td class="max">' .
188             $self->field('max')->xhtml_label . $self->max_prefix_html . $self->field('max')->xhtml . '</td></tr>' .
189 3     3 1 8 ($self->has_errors ? '<tr><td colspan="3">' . $self->xhtml_errors . '</td></tr>' : '') .
190             '</table>';
191 3 100       10 }
192              
193             {
194             my($self) = shift;
195              
196             my $ret = $self->SUPER::validate(@_);
197              
198             return $ret unless($ret);
199              
200             my @errors;
201 2     2 1 1260  
202             foreach my $field (qw(min max))
203 2 50       8 {
204             unless($self->field($field)->validate)
205             {
206             push(@errors, $self->field($field)->errors);
207             $self->field($field)->set_error;
208             }
209             }
210              
211             unless(@errors)
212             {
213 3     3 1 6 my($min, $max) = $self->internal_value;
214              
215 3         14 if($min && $max && $min > $max)
216             {
217 3 50       7 $self->add_error_id(DATE_MIN_GREATER_THAN_MAX);
218             return 0;
219 3         6 }
220             }
221 3         6  
222             if(@errors)
223 6 100       14 {
224             $self->add_errors(map { $_->clone } @errors);
225 1         4 return 0;
226 1         15 }
227              
228             return $ret;
229             }
230 3 100       8  
231             if(__PACKAGE__->localizer->auto_load_messages)
232 2         6 {
233             __PACKAGE__->localizer->load_all_messages;
234 2 100 33     5 }
      66        
235              
236 1         98 use utf8; # The __DATA__ section contains UTF-8 text
237 1         5  
238             1;
239              
240              
241 2 100       98 [% LOCALE en %]
242              
243 1         3 DATE_MIN_GREATER_THAN_MAX = "The min date cannot be later than the max date."
  1         3  
244 1         4  
245             FIELD_ERROR_LABEL_MINIMUM_DATE = "minimum date"
246             FIELD_ERROR_LABEL_MAXIMUM_DATE = "maximum date"
247 1         5  
248             [% LOCALE de %]
249              
250             # von/bis oder doch min/max?
251             DATE_MIN_GREATER_THAN_MAX = "Das Von-Datum darf nicht größer sein, als das Bis-Datum."
252              
253             [% LOCALE fr %]
254              
255 2     2   2393 DATE_MIN_GREATER_THAN_MAX = "La date min ne peut pas être postérieure à la date max."
  2         6  
  2         10  
256              
257             [% LOCALE bg %]
258              
259             DATE_MIN_GREATER_THAN_MAX = "Началната дата трябва да бъде преди крайната."
260              
261             __END__
262              
263             =head1 NAME
264              
265             Rose::HTML::Form::Field::DateTime::Range - Compound field for date ranges with separate text fields for the minimum and maximum dates.
266              
267             =head1 SYNOPSIS
268              
269             $field =
270             Rose::HTML::Form::Field::DateTime::Range->new(
271             label => 'Date',
272             name => 'date',
273             default => [ '1/2/2003', '4/5/2006' ]);
274              
275             my($min, $max) = $field->internal_value; # DateTime objects
276              
277             print $min->strftime('%Y-%m-%d'); # "2003-01-02"
278             print $max->strftime('%Y-%m-%d'); # "2006-04-05"
279              
280             $field->input_value('5/6/1980 3pm to 2003-01-06 20:19:55');
281              
282             my $dates = $field->internal_value;
283              
284             print $dates->[0]->hour; # 15
285             print $dates->[1]->hour; # 20
286              
287             print $dates->[0]->day_name; # Tuesday
288              
289             print $field->html;
290              
291             ...
292              
293             =head1 DESCRIPTION
294              
295             L<Rose::HTML::Form::Field::DateTime::Range> is a compound field that represents a date range. It is made up of two subfields: a L<Rose::HTML::Form::Field::DateTime::StartDate> field and a L<Rose::HTML::Form::Field::DateTime::EndDate> field.
296              
297             The internal value of this field is a list (in list context) or reference to an array (in scalar context) of two L<DateTime> objects. The first object is the start date and the second is the end date. If either of fields are not filled in or are otherwise invalid, then the internal value is undef.
298              
299             The input value can be a reference to an array of L<DateTime> objects, or strings that can be inflated into L<DateTime> objects by the L<Rose::HTML::Form::Field::DateTime::StartDate> and L<Rose::HTML::Form::Field::DateTime::EndDate> classes. The input value can also be a concatenation of two such strings, joined by a string that matches the field's L<range_separator_regex|/range_separator_regex>.
300              
301             This class is a good example of a compound field whose internal value consists of more than one object. See L<below|/"SEE ALSO"> for more compound field examples.
302              
303             It is important that this class inherits from L<Rose::HTML::Form::Field::Compound>. See the L<Rose::HTML::Form::Field::Compound> documentation for more information.
304              
305             =head1 OBJECT METHODS
306              
307             =over 4
308              
309             =item B<range_separator [STRING]>
310              
311             Get or set the string used to join the output values of the start and end date subfields in order to produce this field's output value. The default string is "#". Example:
312              
313             $field->input_value([ '1/2/2003', '4/5/2006' ]);
314              
315             # "2003-01-02 00:00:00#2006-04-05 00:00:00"
316             print $field->output_value;
317              
318             =item B<range_separator_regex [REGEX]>
319              
320             Get or set the regular expression used to split an input string into start date and end date portions. The default value is C<qr(#|\s+to\s+)>. Example:
321              
322             $field->input_value('2005-04-20 8pm to 1/7/2006 3:05 AM');
323              
324             my($min, $max) = $field->internal_value;
325              
326             print $min->day_name; # Wednesday
327             print $max->day_name; # Saturday
328              
329             # Change regex, adding support for " - "
330             $field->range_separator_regex(qr(#|\s+(?:to|-)\s+));
331              
332             $field->input_value('2005-04-20 8pm - 1/7/2006 3:05 AM');
333              
334             ($min, $max) = $field->internal_value;
335              
336             print $min->day_name; # Wednesday
337             print $max->day_name; # Saturday
338              
339             Note that the C<range_separator_regex> B<must> match the C<range_separator> string.
340              
341             When setting C<range_separator_regex>, you should use the C<qr> operator to create a pre-compiled regex (as shown in the example above) If you do not, then the regex will be recompiled each time it's used.
342              
343             =back
344              
345             =head1 SEE ALSO
346              
347             Other examples of custom fields:
348              
349             =over 4
350              
351             =item L<Rose::HTML::Form::Field::Email>
352              
353             A text field that only accepts valid email addresses.
354              
355             =item L<Rose::HTML::Form::Field::Time>
356              
357             Uses inflate/deflate to coerce input into a fixed format.
358              
359             =item L<Rose::HTML::Form::Field::DateTime>
360              
361             Uses inflate/deflate to convert input to a L<DateTime> object.
362              
363             =item L<Rose::HTML::Form::Field::PhoneNumber::US::Split>
364              
365             A simple compound field that coalesces multiple subfields into a single value.
366              
367             =item L<Rose::HTML::Form::Field::DateTime::Split::MonthDayYear>
368              
369             A compound field that uses inflate/deflate convert input from multiple subfields into a L<DateTime> object.
370              
371             =item L<Rose::HTML::Form::Field::DateTime::Split::MDYHMS>
372              
373             A compound field that includes other compound fields and uses inflate/deflate convert input from multiple subfields into a L<DateTime> object.
374              
375             =back
376              
377             =head1 AUTHOR
378              
379             John C. Siracusa (siracusa@gmail.com)
380              
381             =head1 LICENSE
382              
383             Copyright (c) 2010 by John C. Siracusa. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.