strava-ruby-client 0.1.0 → 0.2.0

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.
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