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; =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 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 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 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 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 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 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 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 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 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 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 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 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 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