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