fatsecret-models 0.1.2

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.
@@ -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: []