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
@@ -0,0 +1,29 @@
1
+ module Strava
2
+ module Api
3
+ class Cursor
4
+ include Enumerable
5
+
6
+ attr_reader :client
7
+ attr_reader :path
8
+ attr_reader :params
9
+
10
+ def initialize(client, path, params = {})
11
+ @client = client
12
+ @path = path
13
+ @params = params
14
+ end
15
+
16
+ def each
17
+ next_page = 1
18
+ loop do
19
+ query = params.merge(page: next_page)
20
+ response = client.get(path, query)
21
+ break unless response.any?
22
+
23
+ yield response
24
+ next_page += 1
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,14 +1,15 @@
1
1
  module Strava
2
2
  module Models
3
3
  class Activity < Model
4
+ include Mixins::Distance
5
+ include Mixins::Time
6
+ include Mixins::Elevation
7
+
4
8
  property 'id'
5
9
  property 'resource_state'
6
10
  property 'athlete', transform_with: ->(v) { Strava::Models::Athlete.new(v) }
7
11
  property 'name'
8
- property 'distance'
9
- property 'moving_time'
10
- property 'elapsed_time'
11
- property 'total_elevation_gain'
12
+ property 'description'
12
13
  property 'type'
13
14
  property 'workout_type'
14
15
  property 'id'
@@ -52,6 +53,91 @@ module Strava
52
53
  property 'total_photo_count'
53
54
  property 'has_kudoed'
54
55
  property 'suffer_score'
56
+ property 'calories'
57
+ property 'segment_efforts', transform_with: ->(v) { v.map { |r| Strava::Models::SegmentEffort.new(r) } }
58
+ property 'best_efforts', transform_with: ->(v) { v.map { |r| Strava::Models::SegmentEffort.new(r) } }
59
+ property 'photos', transform_with: ->(v) { Strava::Models::Photos.new(v) }
60
+ property 'similar_activities', transform_with: ->(v) { Strava::Models::SimilarActivities.new(v) }
61
+ property 'embed_token'
62
+ property 'available_zones'
63
+ property 'splits_metric', transform_with: ->(v) { v.map { |r| Strava::Models::Split::Metric.new(r) } }
64
+ property 'splits_standard', transform_with: ->(v) { v.map { |r| Strava::Models::Split::Standard.new(r) } }
65
+ property 'laps', transform_with: ->(v) { v.map { |r| Strava::Models::Lap.new(r) } }
66
+ property 'gear', transform_with: ->(v) { Strava::Models::Gear.new(v) }
67
+ property 'device_name'
68
+
69
+ def units
70
+ :metric
71
+ end
72
+
73
+ def distance_s
74
+ if type == 'Swim'
75
+ case units
76
+ when :metric then distance_in_meters_s
77
+ when :imperial then distance_in_yards_s
78
+ end
79
+ else
80
+ case units
81
+ when :metric then distance_in_kilometers_s
82
+ when :imperial then distance_in_miles_s
83
+ end
84
+ end
85
+ end
86
+
87
+ def pace_s
88
+ case type
89
+ when 'Swim'
90
+ case units
91
+ when :metric then pace_per_100_meters_s
92
+ when :imperial then pace_per_100_yards_s
93
+ end
94
+ else
95
+ case units
96
+ when :metric then pace_per_kilometer_s
97
+ when :imperial then pace_per_mile_s
98
+ end
99
+ end
100
+ end
101
+
102
+ def strava_url
103
+ "https://www.strava.com/activities/#{id}"
104
+ end
105
+
106
+ def type_emoji
107
+ case type
108
+ when 'Run' then '🏃'
109
+ when 'Ride' then '🚴'
110
+ when 'Swim' then '🏊'
111
+ when 'Walk' then '🚶'
112
+ when 'AlpineSki' then '⛷️'
113
+ when 'BackcountrySki' then '🎿️'
114
+ # when 'Canoeing' then ''
115
+ # when 'Crossfit' then ''
116
+ when 'EBikeRide' then '🚴'
117
+ # when 'Elliptical' then ''
118
+ # when 'Hike' then ''
119
+ when 'IceSkate' then '⛸️'
120
+ # when 'InlineSkate' then ''
121
+ # when 'Kayaking' then ''
122
+ # when 'Kitesurf' then ''
123
+ # when 'NordicSki' then ''
124
+ when 'RockClimbing' then '🧗'
125
+ when 'RollerSki' then ''
126
+ when 'Rowing' then '🚣'
127
+ when 'Snowboard' then '🏂'
128
+ # when 'Snowshoe' then ''
129
+ # when 'StairStepper' then ''
130
+ # when 'StandUpPaddling' then ''
131
+ when 'Surfing' then '🏄'
132
+ when 'VirtualRide' then '🚴'
133
+ when 'VirtualRun' then '🏃'
134
+ when 'WeightTraining' then '🏋️'
135
+ # when 'Windsurf' then ''
136
+ when 'Wheelchair' then '♿'
137
+ # when 'Workout' then ''
138
+ # when 'Yoga'' then ''
139
+ end
140
+ end
55
141
  end
56
142
  end
57
143
  end
@@ -0,0 +1,13 @@
1
+ module Strava
2
+ module Models
3
+ class ActivityZone < Model
4
+ property 'score'
5
+ property 'distribution_buckets', transform_with: ->(v) { v.map { |r| Strava::Models::TimedZoneRange.new(r) } }
6
+ property 'type'
7
+ property 'resource_state'
8
+ property 'sensor_based'
9
+ property 'points'
10
+ property 'custom_zones'
11
+ end
12
+ end
13
+ end
@@ -20,6 +20,14 @@ module Strava
20
20
  property 'follower'
21
21
  property 'friend'
22
22
  property 'summit'
23
+
24
+ def name
25
+ [firstname, lastname].compact.join(' ') if firstname || lastname
26
+ end
27
+
28
+ def strava_url
29
+ "https://www.strava.com/athletes/#{username || id}"
30
+ end
23
31
  end
24
32
  end
25
33
  end
@@ -0,0 +1,26 @@
1
+ module Strava
2
+ module Models
3
+ class Club < Model
4
+ property 'id'
5
+ property 'resource_state'
6
+ property 'name'
7
+ property 'profile_medium'
8
+ property 'profile'
9
+ property 'cover_photo'
10
+ property 'cover_photo_small'
11
+ property 'sport_type'
12
+ property 'city'
13
+ property 'state'
14
+ property 'country'
15
+ property 'private'
16
+ property 'member_count'
17
+ property 'featured'
18
+ property 'verified'
19
+ property 'url'
20
+
21
+ def strava_url
22
+ "https://www.strava.com/clubs/#{url || id}"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ module Strava
2
+ module Models
3
+ class Comment < Model
4
+ property 'id'
5
+ property 'activity_id'
6
+ property 'resource_state'
7
+ property 'text'
8
+ property 'created_at', transform_with: ->(v) { Time.parse(v) }
9
+ property 'athlete', transform_with: ->(v) { Strava::Models::Athlete.new(v) }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ module Strava
2
+ module Models
3
+ class Gear < Model
4
+ property 'id'
5
+ property 'resource_state'
6
+ property 'distance'
7
+ property 'name'
8
+ property 'primary'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,26 @@
1
+ module Strava
2
+ module Models
3
+ class Lap < Model
4
+ include Mixins::Time
5
+ include Mixins::Distance
6
+ include Mixins::Elevation
7
+
8
+ property 'id'
9
+ property 'resource_state'
10
+ property 'name'
11
+ property 'activity', transform_with: ->(v) { Strava::Models::Activity.new(v) }
12
+ property 'athlete', transform_with: ->(v) { Strava::Models::Athlete.new(v) }
13
+ property 'start_date', transform_with: ->(v) { Time.parse(v) }
14
+ property 'start_date_local', transform_with: ->(v) { Time.parse(v) }
15
+ property 'start_index'
16
+ property 'end_index'
17
+ property 'average_speed'
18
+ property 'max_speed'
19
+ property 'average_heartrate'
20
+ property 'max_heartrate'
21
+ property 'lap_index'
22
+ property 'split'
23
+ property 'pace_zone'
24
+ end
25
+ end
26
+ end
@@ -4,6 +4,7 @@ module Strava
4
4
  property 'id'
5
5
  property 'summary_polyline'
6
6
  property 'resource_state'
7
+ property 'polyline'
7
8
  end
8
9
  end
9
10
  end
@@ -0,0 +1,73 @@
1
+ module Strava
2
+ module Models
3
+ module Mixins
4
+ module Distance
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ property 'distance'
9
+ end
10
+
11
+ def distance_in_meters
12
+ case units
13
+ when :metric then distance
14
+ when :imperial then distance / 3.28084
15
+ end
16
+ end
17
+
18
+ def distance_in_feet
19
+ case units
20
+ when :metric then distance * 3.28084
21
+ when :imperial then distance
22
+ end
23
+ end
24
+
25
+ def distance_in_miles
26
+ case units
27
+ when :metric then distance_in_meters * 0.00062137
28
+ when :imperial then distance_in_feet / 5280
29
+ end
30
+ end
31
+
32
+ def distance_in_miles_s
33
+ return unless distance && distance.positive?
34
+
35
+ format('%gmi', format('%.2f', distance_in_miles))
36
+ end
37
+
38
+ def distance_in_yards
39
+ distance_in_meters * 1.09361
40
+ end
41
+
42
+ def distance_in_yards_s
43
+ return unless distance && distance.positive?
44
+
45
+ format('%gyd', format('%.1f', distance_in_yards))
46
+ end
47
+
48
+ def distance_in_meters_s
49
+ return unless distance && distance.positive?
50
+
51
+ format('%gm', format('%d', distance_in_meters))
52
+ end
53
+
54
+ def distance_in_kilometers
55
+ distance_in_meters / 1000
56
+ end
57
+
58
+ def distance_in_kilometers_s
59
+ return unless distance && distance.positive?
60
+
61
+ format('%gkm', format('%.2f', distance_in_kilometers))
62
+ end
63
+
64
+ def distance_s
65
+ case units
66
+ when :metric then distance_in_kilometers_s
67
+ when :imperial then distance_in_miles_s
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,46 @@
1
+ module Strava
2
+ module Models
3
+ module Mixins
4
+ module Elevation
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ property 'total_elevation_gain'
9
+ end
10
+
11
+ def total_elevation_gain_in_feet
12
+ case units
13
+ when :metric then total_elevation_gain * 3.28084
14
+ when :imperial then total_elevation_gain
15
+ end
16
+ end
17
+
18
+ def total_elevation_gain_in_meters
19
+ case units
20
+ when :metric then total_elevation_gain
21
+ when :imperial then total_elevation_gain / 3.28084
22
+ end
23
+ end
24
+
25
+ def total_elevation_gain_in_meters_s
26
+ return unless total_elevation_gain && total_elevation_gain.positive?
27
+
28
+ format('%gm', format('%.1f', total_elevation_gain_in_meters))
29
+ end
30
+
31
+ def total_elevation_gain_in_feet_s
32
+ return unless total_elevation_gain && total_elevation_gain.positive?
33
+
34
+ format('%gft', format('%.1f', total_elevation_gain_in_feet))
35
+ end
36
+
37
+ def total_elevation_gain_s
38
+ case units
39
+ when :metric then total_elevation_gain_in_meters_s
40
+ when :imperial then total_elevation_gain_in_feet_s
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,99 @@
1
+ module Strava
2
+ module Models
3
+ module Mixins
4
+ module Time
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ property 'moving_time'
9
+ property 'elapsed_time'
10
+ property 'average_speed'
11
+ end
12
+
13
+ # always in meters per second, even in imperial splits
14
+ def average_speed_meters_per_second
15
+ elapsed_time
16
+ end
17
+
18
+ def moving_time_in_hours_s
19
+ time_in_hours_s moving_time
20
+ end
21
+
22
+ def elapsed_time_in_hours_s
23
+ time_in_hours_s elapsed_time
24
+ end
25
+
26
+ def pace_per_mile_s
27
+ convert_meters_per_second_to_pace average_speed, :mi
28
+ end
29
+
30
+ def pace_per_100_yards_s
31
+ convert_meters_per_second_to_pace average_speed, :"100yd"
32
+ end
33
+
34
+ def pace_per_100_meters_s
35
+ convert_meters_per_second_to_pace average_speed, :"100m"
36
+ end
37
+
38
+ def pace_per_kilometer_s
39
+ convert_meters_per_second_to_pace average_speed, :km
40
+ end
41
+
42
+ def kilometer_per_hour_s
43
+ return unless average_speed && average_speed.positive?
44
+
45
+ format('%.1fkm/h', average_speed * 3.6)
46
+ end
47
+
48
+ def miles_per_hour_s
49
+ return unless average_speed && average_speed.positive?
50
+
51
+ format('%.1fmph', average_speed * 2.23694)
52
+ end
53
+
54
+ def speed_s
55
+ case units
56
+ when :metric then kilometer_per_hour_s
57
+ when :imperial then miles_per_hour_s
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def time_in_hours_s(time)
64
+ return unless time
65
+
66
+ hours = time / 3600 % 24
67
+ minutes = time / 60 % 60
68
+ seconds = time % 60
69
+ [
70
+ hours.to_i.positive? ? format('%dh', hours) : nil,
71
+ minutes.to_i.positive? ? format('%dm', minutes) : nil,
72
+ seconds.to_i.positive? ? format('%ds', seconds) : nil
73
+ ].compact.join
74
+ end
75
+
76
+ # Convert speed (m/s) to pace (min/mile or min/km) in the format of 'x:xx'
77
+ # http://yizeng.me/2017/02/25/convert-speed-to-pace-programmatically-using-ruby
78
+ def convert_meters_per_second_to_pace(speed, unit = :mi)
79
+ return unless speed && speed.positive?
80
+
81
+ total_seconds = case unit
82
+ when :mi then 1609.344 / speed
83
+ when :km then 1000 / speed
84
+ when :"100yd" then 91.44 / speed
85
+ when :"100m" then 100 / speed
86
+ end
87
+ minutes, seconds = total_seconds.divmod(60)
88
+ seconds = seconds.round
89
+ if seconds == 60
90
+ minutes += 1
91
+ seconds = 0
92
+ end
93
+ seconds = seconds < 10 ? "0#{seconds}" : seconds.to_s
94
+ "#{minutes}m#{seconds}s/#{unit}"
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end