Acme-Chef

 view release on metacpan or  search on metacpan

lib/Acme/Chef.pm  view on Meta::CPAN

   }

   return $dump;
}


# private function _get_paragraphs

sub _get_paragraphs {
   my $self = shift;

   my $string = shift;

   $string =~ s/^\s+//;
   $string =~ s/\s+$//;

   return split /\n{2,}/, $string;
}


# private function _paragraphsToRecipes
# 
# Constructs recipes from an array ref of paragraphs.

sub _paragraphsToRecipes {
   my $self = shift;

   my $paragraphs = shift;
   $paragraphs = shift if not defined $paragraphs or not ref $paragraphs;

   while (chomp @$paragraphs) {}

   my @recipes;

   my $paragraph_no = 0;
   while (@$paragraphs) {
      my $recipe_name = shift @$paragraphs;
      $paragraph_no++;
      $recipe_name =~ /^[ ]*([\-\w][\- \w]*)\.[ ]*$/
        or croak "Invalid recipe name specifier in paragraph no. $paragraph_no.";
      $recipe_name = lc($1);

      last unless @$paragraphs;
      my $comments = shift @$paragraphs;
      $paragraph_no++;
      my $ingredients;
      if ( $comments =~ /^[ ]*Ingredients\.[ ]*\n/ ) {
         $ingredients = $comments;
         $comments = '';
      } else {
         last unless @$paragraphs;
         $ingredients = shift @$paragraphs;
         $paragraph_no++;
      }

      last unless @$paragraphs;
      my $cooking_time = shift @$paragraphs;
      $paragraph_no++;
      my $temperature;

      if ($cooking_time =~ /^[ ]*Cooking time:[ ]*(\d+)(?: hours?| minutes?)\.[ ]*$/) {
         $cooking_time = $1;
         last unless @$paragraphs;
         $temperature = shift @$paragraphs;
         $paragraph_no++;
      } else {
         $temperature = $cooking_time;
         $cooking_time = '';
      }

      my $method;
      if ($temperature =~ /^[ ]*Pre-heat oven to (\d+) degrees Celsius(?: gas mark (\d+))?\.[ ]*$/) {
         $temperature = $1;
         $temperature .= ",$2" if defined $2;
         last unless @$paragraphs;
         $method = shift @$paragraphs;
         $paragraph_no++;
      } else {
         $method = $temperature;
         $temperature = '';
      }

      $method =~ /^[ ]*Method\.[ ]*\n/
        or croak "Invalid method specifier in paragraph no. $paragraph_no.";
      
      my $serves = '';
      if (@$paragraphs) {
         $serves = shift @$paragraphs;
         if ($serves =~ /^[ ]*Serves (\d+)\.[ ]*$/) {
            $serves = $1;
            $paragraph_no++;
         } else {
            unshift @$paragraphs, $serves;
            $serves = '';
         }
      }

      push @recipes, Acme::Chef::Recipe->new(
        name         => $recipe_name,
        comments     => $comments,
        ingredients  => $ingredients,
        cooking_time => $cooking_time,
        temperature  => $temperature,
        method       => $method,
        serves       => $serves,
      );
      
   }
   
   return @recipes;
}


1;
__END__

=back

=head1 DESIGN PRINCIPLES

=over 2

lib/Acme/Chef.pm  view on Meta::CPAN


=head2 Recipe Title

The recipe title describes in a few words what the program does. For
example: "Hello World Souffle", or "Fibonacci Numbers with Caramel Sauce".
The recipe title is always the first line of a Chef recipe, and is
followed by a full stop.

  recipe-title.

=head2 Comments

Comments are placed in a free-form paragraph after the recipe title.
Comments are optional.

=head2 Ingredient List

The next item in a Chef recipe is the ingredient list. This lists the
ingredients to be used by the program. The syntax is

  Ingredients.
  [initial-value] [[measure-type] measure] ingredient-name
  [further ingredients]

Ingredients are listed one per line. The intial-value is a number.
I<New specification: The initial-value is now optional. Attempting to
use an ingredient without a defined value is a run-time error.>
The optional measure can be any of the following:

=over 2

=item *

C<g> | C<kg> | C<pinch[es]> : These always indicate dry measures.

=item *

C<ml> | C<l> | C<dash[es]> : These always indicate liquid measures.

=item *

C<cup[s]> | C<teaspoon[s]> | C<tablespoon[s]> : These indicate measures
which may be either dry or liquid.

=back

The optional measure-type may be any of the following:

=over 2

=item *

C<heaped> | C<level> : These indicate that the measure is dry.

=back

The ingredient-name may be anything reasonable, and may include space
characters. The ingredient list is optional. If present, it declares
ingredients with the given initial values and measures.

=head2 Cooking Time

  Cooking time: time (hour[s] | minute[s]).

The cooking time statement is optional. The time is a number.

=head2 Oven Temperature

  Pre-heat oven to temperature degrees Celcius [(gas mark mark)].

Some recipes require baking. If so, there will be an oven
temperature statement. This is optional. The temperature and mark are
numbers.

=head2 Method

  Method.
  method statements

The method contains the actual recipe instructions. These are written
in sentences. Line breaks are ignored in the method of a recipe. Valid
method instructions are:

=over 2

=item *

C<Take ingredient from refrigerator.>

I<New specification!> This reads lines from STDIN until a
numerical value is found. This numerical value is put into the
ingredient overwriting any previous value.

=item *

C<Put ingredient into [nth] mixing bowl.>

This puts the ingredient into the nth mixing bowl.

=item *

C<Fold ingredient into [nth] mixing bowl.>

This removes the top value from the nth mixing bowl and places it in
the ingredient.

=item *

C<Add ingredient [to [nth] mixing bowl].>

This adds the value of ingredient to the value of the ingredient on top
of the nth mixing bowl and stores the result in the nth mixing bowl.

=item *

C<Remove ingredient [from [nth] mixing bowl].>

This subtracts the value of ingredient from the value of the ingredient
on top of the nth mixing bowl and stores the result in the nth mixing bowl.

=item *

C<Combine ingredient [into [nth] mixing bowl].>



( run in 0.355 second using v1.01-cache-2.11-cpan-d7a12ab2c7f )