emeals 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +33 -0
- data/Rakefile +6 -0
- data/emeals.gemspec +19 -0
- data/lib/emeals.rb +6 -0
- data/lib/emeals/client.rb +34 -0
- data/lib/emeals/dish.rb +61 -0
- data/lib/emeals/meal.rb +135 -0
- data/lib/emeals/menu.rb +34 -0
- data/lib/emeals/version.rb +3 -0
- data/spec/client_spec.rb +179 -0
- data/spec/fixtures/menu.pdf +8380 -1
- data/spec/fixtures/menu.txt +285 -0
- data/spec/ingredient_spec.rb +51 -0
- data/spec/spec_helper.rb +17 -0
- metadata +68 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: c883efc225e72b90fe4943bcce321f9933664452
|
|
4
|
+
data.tar.gz: f7b246806d4fc330a5bf42235871ec8ae4615e48
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4f3596e65d12aeb0d1a58362eb83b408aa83fa05210b4b53b81fd63ecf921eebf1f39818edf17481bbb3f28cc0c1e39995888ad82cc68c2cbafc88e70586033f
|
|
7
|
+
data.tar.gz: 27e5874c713d6de1662e74a9d56cb4d54385eb794ee5369925db7f81e6e447fc9d5986c02153f8fb26c8ee8ba509a03a2b604608b69eb4ac5e2634e8aba1ed5f
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.0.0
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2013 Matt Moriarity
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[](https://travis-ci.org/mjm/emeals)
|
|
2
|
+
|
|
3
|
+
# Emeals
|
|
4
|
+
|
|
5
|
+
A library for reading menus from [eMeals][].
|
|
6
|
+
|
|
7
|
+
[emeals]: http://emeals.com
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Add this line to your application's Gemfile:
|
|
12
|
+
|
|
13
|
+
gem 'emeals'
|
|
14
|
+
|
|
15
|
+
And then execute:
|
|
16
|
+
|
|
17
|
+
$ bundle
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
|
|
21
|
+
$ gem install emeals
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
TODO: Write usage instructions here
|
|
26
|
+
|
|
27
|
+
## Contributing
|
|
28
|
+
|
|
29
|
+
1. Fork it
|
|
30
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
31
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
32
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
33
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/emeals.gemspec
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'emeals/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |gem|
|
|
7
|
+
gem.name = "emeals"
|
|
8
|
+
gem.version = Emeals::VERSION
|
|
9
|
+
gem.authors = ["Matt Moriarity"]
|
|
10
|
+
gem.email = ["matt@mattmoriarity.com"]
|
|
11
|
+
gem.description = %q{A client for interpreting eMeals menus.}
|
|
12
|
+
gem.summary = %q{eMeals Client}
|
|
13
|
+
gem.homepage = ""
|
|
14
|
+
|
|
15
|
+
gem.files = `git ls-files`.split($/)
|
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
|
18
|
+
gem.require_paths = ["lib"]
|
|
19
|
+
end
|
data/lib/emeals.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'emeals/menu'
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'tempfile'
|
|
5
|
+
|
|
6
|
+
class Emeals::Client
|
|
7
|
+
def parse(filename_or_file)
|
|
8
|
+
temp_file = copy_to_temp_file(filename_or_file)
|
|
9
|
+
text = pdf_to_text(temp_file.path)
|
|
10
|
+
|
|
11
|
+
Emeals::Menu.parse(text)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def copy_to_temp_file(filename_or_file)
|
|
17
|
+
Tempfile.open(['menu', '.pdf']) do |temp|
|
|
18
|
+
temp.binmode
|
|
19
|
+
temp.write(filename_or_file.is_a?(String) ? File.read(filename_or_file) : filename_or_file.read)
|
|
20
|
+
temp
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def pdf_to_text(filename)
|
|
25
|
+
system(pdf_to_text_path, '-raw', '-enc', 'UTF-8', filename)
|
|
26
|
+
File.read(filename.sub(/\.pdf$/, '.txt'))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def pdf_to_text_path
|
|
30
|
+
path = `which pdftotext`.chop
|
|
31
|
+
return '/opt/boxen/homebrew/bin/pdftotext' if path.empty?
|
|
32
|
+
path
|
|
33
|
+
end
|
|
34
|
+
end
|
data/lib/emeals/dish.rb
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
class Emeals::Dish
|
|
4
|
+
attr_accessor :name, :ingredients, :instructions
|
|
5
|
+
|
|
6
|
+
def initialize(name)
|
|
7
|
+
@name = name
|
|
8
|
+
@ingredients = []
|
|
9
|
+
@instructions = []
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class Emeals::Quantity
|
|
14
|
+
UNITS = %w(teaspoon tablespoon cup oz lb bag clove).map(&:to_sym)
|
|
15
|
+
UNITS_WITH_PLURALS = UNITS + UNITS.map {|u| "#{u}s".to_sym }
|
|
16
|
+
|
|
17
|
+
attr_reader :amount, :unit
|
|
18
|
+
|
|
19
|
+
def initialize(amount, unit = nil)
|
|
20
|
+
@amount = to_amount(amount)
|
|
21
|
+
@unit = unit && unit.to_sym
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def ==(other)
|
|
25
|
+
other.amount == @amount && other.unit == @unit
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def to_amount(str)
|
|
31
|
+
case str
|
|
32
|
+
when "\xC2\xBC"; '1/4'
|
|
33
|
+
when "\xC2\xBD"; '1/2'
|
|
34
|
+
else str
|
|
35
|
+
end.to_r
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class Emeals::Ingredient
|
|
40
|
+
attr_accessor :quantity, :description
|
|
41
|
+
|
|
42
|
+
def initialize(amount, unit, description)
|
|
43
|
+
@quantity = Emeals::Quantity.new(amount, unit)
|
|
44
|
+
@description = description
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
PARSE_REGEX = /((?:\d|\xC2\xBC|\xC2\xBD)+) (?:(#{Emeals::Quantity::UNITS_WITH_PLURALS.join("|")}) )?(.+)/
|
|
48
|
+
|
|
49
|
+
def self.parse(line)
|
|
50
|
+
if line =~ PARSE_REGEX
|
|
51
|
+
amount, unit, description = $1, $2, $3
|
|
52
|
+
new(amount, unit && unit.sub(/s$/, ''), description)
|
|
53
|
+
else
|
|
54
|
+
raise "tried to parse improperly formatted ingredient: #{line.inspect}"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def ==(other)
|
|
59
|
+
other.quantity == @quantity && other.description == @description
|
|
60
|
+
end
|
|
61
|
+
end
|
data/lib/emeals/meal.rb
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# encoding:utf-8
|
|
2
|
+
|
|
3
|
+
require 'emeals/dish'
|
|
4
|
+
|
|
5
|
+
class Emeals::Meal
|
|
6
|
+
attr_reader :entree, :side, :flags, :times
|
|
7
|
+
|
|
8
|
+
def initialize(entree = nil, side = nil)
|
|
9
|
+
@entree = entree
|
|
10
|
+
@side = side
|
|
11
|
+
@flags = []
|
|
12
|
+
@times = {}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def parse!(meal_text)
|
|
16
|
+
parse_state = :header
|
|
17
|
+
names = []
|
|
18
|
+
entree_instructions = []
|
|
19
|
+
side_instructions = []
|
|
20
|
+
meal_text.split("\n").each do |line|
|
|
21
|
+
case parse_state
|
|
22
|
+
when :header
|
|
23
|
+
parse_flags(line)
|
|
24
|
+
parse_state = :names
|
|
25
|
+
when :names
|
|
26
|
+
if line =~ /Prep Cook Total/
|
|
27
|
+
entree_name, side_name = separate_entree_and_side_names(names)
|
|
28
|
+
@entree = Emeals::Dish.new(entree_name)
|
|
29
|
+
@side = Emeals::Dish.new(side_name)
|
|
30
|
+
parse_state = :times
|
|
31
|
+
else
|
|
32
|
+
names << line
|
|
33
|
+
end
|
|
34
|
+
when :times
|
|
35
|
+
parse_times(line)
|
|
36
|
+
parse_state = :entree_ingredients
|
|
37
|
+
when :entree_ingredients
|
|
38
|
+
if line.include? "-------"
|
|
39
|
+
parse_state = :side_ingredients
|
|
40
|
+
else
|
|
41
|
+
add_ingredients_to_dish(line, @entree)
|
|
42
|
+
end
|
|
43
|
+
when :side_ingredients
|
|
44
|
+
if line =~ /^[A-Z]/
|
|
45
|
+
entree_instructions << line
|
|
46
|
+
parse_state = :entree_instructions
|
|
47
|
+
else
|
|
48
|
+
add_ingredients_to_dish(line, @side)
|
|
49
|
+
end
|
|
50
|
+
when :entree_instructions
|
|
51
|
+
if line.include? "-------"
|
|
52
|
+
add_instructions_to_dish(entree_instructions, @entree)
|
|
53
|
+
parse_state = :side_instructions
|
|
54
|
+
else
|
|
55
|
+
entree_instructions << line
|
|
56
|
+
end
|
|
57
|
+
when :side_instructions
|
|
58
|
+
if line =~ /^Copyright/
|
|
59
|
+
side_instructions = side_instructions[0..-3]
|
|
60
|
+
break
|
|
61
|
+
else
|
|
62
|
+
side_instructions << line
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
add_instructions_to_dish(side_instructions, @side)
|
|
70
|
+
self
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
FLAGS = {
|
|
74
|
+
"Slow Cooker" => :slow_cooker,
|
|
75
|
+
"On the Grill" => :on_the_grill,
|
|
76
|
+
"Super Fast" => :super_fast,
|
|
77
|
+
"Marinate Ahead" => :marinate_ahead
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
%w(slow_cooker on_the_grill super_fast marinate_ahead).each do |flag|
|
|
81
|
+
define_method "#{flag}?" do
|
|
82
|
+
@flags.include? flag.to_sym
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def separate_entree_and_side_names(names)
|
|
89
|
+
case names.size
|
|
90
|
+
when 2
|
|
91
|
+
names
|
|
92
|
+
when 3
|
|
93
|
+
if names[1].length < names[0].length
|
|
94
|
+
[join_names(names[0..1]), names[2]]
|
|
95
|
+
else
|
|
96
|
+
[names[0], join_names(names[1..2])]
|
|
97
|
+
end
|
|
98
|
+
else
|
|
99
|
+
[join_names(names[0..1]), join_names(names[2..-1])]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def join_names(names)
|
|
104
|
+
names.join(" ").gsub('- ', '-')
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def parse_flags(line)
|
|
108
|
+
FLAGS.each do |flag, sym|
|
|
109
|
+
@flags << sym if line.include? flag
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def parse_times(line)
|
|
114
|
+
times = line.split(" ")
|
|
115
|
+
@times[:prep] = times.first
|
|
116
|
+
@times[:cook] = times[1]
|
|
117
|
+
@times[:total] = times[2..-1].join(" ")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
INGREDIENT_REGEX = /(?:\d|\xC2\xBC|\xC2\xBD)+ .+?(?=, (?:\d|\xC2\xBC|\xC2\xBD)+|$)/
|
|
121
|
+
|
|
122
|
+
def add_ingredients_to_dish(line, dish)
|
|
123
|
+
if line =~ /^\d|\xC2\xBC|\xC2\xBD/
|
|
124
|
+
line.scan(INGREDIENT_REGEX).each do |match|
|
|
125
|
+
dish.ingredients << Emeals::Ingredient.parse(match)
|
|
126
|
+
end
|
|
127
|
+
else
|
|
128
|
+
dish.ingredients.last.description << " #{line}"
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def add_instructions_to_dish(lines, dish)
|
|
133
|
+
dish.instructions = lines.join(" ").split(/\. ?/)
|
|
134
|
+
end
|
|
135
|
+
end
|
data/lib/emeals/menu.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'emeals/meal'
|
|
2
|
+
|
|
3
|
+
class Emeals::Menu
|
|
4
|
+
attr_reader :count, :meals
|
|
5
|
+
|
|
6
|
+
def initialize
|
|
7
|
+
@count = 0
|
|
8
|
+
@meals = []
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def parse!(menu_text)
|
|
12
|
+
buffer = []
|
|
13
|
+
add_to_buffer = false
|
|
14
|
+
menu_text.split("\n").each do |line|
|
|
15
|
+
if line =~ /Meal (\d+)/
|
|
16
|
+
unless buffer.empty? and !add_to_buffer
|
|
17
|
+
@count = @count + 1
|
|
18
|
+
@meals << Emeals::Meal.new.parse!(buffer.join("\n"))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
add_to_buffer = @count < $1.to_i
|
|
22
|
+
buffer = add_to_buffer ? [line] : []
|
|
23
|
+
next unless add_to_buffer
|
|
24
|
+
else
|
|
25
|
+
buffer << line if add_to_buffer
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
self
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.parse(menu_text)
|
|
32
|
+
new.parse!(menu_text)
|
|
33
|
+
end
|
|
34
|
+
end
|
data/spec/client_spec.rb
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'emeals'
|
|
3
|
+
|
|
4
|
+
describe Emeals::Client do
|
|
5
|
+
subject(:client) { Emeals::Client.new }
|
|
6
|
+
let(:menu_path) { File.join(File.dirname(__FILE__), 'fixtures', 'menu.pdf') }
|
|
7
|
+
|
|
8
|
+
before(:each) do
|
|
9
|
+
@menu = client.parse(menu_path)
|
|
10
|
+
@meals = @menu.meals
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
context "when given a File handle instead of a filename" do
|
|
14
|
+
it "completes successfully" do
|
|
15
|
+
File.open(menu_path) do |file|
|
|
16
|
+
expect(client.parse(file).count).to eq 7
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "counts the number of meals in the menu" do
|
|
22
|
+
expect(@menu.count).to eq 7
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe "entree names" do
|
|
26
|
+
it "reads the names of entrees when the side wraps to two lines" do
|
|
27
|
+
expect(@meals[5].entree.name).to eq "Baked Cod Provencal"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "reads the names of entrees which wrap to two lines" do
|
|
31
|
+
expect(@meals.first.entree.name).to eq "Spicy Sausage and Egg Scramble"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "reads the names of entrees when both the side and entree wrap to two lines" do
|
|
35
|
+
expect(@meals.last.entree.name).to eq "Peppery Grilled Ribeye Steaks"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe "side names" do
|
|
40
|
+
it "reads the names of sides that are a single line" do
|
|
41
|
+
expect(@meals.first.side.name).to eq "Oregano Roasted Zucchini"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "reads the names of sides that wrap to two lines" do
|
|
45
|
+
expect(@meals[5].side.name).to eq "Roasted Asparagus with Sun-Dried Tomatoes"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "reads the names of sides when both the side and entree wrap to two lines" do
|
|
49
|
+
expect(@meals.last.side.name).to eq "Heirloom Tomato and Spinach Salad"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe "flags" do
|
|
54
|
+
it "reads a meal with no flags correctly" do
|
|
55
|
+
expect(@meals.first.flags).to be_empty
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "reads the slow cooker flag correctly" do
|
|
59
|
+
expect(@meals[1].flags).to eq [:slow_cooker]
|
|
60
|
+
expect(@meals[1]).to be_slow_cooker
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "reads the on the grill flag correctly" do
|
|
64
|
+
expect(@meals[3].flags).to eq [:on_the_grill]
|
|
65
|
+
expect(@meals[3]).to be_on_the_grill
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "reads the super fast flag correctly" do
|
|
69
|
+
expect(@meals[5].flags).to eq [:super_fast]
|
|
70
|
+
expect(@meals[5]).to be_super_fast
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "reads the marinate ahead flag correctly" do
|
|
74
|
+
expect(@meals[6].flags).to eq [:marinate_ahead]
|
|
75
|
+
expect(@meals[6]).to be_marinate_ahead
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
describe "times" do
|
|
80
|
+
it "reads prep times correctly" do
|
|
81
|
+
expect(@meals.first.times[:prep]).to eq "10m"
|
|
82
|
+
expect(@meals[5].times[:prep]).to eq "15m"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "reads cook times correctly" do
|
|
86
|
+
expect(@meals.first.times[:cook]).to eq "20m"
|
|
87
|
+
expect(@meals[1].times[:cook]).to eq "4h"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it "reads total times correctly" do
|
|
91
|
+
expect(@meals.first.times[:total]).to eq "30m"
|
|
92
|
+
expect(@meals[1].times[:total]).to eq "4h 10m"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
describe "entree ingredients" do
|
|
97
|
+
include Emeals
|
|
98
|
+
|
|
99
|
+
let(:dish) { @meals.first.entree }
|
|
100
|
+
|
|
101
|
+
it "reads the correct number of ingredients" do
|
|
102
|
+
expect(dish.ingredients.size).to be 5
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "reads the correct descriptions of ingredients" do
|
|
106
|
+
expect(dish.ingredients[1].description).to eq "small onion, minced"
|
|
107
|
+
expect(dish.ingredients[3].description).to eq "kosher salt"
|
|
108
|
+
expect(dish.ingredients[4].description).to eq "pepper"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it "reads the correct quantities of ingredients" do
|
|
112
|
+
expect(dish.ingredients[0].quantity).to eq Emeals::Quantity.new('1/4', 'lb')
|
|
113
|
+
expect(dish.ingredients[1].quantity).to eq Emeals::Quantity.new('1/2')
|
|
114
|
+
expect(dish.ingredients[2].quantity).to eq Emeals::Quantity.new('4')
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
describe "side ingredients" do
|
|
119
|
+
include Emeals
|
|
120
|
+
|
|
121
|
+
let(:dish) { @meals[3].side }
|
|
122
|
+
|
|
123
|
+
it "reads the correct number of ingredients" do
|
|
124
|
+
expect(dish.ingredients.size).to be 5
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it "reads the correct descriptions of ingredients" do
|
|
128
|
+
expect(dish.ingredients[0].description).to eq "(5-oz) bag baby spinach"
|
|
129
|
+
expect(dish.ingredients[1].description).to eq "drained and chopped roasted red bell peppers"
|
|
130
|
+
expect(dish.ingredients[2].description).to eq "pitted kalamata olives, cut in half"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "reads the correct quantities of ingredients" do
|
|
134
|
+
expect(dish.ingredients[0].quantity).to eq Emeals::Quantity.new('1/2')
|
|
135
|
+
expect(dish.ingredients[3].quantity).to eq Emeals::Quantity.new('2')
|
|
136
|
+
expect(dish.ingredients[4].quantity).to eq Emeals::Quantity.new('1/4', 'cup')
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
describe "entree instructions" do
|
|
141
|
+
let(:dish) { @meals[2].entree }
|
|
142
|
+
|
|
143
|
+
it "reads the correct number of instructions" do
|
|
144
|
+
expect(dish.instructions.size).to be 7
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it "reads the correct text of the instructions" do
|
|
148
|
+
expect(dish.instructions[0]).to eq "Preheat oven to 425 degrees"
|
|
149
|
+
expect(dish.instructions[4]).to eq "Dip fish in egg mixture; dredge in almond flour"
|
|
150
|
+
expect(dish.instructions[6]).to eq "Bake 8 minutes or until fish flakes with a fork"
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
describe "side instructions" do
|
|
155
|
+
let(:dish) { @meals[2].side }
|
|
156
|
+
|
|
157
|
+
it "reads the correct number of instructions" do
|
|
158
|
+
expect(dish.instructions.size).to be 7
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "reads the correct text of the instructions" do
|
|
162
|
+
expect(dish.instructions[0]).to eq "Preheat oven to 425 degrees"
|
|
163
|
+
expect(dish.instructions[1]).to eq "Toss zucchini, 1 tablespoon oil, salt and pepper on a large baking sheet; spread into a single layer"
|
|
164
|
+
expect(dish.instructions[6]).to eq "Bake 20 minutes or until center is set"
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
context "when the meal is the last on the page" do
|
|
168
|
+
let(:dish) { @meals[3].side }
|
|
169
|
+
|
|
170
|
+
it "reads the correct number of instructions" do
|
|
171
|
+
expect(dish.instructions.size).to be 1
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it "reads the correct instruction text" do
|
|
175
|
+
expect(dish.instructions.first).to eq "Combine spinach, roasted bell peppers, olives, pepperoncini peppers and vinaigrette in a large bowl; toss well to coat"
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|