Revision 4180aaea
Von Moritz Bunkus vor mehr als 11 Jahren hinzugefügt
SL/DB/Helper/AttrDuration.pm | ||
---|---|---|
1 |
package SL::DB::Helper::AttrDuration; |
|
2 |
|
|
3 |
use strict; |
|
4 |
|
|
5 |
use parent qw(Exporter); |
|
6 |
our @EXPORT = qw(attr_duration); |
|
7 |
|
|
8 |
use Carp; |
|
9 |
|
|
10 |
sub attr_duration { |
|
11 |
my ($package, @attributes) = @_; |
|
12 |
|
|
13 |
_make($package, $_) for @attributes; |
|
14 |
} |
|
15 |
|
|
16 |
sub _make { |
|
17 |
my ($package, $attribute) = @_; |
|
18 |
|
|
19 |
no strict 'refs'; |
|
20 |
|
|
21 |
*{ $package . '::' . $attribute . '_as_hours' } = sub { |
|
22 |
my ($self, $value) = @_; |
|
23 |
|
|
24 |
$self->$attribute(int($value) + ($self->$attribute - int($self->$attribute))) if @_ > 1; |
|
25 |
return int($self->$attribute // 0); |
|
26 |
}; |
|
27 |
|
|
28 |
*{ $package . '::' . $attribute . '_as_minutes' } = sub { |
|
29 |
my ($self, $value) = @_; |
|
30 |
|
|
31 |
$self->$attribute(int($self->$attribute) * 1.0 + ($value // 0) / 60.0) if @_ > 1; |
|
32 |
return int(($self->$attribute // 0) * 60.0 + 0.5) % 60; |
|
33 |
}; |
|
34 |
|
|
35 |
*{ $package . '::' . $attribute . '_as_duration_string' } = sub { |
|
36 |
my ($self, $value) = @_; |
|
37 |
|
|
38 |
$self->$attribute(defined($value) ? $::form->parse_amount(\%::myconfig, $value) * 1 : undef) if @_ > 1; |
|
39 |
return defined($self->$attribute) ? $::form->format_amount(\%::myconfig, $self->$attribute // 0, 2) : undef; |
|
40 |
}; |
|
41 |
|
|
42 |
*{ $package . '::' . $attribute . '_as_man_days' } = sub { |
|
43 |
my ($self, $value) = @_; |
|
44 |
|
|
45 |
if (@_ > 1) { |
|
46 |
return undef if !defined $value; |
|
47 |
$self->$attribute($value); |
|
48 |
} |
|
49 |
$value = $self->$attribute // 0; |
|
50 |
return $value >= 8.0 ? $value / 8.0 : $value; |
|
51 |
}; |
|
52 |
|
|
53 |
*{ $package . '::' . $attribute . '_as_man_days_unit' } = sub { |
|
54 |
my ($self, $unit) = @_; |
|
55 |
|
|
56 |
if (@_ > 1) { |
|
57 |
return undef if !defined $unit; |
|
58 |
croak "Unknown unit '${unit}'" if $unit !~ m/^(?:h|hour|man_day)$/; |
|
59 |
$self->$attribute(($self->$attribute // 0) * 8.0) if $unit eq 'man_day'; |
|
60 |
} |
|
61 |
|
|
62 |
return ($self->$attribute // 0) >= 8.0 ? 'man_day' : 'h' |
|
63 |
}; |
|
64 |
|
|
65 |
*{ $package . '::' . $attribute . '_as_man_days_string' } = sub { |
|
66 |
my ($self, $value) = @_; |
|
67 |
my $method = "${attribute}_as_man_days"; |
|
68 |
|
|
69 |
if (@_ > 1) { |
|
70 |
return undef if !defined $value; |
|
71 |
$self->$method($::form->parse_amount(\%::myconfig, $value)); |
|
72 |
} |
|
73 |
|
|
74 |
return $::form->format_amount(\%::myconfig, $self->$method // 0, 2); |
|
75 |
}; |
|
76 |
} |
|
77 |
|
|
78 |
1; |
|
79 |
__END__ |
|
80 |
|
|
81 |
=pod |
|
82 |
|
|
83 |
=encoding utf8 |
|
84 |
|
|
85 |
=head1 NAME |
|
86 |
|
|
87 |
SL::DB::Helper::AttrDuration - Attribute helper for duration stored in |
|
88 |
numeric columns |
|
89 |
|
|
90 |
=head1 SYNOPSIS |
|
91 |
|
|
92 |
# In a Rose model: |
|
93 |
use SL::DB::Helper::AttrDuration; |
|
94 |
__PACKAGE__->attr_duration('time_estimation'); |
|
95 |
|
|
96 |
# Read access: |
|
97 |
print "Minutes: " . $obj->time_estimation_as_minutes . " hours: " . $obj->time_estimation_as_hours . "\n"; |
|
98 |
|
|
99 |
# Use formatted strings in input fields in templates: |
|
100 |
<form method="post"> |
|
101 |
... |
|
102 |
[% L.input_tag('time_estimation_as_duration_string', SELF.obj.time_estimation_as_duration_string) %] |
|
103 |
</form> |
|
104 |
|
|
105 |
=head1 OVERVIEW |
|
106 |
|
|
107 |
This is a helper for columns that store a duration as a numeric or |
|
108 |
floating point number representing a number of hours. So the value |
|
109 |
1.75 would stand for "1 hour, 45 minutes". |
|
110 |
|
|
111 |
The helper methods created are: |
|
112 |
|
|
113 |
=over 4 |
|
114 |
|
|
115 |
=item C<attribute_as_minutes [$new_value]> |
|
116 |
|
|
117 |
Access only the minutes. Return values are in the range [0 - 59]. |
|
118 |
|
|
119 |
=item C<attribute_as_hours [$new_value]> |
|
120 |
|
|
121 |
Access only the hours. Returns an integer value. |
|
122 |
|
|
123 |
=item C<attribute_as_duration_string [$new_value]> |
|
124 |
|
|
125 |
Access the full value as a formatted string according to the user's |
|
126 |
locale settings. |
|
127 |
|
|
128 |
=item C<attribute_as_man_days [$new_value]> |
|
129 |
|
|
130 |
Access the attribute as a number of man days which are assumed to be 8 |
|
131 |
hours long. If the underlying attribute is less than 8 then the value |
|
132 |
itself will be returned. Otherwise the value divided by 8 is returned. |
|
133 |
|
|
134 |
If used as a setter then the underlying attribute is simply set to |
|
135 |
C<$new_value>. Intentional use is to set the man days first and the |
|
136 |
unit later, e.g. |
|
137 |
|
|
138 |
$obj->attribute_as_man_days($::form->{attribute_as_man_days}); |
|
139 |
$obj->attribute_as_man_days_unit($::form->{attribute_as_man_days_unit}); |
|
140 |
|
|
141 |
Note that L<SL::DB::Object/assign_attributes> is aware of this and |
|
142 |
handles this case correctly. |
|
143 |
|
|
144 |
=item C<attribute_as_man_days_unit [$new_unit]> |
|
145 |
|
|
146 |
Returns the unit that the number returned by L</attribute_as_man_days> |
|
147 |
represents. This can be either C<h> if the underlying attribute is |
|
148 |
less than 8 and C<man_day> otherwise. |
|
149 |
|
|
150 |
If used as a setter then the underlying attribute is multiplied by 8 |
|
151 |
if C<$new_unit> equals C<man_day>. Otherwise the underlying attribute |
|
152 |
is not modified. Intentional use is to set the man days first and the |
|
153 |
unit later, e.g. |
|
154 |
|
|
155 |
$obj->attribute_as_man_days($::form->{attribute_as_man_days}); |
|
156 |
$obj->attribute_as_man_days_unit($::form->{attribute_as_man_days_unit}); |
|
157 |
|
|
158 |
Note that L<SL::DB::Object/assign_attributes> is aware of this and |
|
159 |
handles this case correctly. |
|
160 |
|
|
161 |
=back |
|
162 |
|
|
163 |
=head1 FUNCTIONS |
|
164 |
|
|
165 |
=over 4 |
|
166 |
|
|
167 |
=item C<attr_duration @attributes> |
|
168 |
|
|
169 |
Package method. Call with the names of attributes for which the helper |
|
170 |
methods should be created. |
|
171 |
|
|
172 |
=back |
|
173 |
|
|
174 |
=head1 BUGS |
|
175 |
|
|
176 |
Nothing here yet. |
|
177 |
|
|
178 |
=head1 AUTHOR |
|
179 |
|
|
180 |
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt> |
|
181 |
|
|
182 |
=cut |
SL/DB/Object.pm | ||
---|---|---|
60 | 60 |
|
61 | 61 |
my %types = map { $_->name => $_->type } ref($self)->meta->columns; |
62 | 62 |
|
63 |
# Special case for *_as_man_days/*_as_man_days_unit: the _unit |
|
64 |
# variation must always be called after the non-unit method. |
|
65 |
my @man_days_attributes = grep { m/_as_man_days$/ } keys %attributes; |
|
66 |
foreach my $attribute (@man_days_attributes) { |
|
67 |
my $value = delete $attributes{$attribute}; |
|
68 |
$self->$attribute(defined($value) && ($value eq '') ? undef : $value); |
|
69 |
} |
|
70 |
|
|
63 | 71 |
while (my ($attribute, $value) = each %attributes) { |
64 | 72 |
my $type = lc($types{$attribute} || 'text'); |
65 | 73 |
$value = $type eq 'boolean' ? ($value ? 't' : 'f') |
t/db_helper/attr_duration.t | ||
---|---|---|
1 |
package AttrDurationTestDummy; |
|
2 |
|
|
3 |
use base qw(SL::DB::Object); |
|
4 |
|
|
5 |
__PACKAGE__->meta->setup( |
|
6 |
table => 'dummy', |
|
7 |
columns => [ dummy => { type => 'numeric', precision => 2, scale => 12 }, ] |
|
8 |
); |
|
9 |
|
|
10 |
use SL::DB::Helper::AttrDuration; |
|
11 |
|
|
12 |
__PACKAGE__->attr_duration('dummy'); |
|
13 |
|
|
14 |
package main; |
|
15 |
|
|
16 |
use Test::More tests => 83; |
|
17 |
use Test::Exception; |
|
18 |
|
|
19 |
use strict; |
|
20 |
|
|
21 |
use lib 't'; |
|
22 |
use utf8; |
|
23 |
|
|
24 |
use Data::Dumper; |
|
25 |
use Support::TestSetup; |
|
26 |
|
|
27 |
sub new_item { |
|
28 |
return AttrDurationTestDummy->new(@_); |
|
29 |
} |
|
30 |
|
|
31 |
Support::TestSetup::login(); |
|
32 |
my $item; |
|
33 |
|
|
34 |
# Wenn das Attribut undef ist: |
|
35 |
is(new_item->dummy, undef, 'uninitialized: raw'); |
|
36 |
is(new_item->dummy_as_hours, 0, 'uninitialized: as_hours'); |
|
37 |
is(new_item->dummy_as_minutes, 0, 'uninitialized: as_minutes'); |
|
38 |
is(new_item->dummy_as_duration_string, undef, 'uninitialized: as_duration_string'); |
|
39 |
is(new_item->dummy_as_man_days, 0, 'uninitialized: as_man_days'); |
|
40 |
is(new_item->dummy_as_man_days_unit, 'h', 'uninitialized: as_man_days_unit'); |
|
41 |
is(new_item->dummy_as_man_days_string, '0,00', 'uninitialized: as_man_days_string'); |
|
42 |
|
|
43 |
# Auslesen kleiner 8 Stunden: |
|
44 |
is(new_item(dummy => 2.75)->dummy, 2.75, 'initialized < 8: raw'); |
|
45 |
is(new_item(dummy => 2.75)->dummy_as_hours, 2, 'initialized < 8: as_hours'); |
|
46 |
is(new_item(dummy => 2.75)->dummy_as_minutes, 45, 'initialized < 8: as_minutes'); |
|
47 |
is(new_item(dummy => 2.75)->dummy_as_duration_string, '2,75', 'initialized < 8: as_duration_string'); |
|
48 |
is(new_item(dummy => 2.75)->dummy_as_man_days, 2.75, 'initialized < 8: as_man_days'); |
|
49 |
is(new_item(dummy => 2.75)->dummy_as_man_days_unit, 'h', 'initialized < 8: as_man_days_unit'); |
|
50 |
is(new_item(dummy => 2.75)->dummy_as_man_days_string, '2,75', 'initialized < 8: as_man_days_string'); |
|
51 |
|
|
52 |
# Auslesen größer 8 Stunden: |
|
53 |
is(new_item(dummy => 12.5)->dummy, 12.5, 'initialized > 8: raw'); |
|
54 |
is(new_item(dummy => 12.5)->dummy_as_hours, 12, 'initialized > 8: as_hours'); |
|
55 |
is(new_item(dummy => 12.5)->dummy_as_minutes, 30, 'initialized > 8: as_minutes'); |
|
56 |
is(new_item(dummy => 12.5)->dummy_as_duration_string, '12,50', 'initialized > 8: as_duration_string'); |
|
57 |
is(new_item(dummy => 12.5)->dummy_as_man_days, 1.5625, 'initialized > 8: as_man_days'); |
|
58 |
is(new_item(dummy => 12.5)->dummy_as_man_days_unit, 'man_day', 'initialized > 8: as_man_days_unit'); |
|
59 |
is(new_item(dummy => 12.5)->dummy_as_man_days_string, '1,56', 'initialized > 8: as_man_days_string'); |
|
60 |
|
|
61 |
$item = new_item(dummy => 2.25); $item->dummy_as_duration_string(undef); |
|
62 |
is($item->dummy, undef, 'write as_duration_string undef read raw'); |
|
63 |
is($item->dummy_as_minutes, 0, 'write as_duration_string undef read as_minutes'); |
|
64 |
is($item->dummy_as_hours, 0, 'write as_duration_string undef read as_hours'); |
|
65 |
is($item->dummy_as_duration_string, undef, 'write as_duration_string undef read as_duration_string'); |
|
66 |
|
|
67 |
$item = new_item(dummy => 2.25); $item->dummy_as_duration_string("4,80"); |
|
68 |
is($item->dummy, 4.8, 'write as_duration_string 4,80 read raw'); |
|
69 |
is($item->dummy_as_minutes, 48, 'write as_duration_string 4,80 read as_minutes'); |
|
70 |
is($item->dummy_as_hours, 4, 'write as_duration_string 4,80 read as_hours'); |
|
71 |
is($item->dummy_as_duration_string, "4,80", 'write as_duration_string 4,80 read as_duration_string'); |
|
72 |
|
|
73 |
$item = new_item(dummy => 2.25); $item->dummy_as_minutes(12); |
|
74 |
is($item->dummy, 2.2, 'write as_minutes 12 read raw'); |
|
75 |
is($item->dummy_as_minutes, 12, 'write as_minutes 12 read as_minutes'); |
|
76 |
is($item->dummy_as_hours, 2, 'write as_minutes 12 read as_hours'); |
|
77 |
is($item->dummy_as_duration_string, "2,20", 'write as_minutes 12 read as_duration_string'); |
|
78 |
|
|
79 |
$item = new_item(dummy => 2.25); $item->dummy_as_hours(5); |
|
80 |
is($item->dummy, 5.25, 'write as_hours 5 read raw'); |
|
81 |
is($item->dummy_as_minutes, 15, 'write as_hours 5 read as_minutes'); |
|
82 |
is($item->dummy_as_hours, 5, 'write as_hours 5 read as_hours'); |
|
83 |
is($item->dummy_as_duration_string, "5,25", 'write as_hours 5 read as_duration_string'); |
|
84 |
|
|
85 |
$item = new_item(dummy => undef); |
|
86 |
is($item->dummy, undef, 'write raw undef read raw'); |
|
87 |
is($item->dummy_as_man_days, 0, 'write raw undef read as_man_days'); |
|
88 |
is($item->dummy_as_man_days_unit, 'h', 'write raw undef read as_man_days_unit'); |
|
89 |
is($item->dummy_as_man_days_string, '0,00', 'write raw undef read as_man_days_string'); |
|
90 |
|
|
91 |
$item = new_item(dummy => 4); |
|
92 |
is($item->dummy, 4, 'write raw 4 read raw'); |
|
93 |
is($item->dummy_as_man_days, 4, 'write raw 4 read as_man_days'); |
|
94 |
is($item->dummy_as_man_days_unit, 'h', 'write raw 4 read as_man_days_unit'); |
|
95 |
is($item->dummy_as_man_days_string, '4,00', 'write raw 4 read as_man_days_string'); |
|
96 |
|
|
97 |
$item = new_item(dummy => 18); |
|
98 |
is($item->dummy, 18, 'write raw 18 read raw'); |
|
99 |
is($item->dummy_as_man_days, 2.25, 'write raw 18 read as_man_days'); |
|
100 |
is($item->dummy_as_man_days_unit, 'man_day', 'write raw 18 read as_man_days_unit'); |
|
101 |
is($item->dummy_as_man_days_string, '2,25', 'write raw 18 read as_man_days_string'); |
|
102 |
|
|
103 |
$item = new_item(dummy => 4); |
|
104 |
is($item->dummy, 4, 'should not change anything when writing undef: write raw 4 read raw'); |
|
105 |
is($item->dummy_as_man_days(undef), undef, 'should not change anything when writing undef: write as_man_days undef return undef'); |
|
106 |
is($item->dummy, 4, 'should not change anything when writing undef: read raw 2'); |
|
107 |
is($item->dummy_as_man_days_unit(undef), undef, 'should not change anything when writing undef: write as_man_days_unit undef return undef'); |
|
108 |
is($item->dummy, 4, 'should not change anything when writing undef: read raw 3'); |
|
109 |
is($item->dummy_as_man_days_string(undef), undef, 'should not change anything when writing undef: write as_man_days_string undef return undef'); |
|
110 |
is($item->dummy, 4, 'should not change anything when writing undef: read raw 4'); |
|
111 |
|
|
112 |
|
|
113 |
$item = new_item; |
|
114 |
is($item->dummy(2), 2, 'parse less than a man day: write raw 2 read raw'); |
|
115 |
is($item->dummy_as_man_days(0.75), 0.75, 'parse less than a man day: write as_man_days 0.75 read as_man_days'); |
|
116 |
is($item->dummy_as_man_days_string('0,5'), '0,50', 'parse less than a man day: write as_man_days_string 0,5 read read as_man_days_string'); |
|
117 |
|
|
118 |
$item = new_item; |
|
119 |
is($item->dummy(12), 12, 'parse more than a man day: write raw 12 read raw'); |
|
120 |
is($item->dummy_as_man_days(13.25), 1.65625, 'parse more than a man day: write as_man_days 13.25 read as_man_days'); |
|
121 |
is($item->dummy_as_man_days_string('13,5'), '1,69', 'parse more than a man day: write as_man_days_string 13,5 read read as_man_days_string'); |
|
122 |
|
|
123 |
$item = new_item; |
|
124 |
is($item->dummy(3.25), 3.25, 'parse less than a man day with unit h: write raw 3.25 read raw'); |
|
125 |
is($item->dummy_as_man_days_unit('h'), 'h', 'parse less than a man day with unit h: write as_man_days_unit h read as_man_days_unit'); |
|
126 |
is($item->dummy, 3.25, 'parse less than a man day with unit h: read raw'); |
|
127 |
|
|
128 |
$item = new_item; |
|
129 |
is($item->dummy(3.25), 3.25, 'parse less than a man day with unit hour: write raw 3.25 read raw'); |
|
130 |
is($item->dummy_as_man_days_unit('hour'), 'h', 'parse less than a man day with unit hour: write as_man_days_unit hour read as_man_days_unit'); |
|
131 |
is($item->dummy, 3.25, 'parse less than a man day with unit hour: read raw'); |
|
132 |
|
|
133 |
$item = new_item; |
|
134 |
is($item->dummy(3.25), 3.25, 'parse more than a man day with unit man_day: write raw 3.25 read raw'); |
|
135 |
is($item->dummy_as_man_days_unit('man_day'), 'man_day', 'parse more than a man day with unit man_day: write as_man_days_unit man_day read as_man_days_unit'); |
|
136 |
is($item->dummy, 26, 'parse more than a man day with unit man_day: read raw'); |
|
137 |
|
|
138 |
is(new_item->assign_attributes(dummy_as_man_days => 3, dummy_as_man_days_unit => 'h')->dummy, 3, 'assign_attributes hash 3h'); |
|
139 |
is(new_item->assign_attributes(dummy_as_man_days_unit => 'h', dummy_as_man_days => 3 )->dummy, 3, 'assign_attributes hash h3'); |
|
140 |
|
|
141 |
is(new_item->assign_attributes(dummy_as_man_days => 3, dummy_as_man_days_unit => 'man_day')->dummy, 24, 'assign_attributes hash 3man_day'); |
|
142 |
is(new_item->assign_attributes(dummy_as_man_days_unit => 'man_day', dummy_as_man_days => 3 )->dummy, 24, 'assign_attributes hash man_day3'); |
|
143 |
|
|
144 |
is(new_item->assign_attributes('dummy_as_man_days', 3, 'dummy_as_man_days_unit', 'h')->dummy, 3, 'assign_attributes array 3h'); |
|
145 |
is(new_item->assign_attributes('dummy_as_man_days_unit', 'h', 'dummy_as_man_days', 3 )->dummy, 3, 'assign_attributes array h3'); |
|
146 |
|
|
147 |
is(new_item->assign_attributes('dummy_as_man_days', 3, 'dummy_as_man_days_unit', 'man_day')->dummy, 24, 'assign_attributes array 3man_day'); |
|
148 |
is(new_item->assign_attributes('dummy_as_man_days_unit', 'man_day', 'dummy_as_man_days', 3 )->dummy, 24, 'assign_attributes array man_day3'); |
|
149 |
|
|
150 |
# Parametervalidierung |
|
151 |
throws_ok { new_item()->dummy_as_man_days_unit('invalid') } qr/unknown.*unit/i, 'unknown unit'; |
|
152 |
lives_ok { new_item()->dummy_as_man_days_unit('h') } 'known unit h'; |
|
153 |
lives_ok { new_item()->dummy_as_man_days_unit('hour') } 'known unit hour'; |
|
154 |
lives_ok { new_item()->dummy_as_man_days_unit('man_day') } 'known unit man_day'; |
|
155 |
|
|
156 |
done_testing(); |
Auch abrufbar als: Unified diff
AttrDuration-Helfer