fatsecret-models 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ keys.txt
3
+ spec/vcr
4
+
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source :rubygems
2
+ gemspec
3
+
4
+
5
+
@@ -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
@@ -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
+ ```
@@ -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
@@ -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,5 @@
1
+ module FatSecret
2
+ #All the fun stuff is in FatSecret::Base
3
+ class Serving < FatSecret::Base
4
+ end
5
+ end
@@ -0,0 +1,13 @@
1
+ module FatSecret
2
+ class Relation < Array
3
+ def initialize(klass)
4
+ @klass = klass
5
+ @models = []
6
+ end
7
+
8
+ def new(args = {})
9
+ push @klass.new(args)
10
+ last
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ installs an initializer. Should be modified to include your api information
3
+
4
+ Example:
5
+ rails generate fatsecret::install
6
+
7
+ This will create:
8
+ config/initializers/fatsecret.rb
@@ -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
@@ -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
@@ -0,0 +1,8 @@
1
+ def set_keys
2
+ FatSecret.configure do |config|
3
+ #config.api_key = ""
4
+ #config.api_secret = ""
5
+ end
6
+ config = FatSecret.configuration
7
+ raise RuntimeError, "One or more api_keys not set" unless config.api_key && config.api_secret
8
+ end
@@ -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: []