目の前に僕らの道がある

勉強会とか、技術的にはまったことのメモ

テスト駆動開発入門を Perlで写経してみた。 10

興味深い時 (times)

やったこと

  • times()メソッドをMoneyクラスに移動
  • 等値性の評価をクラスではなく、通貨で行うようにした。

サブクラスはコンストラクタだけなので次回以降、取り除いていきます。

lib/Money.pm

package Money;
use strict;
use warnings;
use Carp;

use version;
our $VERSION = qv('0.0.3');

use overload
    "==" => \&equals,
    "eq" => \&equals,
    "!=" => \&not_equals,
    "ne" => \&not_equals,
    '""' => \&string,
    '*'  => \×

sub new {
    my ($class, $opts) =(@_);
    croak 'Not exists amount option'   if not exists $opts->{amount};
    croak 'Not exists currency option' if not exists $opts->{currency};
    return bless $opts, $class;
}

sub equals {
    my ($self, $money) = (@_);
    use Test::More;
    return $self->{amount} == $money->{amount} &&
           $self->currency eq $money->currency;
}

sub not_equals {
    my ($self, $money) = (@_);
    return not $self->equals($money);
}

sub string {
    my ($self) = (@_);
    return $self->{amount} . ' (' . __PACKAGE__ . ')';
}

sub times {
    my ($self, $multiplier) = (@_);
    return Money->new({
            amount   => $self->{amount} * $multiplier,
            currency => $self->{currency},
        });
}

sub dollar {
    my ($class, $opts) =(@_);
    $opts->{currency} = 'USD';
    return Dollar->new($opts);
}

sub franc {
    my ($class, $opts) =(@_);
    $opts->{currency} = 'CHF';
    return Franc->new($opts);
}

sub currency {
    my $self = shift;
    return $self->{currency};
}

1;

package Dollar;
use strict;
use warnings;

use base 'Money';

sub new {
    my ($class, $opts) = (@_);
    return $class->SUPER::new($opts);
}

1;

package Franc;
use strict;
use warnings;

use base 'Money';

sub new {
    my ($class, $opts) = (@_);
    return $class->SUPER::new($opts);
}

1;

t/00money.t

use strict;
use warnings;

use Test::Class;

Test::Class->runtests;

package TestMoney;
use strict;
use warnings;

use base 'Test::Class';

use Money;
use Test::More;

sub test_multiplication : Test(2) {
    my $five    = Money->dollar({amount => 5});
    is($five * 2, Money->dollar({amount => 10}));
    is($five * 3, Money->dollar({amount => 15}));
}

sub test_equality : Test(5) {
    ok(Money->dollar({amount => 5}) == Money->dollar({amount => 5}));
    ok(Money->dollar({amount => 5}) != Money->dollar({amount => 6}));
    ok(Money->franc ({amount => 5}) == Money->franc ({amount => 5}));
    ok(Money->franc ({amount => 5}) != Money->franc ({amount => 6}));
    ok(Money->franc ({amount => 5}) != Money->dollar({amount => 5}));
}

sub test_franc_multiplication : Test(2) {
    my $five    = Money->franc({amount => 5});
    is($five * 2, Money->franc({amount => 10}));
    is($five * 3, Money->franc({amount => 15}));
}

sub test_currency : Test(2) {
    is(Money->dollar({amount => 1})->currency, 'USD');
    is(Money->franc ({amount => 1})->currency, 'CHF');
}

sub test_different_currency : Test(1) {
    ok(Money->new({amount => 10, currency => 'CHF',}),
       Franc->new({amount => 10, currency => 'CHF',}));
}

1;