fitbit 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.rvmrc +5 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +102 -0
- data/Rakefile +2 -0
- data/fitbit.gemspec +25 -0
- data/lib/fitbit.rb +8 -0
- data/lib/fitbit/activities.rb +59 -0
- data/lib/fitbit/client.rb +88 -0
- data/lib/fitbit/devices.rb +17 -0
- data/lib/fitbit/errors.rb +10 -0
- data/lib/fitbit/foods.rb +63 -0
- data/lib/fitbit/helpers.rb +16 -0
- data/lib/fitbit/time_range.rb +53 -0
- data/lib/fitbit/units.rb +15 -0
- data/lib/fitbit/users.rb +9 -0
- data/lib/fitbit/weight.rb +13 -0
- data/spec/fitbit_spec.rb +69 -0
- data/spec/spec_helper.rb +8 -0
- metadata +98 -0
data/.rvmrc
ADDED
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,102 @@
|
|
1
|
+
# Fitbit #
|
2
|
+
|
3
|
+
Provides access to fitbit.com data through their OAuth/REST API. Without user authentication, any data
|
4
|
+
that the a fitbit.com user has denoted as 'public' can be gathered. If a user logs in via OAuth then all
|
5
|
+
exposed data can be gathered.
|
6
|
+
|
7
|
+
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.
|
8
|
+
|
9
|
+
# Usage #
|
10
|
+
|
11
|
+
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.
|
12
|
+
|
13
|
+
Below I've included two sample scripts that use Fitbit::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.
|
14
|
+
|
15
|
+
require 'fitbit'
|
16
|
+
|
17
|
+
consumer_key = 'your-app-consumer-key'
|
18
|
+
consumer_secret = 'your-app-consumer-secret'
|
19
|
+
|
20
|
+
client = Fitbit::Client.new({:consumer_key => consumer_key, :consumer_secret => consumer_secret})
|
21
|
+
|
22
|
+
request_token = client.request_token
|
23
|
+
token = request_token.token
|
24
|
+
secret = request_token.secret
|
25
|
+
|
26
|
+
puts "Go to http://www.fitbit.com/oauth/authorize?oauth_token=#{token} and then enter the verifier code below and hit Enter"
|
27
|
+
verifier = gets.chomp
|
28
|
+
|
29
|
+
access_token = client.authorize(token, secret, { :oauth_verifier => verifier })
|
30
|
+
|
31
|
+
puts "Verifier is: "+verifier
|
32
|
+
puts "Token is: "+access_token.token
|
33
|
+
puts "Secret is: "+access_token.secret
|
34
|
+
|
35
|
+
After running this and successfully connecting with verifier string that is displayed by the Fitbit 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
|
36
|
+
|
37
|
+
require 'fitbit'
|
38
|
+
|
39
|
+
consumer_key = 'your-app-consumer-key'
|
40
|
+
consumer_secret = 'your-app-consumer-secret'
|
41
|
+
token = 'token-received-in-above-script'
|
42
|
+
secret = 'secret-received-in-above-script'
|
43
|
+
user_id = 'your-user-id' # may be similar to '12345N'
|
44
|
+
|
45
|
+
client = Fitbit::Client.new({:consumer_key => consumer_key, :consumer_secret => consumer_secret, :token => token, :secret => secret, :user_id => user_id})
|
46
|
+
access_token = client.reconnect(token, secret)
|
47
|
+
p client.user_info
|
48
|
+
|
49
|
+
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.
|
50
|
+
|
51
|
+
# Subscriptions #
|
52
|
+
|
53
|
+
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 subscriptions via the [Fitbit dev site](https://dev.fitbit.com/ 'Fitbit Developer Site') or via the API itself.
|
54
|
+
|
55
|
+
__Currently, notification management is not supported in this gem__. We'll be moving to support that once we finish support for reading and writing the existing resources.
|
56
|
+
|
57
|
+
# FAQs #
|
58
|
+
|
59
|
+
## What About ruby-fitbit? ##
|
60
|
+
|
61
|
+
There is a good looking gem called [ruby-fitbit](https://github.com/danmayer/ruby-fitbit "ruby-fitbit") that
|
62
|
+
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
|
63
|
+
to use the new API but after looking through the code I felt it would be more of a total rewrite and so decided
|
64
|
+
to create a new gem that I could design from scratch.
|
65
|
+
|
66
|
+
## This Code Looks Awfully Familiar ##
|
67
|
+
|
68
|
+
First off, that isn't a question. Second off... I shamelessly stole the 'Client' code from the excellent [twitter_oauth client](https://github.com/moomerman/twitter_oauth 'twitter_oauth') and refactored slightly to serve my ends for this particular interface.
|
69
|
+
|
70
|
+
I wouldn't have been able to spin up so fast without the example of twitter_oauth (due to my oauth nubness) to show how to use the [oauth](http://rubygems.org/gems/oauth 'oauth gem') gem in the typical auth scenario.
|
71
|
+
|
72
|
+
# Changelog #
|
73
|
+
|
74
|
+
* 24 March, 2011:
|
75
|
+
* Added logging of activities and foods
|
76
|
+
* Added ability to add favorite activities and foods
|
77
|
+
* Added ability to delete logged activities and foods, and remove favorite activities and foods
|
78
|
+
* Refactored data_by_time_range for more testability
|
79
|
+
* Added ability to query devices
|
80
|
+
* 19 March, 2011:
|
81
|
+
* Updated auth client to support first-time auth and reconnections (if you have previously been authorized and received token/secret).
|
82
|
+
* Added 'named' retrieval of activities and foods (recent_, favorite_, frequent_)
|
83
|
+
* Added ability to log weight back to the site using the API
|
84
|
+
* 18 March, 2001: First revision. Supports the auth process via oauth, and retrieval of user info and activities.
|
85
|
+
|
86
|
+
|
87
|
+
# Contributing to fitbit #
|
88
|
+
|
89
|
+
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.
|
90
|
+
|
91
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
92
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
93
|
+
* Fork the project
|
94
|
+
* Start a feature/bugfix branch
|
95
|
+
* Commit and push until you are happy with your contribution
|
96
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
97
|
+
* 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.
|
98
|
+
|
99
|
+
### Copyright ###
|
100
|
+
|
101
|
+
Copyright (c) 2011 Zachery Moneypenny. See LICENSE.txt for further details.
|
102
|
+
|
data/Rakefile
ADDED
data/fitbit.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 'fitbit'
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "fitbit"
|
9
|
+
s.version = Fitbit::VERSION
|
10
|
+
s.platform = Gem::Platform::RUBY
|
11
|
+
s.authors = ["Zachery Moneypenny"]
|
12
|
+
s.email = ["fitbit-gem@whazzmaster.com"]
|
13
|
+
s.homepage = "http://github.com/whazzmaster/fitbit"
|
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 = "fitbit"
|
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/fitbit.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module Fitbit
|
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,88 @@
|
|
1
|
+
require 'fitbit/helpers'
|
2
|
+
require 'fitbit/errors'
|
3
|
+
require 'fitbit/users'
|
4
|
+
require 'fitbit/activities'
|
5
|
+
require 'fitbit/units'
|
6
|
+
require 'fitbit/foods'
|
7
|
+
require 'fitbit/weight'
|
8
|
+
require 'fitbit/time_range'
|
9
|
+
require 'fitbit/devices'
|
10
|
+
require 'date'
|
11
|
+
require 'uri'
|
12
|
+
|
13
|
+
module Fitbit
|
14
|
+
class Client
|
15
|
+
|
16
|
+
attr_accessor :api_version
|
17
|
+
attr_accessor :api_unit_system
|
18
|
+
attr_accessor :user_id
|
19
|
+
|
20
|
+
def initialize(options = {})
|
21
|
+
@consumer_key = options[:consumer_key]
|
22
|
+
@consumer_secret = options[:consumer_secret]
|
23
|
+
@token = options[:token]
|
24
|
+
@secret = options[:secret]
|
25
|
+
@proxy = options[:proxy]
|
26
|
+
@user_id = options[:user_id]
|
27
|
+
@api_unit_system = Fitbit::ApiUnitSystem.US
|
28
|
+
@api_version = "1"
|
29
|
+
end
|
30
|
+
|
31
|
+
def authorize(token, secret, options = {})
|
32
|
+
request_token = OAuth::RequestToken.new(
|
33
|
+
consumer, token, secret
|
34
|
+
)
|
35
|
+
@access_token = request_token.get_access_token(options)
|
36
|
+
@token = @access_token.token
|
37
|
+
@secret = @access_token.secret
|
38
|
+
@access_token
|
39
|
+
end
|
40
|
+
|
41
|
+
def reconnect(token, secret)
|
42
|
+
@token = token
|
43
|
+
@secret = secret
|
44
|
+
access_token
|
45
|
+
end
|
46
|
+
|
47
|
+
def request_token(options={})
|
48
|
+
consumer.get_request_token(options)
|
49
|
+
end
|
50
|
+
|
51
|
+
def authentication_request_token(options={})
|
52
|
+
consumer.options[:authorize_path] = '/oauth/authenticate'
|
53
|
+
request_token(options)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def consumer
|
59
|
+
@consumer ||= OAuth::Consumer.new(
|
60
|
+
@consumer_key,
|
61
|
+
@consumer_secret,
|
62
|
+
{ :site => 'http://api.fitbit.com', :request_endpoint => @proxy }
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
def access_token
|
67
|
+
@access_token ||= OAuth::AccessToken.new(consumer, @token, @secret)
|
68
|
+
end
|
69
|
+
|
70
|
+
def get(path, headers={})
|
71
|
+
headers.merge!("User-Agent" => "fitbit gem v#{Fitbit::VERSION}", "Accept-Language" => @api_unit_system)
|
72
|
+
oauth_response = access_token.get("/#{@api_version}#{path}", headers)
|
73
|
+
JSON.parse(oauth_response.body)
|
74
|
+
end
|
75
|
+
|
76
|
+
def post(path, body='', headers={})
|
77
|
+
headers.merge!("User-Agent" => "fitbit gem v#{Fitbit::VERSION}", "Accept-Language" => @api_unit_system)
|
78
|
+
oauth_response = access_token.post("/#{@api_version}#{path}", body, headers)
|
79
|
+
JSON.parse(oauth_response.body)
|
80
|
+
end
|
81
|
+
|
82
|
+
def delete(path, headers={})
|
83
|
+
headers.merge!("User-Agent" => "fitbit gem v#{Fitbit::VERSION}", "Accept-Language" => @api_unit_system)
|
84
|
+
oauth_response = access_token.delete("/#{@api_version}#{path}", headers)
|
85
|
+
JSON.parse(oauth_response.body)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Fitbit
|
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/fitbit/foods.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
module Fitbit
|
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 Fitbit
|
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 Fitbit::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,53 @@
|
|
1
|
+
module Fitbit
|
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 Fitbit::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/fitbit/units.rb
ADDED
data/lib/fitbit/users.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Fitbit
|
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/fitbit_spec.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fitbit do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@client = Fitbit::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 == Fitbit::ApiUnitSystem.US
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should allow the unit system to be set to other types' do
|
24
|
+
@client.api_unit_system = Fitbit::ApiUnitSystem.UK
|
25
|
+
@client.api_unit_system.should == Fitbit::ApiUnitSystem.UK
|
26
|
+
@client.api_unit_system = Fitbit::ApiUnitSystem.METRIC
|
27
|
+
@client.api_unit_system.should == Fitbit::ApiUnitSystem.METRIC
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "data retrieval by time range" do
|
32
|
+
|
33
|
+
it 'should format the correct URI fragment based on a base date and end date' do
|
34
|
+
frag = @client.construct_date_range_fragment({:base_date => '2011-03-07', :end_date => '2011-03-14'})
|
35
|
+
frag.should == 'date/2011-03-07/2011-03-14'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should format the correct URI fragment based on a base date and period' do
|
39
|
+
frag = @client.construct_date_range_fragment({:base_date => '2011-03-07', :period => '7d'})
|
40
|
+
frag.should == 'date/2011-03-07/7d'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should raise an error unless there is a base date AND either a period or an end date' do
|
44
|
+
lambda {
|
45
|
+
@client.construct_date_range_fragment({:base_date => '2011-03-07'})
|
46
|
+
}.should raise_error(Fitbit::InvalidTimeRange)
|
47
|
+
|
48
|
+
lambda {
|
49
|
+
@client.construct_date_range_fragment({:period => '1y'})
|
50
|
+
}.should raise_error(Fitbit::InvalidTimeRange)
|
51
|
+
|
52
|
+
lambda {
|
53
|
+
@client.construct_date_range_fragment({:end_date => '2011-03-07', :period => '7d'})
|
54
|
+
}.should raise_error(Fitbit::InvalidTimeRange)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "format_date" do
|
60
|
+
it 'should accept DateTime objects' do
|
61
|
+
date = DateTime.strptime('2011-03-19','%Y-%m-%d')
|
62
|
+
@client.format_date(date).should == '2011-03-19'
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should accept strings in YYYY-MM-DD format' do
|
66
|
+
@client.format_date('2011-03-19').should == '2011-03-19'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fitbit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Zachery Moneypenny
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-03-24 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
|
+
- fitbit-gem@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
|
+
- fitbit.gemspec
|
55
|
+
- lib/fitbit.rb
|
56
|
+
- lib/fitbit/activities.rb
|
57
|
+
- lib/fitbit/client.rb
|
58
|
+
- lib/fitbit/devices.rb
|
59
|
+
- lib/fitbit/errors.rb
|
60
|
+
- lib/fitbit/foods.rb
|
61
|
+
- lib/fitbit/helpers.rb
|
62
|
+
- lib/fitbit/time_range.rb
|
63
|
+
- lib/fitbit/units.rb
|
64
|
+
- lib/fitbit/users.rb
|
65
|
+
- lib/fitbit/weight.rb
|
66
|
+
- spec/fitbit_spec.rb
|
67
|
+
- spec/spec_helper.rb
|
68
|
+
has_rdoc: true
|
69
|
+
homepage: http://github.com/whazzmaster/fitbit
|
70
|
+
licenses: []
|
71
|
+
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: "0"
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: "0"
|
89
|
+
requirements: []
|
90
|
+
|
91
|
+
rubyforge_project: fitbit
|
92
|
+
rubygems_version: 1.5.2
|
93
|
+
signing_key:
|
94
|
+
specification_version: 3
|
95
|
+
summary: OAuth client library to the data on Fitbit.com
|
96
|
+
test_files:
|
97
|
+
- spec/fitbit_spec.rb
|
98
|
+
- spec/spec_helper.rb
|