| MealMaster documentation | Contained in the MealMaster distribution. |
MealMaster - Parse MealMaster format recipe files
my $mm = MealMaster->new();
my @recipes = $mm->parse("t/31000.mmf");
foreach my $r (@recipes) {
print "Title: " . $r->title . "\n";
print "Categories: " . join(", ", sort @{$r->categories}) . "\n";
print "Yield: " . $r->yield . "\n";
print "Directions: " . $r->directions . "\n";
print "Ingredients:\n";
foreach my $i (@{$r->ingredients}) {
print " " . $i->quantity .
" " . $i->measure .
" " . $i->product .
"\n";
}
People like to share food recipes on the internet. MealMaster is a popular program for collating recipes. The MealMaster module parses the MealMaster recipe export format, providing you with recipe objects. You can recognize these MealMaster format files as they generally start with "Recipe via Meal-Master".
The constructor. Takes no arguments:
my $mm = MealMaster->new();
The parse method takes a filename and recipes an array of MealMaster::Recipe objects representing the recipes in the file:
my @recipes = $mm->parse("t/31000.mmf");
Leon Brocard, <acme@astray.com>
Copyright (C) 2005, Leon Brocard
This module is free software; you can redistribute it or modify it under the same terms as Perl itself.
| MealMaster documentation | Contained in the MealMaster distribution. |
package MealMaster; use strict; use MealMaster::Recipe; use MealMaster::Ingredient; our $VERSION = "0.28"; use base qw(Class::Accessor::Chained::Fast); sub parse { my ($self, $filename) = @_; open(my $fh, $filename) || die $!; my $file = join '', <$fh>; return unless $file =~ /^(MMMMM|-----).+Meal-Master/; my @parts = split /^(?:MMMMM|-----).+Meal-Master.+$/m, $file; my @recipes; foreach my $part (@parts) { $part =~ s/^\s+//; my $recipe = MealMaster::Recipe->new; my $lines = [ split /\n/, $part ]; my $line; while (1) { $line = $self->_newline($lines); last unless defined $line; last if $line =~ /Title:/; } next unless defined $line; my ($title) = $line =~ /Title: (.+)$/; next unless $title; $title =~ s/^ +//; $title =~ s/ +$//; $recipe->title($title); $line = $self->_newline($lines); my ($categories) = $line =~ /Categories: (.+)$/; my @categories; @categories = split ', ', $categories if $categories; $recipe->categories(\@categories); $line = $self->_newline($lines); my ($yield) = $line =~ /(?:Yield|Servings): +(.+)$/; next unless $yield; $recipe->yield($yield); my $dflag = 0; my $ingredients; my $directions; while (defined($line = $self->_newline($lines))) { next unless $line; last if (!defined $line); next if (($dflag == 0) && ($line =~ m|^\s*$|)); if ($line =~ /^[M-]+$/) { last; } elsif ($line =~ m(^[M|-]{4,})) { $line =~ s|^MMMMM||; $line =~ s|^\-+||; $line =~ s|\-+$||; $line =~ s|^ +||; $line =~ s|:$||; $directions .= "$line\n"; } elsif ($line =~ m/^ *([A-Z ]+):$/) { $directions .= "$line\n"; } elsif (length($line) > 12 && (substr($line, 0, 7) =~ m|^[ 0-9\.\/]+$|) && (substr($line, 7, 4) =~ m|^ .. $|)) { $ingredients .= "$line\n"; } else { $line =~ s|^\s+||; if ($line) { $directions .= "$line\n"; $dflag = 1; } } } $ingredients = $self->_parse_ingredients($ingredients); $recipe->ingredients($ingredients); $recipe->directions($directions); push @recipes, $recipe; } return @recipes; } sub _parse_ingredients { my ($self, $text) = @_; return [] unless $text; my @ingredients; my @ingredients2; foreach my $line (split "\n", $text) { # Two-column case: # 1/3 c Instant cream of wheat # 1 1/3 c Brown Sugar, Firmly Packed 1 c Unbleached Flour # 12345678910121416182022242628303234363840424446485052 if (length($line) > 40 && $line =~ /^.{40} ...... .. ./) { my $quantity = substr($line, 0, 7); my $measure = substr($line, 8, 2); my $product = substr($line, 11, 29); $quantity =~ s/^ +//; $measure =~ s/ ? ?$//; $product =~ s/ +$//; my $i = MealMaster::Ingredient->new->quantity($quantity)->measure($measure) ->product($product); push @ingredients, $i; $quantity = substr($line, 41, 7); $measure = substr($line, 49, 2); $product = substr($line, 52); $quantity =~ s/^ +//; $measure =~ s/ ? ?$//; $i = MealMaster::Ingredient->new->quantity($quantity)->measure($measure) ->product($product); push @ingredients2, $i; } else { # Simple case: # 1/4 c Butter or margarine my $quantity = substr($line, 0, 7); my $measure = substr($line, 8, 2); my $product = substr($line, 11); $quantity =~ s/^ +//; $measure =~ s/ ? ?$//; $product =~ s/ +$//; my $i = MealMaster::Ingredient->new->quantity($quantity)->measure($measure) ->product($product); push @ingredients, $i; } } @ingredients = (@ingredients, @ingredients2); # Fold # 1/3 c Instant cream of wheat # -(prepared) # to: # 1/3 c Instant cream of wheat (prepared) my @folded_ingredients = ($ingredients[0]); foreach my $n (1 .. @ingredients - 1) { my $i = $ingredients[$n]; if ( not defined $i->quantity && not defined $i->measure && $i->product =~ /^-/) { my $extra = $i->product; $extra =~ s/^--?//; my $old = $folded_ingredients[-1]->product; my $new = "$old $extra"; $folded_ingredients[-1]->product($new); } else { push @folded_ingredients, $i; } } return \@folded_ingredients; } sub _newline { my ($self, $lines) = @_; my $line = shift @$lines; return unless defined $line; $line =~ s{\r|\n|\t}{}g; chomp $line; return $line; } 1; __END__