run_keeper 0.0.4 → 0.0.5
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.
- 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
|