fitgem 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +112 -0
- data/Rakefile +2 -0
- data/fitgem.gemspec +25 -0
- data/lib/fitgem.rb +8 -0
- data/lib/fitgem/activities.rb +59 -0
- data/lib/fitgem/client.rb +91 -0
- data/lib/fitgem/devices.rb +17 -0
- data/lib/fitgem/errors.rb +10 -0
- data/lib/fitgem/foods.rb +63 -0
- data/lib/fitgem/helpers.rb +16 -0
- data/lib/fitgem/notifications.rb +52 -0
- data/lib/fitgem/time_range.rb +53 -0
- data/lib/fitgem/units.rb +15 -0
- data/lib/fitgem/users.rb +9 -0
- data/lib/fitgem/weight.rb +13 -0
- data/spec/fitgem_spec.rb +73 -0
- data/spec/spec_helper.rb +8 -0
- metadata +99 -0
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm ruby-1.9.2-p0@fitbit
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Zachery Moneypenny
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
# Fitgem #
|
2
|
+
|
3
|
+
Provides access to fitbit.com data through their OAuth/REST API. Without user authentication, any data that the a fitbit.com user has denoted as 'public' can be gathered. If a user logs in via OAuth then all exposed data can be gathered.
|
4
|
+
|
5
|
+
The fitbit.com API is currently (March 2011) in BETA and is under development to extend its reach. Since it is early in the lifecycle of the API I expect this gem to go through a number of revisions as we attempt to match the functionality of their platform.
|
6
|
+
|
7
|
+
# Usage #
|
8
|
+
|
9
|
+
If you've ever done any oauth client programming then the model will appear familiar. Your first step, if haven't already, is to visit [https://dev.fitbit.com/](https://dev.fitbit.com/) and register your application to get your __consumer key__ and __consumer secret__ and set your __callback URL__, if appropriate for your app. There's more documentation at the site so I won't belabor it here.
|
10
|
+
|
11
|
+
Below I've included two sample scripts that use Fitgem::Client to retrieve data. One shows how to do the initial authorization _without_ doing the callback; the other shows how to use saved values from that initial authorization to reconnect with the API and get subsequent information.
|
12
|
+
|
13
|
+
require 'fitgem'
|
14
|
+
|
15
|
+
consumer_key = 'your-app-consumer-key'
|
16
|
+
consumer_secret = 'your-app-consumer-secret'
|
17
|
+
|
18
|
+
client = Fitgem::Client.new({:consumer_key => consumer_key, :consumer_secret => consumer_secret})
|
19
|
+
|
20
|
+
request_token = client.request_token
|
21
|
+
token = request_token.token
|
22
|
+
secret = request_token.secret
|
23
|
+
|
24
|
+
puts "Go to http://www.fitbit.com/oauth/authorize?oauth_token=#{token} and then enter the verifier code below and hit Enter"
|
25
|
+
verifier = gets.chomp
|
26
|
+
|
27
|
+
access_token = client.authorize(token, secret, { :oauth_verifier => verifier })
|
28
|
+
|
29
|
+
puts "Verifier is: "+verifier
|
30
|
+
puts "Token is: "+access_token.token
|
31
|
+
puts "Secret is: "+access_token.secret
|
32
|
+
|
33
|
+
After running this and successfully connecting with verifier string that is displayed by the Fitgem site, you can reconnect using the script below. To do so, take the token and secret that were printed out from the script above and paste them in where appropriate. In this example we are using the client to get the
|
34
|
+
|
35
|
+
require 'fitgem'
|
36
|
+
|
37
|
+
consumer_key = 'your-app-consumer-key'
|
38
|
+
consumer_secret = 'your-app-consumer-secret'
|
39
|
+
token = 'token-received-in-above-script'
|
40
|
+
secret = 'secret-received-in-above-script'
|
41
|
+
user_id = 'your-user-id' # may be similar to '12345N'
|
42
|
+
|
43
|
+
client = Fitgem::Client.new({:consumer_key => consumer_key, :consumer_secret => consumer_secret, :token => token, :secret => secret, :user_id => user_id})
|
44
|
+
access_token = client.reconnect(token, secret)
|
45
|
+
p client.user_info
|
46
|
+
|
47
|
+
You can use this script to learn about the data structures that are returned from different API calls. Since this library always retrieves JSON responses and then parses it into Ruby structures, you can interrogate the response data with simple calls to hashes.
|
48
|
+
|
49
|
+
## Usage in a Rails Application ##
|
50
|
+
|
51
|
+
We've started to develop an example app using the fitgem client. See [https://github.com/whazzmaster/fitgem-client](https://github.com/whazzmaster/fitgem-client) for more information.
|
52
|
+
|
53
|
+
# Subscriptions #
|
54
|
+
|
55
|
+
The Fitbit API allows for you to set up notification subscription so that when values change (via automatic syncs with the fitbit device) your applications can be notified automatically. You can set up a default subscription callback URL via the [Fitbit dev site](https://dev.fitbit.com/ 'Fitbit Developer Site') and then use the Subscriptions API to add or remove subscriptions for individual users.
|
56
|
+
|
57
|
+
__Currently, notification management is experimental in this gem__. We've built up code to add and remove specific types of subscriptions (foods, activities, sleep, body) but there seems to be some server-side issues with creating general, all-purpose subscriptions.
|
58
|
+
|
59
|
+
The docs are very fuzzy on subscription support at the moment; we definitely plan on extending this support once the backend has matured (or the online docs have improved).
|
60
|
+
|
61
|
+
# FAQs #
|
62
|
+
|
63
|
+
## What About ruby-fitbit? ##
|
64
|
+
|
65
|
+
There is a good looking gem called [ruby-fitbit](https://github.com/danmayer/ruby-fitbit "ruby-fitbit") that
|
66
|
+
also aims to collect data from the site. It was created before they released their REST API and uses screen-scraping to gather the data rather than through their API. I looked into forking it and refactoring
|
67
|
+
to use the new API but after looking through the code I felt it would be more of a total rewrite and so decided
|
68
|
+
to create a new gem that I could design from scratch.
|
69
|
+
|
70
|
+
## Why the Name Change? ##
|
71
|
+
|
72
|
+
It turns out that Fitbit.com does not want people to create libraries or applications that incorporate the name 'fitbit'. So, on May 12th I changed the name of the project/gem from 'fitbit' to 'fitgem'.
|
73
|
+
|
74
|
+
|
75
|
+
# Changelog #
|
76
|
+
|
77
|
+
* 12 May, 2011:
|
78
|
+
* Changed name and all references of this project from 'fitbit' to 'fitgem'
|
79
|
+
* 11 April, 2011:
|
80
|
+
* Fixed an issue where blank user id's are used and an error is thrown.
|
81
|
+
* Added support for creating/removing subscriptions (this support is experimental for now, more tests coming)
|
82
|
+
* 24 March, 2011:
|
83
|
+
* Added logging of activities and foods
|
84
|
+
* Added ability to add favorite activities and foods
|
85
|
+
* Added ability to delete logged activities and foods, and remove favorite activities and foods
|
86
|
+
* Refactored data_by_time_range for more testability
|
87
|
+
* Added ability to query devices
|
88
|
+
* 19 March, 2011:
|
89
|
+
* Updated auth client to support first-time auth and reconnections (if you have previously been authorized and received token/secret).
|
90
|
+
* Added 'named' retrieval of activities and foods (recent_, favorite_, frequent_)
|
91
|
+
* Added ability to log weight back to the site using the API
|
92
|
+
* 18 March, 2001: First revision. Supports the auth process via oauth, and retrieval of user info and activities.
|
93
|
+
|
94
|
+
# Notice #
|
95
|
+
|
96
|
+
To be clear: __I am not employed by fitbit.com__. I created this library to assist other ruby developers in creating interesting applications on top of fitbit.com's data store and device data stream.
|
97
|
+
|
98
|
+
# Contributing to fitgem #
|
99
|
+
|
100
|
+
The Fitbit REST API is in BETA right now, and so it will quite likely change over time (though I can't be sure whether it will be additive change or change of the non-backwards-compatible variety). I aim to keep as up-to-date as I can but if you absolutely need functionality that isn't included here, feel free to fork and implement it, then send me a pull request.
|
101
|
+
|
102
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
103
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
104
|
+
* Fork the project
|
105
|
+
* Start a feature/bugfix branch
|
106
|
+
* Commit and push until you are happy with your contribution
|
107
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
108
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
109
|
+
|
110
|
+
### Copyright ###
|
111
|
+
|
112
|
+
Copyright (c) 2011 Zachery Moneypenny. See LICENSE.txt for further details.
|
data/Rakefile
ADDED
data/fitgem.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
$:.push File.join(File.dirname(__FILE__), '.', 'lib')
|
4
|
+
|
5
|
+
require 'fitgem'
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "fitgem"
|
9
|
+
s.version = Fitgem::VERSION
|
10
|
+
s.platform = Gem::Platform::RUBY
|
11
|
+
s.authors = ["Zachery Moneypenny"]
|
12
|
+
s.email = ["fitgem@whazzmaster.com"]
|
13
|
+
s.homepage = "http://github.com/whazzmaster/fitgem"
|
14
|
+
s.summary = %q{OAuth client library to the data on fitbit.com}
|
15
|
+
s.description = %q{A client library to send and retrieve workout/weight data from fitbit.com}
|
16
|
+
|
17
|
+
s.rubyforge_project = "fitgem"
|
18
|
+
s.add_dependency "oauth"
|
19
|
+
s.add_development_dependency "rspec"
|
20
|
+
|
21
|
+
s.files = `git ls-files`.split("\n")
|
22
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
23
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
24
|
+
s.require_paths = ["lib"]
|
25
|
+
end
|
data/lib/fitgem.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module Fitgem
|
2
|
+
class Client
|
3
|
+
|
4
|
+
# ==========================================
|
5
|
+
# Activity Retrieval Methods
|
6
|
+
# ==========================================
|
7
|
+
def activities_on_date(date)
|
8
|
+
get("/user/#{@user_id}/activities/date/#{format_date(date)}.json")
|
9
|
+
end
|
10
|
+
|
11
|
+
def frequent_activities()
|
12
|
+
get("/user/#{@user_id}/activities/frequent.json")
|
13
|
+
end
|
14
|
+
|
15
|
+
def recent_activities()
|
16
|
+
get("/user/#{@user_id}/activities/recent.json")
|
17
|
+
end
|
18
|
+
|
19
|
+
def favorite_activities()
|
20
|
+
get("/user/#{@user_id}/activities/favorite.json")
|
21
|
+
end
|
22
|
+
|
23
|
+
def activity(id, options ={})
|
24
|
+
get("/activities/#{id}.json")
|
25
|
+
end
|
26
|
+
|
27
|
+
# ==========================================
|
28
|
+
# Activity Update Methods
|
29
|
+
# ==========================================
|
30
|
+
|
31
|
+
# The following values are REQUIRED when logging an activity:
|
32
|
+
# options[:activityId] => The activity id
|
33
|
+
# options[:durationMillis] => Activity duration in milliseconds
|
34
|
+
# options[:distance] => Distance covered during activity date
|
35
|
+
# options[:startTime] => Activity start time hours and minutes in the format HH:mm
|
36
|
+
# The following values are OPTIONAL when logging an activity:
|
37
|
+
# options[:date] => set to today's date when not provided
|
38
|
+
def log_activity(options)
|
39
|
+
post("/user/#{@user_id}/activities.json", options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_favorite_activity(activity_id)
|
43
|
+
post("/user/#{@user_id}/activities/log/favorite/#{activity_id}.json")
|
44
|
+
end
|
45
|
+
|
46
|
+
# ==========================================
|
47
|
+
# Activity Removal Methods
|
48
|
+
# ==========================================
|
49
|
+
|
50
|
+
def delete_logged_activity(activity_log_id)
|
51
|
+
delete("/user/#{@user_id}/activities/#{activity_log_id}.json")
|
52
|
+
end
|
53
|
+
|
54
|
+
def remove_favorite_activity(activity_id)
|
55
|
+
delete("/user/#{@user_id}/activities/log/favorite/#{activity_id}.json")
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'fitgem/helpers'
|
2
|
+
require 'fitgem/errors'
|
3
|
+
require 'fitgem/users'
|
4
|
+
require 'fitgem/activities'
|
5
|
+
require 'fitgem/units'
|
6
|
+
require 'fitgem/foods'
|
7
|
+
require 'fitgem/weight'
|
8
|
+
require 'fitgem/time_range'
|
9
|
+
require 'fitgem/devices'
|
10
|
+
require 'fitgem/notifications'
|
11
|
+
require 'date'
|
12
|
+
require 'uri'
|
13
|
+
|
14
|
+
module Fitgem
|
15
|
+
class Client
|
16
|
+
|
17
|
+
attr_accessor :api_version
|
18
|
+
attr_accessor :api_unit_system
|
19
|
+
attr_accessor :user_id
|
20
|
+
|
21
|
+
def initialize(options = {})
|
22
|
+
@consumer_key = options[:consumer_key]
|
23
|
+
@consumer_secret = options[:consumer_secret]
|
24
|
+
@token = options[:token]
|
25
|
+
@secret = options[:secret]
|
26
|
+
@proxy = options[:proxy]
|
27
|
+
@user_id = options[:user_id] || "-"
|
28
|
+
@api_unit_system = Fitgem::ApiUnitSystem.US
|
29
|
+
@api_version = "1"
|
30
|
+
end
|
31
|
+
|
32
|
+
def authorize(token, secret, options = {})
|
33
|
+
request_token = OAuth::RequestToken.new(
|
34
|
+
consumer, token, secret
|
35
|
+
)
|
36
|
+
@access_token = request_token.get_access_token(options)
|
37
|
+
@token = @access_token.token
|
38
|
+
@secret = @access_token.secret
|
39
|
+
@access_token
|
40
|
+
end
|
41
|
+
|
42
|
+
def reconnect(token, secret)
|
43
|
+
@token = token
|
44
|
+
@secret = secret
|
45
|
+
access_token
|
46
|
+
end
|
47
|
+
|
48
|
+
def request_token(options={})
|
49
|
+
consumer.get_request_token(options)
|
50
|
+
end
|
51
|
+
|
52
|
+
def authentication_request_token(options={})
|
53
|
+
consumer.options[:authorize_path] = '/oauth/authenticate'
|
54
|
+
request_token(options)
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def consumer
|
60
|
+
@consumer ||= OAuth::Consumer.new(
|
61
|
+
@consumer_key,
|
62
|
+
@consumer_secret,
|
63
|
+
{ :site => 'http://api.fitbit.com', :request_endpoint => @proxy }
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
def access_token
|
68
|
+
@access_token ||= OAuth::AccessToken.new(consumer, @token, @secret)
|
69
|
+
end
|
70
|
+
|
71
|
+
def get(path, headers={})
|
72
|
+
headers.merge!("User-Agent" => "fitgem gem v#{Fitgem::VERSION}", "Accept-Language" => @api_unit_system)
|
73
|
+
oauth_response = access_token.get("/#{@api_version}#{path}", headers)
|
74
|
+
JSON.parse(oauth_response.body)
|
75
|
+
end
|
76
|
+
|
77
|
+
def post(path, body='', headers={})
|
78
|
+
headers.merge!("User-Agent" => "fitgem gem v#{Fitgem::VERSION}", "Accept-Language" => @api_unit_system)
|
79
|
+
oauth_response = access_token.post("/#{@api_version}#{path}", body, headers)
|
80
|
+
JSON.parse(oauth_response.body)
|
81
|
+
end
|
82
|
+
|
83
|
+
def delete(path, headers={})
|
84
|
+
p path
|
85
|
+
p headers
|
86
|
+
headers.merge!("User-Agent" => "fitgem gem v#{Fitgem::VERSION}", "Accept-Language" => @api_unit_system)
|
87
|
+
oauth_response = access_token.delete("/#{@api_version}#{path}", headers)
|
88
|
+
JSON.parse(oauth_response.body)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Fitgem
|
2
|
+
class Client
|
3
|
+
|
4
|
+
# ==========================================
|
5
|
+
# Device Retrieval Methods
|
6
|
+
# ==========================================
|
7
|
+
|
8
|
+
def devices
|
9
|
+
get("/user/#{@user_id}/devices.json")
|
10
|
+
end
|
11
|
+
|
12
|
+
def device_info(device_id)
|
13
|
+
get("/user/#{@user_id}/devices/#{device_id}.json")
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
data/lib/fitgem/foods.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
module Fitgem
|
2
|
+
class Client
|
3
|
+
|
4
|
+
# ==========================================
|
5
|
+
# Food Retrieval Methods
|
6
|
+
# ==========================================
|
7
|
+
|
8
|
+
def foods_on_date(date)
|
9
|
+
get("/user/#{@user_id}/foods/log/date/#{format_date(date)}.json")
|
10
|
+
end
|
11
|
+
|
12
|
+
def recent_foods()
|
13
|
+
get("/user/#{@user_id}/foods/log/recent.json")
|
14
|
+
end
|
15
|
+
|
16
|
+
def frequent_foods()
|
17
|
+
get("/user/#{@user_id}/foods/log/frequent.json")
|
18
|
+
end
|
19
|
+
|
20
|
+
def favorite_foods()
|
21
|
+
get("/user/#{@user_id}/foods/log/favorite.json")
|
22
|
+
end
|
23
|
+
|
24
|
+
def foods_units()
|
25
|
+
get("/foods/units.json")
|
26
|
+
end
|
27
|
+
|
28
|
+
def find_food(query_string)
|
29
|
+
get("/foods/search.json?query=#{URI.escape(query_string)}")
|
30
|
+
end
|
31
|
+
|
32
|
+
# ==========================================
|
33
|
+
# Food Update Methods
|
34
|
+
# ==========================================
|
35
|
+
|
36
|
+
# Send the following required ID's in the options hash:
|
37
|
+
# options[:foodId] => ID of the food to log
|
38
|
+
# options[:mealTypeId] => ID of the meal to log the food for
|
39
|
+
# options[:unitId] => ID of the unit to log with the food
|
40
|
+
# (typically retrieved via a previous call to get Foods (all, recent, frequent, favorite) or Food Units. )
|
41
|
+
# options[:amount] => Amount consumed of the selected unit; a floating point number
|
42
|
+
# options[:date] => Log date in the format yyyy-MM-dd
|
43
|
+
def log_food(options)
|
44
|
+
post("/user/#{@user_id}/foods/log.json", options)
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_favorite_food(food_id)
|
48
|
+
post("/user/#{@user_id}/foods/log/favorite/#{food_id}.json")
|
49
|
+
end
|
50
|
+
|
51
|
+
# ==========================================
|
52
|
+
# Food Removal Methods
|
53
|
+
# ==========================================
|
54
|
+
|
55
|
+
def delete_logged_food(food_log_id)
|
56
|
+
delete("/user/#{@user_id}/foods/log/#{food_log_id}.json")
|
57
|
+
end
|
58
|
+
|
59
|
+
def remove_favorite_food(food_id)
|
60
|
+
delete("/user/#{@user_id}/foods/favorite/#{food_id}.json")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Fitgem
|
2
|
+
class Client
|
3
|
+
|
4
|
+
# Should return date as YYYY-MM-DD
|
5
|
+
def format_date(date)
|
6
|
+
if date.is_a? String
|
7
|
+
return date
|
8
|
+
elsif Date === date || Time === date || DateTime === date
|
9
|
+
return date.strftime("%Y-%m-%d")
|
10
|
+
else
|
11
|
+
raise Fitgem::InvalidArgumentError, "Date used must be a date/time object or a string in the format YYYY=MM-DD; current argument is a #{date.class}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Fitgem
|
2
|
+
class Client
|
3
|
+
|
4
|
+
def create_subscription(options={})
|
5
|
+
unless options[:type] && [:sleep,:body,:activities,:foods].include?(options[:type])
|
6
|
+
raise Error, 'Must include options[:type] (values are :activities, :foods, :sleep, and :body)'
|
7
|
+
end
|
8
|
+
base_url = "/user/#{@user_id}/#{options[:type].to_s}/apiSubscriptions"
|
9
|
+
post_subscription(base_url, options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def remove_subscription(options={})
|
13
|
+
unless options[:type] && [:sleep,:body,:activities,:foods].include?(options[:type])
|
14
|
+
raise Error, 'Must include options[:type] (values are :activities, :foods, :sleep, and :body)'
|
15
|
+
end
|
16
|
+
unless options[:subscription_id]
|
17
|
+
raise Error, "Must include options[:subscription_id] to delete a subscription"
|
18
|
+
end
|
19
|
+
base_url = "/user/#{@user_id}/#{options[:type].to_s}/apiSubscriptions"
|
20
|
+
url = finalize_subscription_url(base_url, options)
|
21
|
+
headers = {}
|
22
|
+
headers['X-Fitgem-Subscriber-Id'] = options[:subscriber_id] if options[:subscriber_id]
|
23
|
+
begin
|
24
|
+
delete(url, headers)
|
25
|
+
rescue TypeError
|
26
|
+
# Deleting a subscription returns a nil response, which causes a TypeError
|
27
|
+
# when the oauth library tries to parse it.
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def finalize_subscription_url(base_url, options={})
|
34
|
+
url = base_url
|
35
|
+
if options[:subscription_id]
|
36
|
+
url += "/#{options[:subscription_id]}"
|
37
|
+
end
|
38
|
+
if options[:subscription_response_format]
|
39
|
+
url += ".#{options[:subscription_response_format]}"
|
40
|
+
else
|
41
|
+
url += ".json"
|
42
|
+
end
|
43
|
+
url
|
44
|
+
end
|
45
|
+
|
46
|
+
def post_subscription(base_url, options)
|
47
|
+
url = finalize_subscription_url(base_url, options)
|
48
|
+
post(url, options)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Fitgem
|
2
|
+
class Client
|
3
|
+
|
4
|
+
# Gets historical resource data in the time range specified by
|
5
|
+
# options param. The time range can either be specified by
|
6
|
+
# :base_date and :end_date OR by using :base_date and a :period
|
7
|
+
# (supported periods are 1d, 7d, 30d, 1w, 1m, 3m, 6m, 1y, max)
|
8
|
+
#
|
9
|
+
# Example values for resource_path are below:
|
10
|
+
#
|
11
|
+
# Food:
|
12
|
+
# /foods/log/caloriesIn
|
13
|
+
#
|
14
|
+
# Activity:
|
15
|
+
# /activities/log/calories
|
16
|
+
# /activities/log/steps
|
17
|
+
# /activities/log/distance
|
18
|
+
# /activities/log/minutesSedentary
|
19
|
+
# /activities/log/minutesLightlyActive
|
20
|
+
# /activities/log/minutesFairlyActive
|
21
|
+
# /activities/log/minutesVeryActive
|
22
|
+
# /activities/log/activeScore
|
23
|
+
# /activities/log/activityCalories
|
24
|
+
#
|
25
|
+
# Sleep:
|
26
|
+
# /sleep/minutesAsleep
|
27
|
+
# /sleep/minutesAwake
|
28
|
+
# /sleep/awakeningsCount
|
29
|
+
# /sleep/timeInBed
|
30
|
+
#
|
31
|
+
# Body:
|
32
|
+
# /body/weight
|
33
|
+
# /body/bmi
|
34
|
+
# /body/fat
|
35
|
+
def data_by_time_range(resource_path, options)
|
36
|
+
range_str = construct_date_range_fragment(options)
|
37
|
+
get("/user/#{@user_id}#{resource_path}/#{range_str}.json")
|
38
|
+
end
|
39
|
+
|
40
|
+
def construct_date_range_fragment(options)
|
41
|
+
range_str = "date/"
|
42
|
+
if options[:base_date] && options[:period]
|
43
|
+
range_str += "#{options[:base_date]}/#{options[:period]}"
|
44
|
+
elsif options[:base_date] && options[:end_date]
|
45
|
+
range_str += "#{options[:base_date]}/#{options[:end_date]}"
|
46
|
+
else
|
47
|
+
raise Fitgem::InvalidTimeRange, "Must supply either base_date and period OR base_date and end_date"
|
48
|
+
end
|
49
|
+
range_str
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
data/lib/fitgem/units.rb
ADDED
data/lib/fitgem/users.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Fitgem
|
2
|
+
class Client
|
3
|
+
|
4
|
+
# ==========================================
|
5
|
+
# Weight Update Methods
|
6
|
+
# ==========================================
|
7
|
+
|
8
|
+
def log_weight(weight, date, options={})
|
9
|
+
post("/user/#{@user_id}/body/weight.json", options.merge(:weight => weight, :date => format_date(date)))
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
data/spec/fitgem_spec.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fitgem do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@client = Fitgem::Client.new
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "global settings" do
|
10
|
+
it 'should expose the api_version' do
|
11
|
+
@client.api_version.should == "1"
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should all clients to set a new api version' do
|
15
|
+
@client.api_version = "2"
|
16
|
+
@client.api_version.should == "2"
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should default to the US unit system' do
|
20
|
+
@client.api_unit_system.should == Fitgem::ApiUnitSystem.US
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should allow the unit system to be set to other types' do
|
24
|
+
@client.api_unit_system = Fitgem::ApiUnitSystem.UK
|
25
|
+
@client.api_unit_system.should == Fitgem::ApiUnitSystem.UK
|
26
|
+
@client.api_unit_system = Fitgem::ApiUnitSystem.METRIC
|
27
|
+
@client.api_unit_system.should == Fitgem::ApiUnitSystem.METRIC
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should default to a user id of \'-\', the currently-logged in user' do
|
31
|
+
@client.user_id.should == '-'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "data retrieval by time range" do
|
36
|
+
|
37
|
+
it 'should format the correct URI fragment based on a base date and end date' do
|
38
|
+
frag = @client.construct_date_range_fragment({:base_date => '2011-03-07', :end_date => '2011-03-14'})
|
39
|
+
frag.should == 'date/2011-03-07/2011-03-14'
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should format the correct URI fragment based on a base date and period' do
|
43
|
+
frag = @client.construct_date_range_fragment({:base_date => '2011-03-07', :period => '7d'})
|
44
|
+
frag.should == 'date/2011-03-07/7d'
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should raise an error unless there is a base date AND either a period or an end date' do
|
48
|
+
lambda {
|
49
|
+
@client.construct_date_range_fragment({:base_date => '2011-03-07'})
|
50
|
+
}.should raise_error(Fitgem::InvalidTimeRange)
|
51
|
+
|
52
|
+
lambda {
|
53
|
+
@client.construct_date_range_fragment({:period => '1y'})
|
54
|
+
}.should raise_error(Fitgem::InvalidTimeRange)
|
55
|
+
|
56
|
+
lambda {
|
57
|
+
@client.construct_date_range_fragment({:end_date => '2011-03-07', :period => '7d'})
|
58
|
+
}.should raise_error(Fitgem::InvalidTimeRange)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "format_date" do
|
64
|
+
it 'should accept DateTime objects' do
|
65
|
+
date = DateTime.strptime('2011-03-19','%Y-%m-%d')
|
66
|
+
@client.format_date(date).should == '2011-03-19'
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should accept strings in YYYY-MM-DD format' do
|
70
|
+
@client.format_date('2011-03-19').should == '2011-03-19'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fitgem
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.2.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Zachery Moneypenny
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-05-12 00:00:00 -05:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: oauth
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: "0"
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id002
|
38
|
+
description: A client library to send and retrieve workout/weight data from fitbit.com
|
39
|
+
email:
|
40
|
+
- fitgem@whazzmaster.com
|
41
|
+
executables: []
|
42
|
+
|
43
|
+
extensions: []
|
44
|
+
|
45
|
+
extra_rdoc_files: []
|
46
|
+
|
47
|
+
files:
|
48
|
+
- .gitignore
|
49
|
+
- .rvmrc
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- fitgem.gemspec
|
55
|
+
- lib/fitgem.rb
|
56
|
+
- lib/fitgem/activities.rb
|
57
|
+
- lib/fitgem/client.rb
|
58
|
+
- lib/fitgem/devices.rb
|
59
|
+
- lib/fitgem/errors.rb
|
60
|
+
- lib/fitgem/foods.rb
|
61
|
+
- lib/fitgem/helpers.rb
|
62
|
+
- lib/fitgem/notifications.rb
|
63
|
+
- lib/fitgem/time_range.rb
|
64
|
+
- lib/fitgem/units.rb
|
65
|
+
- lib/fitgem/users.rb
|
66
|
+
- lib/fitgem/weight.rb
|
67
|
+
- spec/fitgem_spec.rb
|
68
|
+
- spec/spec_helper.rb
|
69
|
+
has_rdoc: true
|
70
|
+
homepage: http://github.com/whazzmaster/fitgem
|
71
|
+
licenses: []
|
72
|
+
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options: []
|
75
|
+
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: "0"
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: "0"
|
90
|
+
requirements: []
|
91
|
+
|
92
|
+
rubyforge_project: fitgem
|
93
|
+
rubygems_version: 1.5.2
|
94
|
+
signing_key:
|
95
|
+
specification_version: 3
|
96
|
+
summary: OAuth client library to the data on fitbit.com
|
97
|
+
test_files:
|
98
|
+
- spec/fitgem_spec.rb
|
99
|
+
- spec/spec_helper.rb
|