strava-ruby-client 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/README.md +176 -15
  4. data/bin/strava-oauth-token.rb +47 -0
  5. data/lib/strava-ruby-client.rb +22 -1
  6. data/lib/strava/api/client.rb +150 -3
  7. data/lib/strava/api/cursor.rb +29 -0
  8. data/lib/strava/models/activity.rb +90 -4
  9. data/lib/strava/models/activity_zone.rb +13 -0
  10. data/lib/strava/models/athlete.rb +8 -0
  11. data/lib/strava/models/club.rb +26 -0
  12. data/lib/strava/models/comment.rb +12 -0
  13. data/lib/strava/models/gear.rb +11 -0
  14. data/lib/strava/models/lap.rb +26 -0
  15. data/lib/strava/models/map.rb +1 -0
  16. data/lib/strava/models/mixins/distance.rb +73 -0
  17. data/lib/strava/models/mixins/elevation.rb +46 -0
  18. data/lib/strava/models/mixins/time.rb +99 -0
  19. data/lib/strava/models/photo.rb +10 -0
  20. data/lib/strava/models/photos.rb +9 -0
  21. data/lib/strava/models/segment.rb +33 -0
  22. data/lib/strava/models/segment_effort.rb +24 -0
  23. data/lib/strava/models/similar_activities.rb +17 -0
  24. data/lib/strava/models/split.rb +29 -0
  25. data/lib/strava/models/timed_zone_range.rb +9 -0
  26. data/lib/strava/models/trend.rb +12 -0
  27. data/lib/strava/version.rb +1 -1
  28. metadata +35 -47
  29. data/.gitignore +0 -6
  30. data/.rspec +0 -2
  31. data/.rubocop.yml +0 -15
  32. data/.rubocop_todo.yml +0 -47
  33. data/.travis.yml +0 -9
  34. data/CONTRIBUTING.md +0 -125
  35. data/Dangerfile +0 -2
  36. data/Gemfile +0 -15
  37. data/RELEASING.md +0 -61
  38. data/Rakefile +0 -17
  39. data/bin/oauth-token.rb +0 -28
  40. data/spec/fixtures/strava/client_athlete.yml +0 -59
  41. data/spec/fixtures/strava/client_athlete_activities.yml +0 -121
  42. data/spec/fixtures/strava/oauth_token_authorization_code.yml +0 -53
  43. data/spec/fixtures/strava/oauth_token_invalid_client.yml +0 -50
  44. data/spec/fixtures/strava/oauth_token_invalid_code.yml +0 -50
  45. data/spec/fixtures/strava/oauth_token_refresh_token.yml +0 -52
  46. data/spec/spec_helper.rb +0 -10
  47. data/spec/strava/api/client_spec.rb +0 -36
  48. data/spec/strava/api/config_spec.rb +0 -22
  49. data/spec/strava/oauth/client_spec.rb +0 -120
  50. data/spec/strava/oauth/config_spec.rb +0 -24
  51. data/spec/strava/version_spec.rb +0 -7
  52. data/spec/strava/web/client_spec.rb +0 -9
  53. data/spec/strava/web/config_spec.rb +0 -4
  54. data/spec/support/shared/it_behaves_like_web_client.rb +0 -131
  55. data/spec/support/vcr.rb +0 -12
  56. data/strava-ruby-client.gemspec +0 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd3998f1cfc79f2b07fc5ef6b9d25f8717e411c7d6a9066d6eb9e77154ea4cd1
4
- data.tar.gz: 82adf5a179351b1d3b410c72710d5ce945bf5f303398e5c21e482b34ce294d95
3
+ metadata.gz: f8e9ff7032aba29792001ccf52b8b089cd567ad747d23ed91351e81d7c2042f7
4
+ data.tar.gz: d3585d62e08b3ed1217e9eb59ca0a8df476ea9a5b4bdec6834849ff3f26aef61
5
5
  SHA512:
6
- metadata.gz: 05fe3b3d099ca87559a99d45b33c160a3cf378a094181b462d5a33e43dd871e451e91eba8ce3b119759206865185f28e51a24bed6be77729fc12e2d886132e1c
7
- data.tar.gz: bb2b409a5e9d0f1b762f230959721c0efb23e2cfdded64a25df02a618b6f8083134854905d0a728e2f004d01ad3324c82749c75bd8c5082a2c520595afb9c672
6
+ metadata.gz: 9d3eae7d155dd7acfa59da08d61cde2ccc102afb4410835c3f4618aebf0652088ab67c8039ada51042d972d0a83d8096e27f61dc23bc4207a43acd2a9bee8628
7
+ data.tar.gz: 572b60ac371b105ff0912ad3643779e168bb44f838010e8ee70dd2cfa1d2855a137a6926b0cc6c67c14965cb8bb4ef85ace8156e0b8fb30ec56a443279f76d34
@@ -1,3 +1,16 @@
1
+ ### 0.2.0 (2018/11/27)
2
+
3
+ * Added `Strava::Api::Client#activity` with segments, photos, similar activities, trends, laps and gear - [@dblock](https://github.com/dblock).
4
+ * Added `Activity#type_emoji` and `Activity#strava_url` - [@dblock](https://github.com/dblock).
5
+ * Added `Athlete#name` and `Athlete#strava_url` - [@dblock](https://github.com/dblock).
6
+ * Added `Strava::Api::Client#athlete_clubs` - [@dblock](https://github.com/dblock).
7
+ * Added `Strava::Api::Client#club_activities` - [@dblock](https://github.com/dblock).
8
+ * Added `Strava::Api::Client#create_activity` and `Strava::Api::Client#update_activity` - [@dblock](https://github.com/dblock).
9
+ * Added `Strava::Api::Client#activity_comments`, `activity_kudos`, `activity_zones` and `activity_laps` - [@dblock](https://github.com/dblock).
10
+ * Added support for converting and displaying activity distance, elevation, speed and pace - [@dblock](https://github.com/dblock).
11
+ * [#5](https://github.com/dblock/strava-ruby-client/issues/5): Added support for pagination - [@dblock](https://github.com/dblock).
12
+ * [#6](https://github.com/dblock/strava-ruby-client/issues/6): The `strava-oauth-token` tool has been renamed and will handle the redirect in the browser - [@dblock](https://github.com/dblock).
13
+
1
14
  ### 0.1.0 (2018/11/23)
2
15
 
3
16
  * Initial public release, OAuth, current Athlete and Athlete Activities - [@dblock](https://github.com/dblock).
data/README.md CHANGED
@@ -6,7 +6,7 @@ Strava Ruby Client
6
6
 
7
7
  A newer Ruby client for the [Strava API v3](https://developers.strava.com).
8
8
 
9
- Unlike [strava-api-v3](https://github.com/jaredholdcroft/strava-api-v3) provides a first class interface to Strava models and more consistent error handling.
9
+ Unlike [strava-api-v3](https://github.com/jaredholdcroft/strava-api-v3) provides complete OAuth refresh token flow support, a richer first class interface to Strava models, natively supports pagination and implements more consistent error handling.
10
10
 
11
11
  # Table of Contents
12
12
 
@@ -15,6 +15,16 @@ Unlike [strava-api-v3](https://github.com/jaredholdcroft/strava-api-v3) provides
15
15
  - [API](#api)
16
16
  - [Athlete](#athlete)
17
17
  - [Athlete Activities](#athlete-activities)
18
+ - [Athlete Clubs](#athlete-clubs)
19
+ - [Club Activities](#club-activities)
20
+ - [Create Activity](#create-activity)
21
+ - [Update Activity](#update-activity)
22
+ - [Get Activity](#get-activity)
23
+ - [Activity Comments](#activity-comments)
24
+ - [Activity Kudoers](#activity-kudoers)
25
+ - [Activity Laps](#activity-laps)
26
+ - [Activity Zones](#activity-zones)
27
+ - [Pagination](#pagination)
18
28
  - [OAuth](#oauth)
19
29
  - [Configuration](#configuration)
20
30
  - [Web Client Options](#web-client-options)
@@ -22,7 +32,7 @@ Unlike [strava-api-v3](https://github.com/jaredholdcroft/strava-api-v3) provides
22
32
  - [OAuth Client Options](#oauth-client-options)
23
33
  - [Errors](#errors)
24
34
  - [Tools](#tools)
25
- - [OAuth Token](#oauth-token)
35
+ - [Strava OAuth Token](#strava-oauth-token)
26
36
  - [Contributing](#contributing)
27
37
  - [Copyright and License](#copyright-and-license)
28
38
 
@@ -40,7 +50,7 @@ Run `bundle install`.
40
50
 
41
51
  ### API
42
52
 
43
- Use an access token obtained from [My API Application](https://www.strava.com/settings/api) in the Strava UI, the [oauth-token tool](#oauth-token) or the [OAuth Workflow](#oauth) in your application.
53
+ Use an access token obtained from [My API Application](https://www.strava.com/settings/api) in the Strava UI, the [strava-oauth-token tool](#strava-oauth-token) or the [OAuth Workflow](#oauth) in your application.
44
54
 
45
55
  ```ruby
46
56
  client = Strava::Api::Client.new(
@@ -63,10 +73,168 @@ See [Strava::Models::Athlete](lib/strava/models/athlete.rb) for all available pr
63
73
  Get currently logged-in athlete activities.
64
74
 
65
75
  ```ruby
66
- client.athlete_activities # => Array[Strava::Models::Activity]
76
+ activities = client.athlete_activities # => Array[Strava::Models::Activity]
77
+
78
+ activity = activities.first # => Strava::Models::Activity
79
+
80
+ activity.name # => 'NYC TCS Marathon 2018'
81
+ activity.strava_url # => 'https://www.strava.com/activities/1477353766'
82
+ activity.type_emoji # => '🏃'
83
+ activity.distance_s # => '42.2km'
84
+ activity.moving_time_in_hours_s # => '3h38m5s'
85
+ activity.elapsed_time_in_hours_s # => '3h42m13s'
86
+ activity.pace_s # => '5m15s/km'
87
+ activity.pace_per_mile_s # => '8m28s/mi'
88
+ activity.speed_s # => '11.4km/h'
89
+ activity.miles_per_hour_s # => '7.1mph'
90
+ activity.total_elevation_gain_s # => '270.9m'
91
+ activity.total_elevation_gain_in_feet_s # => '888.8ft'
67
92
  ```
68
93
 
69
- See [Strava::Models::Activity](lib/strava/models/activity.rb) for all available properties.
94
+ See [Strava::Models::Activity](lib/strava/models/activity.rb), [Strava::Models::Mixins::Distance](lib/strava/models/mixins/distance.rb), [Strava::Models::Mixins::Elevation](lib/strava/models/mixins/elevation.rb) and [Strava::Models::Mixins::Time](lib/strava/models/mixins/time.rb) for all available properties.
95
+
96
+ #### Athlete Clubs
97
+
98
+ Get currently logged-in athlete clubs.
99
+
100
+ ```ruby
101
+ clubs = client.athlete_clubs # => Array[Strava::Models::Club]
102
+
103
+ club = clubs.first # => Strava::Models::Activity
104
+
105
+ activity.name # => 'NYRR'
106
+ activity.strava_url # => 'https://www.strava.com/clubs/nyrr'
107
+ ```
108
+
109
+ See [Strava::Models::Club](lib/strava/models/club.rb) for all available properties.
110
+
111
+ #### Club Activities
112
+
113
+ Get club activities.
114
+
115
+ ```ruby
116
+ activities = client.club_activities(id: 108605) # => Array[Strava::Models::Activity]
117
+
118
+ activity = activities.first # => Strava::Models::Activity
119
+
120
+ activity.name # => 'Afternoon Run'
121
+ ```
122
+
123
+ See [Strava::Models::Activity](lib/strava/models/activity.rb) for all available properties. Note that Strava does not return activity or athlete ID via this API.
124
+
125
+ #### Create Activity
126
+
127
+ Create an activity.
128
+
129
+ ```ruby
130
+ activity = client.create_activity(
131
+ name: 'Afternoon Run',
132
+ type: 'Run',
133
+ start_date_local: Time.now,
134
+ elapsed_time: 1234, # in seconds
135
+ description: 'Test run.',
136
+ distance: 1000 # in meters
137
+ )
138
+
139
+ activity.name # => 'Afternoon Run'
140
+ activity.strava_url # => 'https://www.strava.com/activities/1982980795'
141
+ ```
142
+
143
+ #### Update Activity
144
+
145
+ Update an activity.
146
+
147
+ ```ruby
148
+ activity = client.update_activity(
149
+ id: 1982980795,
150
+ name: 'Afternoon Run (Updated)',
151
+ type: 'Run',
152
+ description: 'It was cold.'
153
+ )
154
+
155
+ activity.name # => 'Afternoon Run (Updated)'
156
+ activity.strava_url # => 'https://www.strava.com/activities/1982980795'
157
+ ```
158
+
159
+ #### Get Activity
160
+
161
+ Get a detailed activity by ID, including description, photos, gear, splits, segments and laps.
162
+
163
+ ```ruby
164
+ activity = client.activity(id: 1982980795)
165
+
166
+ activity.name # => 'Afternoon Run'
167
+ activity.strava_url # => 'https://www.strava.com/activities/1982980795'
168
+ ```
169
+
170
+ #### Activity Comments
171
+
172
+ Get activity comments.
173
+
174
+ ```ruby
175
+ comments = client.activity_comments(id: 1982980795) # => Array[Strava::Models::Comment]
176
+
177
+ comment = comments.first # => Strava::Models::Comment
178
+
179
+ comment.text # => 'Молодчина!'
180
+ comment.athlete.username # => 'zolotov'
181
+ ```
182
+
183
+ See [Strava::Models::Comment](lib/strava/models/comment.rb) for all available properties.
184
+
185
+ #### Activity Kudoers
186
+
187
+ Get activity kodoers.
188
+
189
+ ```ruby
190
+ kudoers = client.activity_kudos(id: 1982980795) # => Array[Strava::Models::Athlete]
191
+
192
+ kodoer = kudoers.first # => Strava::Models::Athlete
193
+
194
+ kudoer.username # => 'zolotov'
195
+ ```
196
+
197
+ #### Activity Laps
198
+
199
+ Get activity laps.
200
+
201
+ ```ruby
202
+ laps = client.activity_laps(id: 1982980795) # => Array[Strava::Models::Lap]
203
+
204
+ lap = laps.first # => Strava::Models::Lap
205
+
206
+ lap.name # => 'Lap 1'
207
+ ```
208
+
209
+ See [Strava::Models::Lap](lib/strava/models/lap.rb) for all available properties.
210
+
211
+ #### Activity Zones
212
+
213
+ Get activity zones.
214
+
215
+ ```ruby
216
+ zones = client.activity_zones(id: 1982980795) # => Array[Strava::Models::ActivityZone]
217
+
218
+ zone = zones.first # => Strava::Models::ActivityZone
219
+ zones.type # => 'heartrate'
220
+
221
+ distribution_bucket = activity_zone.distribution_buckets.first # => Strava::Models::TimedZoneRange
222
+ distribution_bucket.min # => 0
223
+ distribution_bucket.max # => 123
224
+ distribution_bucket.time # => 20
225
+ ```
226
+
227
+ See [Strava::Models::ActivityZone](lib/strava/models/activity_zone.rb) and [Strava::Models::TimedZoneRange](lib/strava/models/timed_zone_range.rb) for all available properties.
228
+
229
+ #### Pagination
230
+
231
+ Some Strava APIs, including [athlete_activities](#athlete-activities) support pagination when supplying an optional `page` and `per_page` parameter. By default the client retrieves one page of data, which Strava currently defaults to 30 items. You can paginate through more data by supplying a block and an optional `per_page` parameter. The underlying implementation makes page-sized calls and increments the `page` argument.
232
+
233
+ ```ruby
234
+ client.athlete_activities(per_page: 30) do |activity|
235
+ activity # => Strava::Models::Activity
236
+ end
237
+ ```
70
238
 
71
239
  ### OAuth
72
240
 
@@ -205,19 +373,12 @@ end
205
373
 
206
374
  ## Tools
207
375
 
208
- ### OAuth Token
376
+ ### Strava OAuth Token
209
377
 
210
- Use [bin/oauth-token](bin/outh-token) to obtain a token from the command-line.
378
+ Use [strava-oauth-token](bin/strava-outh-token.rb) to obtain a token from the command-line. This will open a new browser window, navigate to Strava, request the appropriate permissions, then handle OAuth in a local redirect. The token type, refresh token, access token and token expiration will be displayed in the browser.
211
379
 
212
380
  ```bash
213
- $ STRAVA_CLIENT_ID=... STRAVA_CLIENT_SECRET=... bundle exec bin/oauth-token.rb
214
-
215
- Opening browser at https://www.strava.com/oauth/authorize?...
216
- Copy paste the code from the redirect URL: 1234556789901234567890
217
- token_type: Bearer
218
- refresh_token: 013612374123716234842346234
219
- access_token: 7348562936591928461923619823
220
- expires_at: 2018-11-23 16:25:52 -0500
381
+ $ STRAVA_CLIENT_ID=... STRAVA_CLIENT_SECRET=... strava-oauth-token
221
382
  ```
222
383
 
223
384
  ## Contributing
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'dotenv/load'
4
+ require 'strava-ruby-client'
5
+ require 'webrick'
6
+
7
+ server = WEBrick::HTTPServer.new(Port: 4242)
8
+
9
+ trap 'INT' do
10
+ server.shutdown
11
+ end
12
+
13
+ client = Strava::OAuth::Client.new(
14
+ client_id: ENV['STRAVA_CLIENT_ID'],
15
+ client_secret: ENV['STRAVA_CLIENT_SECRET']
16
+ )
17
+
18
+ server.mount_proc '/' do |req, res|
19
+ code = req.query['code']
20
+ response = client.oauth_token(code: code)
21
+
22
+ res.body = %(
23
+ <html>
24
+ <body>
25
+ <ul>
26
+ <li>token_type: #{response.token_type}</li>
27
+ <li>refresh_token: #{response.refresh_token}</li>
28
+ <li>access_token: #{response.access_token}</li>
29
+ <li>expires_at: #{response.expires_at}</li>
30
+ </ul>
31
+ <body>
32
+ </html>
33
+ )
34
+
35
+ server.shutdown
36
+ end
37
+
38
+ redirect_url = client.authorize_url(
39
+ redirect_uri: 'http://localhost:4242/',
40
+ response_type: 'code',
41
+ scope: 'read_all,activity:read_all,profile:read_all,profile:write,activity:write'
42
+ )
43
+
44
+ server.logger.info "opening browser at #{redirect_url}\n"
45
+ system 'open', redirect_url
46
+
47
+ server.start
@@ -3,19 +3,39 @@ require 'faraday_middleware'
3
3
  require 'json'
4
4
  require 'logger'
5
5
  require 'hashie'
6
- require 'active_support/core_ext/object/to_query'
7
6
  require 'time'
8
7
 
8
+ require 'active_support/core_ext/object/to_query'
9
+ require 'active_support/core_ext/hash'
10
+ require 'active_support/concern'
11
+
9
12
  require_relative 'strava/version'
10
13
  require_relative 'strava/logger'
11
14
 
12
15
  require_relative 'strava/errors/fault'
13
16
 
17
+ require_relative 'strava/models/mixins/distance'
18
+ require_relative 'strava/models/mixins/elevation'
19
+ require_relative 'strava/models/mixins/time'
20
+
14
21
  require_relative 'strava/models/model'
15
22
  require_relative 'strava/models/token'
16
23
  require_relative 'strava/models/athlete'
17
24
  require_relative 'strava/models/map'
18
25
  require_relative 'strava/models/activity'
26
+ require_relative 'strava/models/club'
27
+ require_relative 'strava/models/segment_effort'
28
+ require_relative 'strava/models/photos'
29
+ require_relative 'strava/models/photo'
30
+ require_relative 'strava/models/similar_activities'
31
+ require_relative 'strava/models/trend'
32
+ require_relative 'strava/models/split'
33
+ require_relative 'strava/models/lap'
34
+ require_relative 'strava/models/gear'
35
+ require_relative 'strava/models/segment'
36
+ require_relative 'strava/models/activity_zone'
37
+ require_relative 'strava/models/timed_zone_range'
38
+ require_relative 'strava/models/comment'
19
39
 
20
40
  require_relative 'strava/web/raise_error'
21
41
  require_relative 'strava/web/connection'
@@ -27,4 +47,5 @@ require_relative 'strava/oauth/config'
27
47
  require_relative 'strava/oauth/client'
28
48
 
29
49
  require_relative 'strava/api/config'
50
+ require_relative 'strava/api/cursor'
30
51
  require_relative 'strava/api/client'
@@ -14,13 +14,144 @@ module Strava
14
14
  { 'Authorization' => "Bearer #{access_token}" }
15
15
  end
16
16
 
17
+ #
18
+ # Get logged-in athlete.
19
+ #
17
20
  def athlete
18
21
  Strava::Models::Athlete.new(get('athlete'))
19
22
  end
20
23
 
21
- def athlete_activities(options = {})
22
- get('athlete/activities', options).map do |activity|
23
- Strava::Models::Activity.new(activity)
24
+ #
25
+ # Get logged-in athlete.
26
+ #
27
+ def activity(options = {})
28
+ throw ArgumentError.new('Required argument :id missing') if options[:id].nil?
29
+ Strava::Models::Activity.new(get("activities/#{options[:id]}", options.except(:id)))
30
+ end
31
+
32
+ #
33
+ # List logged-in athlete activities.
34
+ #
35
+ # @option options [Integer] :before
36
+ # An epoch timestamp to use for filtering activities that have taken place before a certain time.
37
+ # @option options [Integer] :after
38
+ # An epoch timestamp to use for filtering activities that have taken place after a certain time.
39
+ # @option options [Integer] :page
40
+ # Page number.
41
+ # @option options [Integer] :per_page
42
+ # Number of items per page. Defaults to 30.
43
+ #
44
+ def athlete_activities(options = {}, &block)
45
+ paginate 'athlete/activities', options, Strava::Models::Activity, &block
46
+ end
47
+
48
+ #
49
+ # Create an activity.
50
+ #
51
+ def create_activity(options = {})
52
+ Strava::Models::Activity.new(post('activities', options))
53
+ end
54
+
55
+ #
56
+ # Update an activity.
57
+ # @option options [Boolean] :commute
58
+ # Whether this activity is a commute.
59
+ # @option options [Boolean] :trainer
60
+ # Whether this activity was recorded on a training machine.
61
+ # @option options [String] :description
62
+ # The description of the activity.
63
+ # @option options [String] :name
64
+ # The name of the activity.
65
+ # @option options [String] :type
66
+ # Activity type.
67
+ # @option options [String] :gear_id
68
+ # Identifier for the gear associated with the activity. Specifying "none" clears gear from activity.
69
+ #
70
+ def update_activity(options = {})
71
+ throw ArgumentError.new('Required argument :id missing') if options[:id].nil?
72
+ Strava::Models::Activity.new(put("activities/#{options[:id]}", options.except(:id)))
73
+ end
74
+
75
+ #
76
+ # List logged-in athlete clubs.
77
+ #
78
+ # @option options [Integer] :page
79
+ # Page number.
80
+ # @option options [Integer] :per_page
81
+ # Number of items per page. Defaults to 30.
82
+ #
83
+ def athlete_clubs(options = {}, &block)
84
+ paginate 'athlete/clubs', options, Strava::Models::Club, &block
85
+ end
86
+
87
+ #
88
+ # List club activities.
89
+ #
90
+ # @option options [String] :id
91
+ # Club id.
92
+ # @option options [Integer] :page
93
+ # Page number.
94
+ # @option options [Integer] :per_page
95
+ # Number of items per page. Defaults to 30.
96
+ #
97
+ def club_activities(options = {}, &block)
98
+ throw ArgumentError.new('Required argument :id missing') if options[:id].nil?
99
+ paginate "clubs/#{options[:id]}/activities", options.except(:id), Strava::Models::Activity, &block
100
+ end
101
+
102
+ #
103
+ # List activity comments.
104
+ #
105
+ # @option options [String] :id
106
+ # Activity id.
107
+ # @option options [Integer] :page
108
+ # Page number.
109
+ # @option options [Integer] :per_page
110
+ # Number of items per page. Defaults to 30.
111
+ #
112
+ def activity_comments(options = {}, &block)
113
+ throw ArgumentError.new('Required argument :id missing') if options[:id].nil?
114
+ paginate "activities/#{options[:id]}/comments", options.except(:id), Strava::Models::Comment, &block
115
+ end
116
+
117
+ #
118
+ # List activity kudoers.
119
+ #
120
+ # @option options [String] :id
121
+ # Activity id.
122
+ # @option options [Integer] :page
123
+ # Page number.
124
+ # @option options [Integer] :per_page
125
+ # Number of items per page. Defaults to 30.
126
+ #
127
+ def activity_kudos(options = {}, &block)
128
+ throw ArgumentError.new('Required argument :id missing') if options[:id].nil?
129
+ paginate "activities/#{options[:id]}/kudos", options.except(:id), Strava::Models::Athlete, &block
130
+ end
131
+
132
+ #
133
+ # Get activity zones.
134
+ #
135
+ # @option options [String] :id
136
+ # Activity id.
137
+ #
138
+ def activity_zones(options = {})
139
+ throw ArgumentError.new('Required argument :id missing') if options[:id].nil?
140
+ get("activities/#{options[:id]}/zones", options).map do |row|
141
+ Strava::Models::ActivityZone.new(row)
142
+ end
143
+ end
144
+
145
+ #
146
+ # Get activity laps.
147
+ #
148
+ # @option options [String] :id
149
+ # Activity id.
150
+ #
151
+ def activity_laps(options = {})
152
+ throw ArgumentError.new('Required argument :id missing') if options[:id].nil?
153
+ get("activities/#{options[:id]}/laps", options).map do |row|
154
+ Strava::Models::Lap.new(row)
24
155
  end
25
156
  end
26
157
 
@@ -33,6 +164,22 @@ module Strava
33
164
  Config
34
165
  end
35
166
  end
167
+
168
+ private
169
+
170
+ def paginate(path, options, model)
171
+ if block_given?
172
+ Cursor.new(self, path, options).each do |page|
173
+ page.each do |row|
174
+ yield model.new(row)
175
+ end
176
+ end
177
+ else
178
+ get(path, options).map do |row|
179
+ model.new(row)
180
+ end
181
+ end
182
+ end
36
183
  end
37
184
  end
38
185
  end