File Coverage

lib/DR/Money.pm
Criterion Covered Total %
statement 114 120 95.0
branch 40 46 86.9
condition 24 33 72.7
subroutine 26 27 96.3
pod 3 3 100.0
total 207 229 90.3


line stmt bran cond sub pod time code
1             package DR::Money;
2              
3 1     1   63008 use 5.008008;
  1         4  
  1         42  
4 1     1   5 use strict;
  1         3  
  1         30  
5 1     1   5 use warnings;
  1         6  
  1         28  
6              
7 1     1   5 use base 'Exporter';
  1         2  
  1         114  
8 1     1   6 use Carp;
  1         7  
  1         336  
9             our %EXPORT_TAGS = ( 'all' => [ qw(Money) ] );
10             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
11             our @EXPORT = qw(Money);
12              
13             our $VERSION = '0.02';
14              
15             use overload
16             '""' => \&value,
17             '0+' => \&value,
18 48 100   48   299 'bool' => sub { $_[0][1] || $_[0][2] },
19             'cmp' => \&_cmp,
20             '<=>' => \&_dcmp,
21 0     0   0 '=' => sub { $_[0]->new($_[1]) },
22 1         24 'int' => \&_int,
23             '+' => \&_add,
24             '*' => \&_mul,
25             '-' => \&_sub,
26             '/' => \&_div,
27              
28             '++' => \&_inc,
29             '--' => \&_dec,
30             '+=' => \&_addself,
31             '-=' => \&_subself,
32             '*=' => \&_mulself,
33             '/=' => \&_divself,
34 1     1   1735 ;
  1         1432  
35              
36              
37             =head1 NAME
38              
39             DR::Money - module to manipulate by money in perl scripts
40              
41             =head1 SYNOPSIS
42              
43             my $m = Money(2.3);
44             print $m; # prints 2.30
45             $m += 2.3; # 4.60
46             $m += Money(4.2); # 8.80
47              
48             The module supports negative moneys.
49              
50             =head1 Functions
51              
52             =head2 Money
53              
54             Functional constructor.
55              
56             my $money = Money(0.1);
57             printf '%s', $money; # prints 0.10
58              
59             =cut
60              
61 95     95 1 2885 sub Money($) { __PACKAGE__->new($_[0]) }
62              
63              
64             =head1 Methods
65              
66             =head2 new
67              
68             Class or instance's method. Construct new instance.
69              
70             =cut
71              
72             sub new {
73 96     96 1 145 my ($class, $value) = @_;
74              
75 96         102 my $self;
76              
77 96 50       169 if (ref $class) {
78 0         0 $self = bless [ '+', 0, 0, '0.00' ] => ref $class;
79 0         0 $self->_assign( $class );
80             } else {
81 96         379 $self = bless [ '+', 0, 0, '0.00' ] => $class;
82             }
83              
84 96 100       316 $self->_assign($value) if @_ > 1;
85 96         693 return $self;
86             }
87              
88              
89             =head2 value
90              
91             Returns value (string).
92              
93             my $money = Money(0.1);
94             $v = $money->value; # 0.10
95             $v = "$money"; # the same
96              
97             =cut
98              
99 9     9 1 3325 sub value { $_[0][3] }
100              
101              
102              
103             =head1 Private (overload methods)
104              
105             =head2 _assign($value)
106              
107             Private method. Assigns new value to instance. Returns instance.
108              
109             my $money = Money(0.1);
110              
111             $money->_assign( 0.2 );
112             $money = 0.2; # the same
113              
114             =cut
115              
116             sub _assign {
117 95     95   122 my ($self, $value) = @_;
118 95 50       160 croak 'usage $money->_assign($value)' unless @_ > 1;
119              
120 95 100       167 if (!$value) {
121 9         25 @$self = ('+', 0, 0, '0.00');
122 9         16 return $self;
123             }
124              
125 86 100 66     294 if (ref $value and $value->isa(__PACKAGE__)) {
126 20         25 @$self = @{ $value }[0, 1, 2, 3];
  20         75  
127 20         36 return $self;
128             }
129              
130 66         311 $value =~ s/\s+//g;
131              
132 66         70 my ($sign, $r, $k, $s);
133              
134 66 100       699 if ($value =~ /^(-)?0*(\d+)[,\.]?$/) {
    50          
135 7   100     28 $sign = $1 || '+';
136 7         11 $r = int $2;
137 7         6 $k = 0;
138 7 100       25 $s = sprintf '%s%d.00', ($sign eq '-' ? '-' : ''), $r;
139             } elsif ($value =~ /^(-)?0*(\d*)[,\.](\d+)$/) {
140 59   100     329 $sign = $1 || '+';
141 59   100     183 $r = int($2 || 0);
142 59         126 $k = substr($3 . '0', 0, 2);
143 59         97 $k =~ s/^0//;
144 59         67 $k = int $k;
145 59 100 100     147 $sign = '+' unless $r or $k;
146 59 100       351 $s = sprintf '%s%d.%02d', ($sign eq '-' ? '-' : ''), $r, $k;
147             } else {
148 0         0 croak "wrong money value: $value";
149             }
150              
151 66         296 @$self = ($sign, $r, $k, $s);
152              
153 66         123 return $self;
154             }
155              
156             =head2 _cmp
157              
158             Private method. Compares two instances as string.
159              
160             my $money1 = Money(0.1);
161             my $money2 = Money(0.01);
162              
163             $money1->_cmp($money2);
164              
165             $money1 cmp $money2; # the same
166              
167             =cut
168              
169             sub _cmp {
170 19     19   1783 my ($self, $cv, $flip) = @_;
171 19 50       59 return $self->[3] cmp Money($cv)->[3] unless $flip;
172 0         0 return Money($cv)->[3] cmp $self->[3];
173             }
174              
175              
176             =head2 _dcmp
177              
178             Private method. Compares two instances as digit.
179              
180             my $money1 = Money(0.1);
181             my $money2 = Money(0.01);
182              
183             $money1->_dcmp($money2);
184              
185             $money1 <=> $money2; # the same
186              
187             =cut
188              
189             sub _dcmp {
190 7     7   1042 my ($self, $cv, $flip) = @_;
191 7 100       129 return Money($self)->_kop <=> Money($cv)->_kop unless $flip;
192 1         6 return Money($cv)->_kop <=> Money($self)->_kop;
193             }
194              
195              
196             sub _kop {
197 44     44   50 my ($self) = @_;
198 44         67 my ($sign, $r, $k) = @$self;
199 44 100       105 return -($r * 100 + $k) if $sign eq '-';
200 38         257 return $r * 100 + $k;
201             }
202              
203             sub _from_kop {
204 18     18   21 my ($class, $kop) = @_;
205 18         16 my ($sign, $r, $k, $s);
206              
207 18         26 $k = abs($kop) % 100;
208 18         25 $r = (abs($kop) - $k) / 100;
209              
210 18 100       373 if ($kop < 0) {
211 4         5 $sign = '-';
212 4         10 $s = sprintf '-%d.%02d', $r, $k;
213             } else {
214 14         15 $sign = '+';
215 14         36 $s = sprintf '%d.%02d', $r, $k;
216             }
217              
218 18   33     148 return bless [ $sign, $r, $k, $s ] => ref($class) || $class;
219             }
220              
221             =head2 _add
222              
223             Private method. Add two instances (or instance and number).
224              
225             my $money1 = Money(1.23);
226             my $money2 = Money(2.34);
227             my $money3 = $money1->_add($money2);
228              
229             $money3 = $money1 + $money2; # the same
230              
231             =cut
232              
233             sub _add {
234 3     3   6 my ($self, $value) = @_;
235 3         5 return $self->_from_kop($self->_kop + Money($value)->_kop);
236              
237             }
238              
239              
240             =head2 _mul
241              
242             Private method. Multiplicate money to number
243              
244             my $money = Money(1.23);
245            
246             $money = $money->_mul(234);
247              
248             $money = $money * 234; # the same
249              
250             =cut
251              
252             sub _mul {
253 5     5   6 my ($self, $mul) = @_;
254 5 50 33     13 croak "Can't multiply money to money"
255             if ref $mul and $mul->isa(__PACKAGE__);
256 5         11 return $self->_from_kop(int($self->_kop * $mul));
257             }
258              
259              
260             =head2 _sub
261              
262             Private method. Substract money.
263              
264             my $money = Money(1.23);
265              
266             $money = $money->_sub(1.22);
267              
268             $money = $money - 1.22; # the same
269              
270             =cut
271              
272             sub _sub {
273 3     3   7 my ($self, $sv, $flip) = @_;
274 3         5 my $v = Money($sv);
275              
276 3 100       9 return $self->_from_kop( $self->_kop - $v->_kop ) unless $flip;
277 1         3 return $self->_from_kop( $v->_kop - $self->_kop );
278             }
279              
280              
281             =head2 _div
282              
283             Private method. Divide money.
284              
285             my $money = Money(1.22);
286              
287             $money = $money / 2;
288             $number = $money / Money(2.54);
289             $number = 1.23 / $money;
290              
291             =cut
292              
293             sub _div {
294 10     10   14 my ($self, $div, $flip) = @_;
295              
296 10 100 66     197 croak "Division by zero"
      100        
      66        
297             if (($div == 0 and !$flip) or ($flip and !$self));
298              
299 7 100 66     30 if (ref $div and $div->isa(__PACKAGE__)) {
300              
301 2 50       8 return $self->_kop / $div->_kop unless $flip;
302 0         0 return $div->_kop / $self->_kop;
303             }
304              
305 5 100       16 return $self->_from_kop( int($self->_kop / $div) ) unless $flip;
306 2         6 return 100 * $div / $self->_kop;
307             }
308            
309              
310             =head2 _inc
311              
312             Private method. Increment money.
313              
314             my $money = Money(1.22);
315             $money->_inc; # 1.23
316              
317             $money++; # the same
318              
319             =cut
320              
321             sub _inc {
322 2     2   4 my ($self) = @_;
323 2         4 @$self = @{ $self->_from_kop($self->_kop + 1) };
  2         6  
324 2         9 return $self;
325             }
326              
327              
328             =head2 _dec
329              
330             Private method. Decrement money.
331              
332             my $money = Money(2.54);
333             $money->_dec; # 2.53
334              
335             $money--; # the same
336              
337             =cut
338              
339             sub _dec {
340 2     2   8 my ($self) = @_;
341 2         3 @$self = @{ $self->_from_kop($self->_kop - 1) };
  2         6  
342 2         12 return $self;
343             }
344              
345              
346              
347             =head2 _addself
348              
349             Private method. Add value to money.
350              
351             $money->_addself(23);
352              
353             $money += 23; # the same
354              
355             =cut
356              
357             sub _addself {
358 1     1   3 my ($self, $av) = @_;
359 1         2 @$self = @{ $self->_add($av) };
  1         5  
360 1         6 return $self;
361             }
362              
363              
364             =head2 _mulself
365              
366             Private method. Mull value to money.
367              
368             $money->_mulself(23);
369             $money *= 23; # the same
370              
371             =cut
372              
373             sub _mulself {
374 1     1   2 my ($self, $av) = @_;
375 1         1 @$self = @{ $self->_mul($av) };
  1         4  
376 1         5 return $self;
377             }
378              
379              
380             =head2 _subself
381              
382             Private method. Substract value from money.
383              
384             $money->_subself(12.34);
385             $money -= 12.34; # the same
386              
387             =cut
388              
389             sub _subself {
390 1     1   2 my ($self, $av) = @_;
391 1         2 @$self = @{ $self->_sub($av) };
  1         4  
392 1         4 return $self;
393             }
394              
395             =head2 _divself
396              
397             Private method. Divide value by number.
398              
399             $money->_subself(12.34);
400             $money -= 12.34; # the same
401              
402             =cut
403              
404             sub _divself {
405 2     2   5 my ($self, $av) = @_;
406 2 100 66     181 croak "Can't divide money to money in-place"
407             if ref $av and $av->isa(__PACKAGE__);
408 1         2 @$self = @{ $self->_div($av) };
  1         3  
409 1         4 return $self;
410             }
411              
412              
413             =head2 _int
414              
415             Private method.
416             my $money = Money(100.11);
417             my $r = $money->_int; # 100
418             my $r = int Money; # the same
419              
420             =cut
421              
422             sub _int {
423 2     2   2 my ($self) = @_;
424 2 100       11 return $self->[1] if $self->[0] eq '+';
425 1         5 return -$self->[1];
426             }
427              
428              
429             =head1 COPYRIGHT AND LICENSE
430              
431             This library is free software; you can redistribute it and/or modify
432             it under the same terms as Perl itself, either Perl version 5.8.8 or,
433             at your option, any later version of Perl 5 you may have available
434              
435             =cut
436              
437             1;