Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 4180aaea

Von Moritz Bunkus vor mehr als 11 Jahren hinzugefügt

  • ID 4180aaea33e9ff3bb35f3fa6cf91651a6225f7ad
  • Vorgänger e3aa3f5b
  • Nachfolger ee62bdab

AttrDuration-Helfer

Unterschiede anzeigen:

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