run_keeper 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +21 -0
- data/README.md +83 -0
- data/Rakefile +24 -1
- data/lib/run_keeper.rb +8 -4
- data/lib/run_keeper/activity.rb +6 -10
- data/lib/run_keeper/activity_request.rb +48 -0
- data/lib/run_keeper/base.rb +3 -42
- data/lib/run_keeper/mock_requests.rb +61 -0
- data/lib/run_keeper/profile.rb +5 -6
- data/lib/run_keeper/request.rb +62 -0
- data/lib/run_keeper/user.rb +9 -6
- data/lib/run_keeper/version.rb +1 -1
- data/run_keeper.gemspec +2 -1
- data/test/activity_request_test.rb +19 -0
- data/test/activity_test.rb +19 -0
- data/test/base_test.rb +7 -0
- data/test/helper.rb +7 -0
- data/test/profile_test.rb +21 -0
- data/test/request_test.rb +108 -0
- data/test/user_test.rb +13 -0
- metadata +45 -10
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2011 Tate Johnson <tate@tatey.com>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
CHANGED
@@ -0,0 +1,83 @@
|
|
1
|
+
# RunKeeper Client API
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
RunKeeper is available from rubygems.org.
|
6
|
+
|
7
|
+
``` sh
|
8
|
+
$ gem install run_keeper
|
9
|
+
```
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
``` ruby
|
14
|
+
require 'run_keeper'
|
15
|
+
|
16
|
+
runkeeper = RunKeeper.new 'client_id', 'client_secret'
|
17
|
+
runkeeper.authorize_url 'http://your.com/callback/url'
|
18
|
+
# => 'https://runkeeper.com/apps/authorize?response_type=code&client_id=client_id&redirect_uri=http%3A%2F%2Fyour.com%2Fcallback%2Furl'
|
19
|
+
|
20
|
+
runkeeper.token = runkeeper.get_token 'authorization_code_value', 'http://your.com/callback/url'
|
21
|
+
|
22
|
+
# return the users profile
|
23
|
+
runkeeper.profile
|
24
|
+
# => RunKeeper::Profile
|
25
|
+
```
|
26
|
+
|
27
|
+
### Fitness Activities
|
28
|
+
|
29
|
+
Fitness Activities (http://developer.runkeeper.com/healthgraph/fitness-activities) are a list of past activities completed by a user. Calling `fitness_activities` without any options will get all activities since the user signed up to RunKeeper.
|
30
|
+
|
31
|
+
``` ruby
|
32
|
+
runkeeper.fitness_activities
|
33
|
+
# => [RunKeeper::Activity, ...]
|
34
|
+
```
|
35
|
+
|
36
|
+
Fitness Activities takes a series of arguments in the form of an options hash. Supported arguments: `start`, `finish` and `limit`:
|
37
|
+
|
38
|
+
``` ruby
|
39
|
+
# Get all activities starting from the 1st Jan, 2011
|
40
|
+
runkeeper.fitness_activities(:start => '2011-01-01')
|
41
|
+
|
42
|
+
# Get all activities between 1st Jan, 2011 and 1st Feb, 2011
|
43
|
+
runkeeper.fitness_activities(:start => '2011-01-01', :finish => '2011-02-01')
|
44
|
+
|
45
|
+
# Get the last 10 activities
|
46
|
+
runkeeper.fitness_activities(:limit => 10)
|
47
|
+
```
|
48
|
+
|
49
|
+
## Dates
|
50
|
+
|
51
|
+
RunKeeper doesn't store users time zones, they rely on the device sending the information for the correct time. Because of this I have decided to format all dates as UTC.
|
52
|
+
|
53
|
+
## Using RunKeeper mock requests in your App
|
54
|
+
|
55
|
+
While developing RunKeeper a few mocked responses were used and have been made available for your test suite.
|
56
|
+
|
57
|
+
``` ruby
|
58
|
+
# helper.rb
|
59
|
+
require 'run_keeper/mock_requests'
|
60
|
+
include MockRequests
|
61
|
+
```
|
62
|
+
|
63
|
+
## Errors
|
64
|
+
|
65
|
+
All errors are a wrapper around the `OAuth2::Error` class. Errors will be raised on all requests that do not return a 200 or 304 response code.
|
66
|
+
|
67
|
+
## Final Notes
|
68
|
+
|
69
|
+
Do not be afraid to open up the source code and peek inside. The library is built on top of the wonderful [OAuth2](https://github.com/intridea/oauth2/) gem and should be fairly easy to debug.
|
70
|
+
|
71
|
+
## Contributing
|
72
|
+
|
73
|
+
Patches welcome!
|
74
|
+
|
75
|
+
1. Fork the repository
|
76
|
+
2. Create a topic branch
|
77
|
+
3. Write tests and make changes
|
78
|
+
4. Make sure the tests pass by running `rake`
|
79
|
+
5. Push and send a pull request on GitHub
|
80
|
+
|
81
|
+
## Copyright
|
82
|
+
|
83
|
+
Copyright © 2011 Tim Cooper. RunKeeper is released under the MIT license. See LICENSE for details.
|
data/Rakefile
CHANGED
@@ -1 +1,24 @@
|
|
1
|
-
|
1
|
+
begin
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler'
|
4
|
+
rescue LoadError
|
5
|
+
raise 'Could not load the bundler gem. Install it with `gem install bundler`.'
|
6
|
+
end
|
7
|
+
|
8
|
+
begin
|
9
|
+
Bundler.setup
|
10
|
+
rescue Bundler::GemNotFound
|
11
|
+
raise RuntimeError, "Bundler couldn't find some gems." +
|
12
|
+
"Did you run `bundle install`?"
|
13
|
+
end
|
14
|
+
|
15
|
+
Bundler::GemHelper.install_tasks
|
16
|
+
|
17
|
+
require 'rake/testtask'
|
18
|
+
|
19
|
+
Rake::TestTask.new(:test) do |test|
|
20
|
+
test.libs << 'test'
|
21
|
+
test.pattern = 'test/**/*_test.rb'
|
22
|
+
end
|
23
|
+
|
24
|
+
task :default => :test
|
data/lib/run_keeper.rb
CHANGED
@@ -1,13 +1,17 @@
|
|
1
|
-
require "
|
1
|
+
require "date"
|
2
2
|
require "oauth2"
|
3
|
-
require "run_keeper/activity"
|
4
3
|
require "run_keeper/base"
|
4
|
+
require "run_keeper/activity"
|
5
|
+
require "run_keeper/activity_request"
|
5
6
|
require "run_keeper/profile"
|
7
|
+
require "run_keeper/request"
|
6
8
|
require "run_keeper/user"
|
7
9
|
require "run_keeper/version"
|
8
10
|
|
9
11
|
module RunKeeper
|
10
|
-
def self.new client_id, client_secret
|
11
|
-
|
12
|
+
def self.new client_id, client_secret, token = nil
|
13
|
+
Request.new client_id, client_secret, token
|
12
14
|
end
|
15
|
+
|
16
|
+
class Error < OAuth2::Error; end
|
13
17
|
end
|
data/lib/run_keeper/activity.rb
CHANGED
@@ -1,20 +1,16 @@
|
|
1
1
|
module RunKeeper
|
2
|
-
class Activity
|
3
|
-
attr_accessor :type, :total_distance, :duration, :uri
|
2
|
+
class Activity < Base
|
3
|
+
attr_accessor :type, :total_distance, :duration, :uri, :id
|
4
4
|
attr_reader :start_time
|
5
5
|
|
6
6
|
def initialize attributes = {}
|
7
|
-
|
8
|
-
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def id
|
13
|
-
uri.split('/').last.to_i
|
7
|
+
super
|
8
|
+
self.id = uri.split('/').last.to_i if uri
|
14
9
|
end
|
15
10
|
|
16
11
|
def start_time= value
|
17
|
-
|
12
|
+
datetime = DateTime.parse value
|
13
|
+
@start_time = Time.utc datetime.year, datetime.month, datetime.day, datetime.hour, datetime.min, datetime.sec
|
18
14
|
end
|
19
15
|
end
|
20
16
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module RunKeeper
|
2
|
+
class ActivityRequest
|
3
|
+
def initialize runkeeper, options
|
4
|
+
@runkeeper, @options = runkeeper, parse_options(options)
|
5
|
+
end
|
6
|
+
|
7
|
+
def get_activities
|
8
|
+
@options[:limit] ? limit_results(request) : request
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def limit_results activities
|
13
|
+
activities[0..@options[:limit] - 1]
|
14
|
+
end
|
15
|
+
|
16
|
+
def request activities = nil
|
17
|
+
response = @runkeeper.request('fitness_activities', @options[:params])
|
18
|
+
activities = response.parsed['items'].map { |activity| Activity.new(activity) }
|
19
|
+
|
20
|
+
if @options[:start]
|
21
|
+
activities -= activities.reject { |activity| (activity.start_time > @options[:start]) && (activity.start_time < @options[:finish]) }
|
22
|
+
end
|
23
|
+
|
24
|
+
if response.parsed['next']
|
25
|
+
if @options[:limit]
|
26
|
+
if activities.size < @options[:limit]
|
27
|
+
@options[:params].update(:page => response.parsed['next'].split('=').last)
|
28
|
+
activities + request(activities)
|
29
|
+
else
|
30
|
+
activities
|
31
|
+
end
|
32
|
+
else
|
33
|
+
@options[:params].update(:page => response.parsed['next'].split('=').last)
|
34
|
+
activities + request(activities)
|
35
|
+
end
|
36
|
+
else
|
37
|
+
activities
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse_options options
|
42
|
+
options[:params] = {}
|
43
|
+
options[:start] = Time.utc(*options[:start].split('-')) if options[:start]
|
44
|
+
options[:finish] = options[:start] && options[:finish] ? Time.utc(*options[:finish].split('-'), 23, 59, 59) : Time.now.utc
|
45
|
+
options
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/run_keeper/base.rb
CHANGED
@@ -1,48 +1,9 @@
|
|
1
1
|
module RunKeeper
|
2
2
|
class Base
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
'user' => 'application/vnd.com.runkeeper.User+json'
|
7
|
-
}
|
8
|
-
|
9
|
-
attr_reader :token
|
10
|
-
|
11
|
-
def initialize client_id, client_secret, token
|
12
|
-
@client_id, @client_secret, @token = client_id, client_secret, token
|
13
|
-
user
|
14
|
-
end
|
15
|
-
|
16
|
-
def request endpoint
|
17
|
-
response = access_token.get user.send(endpoint), :headers => {'Accept' => HEADERS[endpoint]}, :parse => :json
|
18
|
-
end
|
19
|
-
|
20
|
-
def fitness_activities
|
21
|
-
response = request 'fitness_activities'
|
22
|
-
response.parsed['items'].inject([]) do |activities, activity|
|
23
|
-
activities << RunKeeper::Activity.new(activity)
|
3
|
+
def initialize attributes = {}
|
4
|
+
attributes.each do |attribute, value|
|
5
|
+
send :"#{attribute}=", value if respond_to? :"#{attribute}="
|
24
6
|
end
|
25
7
|
end
|
26
|
-
|
27
|
-
def profile
|
28
|
-
response = request 'profile'
|
29
|
-
RunKeeper::Profile.new response.parsed.merge(:userid => user.userid)
|
30
|
-
end
|
31
|
-
|
32
|
-
def user
|
33
|
-
@user ||= begin
|
34
|
-
response = access_token.get '/user', :headers => {'Accept' => HEADERS['user']}, :parse => :json
|
35
|
-
User.new response.parsed
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
def access_token
|
41
|
-
OAuth2::AccessToken.new client, token
|
42
|
-
end
|
43
|
-
|
44
|
-
def client
|
45
|
-
OAuth2::Client.new @client_id, @client_secret, :site => 'https://api.runkeeper.com', :authorize_url => '/apps/authorize', :token_url => '/apps/token', :raise_errors => false
|
46
|
-
end
|
47
8
|
end
|
48
9
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module MockRequests
|
2
|
+
def stub_successful_runkeeper_fitness_activities_request
|
3
|
+
stub_successful_runkeeper_user_request
|
4
|
+
stub_successful_runkeeper_fitness_activities_page_2_request
|
5
|
+
stub_runkeeper_fitness_activities_request.to_return :status => 200, :body => {"size" => "4", "items" => [{"type" => "Running", "start_time" => "Thu, 25 Aug 2011 17:41:28", "total_distance" => "5492.22273600001", "duration" => "1743.946", "uri" => "/activities/39"}, {"type" => "Running", "start_time" => "Thu, 1 Sep 2011 17:41:28", "total_distance" => "5492.22273600001", "duration" => "1743.946", "uri" => "/activities/40"}, {"type" => "Running", "start_time" => "Fri, 2 Sep 2011 17:41:28", "total_distance" => "5492.22273600001", "duration" => "2090.123", "uri" => "/activities/41"}, {"type" => "Running", "start_time" => "Wed, 7 Sep 2011 17:41:28", "total_distance" => "5492.22273600001", "duration" => "1743.946", "uri" => "/activities/42"}], "next" => "/fitnessActivities?page=1"}, :headers => {"content-type" => "application/vnd.com.runkeeper.FitnessActivityFeed+json;charset=ISO-8859-1"}
|
6
|
+
end
|
7
|
+
|
8
|
+
def stub_successful_runkeeper_profile_request
|
9
|
+
stub_successful_runkeeper_user_request
|
10
|
+
stub_runkeeper_profile_request.to_return :status => 200, :body => {"name" => "John Doe", "location" => "Anytown, USA", "athlete_type" => "Runner", "goal" => "To get off the couch", "gender" => "M", "birthday" => "Sat, Jan 1 2011 00:00:00", "elite" => "true", "profile" => "http://www.runkeeper.com/user/JohnDoe", "small_picture" => "http://www.runkeeper.com/user/JohnDoe/small.jpg", "normal_picture" => "http://www.runkeeper.com/user/JohnDoe/normal.jpg", "medium_picture" => "http://www.runkeeper.com/user/JohnDoe/medium.jpg", "large_picture" => "http://www.runkeeper.com/user/JohnDoe/large.jpg"}, :headers => {"content-type" => "application/vnd.com.runkeeper.profile+json;charset=ISO-8859-1"}
|
11
|
+
end
|
12
|
+
|
13
|
+
def stub_successful_runkeeper_token_request
|
14
|
+
stub_runkeeper_token_request.to_return :status => 200, :body => {'access_token' => 'my_token', 'token_type' => 'Bearer'}, :headers => {"content-type" => "application/json;charset=ISO-8859-1"}
|
15
|
+
end
|
16
|
+
|
17
|
+
def stub_successful_runkeeper_user_request
|
18
|
+
stub_runkeeper_user_request.to_return :status => 200, :body => {"userID" => /\d+/, "profile" => "/profile", "settings" => "/settings", "fitness_activities" => "/fitnessActivities", "background_activities" => "/backgroundActivities", "sleep" => "/sleep", "nutrition" => "/nutrition", "weight" => "/weight", "general_measurements" => "/generalMeasurements", "diabetes" => "/diabetes", "records" => "/records", "team" => "/team", "strength_training_activities" => "/strengthTrainingActivities"}, :headers => {'content-type' => 'application/vnd.com.runkeeper.user+json;charset=ISO-8859-1'}
|
19
|
+
end
|
20
|
+
|
21
|
+
def stub_unsuccessful_runkeeper_profile_request
|
22
|
+
stub_successful_runkeeper_user_request
|
23
|
+
stub_runkeeper_profile_request.to_return :status => 500, :body => {}, :headers => {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def stub_unsuccessful_runkeeper_token_request
|
27
|
+
stub_runkeeper_token_request.to_return :status => 500, :body => {}, :headers => {}
|
28
|
+
end
|
29
|
+
|
30
|
+
def stub_unsuccessful_runkeeper_user_request
|
31
|
+
stub_runkeeper_user_request.to_return :status => 500, :body => {}, :headers => {}
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def stub_successful_runkeeper_fitness_activities_page_2_request
|
36
|
+
stub_request(:get, "https://api.runkeeper.com/fitnessActivities?page=1").
|
37
|
+
with(:headers => {'Accept' => 'application/vnd.com.runkeeper.FitnessActivityFeed+json', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => /Bearer \w+/, 'User-Agent'=>'Ruby'}).
|
38
|
+
to_return(:status => 200, :body => {"size" => "2", "items" => [{"type" => "Running", "start_time" => "Tue, 23 Aug 2011 17:41:28", "total_distance" => "5492.22273600001", "duration" => "1743.946", "uri" => "/activities/38"}, {"type" => "Running", "start_time" => "Sun, 21 Aug 2011 17:41:28", "total_distance" => "5492.22273600001", "duration" => "1743.946", "uri" => "/activities/37"}]}, :headers => {"content-type" => "application/vnd.com.runkeeper.FitnessActivityFeed+json;charset=ISO-8859-1"})
|
39
|
+
end
|
40
|
+
|
41
|
+
def stub_runkeeper_fitness_activities_request
|
42
|
+
stub_request(:get, "https://api.runkeeper.com/fitnessActivities").
|
43
|
+
with :headers => {'Accept'=>'application/vnd.com.runkeeper.FitnessActivityFeed+json', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => /Bearer \w+/, 'User-Agent'=>'Ruby'}
|
44
|
+
end
|
45
|
+
|
46
|
+
def stub_runkeeper_profile_request
|
47
|
+
stub_request(:get, "https://api.runkeeper.com/profile").
|
48
|
+
with :headers => {'Accept'=>'application/vnd.com.runkeeper.Profile+json', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => /Bearer \w+/, 'User-Agent'=>'Ruby'}
|
49
|
+
end
|
50
|
+
|
51
|
+
def stub_runkeeper_token_request
|
52
|
+
stub_request(:post, "https://runkeeper.com/apps/token").
|
53
|
+
with :body => {"grant_type" => "authorization_code", "code" => /\w+/, "client_id" => /\w+/, "client_secret" => /\w+/, "redirect_uri" => /\w+/},
|
54
|
+
:headers => {'Accept' => '*/*', 'Content-Type' => 'application/x-www-form-urlencoded', 'User-Agent' => 'Ruby'}
|
55
|
+
end
|
56
|
+
|
57
|
+
def stub_runkeeper_user_request
|
58
|
+
stub_request(:get, "https://api.runkeeper.com/user").
|
59
|
+
with :headers => {'Accept'=>'application/vnd.com.runkeeper.User+json', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => /Bearer \w+/, 'User-Agent'=>'Ruby'}
|
60
|
+
end
|
61
|
+
end
|
data/lib/run_keeper/profile.rb
CHANGED
@@ -1,21 +1,20 @@
|
|
1
1
|
module RunKeeper
|
2
|
-
class Profile
|
2
|
+
class Profile < Base
|
3
3
|
attr_accessor :name, :location, :athlete_type, :goal, :gender, :birthday, :elite, :profile, :small_picture, :normal_picture, :medium_picture, :large_picture, :userid
|
4
4
|
attr_reader :username
|
5
5
|
|
6
6
|
def initialize attributes = {}
|
7
|
-
attributes
|
8
|
-
send :"#{attribute}=", value
|
9
|
-
end
|
7
|
+
super attributes
|
10
8
|
self.username = profile
|
11
9
|
end
|
12
10
|
|
13
11
|
def birthday= value
|
14
|
-
|
12
|
+
datetime = DateTime.parse value
|
13
|
+
@birthday = Time.utc datetime.year, datetime.month, datetime.day, datetime.hour, datetime.min, datetime.sec
|
15
14
|
end
|
16
15
|
|
17
16
|
def username= profile
|
18
|
-
@username = profile.split('/').last
|
17
|
+
@username = profile.split('/').last if profile
|
19
18
|
end
|
20
19
|
end
|
21
20
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module RunKeeper
|
2
|
+
class Request
|
3
|
+
HEADERS = {
|
4
|
+
'fitness_activities' => 'application/vnd.com.runkeeper.FitnessActivityFeed+json',
|
5
|
+
'profile' => 'application/vnd.com.runkeeper.Profile+json',
|
6
|
+
'user' => 'application/vnd.com.runkeeper.User+json'
|
7
|
+
}
|
8
|
+
|
9
|
+
def initialize client_id, client_secret, token
|
10
|
+
@client_id, @client_secret, @token = client_id, client_secret, token
|
11
|
+
end
|
12
|
+
|
13
|
+
def authorize_url redirect_uri
|
14
|
+
client('https://runkeeper.com').auth_code.authorize_url :redirect_uri => redirect_uri
|
15
|
+
end
|
16
|
+
|
17
|
+
def fitness_activities options = {}
|
18
|
+
ActivityRequest.new(self, options).get_activities
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_token code, redirect_uri
|
22
|
+
client('https://runkeeper.com').auth_code.get_token(code, :redirect_uri => redirect_uri).token
|
23
|
+
rescue OAuth2::Error => e
|
24
|
+
raise Error.new(e.response)
|
25
|
+
end
|
26
|
+
|
27
|
+
def profile
|
28
|
+
Profile.new request('profile').parsed.merge(:userid => @user.userid)
|
29
|
+
end
|
30
|
+
|
31
|
+
def request endpoint, params = {}
|
32
|
+
response = access_token.get(user.send(endpoint), :headers => {'Accept' => HEADERS[endpoint]}, :parse => :json) do |request|
|
33
|
+
request.params = params
|
34
|
+
end
|
35
|
+
parse_response response
|
36
|
+
end
|
37
|
+
|
38
|
+
def user
|
39
|
+
@user ||= begin
|
40
|
+
response = access_token.get('/user', :headers => {'Accept' => HEADERS['user']}, :parse => :json)
|
41
|
+
User.new parse_response(response).parsed
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def access_token
|
47
|
+
OAuth2::AccessToken.new client, @token
|
48
|
+
end
|
49
|
+
|
50
|
+
def client site = 'https://api.runkeeper.com'
|
51
|
+
OAuth2::Client.new @client_id, @client_secret, :site => site, :authorize_url => '/apps/authorize', :token_url => '/apps/token', :raise_errors => false
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_response response
|
55
|
+
if [200, 304].include? response.status
|
56
|
+
response
|
57
|
+
else
|
58
|
+
raise Error.new(response)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/run_keeper/user.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
module RunKeeper
|
2
|
-
class User
|
3
|
-
attr_accessor :
|
2
|
+
class User < Base
|
3
|
+
attr_accessor :profile, :settings, :fitness_activities, :background_activities, :sleep, :nutrition, :weight, :general_measurements, :diabetes, :records, :team, :strength_training_activities
|
4
|
+
attr_reader :userid
|
4
5
|
|
5
6
|
def initialize attributes = {}
|
6
|
-
attributes
|
7
|
-
|
8
|
-
|
7
|
+
super attributes
|
8
|
+
self.userid = attributes['userID'] if attributes['userID']
|
9
|
+
end
|
10
|
+
|
11
|
+
def userid= value
|
12
|
+
@userid = value
|
9
13
|
end
|
10
|
-
alias :userid :userID
|
11
14
|
end
|
12
15
|
end
|
data/lib/run_keeper/version.rb
CHANGED
data/run_keeper.gemspec
CHANGED
@@ -16,6 +16,7 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
17
|
s.require_paths = ["lib"]
|
18
18
|
|
19
|
-
s.
|
19
|
+
s.add_development_dependency 'rake', '~> 0.9.2'
|
20
|
+
s.add_development_dependency 'webmock', '~> 1.7.6'
|
20
21
|
s.add_dependency 'oauth2', '~> 0.5.0'
|
21
22
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class ActivityRequestTest < MiniTest::Unit::TestCase
|
4
|
+
def test_limit_request_returns_subset_of_results
|
5
|
+
assert_equal 1, ActivityRequest.new(Object.new, :limit => 1).send(:limit_results, [1, 2, 3]).size
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_parse_options_has_default_keys
|
9
|
+
assert_equal [:params, :finish].sort, ActivityRequest.new(Object.new, {}).send(:parse_options, {}).keys.sort
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_parse_options_includes_limit
|
13
|
+
assert_equal [:limit, :params, :finish].sort, ActivityRequest.new(Object.new, {:limit => 1}).send(:parse_options, {:limit => 1}).keys.sort
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_parse_options_includes_start
|
17
|
+
assert_equal [:params, :start, :finish].sort, ActivityRequest.new(Object.new, {:start => '2011-01-01'}).send(:parse_options, {:start => '2011-01-01'}).keys.sort
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class ActivityTest < MiniTest::Unit::TestCase
|
4
|
+
def test_id_is_nil_when_no_uri
|
5
|
+
assert_nil Activity.new.id
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_id_returns_an_integer_from_uri
|
9
|
+
assert_instance_of Fixnum, Activity.new('uri' => '/profile/12345').id
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_id_returns_id_from_uri
|
13
|
+
assert_equal 12345, Activity.new('uri' => '/profile/12345').id
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_start_time_is_UTC
|
17
|
+
assert Activity.new('start_time' => 'Thu, 1 Sep 2011 17:41:28').start_time.utc?
|
18
|
+
end
|
19
|
+
end
|
data/test/base_test.rb
ADDED
data/test/helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class ProfileTest < MiniTest::Unit::TestCase
|
4
|
+
def test_initailization_sets_username_if_profile
|
5
|
+
profile = Profile.new('profile' => 'http://www.runkeeper.com/user/JohnDoe')
|
6
|
+
assert_includes profile.instance_variables, :@username
|
7
|
+
assert_equal 'JohnDoe', profile.username
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_initailization_sets_username_to_nil_if_no_profile
|
11
|
+
assert_nil Profile.new.username
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_birthday_is_UTC
|
15
|
+
assert Profile.new('birthday' => 'Sat, Jan 1 2011 00:00:00').birthday.utc?
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_birthday_is_nil_if_not_provided
|
19
|
+
assert_nil Profile.new.birthday
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class RequestTest < MiniTest::Unit::TestCase
|
4
|
+
def test_initialization_sets_client_id_and_client_secret
|
5
|
+
assert_includes runkeeper.instance_variables, :@client_id
|
6
|
+
assert_includes runkeeper.instance_variables, :@client_secret
|
7
|
+
assert_includes runkeeper.instance_variables, :@token
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_authorize_url_creates_a_valid_runkeeper_authorize_url
|
11
|
+
assert_equal 'https://runkeeper.com/apps/authorize?response_type=code&client_id=client_id&redirect_uri=http%3A%2F%2Fgoogle.com', runkeeper(nil).authorize_url('http://google.com')
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_get_token_returns_a_token
|
15
|
+
stub_successful_runkeeper_token_request
|
16
|
+
assert_equal 'my_token', runkeeper(nil).get_token('code', 'http://google.com')
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_get_token_raises_an_error_when_it_fails
|
20
|
+
stub_unsuccessful_runkeeper_token_request
|
21
|
+
assert_raises RunKeeper::Error do
|
22
|
+
runkeeper(nil).get_token('code', 'http://google.com')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_fitness_activities_with_a_valid_token_returns_an_array
|
27
|
+
stub_successful_runkeeper_fitness_activities_request
|
28
|
+
assert_instance_of Array, runkeeper.fitness_activities(:limit => 1)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_fitness_activities_with_a_valid_token_returns_runkeeper_activities
|
32
|
+
stub_successful_runkeeper_fitness_activities_request
|
33
|
+
assert_instance_of RunKeeper::Activity, runkeeper.fitness_activities(:limit => 1).first
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_fitness_activities_with_limit
|
37
|
+
stub_successful_runkeeper_fitness_activities_request
|
38
|
+
assert_equal 1, runkeeper.fitness_activities(:limit => 1).size
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_fitness_activities_without_arguments
|
42
|
+
stub_successful_runkeeper_fitness_activities_request
|
43
|
+
assert_equal 6, runkeeper.fitness_activities.size
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_fitness_activities_by_start_date_range
|
47
|
+
stub_successful_runkeeper_fitness_activities_request
|
48
|
+
assert_equal 5, runkeeper.fitness_activities(:start => '2011-08-22').size
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_fitness_activities_by_date_range
|
52
|
+
stub_successful_runkeeper_fitness_activities_request
|
53
|
+
assert_equal 4, runkeeper.fitness_activities(:start => '2011-08-22', :finish => '2011-09-02').size
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_fitness_activities_by_date_range_and_limit
|
57
|
+
stub_successful_runkeeper_fitness_activities_request
|
58
|
+
assert_equal 2, runkeeper.fitness_activities(:start => '2011-08-22', :finish => '2011-09-02', :limit => 2).size
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_fitness_activities_by_date_range_and_limit_where_limit_is_greater_than_returned_results
|
62
|
+
stub_successful_runkeeper_fitness_activities_request
|
63
|
+
assert_equal 6, runkeeper.fitness_activities(:start => '2011-08-01', :limit => 7).size
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_user_returns_an_instance_of_user
|
67
|
+
stub_successful_runkeeper_user_request
|
68
|
+
assert_instance_of RunKeeper::User, runkeeper.user
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_profile_returns_and_instance_of_profile
|
72
|
+
stub_successful_runkeeper_profile_request
|
73
|
+
assert_instance_of RunKeeper::Profile, runkeeper.profile
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_parse_response_raises_exception_unless_response_status_is_200_or_304
|
77
|
+
assert_raises RunKeeper::Error do
|
78
|
+
runkeeper.send :parse_response, mock_response(400)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_parse_response_returns_response_when_status_200
|
83
|
+
response = mock_response 200
|
84
|
+
assert_equal response, runkeeper.send(:parse_response, response)
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_parse_response_returns_response_when_status_304
|
88
|
+
response = mock_response 304
|
89
|
+
assert_equal response, runkeeper.send(:parse_response, response)
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
def mock_response status
|
94
|
+
Object.new.tap do |response|
|
95
|
+
response.extend Module.new {
|
96
|
+
define_method :status do
|
97
|
+
status
|
98
|
+
end
|
99
|
+
def error=(value); end
|
100
|
+
def parsed; end
|
101
|
+
}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def runkeeper token = 'valid_token'
|
106
|
+
Request.new 'client_id', 'client_secret', token
|
107
|
+
end
|
108
|
+
end
|
data/test/user_test.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class UserTest < MiniTest::Unit::TestCase
|
4
|
+
def test_initailization_sets_userid_if_userID_attribute_provided
|
5
|
+
user = User.new('userID' => '123')
|
6
|
+
assert_includes user.instance_variables, :@userid
|
7
|
+
assert_equal '123', user.userid
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_initailization_does_not_set_userid_if_userID_not_provided
|
11
|
+
refute_includes User.new.instance_variables, :@userid
|
12
|
+
end
|
13
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: run_keeper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,23 +9,34 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-09-
|
12
|
+
date: 2011-09-13 00:00:00.000000000 +10:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
|
-
name:
|
17
|
-
requirement: &
|
16
|
+
name: rake
|
17
|
+
requirement: &70113469141360 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ~>
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version:
|
23
|
-
type: :
|
22
|
+
version: 0.9.2
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *70113469141360
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: webmock
|
28
|
+
requirement: &70113469140860 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.7.6
|
34
|
+
type: :development
|
24
35
|
prerelease: false
|
25
|
-
version_requirements: *
|
36
|
+
version_requirements: *70113469140860
|
26
37
|
- !ruby/object:Gem::Dependency
|
27
38
|
name: oauth2
|
28
|
-
requirement: &
|
39
|
+
requirement: &70113469140400 !ruby/object:Gem::Requirement
|
29
40
|
none: false
|
30
41
|
requirements:
|
31
42
|
- - ~>
|
@@ -33,7 +44,7 @@ dependencies:
|
|
33
44
|
version: 0.5.0
|
34
45
|
type: :runtime
|
35
46
|
prerelease: false
|
36
|
-
version_requirements: *
|
47
|
+
version_requirements: *70113469140400
|
37
48
|
description: Run Keeper API client
|
38
49
|
email:
|
39
50
|
- coop@latrobest.com
|
@@ -43,15 +54,26 @@ extra_rdoc_files: []
|
|
43
54
|
files:
|
44
55
|
- .gitignore
|
45
56
|
- Gemfile
|
57
|
+
- LICENSE
|
46
58
|
- README.md
|
47
59
|
- Rakefile
|
48
60
|
- lib/run_keeper.rb
|
49
61
|
- lib/run_keeper/activity.rb
|
62
|
+
- lib/run_keeper/activity_request.rb
|
50
63
|
- lib/run_keeper/base.rb
|
64
|
+
- lib/run_keeper/mock_requests.rb
|
51
65
|
- lib/run_keeper/profile.rb
|
66
|
+
- lib/run_keeper/request.rb
|
52
67
|
- lib/run_keeper/user.rb
|
53
68
|
- lib/run_keeper/version.rb
|
54
69
|
- run_keeper.gemspec
|
70
|
+
- test/activity_request_test.rb
|
71
|
+
- test/activity_test.rb
|
72
|
+
- test/base_test.rb
|
73
|
+
- test/helper.rb
|
74
|
+
- test/profile_test.rb
|
75
|
+
- test/request_test.rb
|
76
|
+
- test/user_test.rb
|
55
77
|
has_rdoc: true
|
56
78
|
homepage: http://github.com/coop/run_keeper
|
57
79
|
licenses: []
|
@@ -65,16 +87,29 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
65
87
|
- - ! '>='
|
66
88
|
- !ruby/object:Gem::Version
|
67
89
|
version: '0'
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
hash: -639340336627056513
|
68
93
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
94
|
none: false
|
70
95
|
requirements:
|
71
96
|
- - ! '>='
|
72
97
|
- !ruby/object:Gem::Version
|
73
98
|
version: '0'
|
99
|
+
segments:
|
100
|
+
- 0
|
101
|
+
hash: -639340336627056513
|
74
102
|
requirements: []
|
75
103
|
rubyforge_project:
|
76
104
|
rubygems_version: 1.6.2
|
77
105
|
signing_key:
|
78
106
|
specification_version: 3
|
79
107
|
summary: Run Keeper API client
|
80
|
-
test_files:
|
108
|
+
test_files:
|
109
|
+
- test/activity_request_test.rb
|
110
|
+
- test/activity_test.rb
|
111
|
+
- test/base_test.rb
|
112
|
+
- test/helper.rb
|
113
|
+
- test/profile_test.rb
|
114
|
+
- test/request_test.rb
|
115
|
+
- test/user_test.rb
|