allrecipes 1.0.0
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 +23 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +148 -0
- data/Rakefile +4 -0
- data/allrecipes.gemspec +26 -0
- data/lib/allrecipes.rb +8 -0
- data/lib/allrecipes/main.rb +47 -0
- data/lib/allrecipes/page_parser.rb +54 -0
- data/lib/allrecipes/recipe_parser.rb +103 -0
- data/lib/allrecipes/url_helper.rb +114 -0
- data/lib/allrecipes/version.rb +3 -0
- data/spec/allrecipes_spec.rb +124 -0
- data/spec/fixtures/recipe.json +128 -0
- data/spec/page_parser_spec.rb +15 -0
- data/spec/recipe_filter_spec.rb +230 -0
- data/spec/recipe_parser_spec.rb +53 -0
- data/spec/spec_helper.rb +18 -0
- metadata +167 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e3495f1afe0f65623bc263f180444a4ddfe556b8
|
4
|
+
data.tar.gz: 2926ce536d61bb64dc702877426cfcc8798fdc81
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 97d5e36c2cabe1fb1199af8de21f9ab5286947465a61855e46a4b8b32b11f881714a7a3bb216ffac4755894f0eb552fdf06dde33a6b7a0f02b675d38ac5e3210
|
7
|
+
data.tar.gz: 17ee339ff6056235e139bc4907d9910032965ad39286a7f9c72738481b90a679049d04d762586ce47c54b28c7d75aef331aa3b597f11a03d9164f1228a147a74
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
spec/fixtures/vcr_cassettes/*
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 shivamd
|
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,148 @@
|
|
1
|
+
# Allrecipes
|
2
|
+
|
3
|
+
A Ruby interface to the Allrecipes API.
|
4
|
+
Allrecipes doesn't have an API, the requests are done using mechanize.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'allrecipes'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install allrecipes
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
**Initialize scraper**
|
23
|
+
```ruby
|
24
|
+
recipes = Allrecipes.new
|
25
|
+
```
|
26
|
+
|
27
|
+
**Get all recipes**
|
28
|
+
```ruby
|
29
|
+
recipes.all #default sorted by popularity, limit 20.
|
30
|
+
```
|
31
|
+
|
32
|
+
**Get recipes by course**
|
33
|
+
```ruby
|
34
|
+
recipes.course("breakfast") #default sorted by popularity, limit 20.
|
35
|
+
```
|
36
|
+
List of courses:
|
37
|
+
```ruby
|
38
|
+
[ "appetizer", "bread", "breakfast", "dessert", "drink","fruit", "herbs", "main", "salad", "soup", "meat", "pasta", "seafood", "wholegrains"]
|
39
|
+
```
|
40
|
+
|
41
|
+
**Get recipes by region**
|
42
|
+
```ruby
|
43
|
+
recipes.region("european") #default sorted by popularity, limit 20.
|
44
|
+
```
|
45
|
+
|
46
|
+
Specific options available
|
47
|
+
```ruby
|
48
|
+
recipes.region("french") #default sorted by popularity, limit 20.
|
49
|
+
```
|
50
|
+
|
51
|
+
List of regions available:
|
52
|
+
```ruby
|
53
|
+
[
|
54
|
+
"asian"
|
55
|
+
"chinese"
|
56
|
+
"thai"
|
57
|
+
"japanese"
|
58
|
+
"filipino"
|
59
|
+
"korean"
|
60
|
+
"vietnamese"
|
61
|
+
"malaysian"
|
62
|
+
"pakistani"
|
63
|
+
"indonesian"
|
64
|
+
"iranian"
|
65
|
+
"bangladeshi"
|
66
|
+
"indian"
|
67
|
+
|
68
|
+
"african"
|
69
|
+
"african_main"
|
70
|
+
"moroccan"
|
71
|
+
"south_african"
|
72
|
+
"egyptian"
|
73
|
+
|
74
|
+
"australian"
|
75
|
+
|
76
|
+
"canadian"
|
77
|
+
"vancouver"
|
78
|
+
"toronto"
|
79
|
+
"quebec"
|
80
|
+
|
81
|
+
"european"
|
82
|
+
"italian"
|
83
|
+
"uk"
|
84
|
+
"french"
|
85
|
+
"greek"
|
86
|
+
"eastern_european"
|
87
|
+
"german"
|
88
|
+
"scandinavian"
|
89
|
+
"spanish"
|
90
|
+
"portuguese"
|
91
|
+
"dutch"
|
92
|
+
"austrian"
|
93
|
+
"swiss"
|
94
|
+
"belgian"
|
95
|
+
|
96
|
+
"latin_american"
|
97
|
+
"mexican"
|
98
|
+
"caribbean"
|
99
|
+
"south_american"
|
100
|
+
|
101
|
+
"middle_eastern"
|
102
|
+
"lebanese"
|
103
|
+
"turkish"
|
104
|
+
"israeli"
|
105
|
+
]
|
106
|
+
```
|
107
|
+
|
108
|
+
**Get recipes by ingredient**
|
109
|
+
```ruby
|
110
|
+
recipes.ingredient("apples") #default sorted by relevance, limit 21.
|
111
|
+
```
|
112
|
+
|
113
|
+
**Sample response**
|
114
|
+
```ruby
|
115
|
+
[
|
116
|
+
{
|
117
|
+
:name => "Lasagna",
|
118
|
+
:image => "http://images.media-allrecipes.com/userphotos/250x250/00/03/24/32427.jpg",
|
119
|
+
:servings => 12,
|
120
|
+
:ingredients =>
|
121
|
+
[
|
122
|
+
{
|
123
|
+
"quantity" => 1.0,
|
124
|
+
"unit" => "pound",
|
125
|
+
"name" => "ground beef"
|
126
|
+
}
|
127
|
+
],
|
128
|
+
:directions =>
|
129
|
+
[
|
130
|
+
{
|
131
|
+
"Bring a large pot of lightly salted water to a boil"
|
132
|
+
}
|
133
|
+
]
|
134
|
+
}
|
135
|
+
]
|
136
|
+
```
|
137
|
+
|
138
|
+
## Todo
|
139
|
+
|
140
|
+
1. Filters on search (offset, limit & sort_by)
|
141
|
+
2. Complex search with queries such as multiple ingredients, course, time etc.
|
142
|
+
3. Add prep time & cook time to results
|
143
|
+
4. Add nutrional info to results.
|
144
|
+
5. Add rating to results
|
145
|
+
6. Add user photos for recipe to results
|
146
|
+
7. Able to get popular recipes of the day.
|
147
|
+
8. Able to insert recipe URL directly.
|
148
|
+
9. Able to add page URL directly.
|
data/Rakefile
ADDED
data/allrecipes.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'allrecipes/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.add_dependency "mechanize"
|
8
|
+
spec.add_dependency "nokogiri"
|
9
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
10
|
+
spec.add_development_dependency "rake"
|
11
|
+
spec.add_development_dependency "rspec"
|
12
|
+
spec.add_development_dependency "webmock"
|
13
|
+
spec.add_development_dependency "vcr"
|
14
|
+
spec.name = "allrecipes"
|
15
|
+
spec.version = Allrecipes::VERSION
|
16
|
+
spec.authors = ["shivamd"]
|
17
|
+
spec.email = ["daryananis@gmail.com"]
|
18
|
+
spec.summary = %q{A Ruby wrapper for allrecipes.com}
|
19
|
+
spec.description = %q{A Ruby wrapper for allrecipes.com. A simple interface to scrape recipes from allrecipes.com}
|
20
|
+
spec.homepage = "https://github.com/shivamd/allrecipes"
|
21
|
+
spec.license = "MIT"
|
22
|
+
spec.files = `git ls-files -z`.split("\x0")
|
23
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
24
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
25
|
+
spec.require_paths = ["lib"]
|
26
|
+
end
|
data/lib/allrecipes.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
class Allrecipes
|
2
|
+
$URL= "http://allrecipes.com"
|
3
|
+
include URLHelper
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@agent = Mechanize.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def all(options={})
|
10
|
+
page = @agent.get $URL + "/recipes" + "?page=#{options[:page]}&st=#{search_sort_parameter(options[:sort_by])}"
|
11
|
+
PageParser.new(page, options).recipes
|
12
|
+
end
|
13
|
+
|
14
|
+
def course(course_type, options={})
|
15
|
+
begin
|
16
|
+
course_url = get_course_url(course_type) + "?page=#{options[:page]}&st=#{search_sort_parameter(options[:sort_by])}"
|
17
|
+
page = @agent.get(course_url)
|
18
|
+
PageParser.new(page, options).recipes
|
19
|
+
rescue Exception
|
20
|
+
raise "Course type doesn't exist"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def region(region_type, options={})
|
25
|
+
begin
|
26
|
+
region_url = get_region_url(region_type) + "?page=#{options[:page]}&st=#{search_sort_parameter(options[:sort_by])}"
|
27
|
+
page = @agent.get(region_url)
|
28
|
+
PageParser.new(page, options).recipes
|
29
|
+
rescue Exception
|
30
|
+
raise "Region doesn't exist"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def ingredient(name, options={})
|
35
|
+
begin
|
36
|
+
url = $URL + "/search/?wt=#{name}&page=#{options[:page]}&sb=#{ingredient_sort_parameter(options[:sort_by])}"
|
37
|
+
page = @agent.get(url)
|
38
|
+
options = options.merge({type: "ingredient"})
|
39
|
+
PageParser.new(page, options).recipes
|
40
|
+
rescue Exception
|
41
|
+
raise "Could not find recipes that include this ingredient"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class PageParser
|
2
|
+
|
3
|
+
def initialize(page, options={})
|
4
|
+
@page = page
|
5
|
+
@recipes = []
|
6
|
+
@options = options
|
7
|
+
@limit = options[:limit]
|
8
|
+
get_recipes
|
9
|
+
end
|
10
|
+
|
11
|
+
def recipe_info_class
|
12
|
+
".recipe-info"
|
13
|
+
end
|
14
|
+
|
15
|
+
def title_class
|
16
|
+
".title"
|
17
|
+
end
|
18
|
+
|
19
|
+
def recipe_info
|
20
|
+
if @options[:type] == "ingredient"
|
21
|
+
@page.search(recipe_info_class)
|
22
|
+
else
|
23
|
+
recipes_grid.search(recipe_info_class)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def filtered_recipes
|
28
|
+
@limit ? recipe_info.take(@limit) : recipe_info
|
29
|
+
end
|
30
|
+
|
31
|
+
def recipes_grid_class
|
32
|
+
".grid-view"
|
33
|
+
end
|
34
|
+
|
35
|
+
def recipes_grid
|
36
|
+
@page.search(recipes_grid_class)[-1]
|
37
|
+
end
|
38
|
+
|
39
|
+
def recipe_link(info)
|
40
|
+
info.search(title_class)[0].attributes["href"].value
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_recipes
|
44
|
+
filtered_recipes.each do |info|
|
45
|
+
recipe_link = recipe_link(info)
|
46
|
+
@recipes << RecipeParser.new(recipe_link).recipe
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def recipes
|
51
|
+
@recipes
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
class RecipeParser
|
2
|
+
$BASE_URL = "http://allrecipes.com"
|
3
|
+
|
4
|
+
def initialize(page)
|
5
|
+
@page = agent.get $BASE_URL + page
|
6
|
+
@directions = []
|
7
|
+
@ingredients = []
|
8
|
+
get_ingredients
|
9
|
+
get_directions
|
10
|
+
end
|
11
|
+
|
12
|
+
def agent
|
13
|
+
Mechanize.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def name
|
17
|
+
@page.search(name_id).inner_text
|
18
|
+
end
|
19
|
+
|
20
|
+
def name_id
|
21
|
+
"#itemTitle"
|
22
|
+
end
|
23
|
+
|
24
|
+
def image
|
25
|
+
@page.search(image_id).first.attributes["src"].value
|
26
|
+
end
|
27
|
+
|
28
|
+
def image_id
|
29
|
+
"#imgPhoto"
|
30
|
+
end
|
31
|
+
|
32
|
+
def servings
|
33
|
+
@page.search(servings_id).inner_text.gsub(" servings", "").to_i
|
34
|
+
end
|
35
|
+
|
36
|
+
def servings_id
|
37
|
+
"#lblYield"
|
38
|
+
end
|
39
|
+
|
40
|
+
def directions_class
|
41
|
+
".directions ol li span"
|
42
|
+
end
|
43
|
+
|
44
|
+
def directions_list
|
45
|
+
@page.search(directions_class)
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_directions
|
49
|
+
directions_list.each do |direction|
|
50
|
+
@directions << direction.text
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def ingredients_list
|
55
|
+
@page.search(ingredients_class)
|
56
|
+
end
|
57
|
+
|
58
|
+
def ingredients_class
|
59
|
+
".fl-ing"
|
60
|
+
end
|
61
|
+
|
62
|
+
def get_ingredients
|
63
|
+
ingredients_list.each do |ingredient|
|
64
|
+
amount = amount(ingredient)
|
65
|
+
ingredient_name= ingredient_name(ingredient)
|
66
|
+
add_ingredient_to_list(amount, ingredient_name)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_ingredient_to_list(amount, ingredient_name)
|
71
|
+
if amount && ingredient_name #some recipes have empty ingredients
|
72
|
+
quantity,unit = amount.text.split(" ")
|
73
|
+
@ingredients << { "quantity" => quantity.to_f, "unit"=>unit, "name" => ingredient_name.text }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def amount(ingredient)
|
78
|
+
ingredient.search(ingredient_amount_class).children[0]
|
79
|
+
end
|
80
|
+
|
81
|
+
def ingredient_amount_class
|
82
|
+
".ingredient-amount"
|
83
|
+
end
|
84
|
+
|
85
|
+
def ingredient_name(ingredient)
|
86
|
+
ingredient.search(ingredient_name_class).children[0]
|
87
|
+
end
|
88
|
+
|
89
|
+
def ingredient_name_class
|
90
|
+
".ingredient-name"
|
91
|
+
end
|
92
|
+
|
93
|
+
def recipe
|
94
|
+
{
|
95
|
+
name: name,
|
96
|
+
image: image,
|
97
|
+
servings: servings,
|
98
|
+
ingredients: @ingredients,
|
99
|
+
directions: @directions
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module URLHelper
|
2
|
+
|
3
|
+
def get_course_url(course_type)
|
4
|
+
begin
|
5
|
+
"http://allrecipes.com/recipes/" + courses[course_type]
|
6
|
+
rescue Exception => e
|
7
|
+
puts e.message
|
8
|
+
puts e.backtrace.inspect
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def courses
|
13
|
+
#course type and matching URL based on allrecipes.com
|
14
|
+
{
|
15
|
+
"bread"=> "bread",
|
16
|
+
"appetizer"=> "appetizers-and-snacks",
|
17
|
+
"breakfast"=> "breakfast-and-brunch",
|
18
|
+
"dessert"=> "desserts",
|
19
|
+
"drink"=> "drinks",
|
20
|
+
"main"=> "main-dish",
|
21
|
+
"salad"=> "salad",
|
22
|
+
"side"=> "side-dish",
|
23
|
+
"soup"=> "soups-stews-and-chili",
|
24
|
+
"fruit"=> "fruits-and-vegetables",
|
25
|
+
"herbs"=> "herbs-and-spices",
|
26
|
+
"meat"=> "meat-and-poultry",
|
27
|
+
"pasta"=> "pasta",
|
28
|
+
"seafood"=> "seafood",
|
29
|
+
"wholegrains"=> "whole-grains"
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_region_url(region_type)
|
34
|
+
begin
|
35
|
+
"http://allrecipes.com/recipes/world-cuisine/" + regions[region_type]
|
36
|
+
rescue Exception => e
|
37
|
+
puts e.message
|
38
|
+
puts e.backtrace.inspect
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def regions
|
43
|
+
{
|
44
|
+
"asian"=> "asian",
|
45
|
+
"chinese" => "asian/chinese",
|
46
|
+
"thai" => "asian/thai",
|
47
|
+
"japanese" => "asian/japanese",
|
48
|
+
"filipino" => "asian/filipino",
|
49
|
+
"korean" => "asian/korean",
|
50
|
+
"vietnamese" => "asian/vietnamese",
|
51
|
+
"malaysian" => "asian/malaysian",
|
52
|
+
"pakistani" => "asian/pakistani",
|
53
|
+
"indonesian" => "asian/indonesian",
|
54
|
+
"iranian" => "asian/iranian",
|
55
|
+
"bangladeshi" => "asian/bangladeshi",
|
56
|
+
"indian" => "asian/indian",
|
57
|
+
|
58
|
+
"african"=> "african",
|
59
|
+
"african_main"=> "african/main-dishes",
|
60
|
+
"moroccan"=> "african/moroccan",
|
61
|
+
"south_african"=> "african/south-african",
|
62
|
+
"egyptian"=> "african/egyptian",
|
63
|
+
|
64
|
+
"australian"=> "australian-and-new-zealander",
|
65
|
+
|
66
|
+
"canadian"=> "canadian",
|
67
|
+
"vancouver" => "canadian/vancouver",
|
68
|
+
"toronto" => "canadian/toronto",
|
69
|
+
"quebec" => "canadian/quebec-recipes",
|
70
|
+
|
71
|
+
"european"=> "european",
|
72
|
+
"italian"=> "european/italian",
|
73
|
+
"uk"=> "european/uk-and-ireland",
|
74
|
+
"french"=> "european/french",
|
75
|
+
"greek"=> "european/greek",
|
76
|
+
"eastern_european"=> "european/eastern-european",
|
77
|
+
"german"=> "european/german",
|
78
|
+
"scandinavian"=> "european/scandinavian",
|
79
|
+
"spanish"=> "european/spanish",
|
80
|
+
"portuguese"=> "european/portuguese",
|
81
|
+
"dutch"=> "european/dutch",
|
82
|
+
"austrian"=> "european/austrian",
|
83
|
+
"swiss"=> "european/swiss",
|
84
|
+
"belgian"=> "european/belgian",
|
85
|
+
|
86
|
+
"latin_american"=> "latin-american",
|
87
|
+
"mexican" => "latin-american/mexican",
|
88
|
+
"caribbean" => "latin-american/caribbean",
|
89
|
+
"south_american" => "latin-american/south-american",
|
90
|
+
|
91
|
+
"middle_eastern"=> "middle-eastern",
|
92
|
+
"lebanese" => "middle-eastern/lebanese",
|
93
|
+
"turkish" => "middle-eastern/turkish",
|
94
|
+
"israeli" => "middle-eastern/israeli",
|
95
|
+
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
def search_sort_parameter(sort_type)
|
100
|
+
case sort_type
|
101
|
+
when "date" then "n"
|
102
|
+
when "title" then "t"
|
103
|
+
else "p"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def ingredient_sort_parameter(sort_type)
|
108
|
+
case sort_type
|
109
|
+
when "rating" then "ra"
|
110
|
+
when "popularity" then "p"
|
111
|
+
else "re"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
describe Allrecipes do
|
3
|
+
subject { Allrecipes.new }
|
4
|
+
|
5
|
+
describe "#all" do
|
6
|
+
context "without options" do
|
7
|
+
before do
|
8
|
+
VCR.use_cassette "all" do
|
9
|
+
@recipes = subject.all
|
10
|
+
end
|
11
|
+
end
|
12
|
+
it "requests the correct url" do
|
13
|
+
expect(a_request(:get, "http://allrecipes.com/recipes?page=&st=p")).to have_been_made
|
14
|
+
end
|
15
|
+
it "count should be 20" do
|
16
|
+
expect(@recipes.count).to eq 20
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#course" do
|
22
|
+
|
23
|
+
context "without options" do
|
24
|
+
|
25
|
+
before do
|
26
|
+
VCR.use_cassette "course_1" do
|
27
|
+
@recipes = subject.course("bread")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "requests the correct url" do
|
32
|
+
expect(a_request(:get, "http://allrecipes.com/recipes/bread?page=&st=p")).to have_been_made
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should return 20 recipes" do
|
36
|
+
expect(@recipes.count).to eq 20
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
context "non existent course" do
|
42
|
+
let(:nonexistent_course) { -> { subject.course("nonexistent") } }
|
43
|
+
it "should give appropriate error message" do
|
44
|
+
expect(nonexistent_course).to raise_error("Course type doesn't exist")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#region" do
|
51
|
+
context "without options main region" do
|
52
|
+
before do
|
53
|
+
VCR.use_cassette "region" do
|
54
|
+
@recipes = subject.region("asian")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it "requests the correct url" do
|
59
|
+
expect(a_request(:get, "http://allrecipes.com/recipes/world-cuisine/asian?page=&st=p")).to have_been_made
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should return 20 recipes" do
|
63
|
+
expect(@recipes.count).to eq 20
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
context "without options sub region" do
|
69
|
+
|
70
|
+
before do
|
71
|
+
VCR.use_cassette "sub_region" do
|
72
|
+
@recipes = subject.region("chinese")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it "requests the correct url" do
|
77
|
+
expect(a_request(:get, "http://allrecipes.com/recipes/world-cuisine/asian/chinese?page=&st=p")).to have_been_made
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should return 20 recipes" do
|
81
|
+
expect(@recipes.count).to eq 20
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
context "non existent region" do
|
87
|
+
let(:nonexistent_region) { -> { subject.region("nonexistent") } }
|
88
|
+
it "should give appropriate error message" do
|
89
|
+
expect(nonexistent_region).to raise_error("Region doesn't exist")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "#ingredient" do
|
95
|
+
context "without options" do
|
96
|
+
before do
|
97
|
+
VCR.use_cassette "ingredients" do
|
98
|
+
@recipes = subject.ingredient("apples")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
it "requests the correct url" do
|
103
|
+
expect(a_request(:get, "http://allrecipes.com/search/?wt=apples&page=&sb=re")).to have_been_made
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should return 21 recipes" do
|
107
|
+
expect(@recipes.count).to eq 21
|
108
|
+
end
|
109
|
+
|
110
|
+
its "recipes should include apple" do
|
111
|
+
expect(@recipes.first[:ingredients].select{ |ingredient| ingredient["name"].include?("apples") }.count).to eq 1
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
context "no recipes for ingredient" do
|
117
|
+
let(:nonexistent_ingredient) { -> { subject.ingredient("nonexistent") } }
|
118
|
+
it "should give appropriate error message" do
|
119
|
+
expect(nonexistent_ingredient).to raise_error("Could not find recipes that include this ingredient")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
{
|
2
|
+
{
|
3
|
+
"name": "Ravioli",
|
4
|
+
"image": "http://placehold.it/350x150",
|
5
|
+
servings: 5,
|
6
|
+
ingredients: [],
|
7
|
+
directions: []
|
8
|
+
},
|
9
|
+
{
|
10
|
+
"name": "Ravioli",
|
11
|
+
"image": "http://placehold.it/350x150",
|
12
|
+
servings: 5,
|
13
|
+
ingredients: [],
|
14
|
+
directions: []
|
15
|
+
},
|
16
|
+
{
|
17
|
+
"name": "Ravioli",
|
18
|
+
"image": "http://placehold.it/350x150",
|
19
|
+
servings: 5,
|
20
|
+
ingredients: [],
|
21
|
+
directions: []
|
22
|
+
},
|
23
|
+
{
|
24
|
+
"name": "Ravioli",
|
25
|
+
"image": "http://placehold.it/350x150",
|
26
|
+
servings: 5,
|
27
|
+
ingredients: [],
|
28
|
+
directions: []
|
29
|
+
},
|
30
|
+
{
|
31
|
+
"name": "Ravioli",
|
32
|
+
"image": "http://placehold.it/350x150",
|
33
|
+
servings: 5,
|
34
|
+
ingredients: [],
|
35
|
+
directions: []
|
36
|
+
},
|
37
|
+
{
|
38
|
+
"name": "Ravioli",
|
39
|
+
"image": "http://placehold.it/350x150",
|
40
|
+
servings: 5,
|
41
|
+
ingredients: [],
|
42
|
+
directions: []
|
43
|
+
},
|
44
|
+
{
|
45
|
+
"name": "Ravioli",
|
46
|
+
"image": "http://placehold.it/350x150",
|
47
|
+
servings: 5,
|
48
|
+
ingredients: [],
|
49
|
+
directions: []
|
50
|
+
},
|
51
|
+
{
|
52
|
+
"name": "Ravioli",
|
53
|
+
"image": "http://placehold.it/350x150",
|
54
|
+
servings: 5,
|
55
|
+
ingredients: [],
|
56
|
+
directions: []
|
57
|
+
},
|
58
|
+
{
|
59
|
+
"name": "Ravioli",
|
60
|
+
"image": "http://placehold.it/350x150",
|
61
|
+
servings: 5,
|
62
|
+
ingredients: [],
|
63
|
+
directions: []
|
64
|
+
},
|
65
|
+
{
|
66
|
+
"name": "Ravioli",
|
67
|
+
"image": "http://placehold.it/350x150",
|
68
|
+
servings: 5,
|
69
|
+
ingredients: [],
|
70
|
+
directions: []
|
71
|
+
},
|
72
|
+
{
|
73
|
+
"name": "Ravioli",
|
74
|
+
"image": "http://placehold.it/350x150",
|
75
|
+
servings: 5,
|
76
|
+
ingredients: [],
|
77
|
+
directions: []
|
78
|
+
},
|
79
|
+
{
|
80
|
+
"name": "Ravioli",
|
81
|
+
"image": "http://placehold.it/350x150",
|
82
|
+
servings: 5,
|
83
|
+
ingredients: [],
|
84
|
+
directions: []
|
85
|
+
},
|
86
|
+
{
|
87
|
+
"name": "Ravioli",
|
88
|
+
"image": "http://placehold.it/350x150",
|
89
|
+
servings: 5,
|
90
|
+
ingredients: [],
|
91
|
+
directions: []
|
92
|
+
},
|
93
|
+
{
|
94
|
+
"name": "Ravioli",
|
95
|
+
"image": "http://placehold.it/350x150",
|
96
|
+
servings: 5,
|
97
|
+
ingredients: [],
|
98
|
+
directions: []
|
99
|
+
},
|
100
|
+
{
|
101
|
+
"name": "Ravioli",
|
102
|
+
"image": "http://placehold.it/350x150",
|
103
|
+
servings: 5,
|
104
|
+
ingredients: [],
|
105
|
+
directions: []
|
106
|
+
},
|
107
|
+
{
|
108
|
+
"name": "Ravioli",
|
109
|
+
"image": "http://placehold.it/350x150",
|
110
|
+
servings: 5,
|
111
|
+
ingredients: [],
|
112
|
+
directions: []
|
113
|
+
},
|
114
|
+
{
|
115
|
+
"name": "Ravioli",
|
116
|
+
"image": "http://placehold.it/350x150",
|
117
|
+
servings: 5,
|
118
|
+
ingredients: [],
|
119
|
+
directions: []
|
120
|
+
},
|
121
|
+
{
|
122
|
+
"name": "Ravioli",
|
123
|
+
"image": "http://placehold.it/350x150",
|
124
|
+
servings: 5,
|
125
|
+
ingredients: [],
|
126
|
+
directions: []
|
127
|
+
}
|
128
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
describe PageParser do
|
3
|
+
before do
|
4
|
+
VCR.use_cassette "page" do
|
5
|
+
url = "http://allrecipes.com/recipes"
|
6
|
+
page = Mechanize.new.get(url)
|
7
|
+
@parser = PageParser.new(page)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should have the right amount of recipes" do
|
12
|
+
expect(@parser.recipes.count).to eq 20
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
describe Allrecipes do
|
3
|
+
subject { Allrecipes.new }
|
4
|
+
describe "options" do
|
5
|
+
context "limit" do
|
6
|
+
describe "valid limit" do
|
7
|
+
before do
|
8
|
+
VCR.use_cassette "valid_limit" do
|
9
|
+
@all_recipes = subject.all({limit: 10})
|
10
|
+
@region_recipes = subject.region("asian", {limit: 10})
|
11
|
+
@course_recipes = subject.course("bread", {limit: 10})
|
12
|
+
@ingredient_recipes = subject.ingredient("apples", {limit: 10})
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should restrict recipes to 10" do
|
17
|
+
expect(@all_recipes.count).to eq 10
|
18
|
+
expect(@region_recipes.count).to eq 10
|
19
|
+
expect(@course_recipes.count).to eq 10
|
20
|
+
expect(@ingredient_recipes.count).to eq 10
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "page" do
|
26
|
+
describe "valid page" do
|
27
|
+
before do
|
28
|
+
VCR.use_cassette "valid_pages" do
|
29
|
+
@all_recipes = subject.all({page: 2})
|
30
|
+
@all_recipes_initial = subject.all
|
31
|
+
|
32
|
+
@region_recipes = subject.region("asian", {page: 3})
|
33
|
+
@region_recipes_initial = subject.region("asian")
|
34
|
+
|
35
|
+
@course_recipes = subject.course("bread", {page: 4})
|
36
|
+
@course_recipes_initial = subject.course("bread")
|
37
|
+
|
38
|
+
@ingredient_recipes = subject.ingredient("apples", {page: 5})
|
39
|
+
@ingredient_recipes_intiial = subject.ingredient("apples")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should call the correct urls" do
|
44
|
+
expect(a_request(:get, "http://allrecipes.com/recipes?page=2&st=p")).to have_been_made
|
45
|
+
expect(a_request(:get, "http://allrecipes.com/recipes/world-cuisine/asian?page=3&st=p")).to have_been_made
|
46
|
+
expect(a_request(:get, "http://allrecipes.com/recipes/bread?page=4&st=p")).to have_been_made
|
47
|
+
expect(a_request(:get, "http://allrecipes.com/search/?wt=apples&page=5&sb=re")).to have_been_made
|
48
|
+
end
|
49
|
+
|
50
|
+
its "results should be different from the initial page" do
|
51
|
+
expect(@all_recipes).to_not eq @all_recipes_initial
|
52
|
+
expect(@region_recipes).to_not eq @region_recipes_initial
|
53
|
+
expect(@course_recipes).to_not eq @course_recipes_initial
|
54
|
+
expect(@ingredient_recipes).to_not eq @ingredient_recipes_initial
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "invalid page" do
|
59
|
+
before do
|
60
|
+
VCR.use_cassette "invalid page" do
|
61
|
+
@all_recipes = subject.all({ page: 10000 })
|
62
|
+
@region_recipes = subject.region("asian", { page: 10000 })
|
63
|
+
@course_recipes = subject.course("bread", { page: 10000 })
|
64
|
+
@ingredient_recipes = subject.ingredient("apples", { page: 10000 })
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should call correct url" do
|
69
|
+
expect(a_request(:get, "http://allrecipes.com/recipes?page=10000&st=p")).to have_been_made
|
70
|
+
expect(a_request(:get, "http://allrecipes.com/recipes/world-cuisine/asian?page=10000&st=p")).to have_been_made
|
71
|
+
expect(a_request(:get, "http://allrecipes.com/recipes/bread?page=10000&st=p")).to have_been_made
|
72
|
+
expect(a_request(:get, "http://allrecipes.com/search/?wt=apples&page=10000&sb=re")).to have_been_made
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should return an empty array" do
|
76
|
+
expect(@all_recipes).to eq []
|
77
|
+
expect(@region_recipes).to eq []
|
78
|
+
expect(@course_recipes).to eq []
|
79
|
+
expect(@ingredient_recipes).to eq []
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "sort by" do
|
85
|
+
describe "valid sort" do
|
86
|
+
before do
|
87
|
+
VCR.use_cassette "default search" do
|
88
|
+
@all_recipes_initial = subject.all
|
89
|
+
@region_recipes_initial = subject.region("asian")
|
90
|
+
@course_recipes_initial = subject.course("bread")
|
91
|
+
@ingredient_recipes_initial = subject.ingredient("apples")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
context "popularity" do
|
95
|
+
before do
|
96
|
+
VCR.use_cassette "popularity_sort" do
|
97
|
+
@all_recipes = subject.all({ sort_by: "popularity" })
|
98
|
+
|
99
|
+
@region_recipes = subject.region("asian", { sort_by: "popularity"})
|
100
|
+
@course_recipes = subject.course("bread", { sort_by: "popularity"})
|
101
|
+
@ingredient_recipes = subject.ingredient("apples", { sort_by: "popularity"})
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should call the correct URLs" do
|
106
|
+
expect(a_request(:get, "http://allrecipes.com/recipes?page=&st=p")).to have_been_made.times(2)
|
107
|
+
expect(a_request(:get, "http://allrecipes.com/recipes/world-cuisine/asian?page=&st=p")).to have_been_made.times(2)
|
108
|
+
expect(a_request(:get, "http://allrecipes.com/recipes/bread?page=&st=p")).to have_been_made.times(2)
|
109
|
+
expect(a_request(:get, "http://allrecipes.com/search/?wt=apples&page=&sb=re")).to have_been_made
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should have the correct count of recipes" do
|
114
|
+
expect(@all_recipes.count).to eq 20
|
115
|
+
expect(@region_recipes.count).to eq 20
|
116
|
+
expect(@course_recipes.count).to eq 20
|
117
|
+
expect(@ingredient_recipes.count).to eq 21
|
118
|
+
end
|
119
|
+
|
120
|
+
its "default search should be popularity" do
|
121
|
+
expect(@all_recipes).to eq @all_recipes_initial
|
122
|
+
expect(@region_recipes).to eq @region_recipes_initial
|
123
|
+
expect(@course_recipes).to eq @course_recipes_initial
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should search for recipes based on ingredients sorted by popularity" do
|
127
|
+
expect(@ingredient_recipes).to_not eq @ingredient_recipes_initial
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context "date" do
|
132
|
+
before do
|
133
|
+
VCR.use_cassette "date_sort" do
|
134
|
+
@all_recipes = subject.all({ sort_by: "date" })
|
135
|
+
|
136
|
+
@region_recipes = subject.region("asian", { sort_by: "date"})
|
137
|
+
@course_recipes = subject.course("bread", { sort_by: "date"})
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should call the correct URLs" do
|
142
|
+
expect(a_request(:get, "http://allrecipes.com/recipes?page=&st=n")).to have_been_made
|
143
|
+
expect(a_request(:get, "http://allrecipes.com/recipes/world-cuisine/asian?page=&st=n")).to have_been_made
|
144
|
+
expect(a_request(:get, "http://allrecipes.com/recipes/bread?page=&st=n")).to have_been_made
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should have the correct count of recipes" do
|
148
|
+
expect(@all_recipes.count).to eq 20
|
149
|
+
expect(@region_recipes.count).to eq 20
|
150
|
+
expect(@course_recipes.count).to eq 20
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should be different than default search" do
|
154
|
+
expect(@all_recipes).to_not eq @all_recipes_initial
|
155
|
+
expect(@region_recipes).to_not eq @region_recipes_initial
|
156
|
+
expect(@course_recipes).to_not eq @course_recipes_initial
|
157
|
+
end
|
158
|
+
end
|
159
|
+
context "title" do
|
160
|
+
before do
|
161
|
+
VCR.use_cassette "title_sort" do
|
162
|
+
@all_recipes = subject.all({ sort_by: "title" })
|
163
|
+
|
164
|
+
@region_recipes = subject.region("asian", { sort_by: "title"})
|
165
|
+
@course_recipes = subject.course("bread", { sort_by: "title"})
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should call the correct URLs" do
|
170
|
+
expect(a_request(:get, "http://allrecipes.com/recipes?page=&st=t")).to have_been_made
|
171
|
+
expect(a_request(:get, "http://allrecipes.com/recipes/world-cuisine/asian?page=&st=t")).to have_been_made
|
172
|
+
expect(a_request(:get, "http://allrecipes.com/recipes/bread?page=&st=t")).to have_been_made
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should have the correct count of recipes" do
|
176
|
+
expect(@all_recipes.count).to eq 20
|
177
|
+
expect(@region_recipes.count).to eq 20
|
178
|
+
expect(@course_recipes.count).to eq 20
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should be different than default search" do
|
182
|
+
expect(@all_recipes).to_not eq @all_recipes_initial
|
183
|
+
expect(@region_recipes).to_not eq @region_recipes_initial
|
184
|
+
expect(@course_recipes).to_not eq @course_recipes_initial
|
185
|
+
end
|
186
|
+
end
|
187
|
+
context "Relevance for ingredient search" do
|
188
|
+
before do
|
189
|
+
VCR.use_cassette "relevance_sort" do
|
190
|
+
@ingredient_recipes = subject.ingredient("apples",{ sort_by: "relevance" })
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should call the correct URL" do
|
195
|
+
expect(a_request(:get, "http://allrecipes.com/search/?wt=apples&page=&sb=re")).to have_been_made.times(2)
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should have the correct count of recipes" do
|
199
|
+
expect(@ingredient_recipes.count).to eq 21
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should be the same as default search" do
|
203
|
+
recipe_names = @ingredient_recipes.map{ |ing| ing[:name] }.sort
|
204
|
+
initial_recipe_names = @ingredient_recipes_initial.map{ |ing| ing[:name] }.sort
|
205
|
+
expect(recipe_names).to eq initial_recipe_names@ingredient_recipes_initial
|
206
|
+
end
|
207
|
+
end
|
208
|
+
context "Rating sort for ingredient search" do
|
209
|
+
before do
|
210
|
+
VCR.use_cassette "rating_sort" do
|
211
|
+
@ingredient_recipes = subject.ingredient("apples",{ sort_by: "rating" })
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should call the correct URL" do
|
216
|
+
expect(a_request(:get, "http://allrecipes.com/search/?wt=apples&page=&sb=ra")).to have_been_made
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should have the correct count of recipes" do
|
220
|
+
expect(@ingredient_recipes.count).to eq 21
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should be different from default search" do
|
224
|
+
expect(@ingredient_recipes).to_not eq @ingredient_recipes_initial
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
describe RecipeParser do
|
3
|
+
before do
|
4
|
+
VCR.use_cassette "recipe" do
|
5
|
+
url = "/Recipe/Worlds-Best-Lasagna/"
|
6
|
+
@recipe = RecipeParser.new(url).recipe
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should have the right name" do
|
11
|
+
expect(@recipe[:name]).to eq "World's Best Lasagna"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should have the right image url" do
|
15
|
+
expect(@recipe[:image]).to eq "http://images.media-allrecipes.com/userphotos/250x250/00/03/24/32427.jpg"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should have the right amount of servings" do
|
19
|
+
expect(@recipe[:servings]).to eq 12
|
20
|
+
end
|
21
|
+
|
22
|
+
context "ingredients" do
|
23
|
+
|
24
|
+
it "should have the right amount of ingredients" do
|
25
|
+
expect(@recipe[:ingredients].count).to eq 21
|
26
|
+
end
|
27
|
+
it "should have the right quantity" do
|
28
|
+
expect(@recipe[:ingredients].first["quantity"]).to eq 1
|
29
|
+
end
|
30
|
+
it "should have the right unit" do
|
31
|
+
expect(@recipe[:ingredients].first["unit"]).to eq "pound"
|
32
|
+
end
|
33
|
+
it "should have the right name" do
|
34
|
+
expect(@recipe[:ingredients].first["name"]).to eq "sweet Italian sausage"
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should have the right amount of directions" do
|
40
|
+
expect(@recipe[:directions].count).to eq 5
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should return a formatted recipe" do
|
44
|
+
expected_output = {
|
45
|
+
name: @recipe[:name],
|
46
|
+
image: @recipe[:image],
|
47
|
+
servings: @recipe[:servings],
|
48
|
+
ingredients: @recipe[:ingredients],
|
49
|
+
directions: @recipe[:directions]
|
50
|
+
}
|
51
|
+
expect(@recipe).to eq expected_output
|
52
|
+
end
|
53
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'allrecipes'
|
2
|
+
require 'vcr'
|
3
|
+
require 'webmock/rspec'
|
4
|
+
|
5
|
+
VCR.configure do |c|
|
6
|
+
c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
|
7
|
+
c.hook_into :webmock
|
8
|
+
end
|
9
|
+
|
10
|
+
def fixture_path
|
11
|
+
File.expand_path('../fixtures', __FILE__)
|
12
|
+
end
|
13
|
+
|
14
|
+
def fixture(file)
|
15
|
+
File.new(fixture_path + '/' + file)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
metadata
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: allrecipes
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- shivamd
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mechanize
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nokogiri
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.6'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.6'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: webmock
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: vcr
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: A Ruby wrapper for allrecipes.com. A simple interface to scrape recipes
|
112
|
+
from allrecipes.com
|
113
|
+
email:
|
114
|
+
- daryananis@gmail.com
|
115
|
+
executables: []
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- .gitignore
|
120
|
+
- Gemfile
|
121
|
+
- LICENSE.txt
|
122
|
+
- README.md
|
123
|
+
- Rakefile
|
124
|
+
- allrecipes.gemspec
|
125
|
+
- lib/allrecipes.rb
|
126
|
+
- lib/allrecipes/main.rb
|
127
|
+
- lib/allrecipes/page_parser.rb
|
128
|
+
- lib/allrecipes/recipe_parser.rb
|
129
|
+
- lib/allrecipes/url_helper.rb
|
130
|
+
- lib/allrecipes/version.rb
|
131
|
+
- spec/allrecipes_spec.rb
|
132
|
+
- spec/fixtures/recipe.json
|
133
|
+
- spec/page_parser_spec.rb
|
134
|
+
- spec/recipe_filter_spec.rb
|
135
|
+
- spec/recipe_parser_spec.rb
|
136
|
+
- spec/spec_helper.rb
|
137
|
+
homepage: https://github.com/shivamd/allrecipes
|
138
|
+
licenses:
|
139
|
+
- MIT
|
140
|
+
metadata: {}
|
141
|
+
post_install_message:
|
142
|
+
rdoc_options: []
|
143
|
+
require_paths:
|
144
|
+
- lib
|
145
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - '>='
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '0'
|
155
|
+
requirements: []
|
156
|
+
rubyforge_project:
|
157
|
+
rubygems_version: 2.0.6
|
158
|
+
signing_key:
|
159
|
+
specification_version: 4
|
160
|
+
summary: A Ruby wrapper for allrecipes.com
|
161
|
+
test_files:
|
162
|
+
- spec/allrecipes_spec.rb
|
163
|
+
- spec/fixtures/recipe.json
|
164
|
+
- spec/page_parser_spec.rb
|
165
|
+
- spec/recipe_filter_spec.rb
|
166
|
+
- spec/recipe_parser_spec.rb
|
167
|
+
- spec/spec_helper.rb
|