fatsecret-models 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +46 -0
- data/README.md +145 -0
- data/Rakefile +16 -0
- data/fatsecret.gemspec +21 -0
- data/lib/fatsecret-models.rb +142 -0
- data/lib/fatsecret-models/connection.rb +90 -0
- data/lib/fatsecret-models/models/base.rb +74 -0
- data/lib/fatsecret-models/models/food.rb +44 -0
- data/lib/fatsecret-models/models/serving.rb +5 -0
- data/lib/fatsecret-models/relation.rb +13 -0
- data/lib/generators/fat_secret/install/USAGE +8 -0
- data/lib/generators/fat_secret/install/install_generator.rb +13 -0
- data/lib/generators/fat_secret/install/templates/fatsecret.rb +14 -0
- data/spec/connection/conection_spec.rb +76 -0
- data/spec/fatsecret_spec.rb +29 -0
- data/spec/models/base_spec.rb +56 -0
- data/spec/models/food_spec.rb +128 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/active_model_lint.rb +16 -0
- data/spec/support/set_keys.rb +8 -0
- data/spec/support/vcr.rb +20 -0
- metadata +180 -0
data/.gitignore
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
fatsecret-api (0.1.0)
|
5
|
+
active_attr
|
6
|
+
activemodel (~> 3.0)
|
7
|
+
json (~> 1.5)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: http://rubygems.org/
|
11
|
+
specs:
|
12
|
+
active_attr (0.6.0)
|
13
|
+
activemodel (>= 3.0.2, < 4.1)
|
14
|
+
activesupport (>= 3.0.2, < 4.1)
|
15
|
+
activemodel (3.2.8)
|
16
|
+
activesupport (= 3.2.8)
|
17
|
+
builder (~> 3.0.0)
|
18
|
+
activesupport (3.2.8)
|
19
|
+
i18n (~> 0.6)
|
20
|
+
multi_json (~> 1.0)
|
21
|
+
builder (3.0.0)
|
22
|
+
diff-lcs (1.1.3)
|
23
|
+
fakeweb (1.3.0)
|
24
|
+
i18n (0.6.0)
|
25
|
+
json (1.7.4)
|
26
|
+
multi_json (1.3.6)
|
27
|
+
rake (0.9.2.2)
|
28
|
+
rspec (2.11.0)
|
29
|
+
rspec-core (~> 2.11.0)
|
30
|
+
rspec-expectations (~> 2.11.0)
|
31
|
+
rspec-mocks (~> 2.11.0)
|
32
|
+
rspec-core (2.11.1)
|
33
|
+
rspec-expectations (2.11.2)
|
34
|
+
diff-lcs (~> 1.1.3)
|
35
|
+
rspec-mocks (2.11.2)
|
36
|
+
vcr (2.2.4)
|
37
|
+
|
38
|
+
PLATFORMS
|
39
|
+
ruby
|
40
|
+
|
41
|
+
DEPENDENCIES
|
42
|
+
fakeweb
|
43
|
+
fatsecret-api!
|
44
|
+
rake
|
45
|
+
rspec
|
46
|
+
vcr
|
data/README.md
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
FatSecret Gem
|
2
|
+
=============
|
3
|
+
|
4
|
+
Introduction
|
5
|
+
------------
|
6
|
+
An activemodel compliant wrapper to the FatSecret api. You need to register with FatSecret to get
|
7
|
+
a key and secret. At the moment only the food search, and get methods are implemented. More methods
|
8
|
+
can be easily implemented. See development for more information
|
9
|
+
|
10
|
+
Installation
|
11
|
+
------------
|
12
|
+
If you are using bundler, you put this in your Gemfile:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
source :rubygems
|
16
|
+
gem 'fatsecret-models', :git => 'https://github.com/joeyjoejoejr/Fatsecret.git'
|
17
|
+
```
|
18
|
+
Then run bundle install
|
19
|
+
|
20
|
+
or `gem install fatsecret-models`
|
21
|
+
|
22
|
+
Useage
|
23
|
+
------
|
24
|
+
|
25
|
+
Rails:
|
26
|
+
|
27
|
+
run `rails generate fatsecret::install`
|
28
|
+
this will create a `fatsecret.rb` initializer file in `config/initializers`
|
29
|
+
fill in your fatsecret key and secret. Make sure to exclude this file from version control
|
30
|
+
|
31
|
+
|
32
|
+
Ruby:
|
33
|
+
|
34
|
+
You must set up configuration before instantiating any of the FatSecret classes:
|
35
|
+
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
FatSecret.configure do |config|
|
39
|
+
config.api_key = <your key>
|
40
|
+
config.api_secret = <your secret key>
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
`FatSecret::Connection` is a lightweight wrapper of the api and can be used alone.
|
45
|
+
It currently has a search, and a get method. Both take three arguments and return a ruby hash.
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
connection = FatSecret::Conection.new
|
49
|
+
connection.search(method, searchArguments, additionalArguments)
|
50
|
+
connection.get(method, ID, additionalArguments)
|
51
|
+
```
|
52
|
+
|
53
|
+
A real world example:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
connection = FatSecret::Connection.new
|
57
|
+
connection.search :food, "Apple", :max_results => "10"
|
58
|
+
connection.get :food, 12345
|
59
|
+
```
|
60
|
+
|
61
|
+
`FatSecret::Food` is an ActiveModel Compliant representation of the api data that
|
62
|
+
fits a number of useful rails model idioms. Usage is simple.
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
foodlist = FatSecret::Food.find_by_name("Apple") # returns an array of Food objects
|
66
|
+
first_food = foodlist.first.reload #returns a full food model from the api
|
67
|
+
first_food.servings.first #returns the first serving object
|
68
|
+
```
|
69
|
+
|
70
|
+
You can also instantiate and create your own food objects using mass assignment
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
food = FatSecret::Food.new(:food_id => 12345, :food_name => "Bad and Plenties")
|
74
|
+
food.servings.new(:calories => "45", :vitamin_a => "millions of mgs")
|
75
|
+
food.servings_attributes = [{array => of}, {hashes => ""}]
|
76
|
+
```
|
77
|
+
|
78
|
+
See the `food_spec.rb` file for more examples
|
79
|
+
|
80
|
+
Development
|
81
|
+
-------------
|
82
|
+
|
83
|
+
Setup:
|
84
|
+
|
85
|
+
```
|
86
|
+
git clone 'https://github.com/joeyjoejoejr/Fatsecret.git'
|
87
|
+
cd Fatsecret
|
88
|
+
git checkout 'active_model'
|
89
|
+
bundle install
|
90
|
+
```
|
91
|
+
|
92
|
+
You'll need to set up your api-key and api-secret in the `spec/support/set_keys` files
|
93
|
+
then run the specs
|
94
|
+
|
95
|
+
```
|
96
|
+
bundle exec rspec spec/
|
97
|
+
```
|
98
|
+
|
99
|
+
Please submit pull requests with fixes, updates, new models.
|
100
|
+
|
101
|
+
Setting up a new model
|
102
|
+
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
# file "/lib/fatsecret.rb"
|
106
|
+
require "fatsecret/models/your_new_model.rb"
|
107
|
+
```
|
108
|
+
Most of what you need is in the base class
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
"lib/fatsecret/models/your_new_model"
|
112
|
+
module FatSecret
|
113
|
+
class YourNewModel < FatSecret::Base
|
114
|
+
#predefine attributes for validations, others will be created dynamically
|
115
|
+
attribute :monkey, :type => String, :default => 'Oooh, ohh, Ahh'
|
116
|
+
|
117
|
+
validates :monkey, :presence => True
|
118
|
+
|
119
|
+
#creates an instance of FatSecret::Relation so that mass assigment and
|
120
|
+
#"food_object.insects.new" will work. You will need to create that related model.
|
121
|
+
has_many :insects
|
122
|
+
|
123
|
+
#this model has access to the connection object so you can define methods
|
124
|
+
#this should all be customized to deal with how Fatsecret returns
|
125
|
+
def self.find(id, args = {})
|
126
|
+
attrs = connection.get(api_method, id.to_s, args)
|
127
|
+
attrs['your_new_model']['insect_attributes'] = attrs['your_new_model']['insects']['insect']
|
128
|
+
attrs['your_new_model'].delete 'insects'
|
129
|
+
self.new attrs['your_new_model']
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.find_by_name(name, args = {})
|
133
|
+
results = connection.search(api_method, name, args)
|
134
|
+
return_results = []
|
135
|
+
results['your_new_models']['your_new_model'].each do |thing|
|
136
|
+
return_results.push self.new thing
|
137
|
+
end
|
138
|
+
return_results
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class Insect < FatSecret::Base
|
143
|
+
end
|
144
|
+
end
|
145
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rspec/core/rake_task'
|
2
|
+
RSpec::Core::RakeTask.new('spec')
|
3
|
+
|
4
|
+
# Setting up default task
|
5
|
+
desc "Run specs"
|
6
|
+
task :default => :spec
|
7
|
+
|
8
|
+
namespace :fatsecret do
|
9
|
+
|
10
|
+
current_path = File.dirname(__FILE__)
|
11
|
+
|
12
|
+
desc "Install fatsecret-api"
|
13
|
+
task :install do
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
data/fatsecret.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'fatsecret-models'
|
3
|
+
s.version = '0.1.2'
|
4
|
+
s.date = '2012-08-21'
|
5
|
+
s.summary = "An active_model compliant wrapper for FatSecret API"
|
6
|
+
s.description = "An active_model compliant wrapper for FatSecret API"
|
7
|
+
s.authors = ["Joseph Jackson", "Ibrahim Muhammad"]
|
8
|
+
s.email = 'cpmhjoe@gmail.com'
|
9
|
+
s.files = `git ls-files`.split("\n")
|
10
|
+
s.require_paths = ["lib"]
|
11
|
+
s.homepage = 'http://www.github.com/joeyjoejoejr/fatsecret'
|
12
|
+
|
13
|
+
s.add_runtime_dependency 'json', '~> 1.5'
|
14
|
+
s.add_runtime_dependency 'activemodel', '~> 3.0'
|
15
|
+
s.add_runtime_dependency 'active_attr'
|
16
|
+
|
17
|
+
s.add_development_dependency 'rake'
|
18
|
+
s.add_development_dependency 'rspec'
|
19
|
+
s.add_development_dependency 'vcr'
|
20
|
+
s.add_development_dependency 'fakeweb'
|
21
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'json'
|
5
|
+
require 'openssl'
|
6
|
+
require 'cgi'
|
7
|
+
require 'base64'
|
8
|
+
|
9
|
+
#fatsecret files
|
10
|
+
require 'fatsecret-models/connection'
|
11
|
+
require 'fatsecret-models/relation'
|
12
|
+
require 'fatsecret-models/models/base'
|
13
|
+
require 'fatsecret-models/models/serving'
|
14
|
+
require 'fatsecret-models/models/food'
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
module FatSecret
|
19
|
+
class << self
|
20
|
+
attr_accessor :configuration
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.configure
|
24
|
+
self.configuration ||= Configuration.new
|
25
|
+
yield(configuration)
|
26
|
+
return self.configuration
|
27
|
+
end
|
28
|
+
|
29
|
+
class Configuration
|
30
|
+
attr_accessor :api_key, :api_secret, :api_url, :api_methods, :oauth_token
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
@api_url = "http://platform.fatsecret.com/rest/server.api"
|
34
|
+
@api_methods = %w{food recipe}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
#class FatSecret
|
40
|
+
|
41
|
+
# @@key = ""
|
42
|
+
# @@secret = ""
|
43
|
+
|
44
|
+
# SHA1 = "HMAC-SHA1"
|
45
|
+
# SITE = "http://platform.fatsecret.com/keyrest/server.api"
|
46
|
+
# DIGEST = OpenSSL::Digest::Digest.new('sha1')
|
47
|
+
|
48
|
+
# def self.init(key, secret)
|
49
|
+
# @@key = key
|
50
|
+
# @@secret = secret
|
51
|
+
# return nil #don't return the secret key
|
52
|
+
# end
|
53
|
+
|
54
|
+
|
55
|
+
# #--------------------------------
|
56
|
+
# #--- Food Functionality
|
57
|
+
# #--------------------------------
|
58
|
+
|
59
|
+
# def self.search_food(expression)
|
60
|
+
# query = {
|
61
|
+
# :method => 'foods.search',
|
62
|
+
# :search_expression => expression.esc
|
63
|
+
# }
|
64
|
+
# get(query)
|
65
|
+
# end
|
66
|
+
|
67
|
+
# def self.food(id)
|
68
|
+
# query = {
|
69
|
+
# :method => 'food.get',
|
70
|
+
# :food_id => id
|
71
|
+
# }
|
72
|
+
# get(query)
|
73
|
+
# end
|
74
|
+
|
75
|
+
# #--------------------------------
|
76
|
+
# #--- Recipe Functionality
|
77
|
+
# #--------------------------------
|
78
|
+
|
79
|
+
# def self.search_recipes(expression,max_results=20)
|
80
|
+
# query = {
|
81
|
+
# :method => 'recipes.search',
|
82
|
+
# :search_expression => expression.esc,
|
83
|
+
# :max_results => max_results
|
84
|
+
# }
|
85
|
+
# get(query)
|
86
|
+
# end
|
87
|
+
|
88
|
+
# def self.recipe(id)
|
89
|
+
# query = {
|
90
|
+
# :method => 'recipe.get',
|
91
|
+
# :recipe_id => id
|
92
|
+
# }
|
93
|
+
# get(query)
|
94
|
+
# end
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
# private
|
99
|
+
|
100
|
+
# def self.get(query)
|
101
|
+
# params = {
|
102
|
+
# :format => 'json',
|
103
|
+
# :oauth_consumer_key => @@key,
|
104
|
+
# :oauth_nonce => "1234",
|
105
|
+
# :oauth_signature_method => SHA1,
|
106
|
+
# :oauth_timestamp => Time.now.to_i,
|
107
|
+
# :oauth_version => "1.0",
|
108
|
+
# }
|
109
|
+
# params.merge!(query)
|
110
|
+
# sorted_params = params.sort {|a, b| a.first.to_s <=> b.first.to_s}
|
111
|
+
# base = base_string("GET", sorted_params)
|
112
|
+
# http_params = http_params("GET", params)
|
113
|
+
# sig = sign(base).esc
|
114
|
+
# uri = uri_for(http_params, sig)
|
115
|
+
# results = JSON.parse(Net::HTTP.get(uri))
|
116
|
+
# end
|
117
|
+
|
118
|
+
# def self.base_string(http_method, param_pairs)
|
119
|
+
# param_str = param_pairs.collect{|pair| "#{pair.first}=#{pair.last}"}.join('&')
|
120
|
+
# list = [http_method.esc, SITE.esc, param_str.esc]
|
121
|
+
# list.join("&")
|
122
|
+
# end
|
123
|
+
|
124
|
+
# def self.http_params(method, args)
|
125
|
+
# pairs = args.sort {|a, b| a.first.to_s <=> b.first.to_s}
|
126
|
+
# list = []
|
127
|
+
# pairs.inject(list) {|arr, pair| arr << "#{pair.first.to_s}=#{pair.last}"}
|
128
|
+
# list.join("&")
|
129
|
+
# end
|
130
|
+
|
131
|
+
# def self.sign(base, token='')
|
132
|
+
# secret_token = "#{@@secret.esc}&#{token.esc}"
|
133
|
+
# base64 = Base64.encode64(OpenSSL::HMAC.digest(DIGEST, secret_token, base)).gsub(/\n/, '')
|
134
|
+
# end
|
135
|
+
|
136
|
+
# def self.uri_for(params, signature)
|
137
|
+
# parts = params.split('&')
|
138
|
+
# parts << "oauth_signature=#{signature}"
|
139
|
+
# URI.parse("#{SITE}?#{parts.join('&')}")
|
140
|
+
# end
|
141
|
+
|
142
|
+
#end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
class String
|
4
|
+
def esc
|
5
|
+
CGI.escape(self).gsub("%7E", "~").gsub("+", "%20")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module FatSecret
|
10
|
+
class Connection
|
11
|
+
def initialize
|
12
|
+
@config = FatSecret.configuration
|
13
|
+
unless @config && @config && @config.api_secret
|
14
|
+
raise RuntimeError, "One or more api_keys not set"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def search (method, expression = "", args = {})
|
19
|
+
is_api_method method
|
20
|
+
|
21
|
+
query = {:method => "#{method}s.search",
|
22
|
+
:search_expression => expression.esc}
|
23
|
+
query.merge!(args)
|
24
|
+
api_get(query)
|
25
|
+
end
|
26
|
+
|
27
|
+
def get (method, id, args = {})
|
28
|
+
is_api_method method
|
29
|
+
|
30
|
+
query = {:method => "#{method}.get",
|
31
|
+
"#{method}_id" => id}
|
32
|
+
query.merge!(args)
|
33
|
+
|
34
|
+
#add oauth token if it is defined to make it ready for more methods
|
35
|
+
query[oauth_token] = @config.oauth_token if @config.oauth_token
|
36
|
+
api_get(query)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def is_api_method (method)
|
42
|
+
unless @config.api_methods.include? method
|
43
|
+
raise TypeError, "#{method} api_method not defined"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def api_get(query)
|
48
|
+
params = {
|
49
|
+
:format => 'json',
|
50
|
+
:oauth_consumer_key => @config.api_key,
|
51
|
+
:oauth_nonce => Digest::MD5.hexdigest(rand(11).to_s),
|
52
|
+
:oauth_signature_method => "HMAC-SHA1",
|
53
|
+
:oauth_timestamp => Time.now.to_i,
|
54
|
+
:oauth_version => "1.0",
|
55
|
+
}
|
56
|
+
params.merge!(query)
|
57
|
+
sorted_params = params.sort {|a, b| a.first.to_s <=> b.first.to_s}
|
58
|
+
base = base_string("GET", sorted_params)
|
59
|
+
http_params = http_params("GET", params)
|
60
|
+
sig = sign(base).esc
|
61
|
+
uri = uri_for(http_params, sig)
|
62
|
+
results = JSON.parse(Net::HTTP.get(uri))
|
63
|
+
end
|
64
|
+
|
65
|
+
def base_string(http_method, param_pairs)
|
66
|
+
param_str = param_pairs.collect{|pair| "#{pair.first}=#{pair.last}"}.join('&')
|
67
|
+
list = [http_method.esc, @config.api_url.esc, param_str.esc]
|
68
|
+
list.join("&")
|
69
|
+
end
|
70
|
+
|
71
|
+
def http_params(method, args)
|
72
|
+
pairs = args.sort {|a, b| a.first.to_s <=> b.first.to_s}
|
73
|
+
list = []
|
74
|
+
pairs.inject(list) {|arr, pair| arr << "#{pair.first.to_s}=#{pair.last}"}
|
75
|
+
list.join("&")
|
76
|
+
end
|
77
|
+
|
78
|
+
def sign(base, token='')
|
79
|
+
digest = OpenSSL::Digest::Digest.new('sha1')
|
80
|
+
secret_token = "#{@config.api_secret.esc}&#{token.esc}"
|
81
|
+
base64 = Base64.encode64(OpenSSL::HMAC.digest(digest, secret_token, base)).gsub(/\n/, '')
|
82
|
+
end
|
83
|
+
|
84
|
+
def uri_for(params, signature)
|
85
|
+
parts = params.split('&')
|
86
|
+
parts << "oauth_signature=#{signature}"
|
87
|
+
URI.parse("#{@config.api_url}?#{parts.join('&')}")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'active_attr'
|
2
|
+
|
3
|
+
module FatSecret
|
4
|
+
class Base
|
5
|
+
include ActiveAttr::Model
|
6
|
+
|
7
|
+
#Has many sets up the relationship methods needed to create new nested items
|
8
|
+
#through mass assignment. Use <class_symbol>_attributes to do so.
|
9
|
+
def self.has_many(klass_sym)
|
10
|
+
relation = FatSecret::Relation.new(sym_to_class(klass_sym))
|
11
|
+
attribute klass_sym , :default => relation
|
12
|
+
str = "#{klass_sym.to_s}_attributes="
|
13
|
+
block = lambda do |args|
|
14
|
+
args = [args] unless args.kind_of? Array
|
15
|
+
args.each { |item| send(klass_sym).new(item) if send(klass_sym)}
|
16
|
+
end
|
17
|
+
send(:define_method, str.to_sym, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
#instance methods
|
21
|
+
def initialize args = {}
|
22
|
+
@dynattrs ||= {}
|
23
|
+
super
|
24
|
+
args.each do |k,v|
|
25
|
+
#puts k.to_s + " : " + v.to_s
|
26
|
+
send (k.to_s + "=").to_sym, v
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
def method_missing sym, *args
|
32
|
+
if sym =~ /^(\w+)=$/
|
33
|
+
@dynattrs ||= {}
|
34
|
+
@dynattrs[$1.to_sym] = args[0]
|
35
|
+
else
|
36
|
+
@dynattrs[sym]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
str = super
|
42
|
+
@dynattrs.merge(attributes).each do |k,v|
|
43
|
+
#iv = iv.to_s.gsub(/@/, "")
|
44
|
+
str = str.gsub(/[^=]>$/, " :#{k} => '#{v}' >")
|
45
|
+
end
|
46
|
+
return str
|
47
|
+
end
|
48
|
+
|
49
|
+
def reload
|
50
|
+
if self.class.respond_to? :api_method and self.class.respond_to? :find
|
51
|
+
find_id = send (self.class.api_method + "_id").to_sym
|
52
|
+
self.class.find(find_id)
|
53
|
+
else
|
54
|
+
self
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
#Hack so that all the various sends aren't stuck in the protected space
|
59
|
+
class << self
|
60
|
+
protected :has_many
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
def self.connection
|
66
|
+
@connection ||= FatSecret::Connection.new
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.sym_to_class(klass_sym)
|
70
|
+
klass = klass_sym.to_s.gsub(/s$/, "").split("_").map { |x| x.capitalize }.join
|
71
|
+
FatSecret.const_get(klass)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module FatSecret
|
2
|
+
class Food < FatSecret::Base
|
3
|
+
attribute :food_name, :type => String
|
4
|
+
attribute :food_id, :type => Integer
|
5
|
+
attribute :food_url, :type => String
|
6
|
+
|
7
|
+
has_many :servings
|
8
|
+
|
9
|
+
url_regex = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/
|
10
|
+
|
11
|
+
validates :food_name, :presence => true
|
12
|
+
validates :food_id, :presence => true, :numericality => true
|
13
|
+
validates :food_url, :presence => true, :format => { :with => url_regex }
|
14
|
+
validate :has_serving
|
15
|
+
|
16
|
+
#Instance Methods
|
17
|
+
|
18
|
+
def self.find(id, args = {})
|
19
|
+
attrs = connection.get(api_method, id.to_s, args)
|
20
|
+
attrs['food']['servings_attributes'] = attrs['food']['servings']['serving']
|
21
|
+
attrs['food'].delete 'servings'
|
22
|
+
self.new attrs['food']
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.find_by_name(name, args = {})
|
26
|
+
results = connection.search(api_method, name, args)
|
27
|
+
return_results = []
|
28
|
+
results['foods']['food'].each do |food|
|
29
|
+
return_results.push self.new food
|
30
|
+
end
|
31
|
+
return_results
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def self.api_method
|
37
|
+
"food"
|
38
|
+
end
|
39
|
+
|
40
|
+
def has_serving
|
41
|
+
errors.add(:base, 'must have one or more servings') if servings.empty?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
if defined? Rails
|
2
|
+
module FatSecret
|
3
|
+
module Generators
|
4
|
+
class InstallGenerator < Rails::Generators::Base
|
5
|
+
source_root File.expand_path('../templates', __FILE__)
|
6
|
+
|
7
|
+
def copy_initializer
|
8
|
+
copy_file "fatsecret.rb", "config/initializers/fatsecret.rb"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'fatsecret-models'
|
2
|
+
|
3
|
+
FatSecret.configure do |config|
|
4
|
+
# Config options include:
|
5
|
+
# api_key
|
6
|
+
# api_secret
|
7
|
+
# api_url (defaults to the current fatsecret api http://platform.fatsecret.com/rest/server.api)
|
8
|
+
# api_methods (an array of methods for the fs api currently [food, recipies], more can be added)
|
9
|
+
# oauth_token (for future implementation with user-specific information)
|
10
|
+
#
|
11
|
+
# uncomment the following two lines and add your information
|
12
|
+
#config.api_key =
|
13
|
+
#config.api_secret =
|
14
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FatSecret::Connection do
|
4
|
+
|
5
|
+
describe "Sting method" do
|
6
|
+
it "should correctly format a sring" do
|
7
|
+
"foo~bar baz".esc.should eq "foo~bar%20baz"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "initialze" do
|
12
|
+
it "should throw an error if api_keys aren't set" do
|
13
|
+
expect {FatSecret::Connection.new()}.to raise_error(RuntimeError)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "search" do
|
18
|
+
before(:each) do
|
19
|
+
set_keys
|
20
|
+
@connection = FatSecret::Connection.new()
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should respond_to search" do
|
24
|
+
@connection.should respond_to :search
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should reject an undefined method" do
|
28
|
+
expect {@connection.search("notfood")}.to raise_error(TypeError)
|
29
|
+
end
|
30
|
+
|
31
|
+
#searches for different methods
|
32
|
+
FatSecret.configure {}.api_methods.each do |method|
|
33
|
+
it "should return a list of #{method}s for #{method} method", :vcr do
|
34
|
+
results = @connection.search(method, "apple")
|
35
|
+
results[method + 's'].should_not be_nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should accept page_number and max_results arguments", :vcr do
|
40
|
+
results = @connection.search("food", "apple", :max_results => "10",
|
41
|
+
:page_number => "5")
|
42
|
+
results["foods"]["max_results"].should eq "10"
|
43
|
+
results["foods"]["page_number"].should eq "5"
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should return for no records found", :vcr do
|
47
|
+
results = @connection.search("food","blablabla nmek123")
|
48
|
+
results["foods"]["food"].should be_nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "get" do
|
53
|
+
before(:each) do
|
54
|
+
set_keys
|
55
|
+
@connection = FatSecret::Connection.new()
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should respond_to get" do
|
59
|
+
@connection.should respond_to :get
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should reject an undefined method" do
|
63
|
+
expect {@connection.get("notfood", "99967")}.to raise_error(TypeError)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should return a food for food.get method", :vcr do
|
67
|
+
results = @connection.get("food", "99967")
|
68
|
+
results["food"].should_not be_nil
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should return a recipe for recipe.get method", :vcr do
|
72
|
+
results = @connection.get("recipe", "30633")
|
73
|
+
results["recipe"].should_not be_nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
describe FatSecret do
|
3
|
+
|
4
|
+
describe "configuration" do
|
5
|
+
|
6
|
+
it "responds to configure" do
|
7
|
+
FatSecret.should respond_to :configure
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should return the configuration object" do
|
11
|
+
config = FatSecret.configure { |config| config.api_key, config.api_secret = "key", "secret"}
|
12
|
+
config.should be_kind_of FatSecret::Configuration
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should set the api keys with a config block" do
|
16
|
+
FatSecret.configure { |config| config.api_key, config.api_secret = "key", "secret"}
|
17
|
+
config = FatSecret.configuration
|
18
|
+
|
19
|
+
config.should be_kind_of FatSecret::Configuration
|
20
|
+
config.api_key.should eq "key"
|
21
|
+
config.api_secret.should eq "secret"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should set the api keys with direct assignment" do
|
25
|
+
FatSecret.configuration.api_key = "NewKey"
|
26
|
+
FatSecret.configuration.api_key.should eq "NewKey"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_attr/mass_assignment'
|
3
|
+
|
4
|
+
|
5
|
+
describe FatSecret::Base do
|
6
|
+
it_should_behave_like "ActiveModel"
|
7
|
+
|
8
|
+
it "should not respond to save or create" do
|
9
|
+
FatSecret::Base.new.should_not respond_to :save
|
10
|
+
FatSecret::Base.new.should_not respond_to :save!
|
11
|
+
FatSecret::Base.new.should_not respond_to :create
|
12
|
+
FatSecret::Base.new.should_not respond_to :create!
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "has_many" do
|
16
|
+
before(:each) do
|
17
|
+
class FatSecret::HasManyTest
|
18
|
+
include ActiveAttr::MassAssignment
|
19
|
+
attr_accessor :first_name, :last_name
|
20
|
+
end
|
21
|
+
|
22
|
+
class TestClass < FatSecret::Base
|
23
|
+
has_many :has_many_tests
|
24
|
+
end
|
25
|
+
|
26
|
+
@testclass = TestClass.new
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should create an accessible attribute based on the class class" do
|
30
|
+
@testclass.should respond_to :has_many_tests
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should return an array" do
|
34
|
+
@testclass.has_many_tests.should be_kind_of Array
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should create a new instance of the class and add it to the array" do
|
38
|
+
@testclass.has_many_tests.new
|
39
|
+
@testclass.has_many_tests.first.should be_kind_of FatSecret::HasManyTest
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should destroy an associated record" do
|
43
|
+
@testclass.has_many_tests.new
|
44
|
+
@to_remove = @testclass.has_many_tests.first
|
45
|
+
@testclass.has_many_tests.delete(@to_remove)
|
46
|
+
@testclass.has_many_tests.should be_empty
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should accept nested attributes" do
|
50
|
+
@testclass.has_many_tests_attributes = {:first_name => "Joe",
|
51
|
+
:last_name => "Everyman"}
|
52
|
+
@testclass.has_many_tests.first.first_name.should eq "Joe"
|
53
|
+
@testclass.has_many_tests.first.last_name.should eq "Everyman"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FatSecret::Food do
|
4
|
+
|
5
|
+
%w(food_id food_name food_id food_url servings).each do |food_attr|
|
6
|
+
it "should respond to #{food_attr}" do
|
7
|
+
FatSecret::Food.new.should respond_to food_attr
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "validations" do
|
12
|
+
before(:each) do
|
13
|
+
@valid_attrs = { :food_name => "Blueberries", :food_id => "23",
|
14
|
+
:food_url => "http://www.example.com/one-two-three"}
|
15
|
+
@valid_serving = { :servings_attributes =>[{"calcium"=>"8", "calories"=>"200",
|
16
|
+
"carbohydrate"=>"27", "cholesterol"=>"20", "fat"=>"10",
|
17
|
+
"fiber"=>"3", "iron"=>"2", "measurement_description"=>"serving",
|
18
|
+
"metric_serving_amount"=>"142.000", "metric_serving_unit"=>"g",
|
19
|
+
"number_of_units"=>"1.000", "protein"=>"4", "saturated_fat"=>"4",
|
20
|
+
"serving_description"=>"1 serving", "serving_id"=>"141998",
|
21
|
+
"serving_url"=>"http://www.fatsecret.com/calories-nutrition/au-bon-pain/apples-blue-cheese-and-cranberries",
|
22
|
+
"sodium"=>"290", "sugar"=>"20", "vitamin_a"=>"4", "vitamin_c"=>"8"},
|
23
|
+
{"calcium"=>"8", "calories"=>"500",
|
24
|
+
"carbohydrate"=>"27", "cholesterol"=>"20", "fat"=>"10",
|
25
|
+
"fiber"=>"3", "iron"=>"2", "measurement_description"=>"serving",
|
26
|
+
"metric_serving_amount"=>"142.000", "metric_serving_unit"=>"g",
|
27
|
+
"number_of_units"=>"1.000", "protein"=>"4", "saturated_fat"=>"4",
|
28
|
+
"serving_description"=>"1 serving", "serving_id"=>"141998",
|
29
|
+
"serving_url"=>"http://www.fatsecret.com/calories-nutrition/au-bon-pain/apples-blue-cheese-and-cranberries",
|
30
|
+
"sodium"=>"290", "sugar"=>"20", "vitamin_a"=>"4", "vitamin_c"=>"8"}]
|
31
|
+
}
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should be valid with valid attributes" do
|
36
|
+
food = FatSecret::Food.new(@valid_attrs)
|
37
|
+
food.servings = @valid_serving[:servings_attributes]
|
38
|
+
food.should be_valid
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should not be valid if not set", :vcr do
|
42
|
+
FatSecret::Food.new.should_not be_valid
|
43
|
+
end
|
44
|
+
|
45
|
+
[:food_name, :food_id, :food_url].each do |model_attr|
|
46
|
+
it "should not be valid without #{model_attr}" do
|
47
|
+
food = FatSecret::Food.new(@valid_attrs.merge(model_attr => ""))
|
48
|
+
food.should_not be_valid
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should not be valid with a non-numerical food_id" do
|
53
|
+
food = FatSecret::Food.new(@valid_attrs.merge(:food_id => "Monkey"))
|
54
|
+
food.should_not be_valid
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should accept nested attribures for servings" do
|
58
|
+
valid = @valid_attrs.merge(@valid_serving)
|
59
|
+
food = FatSecret::Food.new(@valid_attrs)
|
60
|
+
food.should respond_to :servings
|
61
|
+
food.servings_attributes = @valid_serving[:servings_attributes]
|
62
|
+
food.servings.should_not be_empty
|
63
|
+
food.servings.first.calcium.should eq '8'
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should directly accept nested attribues' do
|
67
|
+
valid = @valid_attrs.merge(@valid_serving)
|
68
|
+
food = FatSecret::Food.new(valid)
|
69
|
+
food.should be_valid
|
70
|
+
#puts food.servings.first
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "find" do
|
75
|
+
|
76
|
+
before(:each) do
|
77
|
+
set_keys
|
78
|
+
@food = FatSecret::Food.find(99967)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should respond to find", :vcr do
|
82
|
+
FatSecret::Food.should respond_to :find
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should return something", :vcr do
|
86
|
+
@food.should_not be_nil
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should return a valid food model", :vcr do
|
90
|
+
@food.should be_kind_of FatSecret::Food
|
91
|
+
@food.should be_valid
|
92
|
+
@food.servings.first.should be_kind_of FatSecret::Serving
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "find_by_name" do
|
98
|
+
before(:each) do
|
99
|
+
set_keys
|
100
|
+
@food_list = FatSecret::Food.find_by_name("Apple")
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should respond to find_by_name", :vcr do
|
104
|
+
FatSecret::Food.should respond_to :find_by_name
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should return an array", :vcr do
|
108
|
+
@food_list.should_not be_nil
|
109
|
+
@food_list.should be_kind_of Array
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should be a list of food objects", :vcr do
|
113
|
+
@food_list.first.should be_kind_of FatSecret::Food
|
114
|
+
@food_list.first.should_not be_valid
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "reload" do
|
120
|
+
it "should return a new model from the api", :vcr do
|
121
|
+
food_list = FatSecret::Food.find_by_name("Apple")
|
122
|
+
food = food_list.first
|
123
|
+
food.should_not be_valid
|
124
|
+
food.reload.should be_valid
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'active_model'
|
4
|
+
require 'vcr'
|
5
|
+
require 'fakeweb'
|
6
|
+
require "active_attr/rspec"
|
7
|
+
|
8
|
+
require 'fatsecret' # and any other gems you need
|
9
|
+
|
10
|
+
|
11
|
+
Dir["spec/support/**/*.rb"].each { |f| require File.expand_path(f) }
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.color_enabled = true
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
shared_examples_for "ActiveModel" do
|
2
|
+
require 'test/unit/assertions'
|
3
|
+
require 'active_model/lint'
|
4
|
+
include Test::Unit::Assertions
|
5
|
+
include ActiveModel::Lint::Tests
|
6
|
+
|
7
|
+
ActiveModel::Lint::Tests.public_instance_methods.map{|m| m.to_s}.grep(/^test/).each do |m|
|
8
|
+
example m.gsub('_',' ') do
|
9
|
+
send m
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def model
|
14
|
+
subject
|
15
|
+
end
|
16
|
+
end
|
data/spec/support/vcr.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'active_support/core_ext/hash/slice'
|
2
|
+
require 'active_support/core_ext/hash/except'
|
3
|
+
|
4
|
+
VCR.configure do |c|
|
5
|
+
c.cassette_library_dir = File.join("spec", "vcr")
|
6
|
+
c.hook_into :fakeweb
|
7
|
+
c.default_cassette_options = {
|
8
|
+
:match_requests_on => [:method,
|
9
|
+
VCR.request_matchers.uri_without_params(
|
10
|
+
:oauth_timestamp, :oauth_signature, :oauth_nonce)]}
|
11
|
+
end
|
12
|
+
|
13
|
+
RSpec.configure do |c|
|
14
|
+
c.treat_symbols_as_metadata_keys_with_true_values = true
|
15
|
+
c.around(:each, :vcr) do |example|
|
16
|
+
name = example.metadata[:full_description].split(/\s+/, 2).join("/").gsub(/[^\w\/]+/, "_")
|
17
|
+
options = example.metadata.slice(:record, :match_requests_on).except(:example_group)
|
18
|
+
VCR.use_cassette(name, options) { example.call }
|
19
|
+
end
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fatsecret-models
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Joseph Jackson
|
9
|
+
- Ibrahim Muhammad
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-08-21 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: json
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '1.5'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '1.5'
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: activemodel
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ~>
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '3.0'
|
39
|
+
type: :runtime
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '3.0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: active_attr
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: rake
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
type: :development
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
name: rspec
|
81
|
+
requirement: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ! '>='
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
type: :development
|
88
|
+
prerelease: false
|
89
|
+
version_requirements: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: vcr
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ! '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: fakeweb
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ! '>='
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ! '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
description: An active_model compliant wrapper for FatSecret API
|
128
|
+
email: cpmhjoe@gmail.com
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- .gitignore
|
134
|
+
- Gemfile
|
135
|
+
- Gemfile.lock
|
136
|
+
- README.md
|
137
|
+
- Rakefile
|
138
|
+
- fatsecret.gemspec
|
139
|
+
- lib/fatsecret-models.rb
|
140
|
+
- lib/fatsecret-models/connection.rb
|
141
|
+
- lib/fatsecret-models/models/base.rb
|
142
|
+
- lib/fatsecret-models/models/food.rb
|
143
|
+
- lib/fatsecret-models/models/serving.rb
|
144
|
+
- lib/fatsecret-models/relation.rb
|
145
|
+
- lib/generators/fat_secret/install/USAGE
|
146
|
+
- lib/generators/fat_secret/install/install_generator.rb
|
147
|
+
- lib/generators/fat_secret/install/templates/fatsecret.rb
|
148
|
+
- spec/connection/conection_spec.rb
|
149
|
+
- spec/fatsecret_spec.rb
|
150
|
+
- spec/models/base_spec.rb
|
151
|
+
- spec/models/food_spec.rb
|
152
|
+
- spec/spec_helper.rb
|
153
|
+
- spec/support/active_model_lint.rb
|
154
|
+
- spec/support/set_keys.rb
|
155
|
+
- spec/support/vcr.rb
|
156
|
+
homepage: http://www.github.com/joeyjoejoejr/fatsecret
|
157
|
+
licenses: []
|
158
|
+
post_install_message:
|
159
|
+
rdoc_options: []
|
160
|
+
require_paths:
|
161
|
+
- lib
|
162
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
163
|
+
none: false
|
164
|
+
requirements:
|
165
|
+
- - ! '>='
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: '0'
|
168
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
requirements: []
|
175
|
+
rubyforge_project:
|
176
|
+
rubygems_version: 1.8.24
|
177
|
+
signing_key:
|
178
|
+
specification_version: 3
|
179
|
+
summary: An active_model compliant wrapper for FatSecret API
|
180
|
+
test_files: []
|