food_info 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.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.markdown +111 -0
- data/Rakefile +2 -0
- data/TODO.txt +4 -0
- data/food_info.gemspec +20 -0
- data/lib/food_info/adapters/fat_secret/data/food_item.rb +23 -0
- data/lib/food_info/adapters/fat_secret/data/food_serving.rb +45 -0
- data/lib/food_info/adapters/fat_secret/data/search_result.rb +18 -0
- data/lib/food_info/adapters/fat_secret/data/search_results.rb +31 -0
- data/lib/food_info/adapters/fat_secret/request.rb +70 -0
- data/lib/food_info/adapters/fat_secret.rb +45 -0
- data/lib/food_info/adapters.rb +9 -0
- data/lib/food_info/errors.rb +5 -0
- data/lib/food_info/utils.rb +5 -0
- data/lib/food_info/version.rb +3 -0
- data/lib/food_info.rb +57 -0
- metadata +114 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Deviantech, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
# FoodInfo
|
2
|
+
|
3
|
+
FoodInfo is a ruby gem that retrieves food nutrition information from various online data sources.
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
FoodInfo is available as a gem, so installation is as simple as:
|
10
|
+
|
11
|
+
gem install food_info
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
## Supported Data Sources
|
16
|
+
|
17
|
+
There's currently only one adapter implemented, which pulls data from [FatSecret's REST API](http://platform.fatsecret.com/api/Default.aspx?screen=rapih). The code's modular and adding additional data sources should be fairly straightforward, but since DailyBurn discontinued their API access I don't know of any other solid sources (if you do, though, please let me know and/or add an adapter!).
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
### Housekeeping
|
24
|
+
|
25
|
+
To use the FatSecret API (currently your only option), you'll first need to [sign up for a free developer account](http://platform.fatsecret.com/api/Default.aspx?screen=r) and retrieve the "REST API Consumer Key" and "REST API Consumer Secret" from your "My Account" tab.
|
26
|
+
|
27
|
+
Once that's done, the first step is to tell FoodInfo which adapter you want to use and what authorization to send.
|
28
|
+
|
29
|
+
FoodInfo.establish_connection(:fat_secret, :key => 'YOUR-KEY', :secret => 'YOUR-KEY')
|
30
|
+
|
31
|
+
|
32
|
+
### Searching
|
33
|
+
|
34
|
+
Now we can search for foods.
|
35
|
+
|
36
|
+
cheese = FoodInfo.search('cheese')
|
37
|
+
cheese.total_results # => 2469
|
38
|
+
cheese.per_page # => 20
|
39
|
+
cheese.page # => 1
|
40
|
+
cheese.results # => ... big array ...
|
41
|
+
cheese.results.first # =>
|
42
|
+
# {
|
43
|
+
# "description" => "Per 100g - Calories: 403kcal | Fat: 33.14g | Carbs: 1.28g | Protein: 24.90g",
|
44
|
+
# "id" => "33689",
|
45
|
+
# "kind" => "Generic",
|
46
|
+
# "name" => "Cheddar Cheese",
|
47
|
+
# "url" => "http://www.fatsecret.com/calories-nutrition/usda/cheddar-cheese"
|
48
|
+
# }
|
49
|
+
|
50
|
+
(As an aside, I get that pretty, nicely-lined-up console formatting from the remarkably awesome [AwesomePrint Gem](https://github.com/michaeldv/awesome_print)).
|
51
|
+
|
52
|
+
Also, note that search supports pagination via the <tt>page</tt> and <tt>per_page</tt> (max 50) parameters:
|
53
|
+
|
54
|
+
FoodInfo.search('cheese', :page => 2, :per_page => 50)
|
55
|
+
|
56
|
+
|
57
|
+
### Nutritional Details
|
58
|
+
|
59
|
+
Once you have a specific food item in mind from the search results, you can retrieve a whole lot of additional information.
|
60
|
+
|
61
|
+
cheddar = FoodInfo.search('cheese').results.first
|
62
|
+
info = FoodInfo.details( cheddar.id ) # => ... a whole lotta data ...
|
63
|
+
|
64
|
+
General metadata about the cheese includes id, name, kind, and url, which are identical to what you'd get from the <tt>search</tt> method. It also has one or more servings, however, and this is where we finally get our nutrition info.
|
65
|
+
|
66
|
+
serving = info.servings.first # =>
|
67
|
+
# {
|
68
|
+
# "calcium" => 95,
|
69
|
+
# "calories" => 532.0,
|
70
|
+
# "carbohydrate" => 1.69,
|
71
|
+
# "cholesterol" => 139.0,
|
72
|
+
# "fat" => 43.74,
|
73
|
+
# "fiber" => 0.0,
|
74
|
+
# "id" => "29131",
|
75
|
+
# "iron" => 5,
|
76
|
+
# "measurement_description" => "cup, diced",
|
77
|
+
# "metric_serving_amount" => 132.0,
|
78
|
+
# "metric_serving_unit" => "g",
|
79
|
+
# "monounsaturated_fat" => 12.396,
|
80
|
+
# "number_of_units" => 1.0,
|
81
|
+
# "polyunsaturated_fat" => 1.243,
|
82
|
+
# "potassium" => 129.0,
|
83
|
+
# "protein" => 32.87,
|
84
|
+
# "saturated_fat" => 27.841,
|
85
|
+
# "serving_description" => "1 cup diced",
|
86
|
+
# "sodium" => 820.0,
|
87
|
+
# "sugar" => 0.69,
|
88
|
+
# "trans_fat" => 0.0,
|
89
|
+
# "url" => "http://www.fatsecret.com/calories-nutrition/usda/cheddar-cheese?portionid=29131&portionamount=1.000",
|
90
|
+
# "vitamin_a" => 26,
|
91
|
+
# "vitamin_c" => 0
|
92
|
+
# }
|
93
|
+
|
94
|
+
For full details on what each of those fields contains, check [the FatSecret documentation](http://platform.fatsecret.com/api/Default.aspx?screen=rapiref&method=food.get#methodResponse).
|
95
|
+
|
96
|
+
|
97
|
+
## Legal Note
|
98
|
+
|
99
|
+
The FatSecret TOS requires you not to store, well, [pretty much anything](http://platform.fatsecret.com/api/Default.aspx?screen=rapisd) aside from food or serving IDs for more than 24 hours. This is annoying, but I figured I'd give you a heads up.
|
100
|
+
|
101
|
+
|
102
|
+
## Note on Patches/Pull Requests
|
103
|
+
|
104
|
+
Contributions are welcome, particularly adding adapters for additional data sources.
|
105
|
+
|
106
|
+
As always, the process is to fork this project on Github, make your changes (preferably in a topic branch, and without changing the gem version), send a pull request, and then receive much appreciation!
|
107
|
+
|
108
|
+
## License
|
109
|
+
|
110
|
+
Copyright © 2011 [Deviantech, Inc.](http://www.deviantech.com) and released under the MIT license.
|
111
|
+
|
data/Rakefile
ADDED
data/TODO.txt
ADDED
@@ -0,0 +1,4 @@
|
|
1
|
+
* Error handling (currently returns empty results for invalid key)
|
2
|
+
* Better search API (search('cheese'), not search('cheese').results)
|
3
|
+
* Add DB caching
|
4
|
+
* It'd be great to have a connection pool, so we could process multiple requests concurrently. I've added a skeleton for it, but actually using it would require a non-blocking HTTP library in HTTParty.
|
data/food_info.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/food_info/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Kali Donovan"]
|
6
|
+
gem.email = ["kali@deviantech.com"]
|
7
|
+
gem.description = %q{Generic Ruby interface to look up nutritional information on food. Design is modular so other adapters can be plugged in, but only data source currently implemented is FatSecret.}
|
8
|
+
gem.summary = %q{API for researching nutritional information of various foods}
|
9
|
+
gem.homepage = "https://github.com/deviantech/food_info"
|
10
|
+
|
11
|
+
gem.add_dependency('httparty', '>= 0.7.7')
|
12
|
+
gem.add_dependency('hashie', '>= 1.1.0')
|
13
|
+
|
14
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
15
|
+
gem.files = `git ls-files`.split("\n")
|
16
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
gem.name = "food_info"
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
gem.version = FoodInfo::VERSION
|
20
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module FoodInfo
|
2
|
+
module Adapters
|
3
|
+
class FatSecret
|
4
|
+
module Data
|
5
|
+
|
6
|
+
class FoodItem < Hashie::Trash
|
7
|
+
property :servings
|
8
|
+
property :id, :from => :food_id
|
9
|
+
property :name, :from => :food_name
|
10
|
+
property :kind, :from => :food_type
|
11
|
+
property :url, :from => :food_url
|
12
|
+
property :brand, :from => :brand_name
|
13
|
+
|
14
|
+
def initialize(*args)
|
15
|
+
super(*args)
|
16
|
+
self[:servings] = self[:servings]['serving'].collect{|s| FoodServing.new(s) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module FoodInfo
|
2
|
+
module Adapters
|
3
|
+
class FatSecret
|
4
|
+
module Data
|
5
|
+
|
6
|
+
class FoodServing < Hashie::Trash
|
7
|
+
property :id, :from => :serving_id
|
8
|
+
property :url, :from => :serving_url
|
9
|
+
|
10
|
+
# These are strange from FatSecret, we'll set them as properties to get it by Hashie::Trash, then clean up after initialized
|
11
|
+
property :metric_serving_amount # => "132.000"
|
12
|
+
property :metric_serving_unit # => "g"
|
13
|
+
property :serving_description # => "1 cup, diced"
|
14
|
+
property :measurement_description # => "cup, diced"
|
15
|
+
property :number_of_units # => "1.000"
|
16
|
+
|
17
|
+
# For all attributes expected, see http://platform.fatsecret.com/api/Default.aspx?screen=rapiref&method=food.get
|
18
|
+
DECIMALS = %w(calories carbohydrate protein fat saturated_fat polyunsaturated_fat monounsaturated_fat trans_fat cholesterol sodium potassium fiber sugar)
|
19
|
+
INTEGERS = %w(vitamin_a vitamin_c calcium iron)
|
20
|
+
(DECIMALS + INTEGERS).each {|n| property(n) }
|
21
|
+
|
22
|
+
def initialize(*args)
|
23
|
+
super(*args)
|
24
|
+
normalize_data
|
25
|
+
end
|
26
|
+
|
27
|
+
def normalize_data
|
28
|
+
self[:metric_serving_amount] = self[:metric_serving_amount].to_f
|
29
|
+
self[:number_of_units] = self[:number_of_units].to_f
|
30
|
+
|
31
|
+
INTEGERS.each do |n|
|
32
|
+
self[n.to_sym] = self[n.to_sym].to_i
|
33
|
+
end
|
34
|
+
|
35
|
+
DECIMALS.each do |n|
|
36
|
+
self[n.to_sym] = self[n.to_sym].to_f
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module FoodInfo
|
2
|
+
module Adapters
|
3
|
+
class FatSecret
|
4
|
+
module Data
|
5
|
+
|
6
|
+
class SearchResult < Hashie::Trash
|
7
|
+
property :id, :from => :food_id
|
8
|
+
property :name, :from => :food_name
|
9
|
+
property :kind, :from => :food_type
|
10
|
+
property :url, :from => :food_url
|
11
|
+
property :brand, :from => :brand_name
|
12
|
+
property :description, :from => :food_description
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module FoodInfo
|
2
|
+
module Adapters
|
3
|
+
class FatSecret
|
4
|
+
module Data
|
5
|
+
|
6
|
+
class SearchResults < Hashie::Trash
|
7
|
+
property :results, :from => :food
|
8
|
+
property :page, :from => :page_number
|
9
|
+
property :per_page, :from => :max_results
|
10
|
+
property :total_results
|
11
|
+
|
12
|
+
def initialize(*args)
|
13
|
+
super(*args)
|
14
|
+
normalize_data
|
15
|
+
end
|
16
|
+
|
17
|
+
def normalize_data
|
18
|
+
[:page, :per_page, :total_results].each do |n|
|
19
|
+
self[n] = self[n].to_i
|
20
|
+
end
|
21
|
+
|
22
|
+
self[:page] += 1 # FatSecret indexes their pages from 0
|
23
|
+
self[:results] = [self[:results]] unless self[:results].is_a?(Array)
|
24
|
+
self[:results] = (self[:results] || []).collect {|result| SearchResult.new(result) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'hmac-sha1'
|
3
|
+
|
4
|
+
module FoodInfo
|
5
|
+
module Adapters
|
6
|
+
class FatSecret
|
7
|
+
|
8
|
+
class Request
|
9
|
+
HOST = 'http://platform.fatsecret.com/rest/server.api'
|
10
|
+
|
11
|
+
# Returns the query string necessary to run the specified +method+
|
12
|
+
# against the FatSecret API, using the +auth+ (+key+ and +secret+)
|
13
|
+
# to sign it.
|
14
|
+
#
|
15
|
+
# If this class were accessed externally, I'd refactor it a bit
|
16
|
+
# so it didn't require auth info to be passed on every request.
|
17
|
+
def initialize(method, auth, optional_params = {})
|
18
|
+
@auth = auth
|
19
|
+
@request_nonce = (0...10).map{65.+(rand(25)).chr}.join
|
20
|
+
@request_time = Time.now.to_i.to_s
|
21
|
+
@http_method = optional_params.delete(:http_method) || 'GET'
|
22
|
+
|
23
|
+
@params = {
|
24
|
+
:method => method,
|
25
|
+
:format => 'json'
|
26
|
+
}.merge(optional_params || {})
|
27
|
+
end
|
28
|
+
|
29
|
+
def signed_request
|
30
|
+
"#{HOST}?#{make_query_string(query_params)}&oauth_signature=#{request_signature}"
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def request_signature(token=nil)
|
37
|
+
signing_key = [@auth[:secret], token].join('&')
|
38
|
+
|
39
|
+
sha = HMAC::SHA1.digest(signing_key, signature_base_string)
|
40
|
+
Base64.encode64(sha).strip.oauth_escape
|
41
|
+
end
|
42
|
+
|
43
|
+
def signature_base_string
|
44
|
+
[@http_method, HOST, make_query_string(query_params)].map(&:oauth_escape).join('&')
|
45
|
+
end
|
46
|
+
|
47
|
+
def make_query_string(pairs)
|
48
|
+
sorted = pairs.sort{|a,b| a[0].to_s <=> b[0].to_s}
|
49
|
+
sorted.collect{|p| p.join('=')}.join('&')
|
50
|
+
end
|
51
|
+
|
52
|
+
def query_params
|
53
|
+
oauth_components.merge(@params)
|
54
|
+
end
|
55
|
+
|
56
|
+
def oauth_components
|
57
|
+
{
|
58
|
+
:oauth_consumer_key => @auth[:key],
|
59
|
+
:oauth_signature_method => 'HMAC-SHA1',
|
60
|
+
:oauth_timestamp => @request_time,
|
61
|
+
:oauth_nonce => @request_nonce,
|
62
|
+
:oauth_version => '1.0'
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'food_info/adapters/fat_secret/request'
|
3
|
+
require 'food_info/adapters/fat_secret/data/search_result'
|
4
|
+
require 'food_info/adapters/fat_secret/data/search_results'
|
5
|
+
require 'food_info/adapters/fat_secret/data/food_item'
|
6
|
+
require 'food_info/adapters/fat_secret/data/food_serving'
|
7
|
+
|
8
|
+
module FoodInfo
|
9
|
+
module Adapters
|
10
|
+
class FatSecret
|
11
|
+
include HTTParty
|
12
|
+
|
13
|
+
def initialize(opts = {})
|
14
|
+
raise AuthorizationError.new("Missing required argument :key") unless @key = opts[:key]
|
15
|
+
raise AuthorizationError.new("Missing required argument :secret") unless @secret = opts[:secret]
|
16
|
+
end
|
17
|
+
|
18
|
+
def search(q, opts = {})
|
19
|
+
params = {
|
20
|
+
:search_expression => q,
|
21
|
+
:page_number => opts[:page] || 1,
|
22
|
+
:max_results => opts[:per_page] || 20
|
23
|
+
}
|
24
|
+
params[:page_number] = [params[:page_number].to_i - 1, 0].max # FatSecret's pagination starts at 0
|
25
|
+
params[:max_results] = [params[:max_results].to_i, 50].min # FatSecret's max allowed results per page
|
26
|
+
|
27
|
+
data = query('foods.search', params)
|
28
|
+
Data::SearchResults.new( data['foods'] )
|
29
|
+
end
|
30
|
+
|
31
|
+
def details(food_id)
|
32
|
+
data = query('food.get', :food_id => food_id)
|
33
|
+
Data::FoodItem.new( data['food'] )
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def query(method, opts = {})
|
39
|
+
query_url = Request.new(method, {:key => @key, :secret => @secret}, opts).signed_request
|
40
|
+
self.class.get( query_url )
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require "food_info/adapters/fat_secret"
|
2
|
+
|
3
|
+
module FoodInfo
|
4
|
+
# All FoodInfo adapters must expose two public methods, +search+ and +details+, and will need to
|
5
|
+
# define their own classes to return data in a unified manner consistent with that laid out by
|
6
|
+
# the existing FatSecret adapter's <tt>search_results</tt>, <tt>search_result</tt>, and <tt>details</tt> classes.
|
7
|
+
module Adapters
|
8
|
+
end
|
9
|
+
end
|
data/lib/food_info.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
require "food_info/utils"
|
4
|
+
require "food_info/errors"
|
5
|
+
require "food_info/adapters"
|
6
|
+
require "food_info/version"
|
7
|
+
|
8
|
+
|
9
|
+
module FoodInfo
|
10
|
+
|
11
|
+
# Allow extending to additional data sources in the future
|
12
|
+
# Each adapter should implement +search+ and +details+ methods.
|
13
|
+
ADAPTERS = {:fat_secret => FoodInfo::Adapters::FatSecret}
|
14
|
+
|
15
|
+
class << self
|
16
|
+
|
17
|
+
# Sets the adapter we'll be pulling data from.
|
18
|
+
def establish_connection(adapter_name, opts = {})
|
19
|
+
klass = ADAPTERS[adapter_name.to_sym]
|
20
|
+
raise UnsupportedAdapter.new("Requested adapter ('#{adapter_name}') is unknown") unless klass
|
21
|
+
@@pool = []
|
22
|
+
@@cursor = 0
|
23
|
+
(opts.delete(:pool) || 1).to_i.times do
|
24
|
+
@@pool << klass.new(opts)
|
25
|
+
end
|
26
|
+
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
def search(q, opts = {})
|
31
|
+
next_adapter.search(q, opts)
|
32
|
+
end
|
33
|
+
|
34
|
+
def details(id)
|
35
|
+
next_adapter.details(id)
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
# FUTURE: This connection pool code won't do much good until HTTParty is non-blocking
|
40
|
+
def next_adapter
|
41
|
+
raise NoAdapterSpecified.new("You must run FoodInfo.establish_connection first") unless @@pool
|
42
|
+
@@cursor = (@@cursor + 1) % @@pool.length
|
43
|
+
@@pool[@@cursor]
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
__END__
|
51
|
+
|
52
|
+
FoodInfo.establish_connection(:fat_secret, :key => ENV['KEY'], :secret => ENV['SECRET'])
|
53
|
+
a=FoodInfo.search('cheese')
|
54
|
+
a=FoodInfo.search('cheese', :page => 1, :per_page => 1)
|
55
|
+
|
56
|
+
FoodInfo.establish_connection(:fat_secret, :key => ENV['KEY'], :secret => ENV['SECRET'])
|
57
|
+
a=FoodInfo.details("33689")
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: food_info
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Kali Donovan
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-09-10 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: httparty
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 13
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
- 7
|
32
|
+
- 7
|
33
|
+
version: 0.7.7
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: hashie
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 19
|
45
|
+
segments:
|
46
|
+
- 1
|
47
|
+
- 1
|
48
|
+
- 0
|
49
|
+
version: 1.1.0
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: *id002
|
52
|
+
description: Generic Ruby interface to look up nutritional information on food. Design is modular so other adapters can be plugged in, but only data source currently implemented is FatSecret.
|
53
|
+
email:
|
54
|
+
- kali@deviantech.com
|
55
|
+
executables: []
|
56
|
+
|
57
|
+
extensions: []
|
58
|
+
|
59
|
+
extra_rdoc_files: []
|
60
|
+
|
61
|
+
files:
|
62
|
+
- .gitignore
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE
|
65
|
+
- README.markdown
|
66
|
+
- Rakefile
|
67
|
+
- TODO.txt
|
68
|
+
- food_info.gemspec
|
69
|
+
- lib/food_info.rb
|
70
|
+
- lib/food_info/adapters.rb
|
71
|
+
- lib/food_info/adapters/fat_secret.rb
|
72
|
+
- lib/food_info/adapters/fat_secret/data/food_item.rb
|
73
|
+
- lib/food_info/adapters/fat_secret/data/food_serving.rb
|
74
|
+
- lib/food_info/adapters/fat_secret/data/search_result.rb
|
75
|
+
- lib/food_info/adapters/fat_secret/data/search_results.rb
|
76
|
+
- lib/food_info/adapters/fat_secret/request.rb
|
77
|
+
- lib/food_info/errors.rb
|
78
|
+
- lib/food_info/utils.rb
|
79
|
+
- lib/food_info/version.rb
|
80
|
+
homepage: https://github.com/deviantech/food_info
|
81
|
+
licenses: []
|
82
|
+
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
hash: 3
|
94
|
+
segments:
|
95
|
+
- 0
|
96
|
+
version: "0"
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
hash: 3
|
103
|
+
segments:
|
104
|
+
- 0
|
105
|
+
version: "0"
|
106
|
+
requirements: []
|
107
|
+
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 1.8.10
|
110
|
+
signing_key:
|
111
|
+
specification_version: 3
|
112
|
+
summary: API for researching nutritional information of various foods
|
113
|
+
test_files: []
|
114
|
+
|