package Acme::Chef::Container;
use strict;
use warnings;
use Carp;
use Acme::Chef::Ingredient;
use vars qw/$VERSION/;
$VERSION = '1.00';
=head1 NAME
Acme::Chef::Container - Internal module used by Acme::Chef
=head1 SYNOPSIS
use Acme::Chef;
=head1 DESCRIPTION
Please see L<Acme::Chef>;
=head2 METHODS
This is a list of methods in this package.
=over 2
=cut
=item new
This is the Acme::Chef::Container constructor. Creates a new
Acme::Chef::Container object. All arguments are treated as key/value pairs for
object attributes.
=cut
sub new {
my $proto = shift;
my $class = ref $proto || $proto;
my $self = {};
if (ref $proto) {
%$self = %$proto;
$self->{contents} = [ map { $_->new() } @{$self -> {contents}} ];
}
%$self = (
contents => [],
%$self,
@_,
);
return bless $self => $class;
}
=item put
This method implements the 'put' command. Please refer to L<Acme::Chef> for
details.
=cut
sub put {
my $self = shift;
my @ingredients = @_;
push @{$self->{contents}}, $_->new() for @ingredients;
return $self;
}
=item fold
This method implements the 'fold' command. Please refer to L<Acme::Chef> for
details.
=cut
sub fold {
my $self = shift;
my $ingredient = shift;
croak "Invalid operation on empty container: fold."
unless @{$self->{contents}};
my $new_val = pop @{ $self->{contents} };
$ingredient->value( $new_val->value() );
return $ingredient;
}
=item add
This method implements the 'add' command. Please refer to L<Acme::Chef> for
details.
=cut
sub add {
my $self = shift;
my $ingredient = shift;
croak "Invalid operation on empty container: add."
unless @{$self->{contents}};
$self->{contents}->[-1]->value(
$self->{contents}->[-1]->value() +
$ingredient->value()
);
return $ingredient;
}
=item remove
This method implements the 'remove' command. Please refer to L<Acme::Chef> for
details.
=cut
sub remove {
my $self = shift;
my $ingredient = shift;
croak "Invalid operation on empty container: remove."
unless @{$self->{contents}};
$self->{contents}->[-1]->value(
$self->{contents}->[-1]->value() -
$ingredient->value()
);
return $ingredient;
}
=item combine
This method implements the 'combine' command. Please refer to L<Acme::Chef> for
details.
=cut
sub combine {
my $self = shift;
my $ingredient = shift;
croak "Invalid operation on empty container: combine."
unless @{$self->{contents}};
$self->{contents}->[-1]->value(
$self->{contents}->[-1]->value() *
$ingredient->value()
);
return $ingredient;
}
=item divide
This method implements the 'divide' command. Please refer to L<Acme::Chef> for
details.
=cut
sub divide {
my $self = shift;
my $ingredient = shift;
croak "Invalid operation on empty container: divide."
unless @{$self->{contents}};
$self->{contents}->[-1]->value(
$self->{contents}->[-1]->value() /
$ingredient->value()
);
return $ingredient;
}
=item put_sum
This method takes a number of Acme::Chef::Ingredient objects as arguments and
creates and 'puts' the sum of the ingredients.
Please refer to L<Acme::Chef> for details.
=cut
sub put_sum {
my $self = shift;
my @ingredients = @_;
my $sum = 0;
$sum += $_->value() for @ingredients;
my $ingredient = Acme::Chef::Ingredient->new(
name => '',
value => $sum,
measure => '',
type => 'dry',
);
$self->put($ingredient);
return $ingredient;
}
=item liquify_contents
This method implements the 'liquify' command for all ingredients.
Please refer to L<Acme::Chef> for details.
=cut
sub liquify_contents {
my $self = shift;
foreach my $ingredient (@{$self->{contents}}) {
$ingredient->liquify();
}
return $self;
}
=item stir_time
This method implements the 'stir' command.
First argument should be the depth ("time") to stir.
Please refer to L<Acme::Chef> for details.
=cut
sub stir_time {
my $self = shift;
my $depth = shift;
return $self unless scalar @{$self->{contents}};
$depth = $#{$self->{contents}} if $depth > $#{$self->{contents}};
my $top = pop @{ $self->{contents} };
splice @{$self->{contents}}, (@{$self->{contents}}-$depth), 0, $top;
return $self;
}
=item stir_ingredient
This method implements the 'stir_ingredient' command. Please refer to
L<Acme::Chef> for details.
=cut
sub stir_ingredient {
my $self = shift;
my $ingredient = shift;
$self->stir_time($ingredient->value());
return $self;
}
=item mix
This method implements the 'mix' command. Please refer to L<Acme::Chef> for
details.
Shuffles the container's contents.
=cut
sub mix {
my $self = shift;
_fisher_yates_shuffle( $self->{contents} );
return $self;
}
=item clean
This method implements the 'clean' command. Please refer to L<Acme::Chef> for
details.
Empties the container.
=cut
sub clean {
my $self = shift;
@{$self->{contents}} = ();
return $self;
}
=item pour
This method implements the 'pour' command. Please refer to L<Acme::Chef> for
details.
Returns the contained ingredients.
=cut
sub pour {
my $self = shift;
return @{ $self->{contents} };
}
=item print
Returns stringification of the object.
=cut
sub print {
my $self = shift;
my $string = '';
foreach my $ingr ( reverse @{$self->{contents}} ) {
if ($ingr->type() eq 'liquid') {
$string .= chr( $ingr->value() );
} else {
$string .= ' '.$ingr->value();
}
}
return $string;
}
# From the Perl FAQ: (NOT a method)
# fisher_yates_shuffle( \@array ) :
# generate a random permutation of @array in place
sub _fisher_yates_shuffle {
my $array = shift;
my $i;
for ($i = @$array; --$i; ) {
my $j = int rand ($i+1);
@$array[$i,$j] = @$array[$j,$i];
}
}
__END__
=back
=head1 AUTHOR
Steffen Mueller.
Chef designed by David Morgan-Mar.
=head1 COPYRIGHT AND LICENSE
Copyright (c) 2002-2008 Steffen Mueller. All rights reserved. This program is
free software; you can redistribute it and/or modify it under the same
terms as Perl itself.
Author can be reached at chef-module at steffen-mueller dot net
=cut