strava 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,86 @@
1
+ module Strava
2
+ # Base class for Strava objects.
3
+ # Handles setting up the object, mainly data and a client.
4
+ #
5
+ # @abstract
6
+ class Base
7
+ attr_reader :response, :client, :id
8
+
9
+ def initialize(data, client: nil, token: nil, **opts)
10
+ raise 'missing client or access token' unless (client || token)
11
+ @client = client || Client.new(token)
12
+ if data.is_a?(Hash)
13
+ @id = data['id']
14
+ set_ivars
15
+ update(data, **opts)
16
+ else
17
+ @id = data
18
+ set_ivars
19
+ end
20
+ end
21
+
22
+ # Parse incoming data.
23
+ # Should be defined by subclasses.
24
+ #
25
+ # @abstract
26
+ def update(data, **opts)
27
+ @response = data
28
+ @resource_state = data['resource_state']
29
+ self
30
+ end
31
+
32
+ # Set up instance variables upon instantiation.
33
+ # Should be defined by subclasses.
34
+ # May not always be necessary.
35
+ #
36
+ # @abstract
37
+ # @return [void]
38
+ private def set_ivars
39
+ # this should be defined by subclasses
40
+ end
41
+
42
+ private def parse_data(existing, data, klass: nil, **opts)
43
+ existing ||= {}
44
+ case data
45
+ when [], {}
46
+ []
47
+ when Array
48
+ data.map do |hash|
49
+ current = existing[hash['id']]
50
+ if current
51
+ current.send(:update, hash, **opts)
52
+ else
53
+ current = klass.new(hash, **opts)
54
+ existing[current.id] = current
55
+ end
56
+ existing[current.id]
57
+ end
58
+ when Hash
59
+ existing[data['id']] = klass.new(data, **opts)
60
+ else
61
+ # raise
62
+ end
63
+ end
64
+
65
+ def resource_state
66
+ self.class.resource_states[@resource_state]
67
+ end
68
+
69
+ def summary?
70
+ @resource_state == 2
71
+ end
72
+
73
+ def detailed?
74
+ @resource_state == 3
75
+ end
76
+
77
+ def self.resource_states
78
+ @resource_states ||= {
79
+ 1 => 'meta',
80
+ 2 => 'summary',
81
+ 3 => 'detailed',
82
+ }
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,65 @@
1
+ require 'httparty'
2
+ module Strava
3
+ class Client
4
+ attr_reader :token
5
+ # @return [Usage] Information on API quota usage
6
+ attr_reader :usage
7
+ BASE_URL = 'https://www.strava.com/api/v3/' # can be overridden for individual requests
8
+
9
+ def initialize(token)
10
+ @token = token
11
+ end
12
+
13
+ def get(path, **params)
14
+ make_request(:get, path, **params)
15
+ end
16
+
17
+ def post(path, **params)
18
+ make_request(:post, path, **params)
19
+ end
20
+
21
+ def put(path, **params)
22
+ make_request(:put, path, **params)
23
+ end
24
+
25
+ def delete(path, **params)
26
+ make_request(:delete, path, **params)
27
+ end
28
+
29
+ def make_request(verb, path, **params)
30
+ puts (params[:host] || BASE_URL) + path
31
+ handle_params(params)
32
+ res = HTTParty.send(verb, (params.delete(:host) || BASE_URL) + path, query: params)
33
+ check_for_error(res)
34
+ res
35
+ end
36
+
37
+ def handle_params(params)
38
+ if @token
39
+ params.merge!(access_token: @token)
40
+ else
41
+ params.merge!(client_id: Strava.client_id, client_secret: Strava.secret)
42
+ end
43
+ params.reverse_each { |k, v| params.delete(k) if v.nil? }
44
+ end
45
+
46
+ def check_for_error(response)
47
+ @usage = Usage.new(response.headers['X-Ratelimit-Limit'], response.headers['X-Ratelimit-Usage'])
48
+ case response.code
49
+ when 401, 403
50
+ raise Strava::AccessError.new(response.to_h)
51
+ end
52
+ end
53
+
54
+
55
+ ## non athlete calls
56
+ def list_races(year = Time.now.year)
57
+ RunningRace.list_races(self, year)
58
+ end
59
+
60
+ def segment_explorer(bounds = '37.821362,-122.505373,37.842038,-122.465977')
61
+ Segment.explorer(self, bounds)
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,164 @@
1
+ module Strava
2
+ # Clubs represent groups of athletes on Strava. They can be public or private.
3
+ # Clubs have both summary and detailed representations.
4
+ #
5
+ # @see https://strava.github.io/api/v3/clubs/ Strava Docs - Clubs
6
+ class Club < Base
7
+
8
+ # Set up instance variables upon instantiation.
9
+ #
10
+ # @abstract
11
+ # @return [void]
12
+ private def set_ivars
13
+ @activities = {}
14
+ @group_events = {}
15
+ @announcements = []
16
+ @members = {}
17
+ @admins = []
18
+ @segment_efforts = {}
19
+ end
20
+
21
+ # Update an existing club.
22
+ # Used by other methods in the gem.
23
+ # Should not be used directly.
24
+ #
25
+ # @param data [Hash] data to update the club with
26
+ # @return [self]
27
+ def update(data, **opts)
28
+ @response = data
29
+ @id = data["id"]
30
+ @resource_state = data['resource_state']
31
+
32
+ self
33
+ end
34
+
35
+ def activities(per_page: nil, page: nil, before: nil)
36
+ if page || per_page || before
37
+ get_activities(per_page: per_page, page: page, before: before)
38
+ else
39
+ get_activities if @activities.empty?
40
+ @activities.values
41
+ end
42
+ end
43
+
44
+ def group_events(per_page: nil, page: nil, before: nil)
45
+ if page || per_page || before
46
+ get_group_events(per_page: per_page, page: page, before: before)
47
+ else
48
+ get_group_events if @group_events.empty?
49
+ @group_events.values
50
+ end
51
+ end
52
+
53
+ def announcements
54
+ get_announcements if @announcements.empty?
55
+ @announcements
56
+ end
57
+
58
+ def members(per_page: nil, page: nil)
59
+ if page || per_page
60
+ get_members(per_page: per_page, page: page)
61
+ else
62
+ get_members if @members.empty? || !@members_fetched
63
+ @members_fetched = true
64
+ @members.values
65
+ end
66
+ end
67
+
68
+ def admins(per_page: nil, page: nil)
69
+ if page || per_page
70
+ get_admins(per_page: per_page, page: page)
71
+ else
72
+ get_admins if @admins.empty?
73
+ @admins
74
+ end
75
+ end
76
+
77
+ # {"success"=>true, "active"=>false}
78
+ def join
79
+ res = client.post(path_join).to_h
80
+ end
81
+
82
+ # {"success"=>true, "active"=>true, "membership"=>"member"}
83
+ def leave
84
+ res = client.post(path_leave).to_h
85
+ end
86
+
87
+ def get_details
88
+ return self if detailed?
89
+ res = client.get(path_base).to_h
90
+ update(res)
91
+ end
92
+
93
+ private def get_activities(per_page: nil, page: nil, before: nil)
94
+ res = client.get(path_activities, per_page: per_page, page: page, before: before).to_a
95
+ parse_data(@activities, res, klass: Activity, client: @client)
96
+ end
97
+
98
+ private def get_group_events(per_page: nil, page: nil, before: nil)
99
+ res = client.get(path_group_events, per_page: per_page, page: page, before: before).to_a
100
+ parse_data(@group_events, res, klass: Activity, client: @client)
101
+ end
102
+
103
+ private def get_announcements
104
+ res = client.get(path_announcements).to_a
105
+ @announcements = parse_data({}, res, klass: ClubAnnouncement, client: @client)
106
+ end
107
+
108
+ private def get_members
109
+ res = client.get(path_members).to_a
110
+ parse_data(@members, res, klass: Athlete, client: @client)
111
+ end
112
+
113
+ private def get_admins
114
+ res = client.get(path_admins).to_a
115
+ @admins = parse_data(@members, res, klass: Athlete, client: @client)
116
+ end
117
+
118
+ private def path_base
119
+ "clubs/#{id}"
120
+ end
121
+
122
+ private def path_activities
123
+ "#{path_base}/activities"
124
+ end
125
+
126
+ private def path_group_events
127
+ "#{path_base}/group_events"
128
+ end
129
+
130
+ private def path_announcements
131
+ "#{path_base}/announcements"
132
+ end
133
+
134
+ private def path_members
135
+ "#{path_base}/members"
136
+ end
137
+
138
+ private def path_admins
139
+ "#{path_base}/admins"
140
+ end
141
+
142
+ private def path_join
143
+ "#{path_base}/join"
144
+ end
145
+
146
+ private def path_leave
147
+ "#{path_base}/leave"
148
+ end
149
+
150
+ end
151
+ end
152
+
153
+ __END__
154
+
155
+ ca = Strava::Athlete.current_athlete;
156
+ club = ca.clubs.last
157
+ club.admins
158
+ club.members
159
+ club.get_details
160
+ club.activities
161
+ club.group_events
162
+ club.announcements
163
+ club.leave
164
+ club.join
@@ -0,0 +1,28 @@
1
+ module Strava
2
+ # Class to represent Strava Club Announcement
3
+ # https://strava.github.io/api/v3/activities/
4
+ class ClubAnnouncement < Base
5
+
6
+ def update(data, **opts)
7
+ @response = data
8
+ @id = data['id']
9
+ @resource_state = data['resource_state']
10
+
11
+ @message = data['message']
12
+ @created_at = data['created_at']
13
+ @club_id = data['club_id']
14
+ @athlete = Athlete.new(data['athlete'], client: @client)
15
+ end
16
+
17
+ end
18
+ end
19
+
20
+ __END__
21
+
22
+ ca = Strava::Athlete.current_athlete;
23
+ ca.activities;
24
+ ca.activities(page: 2);
25
+ ca.activities(page: 3);
26
+ ca.activities(page: 4);
27
+ act = ca.activities.detect{|act| act.response['comment_count'] > 0 && act.response['kudos_count'] > 0 }
28
+ act.comments
@@ -0,0 +1,36 @@
1
+ module Strava
2
+ # Class to represent Strava Activity
3
+ # https://strava.github.io/api/v3/activities/
4
+ class Comment < Base
5
+
6
+ attr_reader :activity_id
7
+
8
+ def update(data, **opts)
9
+ @response = data
10
+ @id = data['id']
11
+ @resource_state = data['resource_state']
12
+
13
+ @text = data['text']
14
+ @activity_id = data['activity_id']
15
+ @athlete = Athlete.new(data['athlete'], client: @client)
16
+ end
17
+
18
+ def delete
19
+ res = client.delete(path_base).to_h
20
+ end
21
+
22
+ def path_base
23
+ "activities/#{activity_id}/comments/#{id}"
24
+ end
25
+ end
26
+ end
27
+
28
+ __END__
29
+
30
+ ca = Strava::Athlete.current_athlete;
31
+ ca.activities;
32
+ ca.activities(page: 2);
33
+ ca.activities(page: 3);
34
+ ca.activities(page: 4);
35
+ act = ca.activities.detect{|act| act.response['comment_count'] > 0 && act.response['kudos_count'] > 0 }
36
+ act.comments
@@ -0,0 +1,15 @@
1
+ {"message"=>"Authorization Error", "errors"=>[{"resource"=>"AccessToken", "field"=>"write_permission", "code"=>"missing"}]}
2
+
3
+ module Strava
4
+ class Error < StandardError
5
+ attr_accessor :response, :strava_errors
6
+ end
7
+
8
+ class AccessError < Error
9
+ def initialize(response)
10
+ message = response['message']
11
+ strava_errors = response['errors']
12
+ super(message)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,41 @@
1
+ module Strava
2
+ # Gear represents both shoes and bikes.
3
+ # These are returned as part of the athlete summary.
4
+ #
5
+ # @see https://strava.github.io/api/v3/gear/ Strava Gear API Docs
6
+ class Gear < Base
7
+
8
+ # Updates gear with passed data attributes.
9
+ #
10
+ # @param data [Hash] data hash containing gear data
11
+ # @return [self]
12
+ def update(data, **opts)
13
+ @response = data
14
+ @id = data['id']
15
+ @resource_state = data['resource_state']
16
+ self
17
+ end
18
+
19
+ # Retrieve full details for Gear object.
20
+ # Sets all data attributes on self.
21
+ #
22
+ # @return [Hash] raw API response
23
+ def get_details
24
+ return self if detailed?
25
+ res = client.get(path_base).to_h
26
+ update(res)
27
+ res
28
+ end
29
+
30
+ # URL path for Gear object.
31
+ #
32
+ # @return [String] URL path
33
+ private def path_base
34
+ "gear/#{id}"
35
+ end
36
+ end
37
+ end
38
+
39
+ __END__
40
+
41
+ ca = Strava::Athlete.current_athlete;
@@ -0,0 +1,62 @@
1
+ module Strava
2
+ # Group events for Strava Clubs
3
+ #
4
+ # @see http://strava.github.io/api/v3/club_group_events/ Strava Docs - Group Events
5
+ class GroupEvent < Base
6
+
7
+ def update(data, **opts)
8
+ @response = data
9
+ @id = data["id"]
10
+ @resource_state = data['resource_state']
11
+ end
12
+
13
+ def get_details
14
+ return self if detailed?
15
+ res = client.get(path_base).to_h
16
+ update(res)
17
+ res
18
+ end
19
+
20
+ def athletes(per_page: nil, page: nil)
21
+ if page || per_page
22
+ get_athletes(per_page: per_page, page: page)
23
+ else
24
+ get_athletes if @athletes.empty?
25
+ @athletes.values
26
+ end
27
+ end
28
+
29
+ def delete
30
+ res = client.delete(path_base).to_h
31
+ end
32
+
33
+ # {"success"=>true, "active"=>false}
34
+ def join
35
+ res = client.post(path_rsvp).to_h
36
+ end
37
+
38
+ # {"success"=>true, "active"=>true, "membership"=>"member"}
39
+ def leave
40
+ res = client.delete(path_rsvp).to_h
41
+ end
42
+
43
+ private def path_base
44
+ "group_events/#{id}"
45
+ end
46
+
47
+ private def path_rsvp
48
+ "#{path_base}/rsvps"
49
+ end
50
+
51
+ private def path_athletes
52
+ "#{path_base}/athletes"
53
+ end
54
+
55
+ end
56
+ end
57
+
58
+ __END__
59
+
60
+ ca = Strava::Athlete.current_athlete;
61
+ miz = ca.clubs.last;
62
+ miz.group_events