strava-ruby-client 3.0.0 → 3.1.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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/README.md +41 -57
  4. data/lib/strava/api/client.rb +69 -0
  5. data/lib/strava/api/config.rb +40 -0
  6. data/lib/strava/api/cursor.rb +32 -1
  7. data/lib/strava/api/endpoints/activities.rb +197 -57
  8. data/lib/strava/api/endpoints/athletes.rb +64 -8
  9. data/lib/strava/api/endpoints/clubs.rb +24 -59
  10. data/lib/strava/api/endpoints/gears.rb +11 -2
  11. data/lib/strava/api/endpoints/oauth.rb +45 -0
  12. data/lib/strava/api/endpoints/routes.rb +20 -13
  13. data/lib/strava/api/endpoints/segment_efforts.rb +15 -8
  14. data/lib/strava/api/endpoints/segments.rb +74 -21
  15. data/lib/strava/api/endpoints/streams.rb +22 -18
  16. data/lib/strava/api/endpoints/uploads.rb +12 -2
  17. data/lib/strava/api/pagination.rb +25 -0
  18. data/lib/strava/api/ratelimit.rb +34 -0
  19. data/lib/strava/deep_copyable.rb +20 -1
  20. data/lib/strava/errors/fault.rb +32 -0
  21. data/lib/strava/errors/ratelimit_error.rb +35 -0
  22. data/lib/strava/errors/upload_error.rb +55 -0
  23. data/lib/strava/logger.rb +27 -0
  24. data/lib/strava/models/achievement.rb +28 -1
  25. data/lib/strava/models/activity_stats.rb +47 -1
  26. data/lib/strava/models/activity_total.rb +25 -1
  27. data/lib/strava/models/activity_zone.rb +39 -2
  28. data/lib/strava/models/authorization.rb +13 -0
  29. data/lib/strava/models/base_stream.rb +22 -1
  30. data/lib/strava/models/club_event.rb +64 -1
  31. data/lib/strava/models/comment.rb +39 -1
  32. data/lib/strava/models/destination.rb +28 -1
  33. data/lib/strava/models/detailed_activity.rb +203 -2
  34. data/lib/strava/models/detailed_athlete.rb +104 -2
  35. data/lib/strava/models/detailed_club.rb +76 -2
  36. data/lib/strava/models/detailed_gear.rb +45 -2
  37. data/lib/strava/models/detailed_photo.rb +61 -1
  38. data/lib/strava/models/detailed_photos.rb +29 -1
  39. data/lib/strava/models/detailed_segment.rb +89 -2
  40. data/lib/strava/models/detailed_segment_effort.rb +92 -3
  41. data/lib/strava/models/explorer_segment.rb +55 -2
  42. data/lib/strava/models/heart_rate_zone_ranges.rb +29 -1
  43. data/lib/strava/models/kudoser.rb +29 -1
  44. data/lib/strava/models/lap.rb +62 -2
  45. data/lib/strava/models/lat_lng.rb +42 -4
  46. data/lib/strava/models/local_legend.rb +41 -1
  47. data/lib/strava/models/map.rb +31 -0
  48. data/lib/strava/models/meta_activity.rb +15 -2
  49. data/lib/strava/models/meta_athlete.rb +17 -2
  50. data/lib/strava/models/meta_club.rb +15 -1
  51. data/lib/strava/models/mixins/average_speed.rb +81 -3
  52. data/lib/strava/models/mixins/distance.rb +25 -0
  53. data/lib/strava/models/mixins/elapsed_time.rb +18 -0
  54. data/lib/strava/models/mixins/elevation_difference.rb +42 -0
  55. data/lib/strava/models/mixins/elevation_gain.rb +39 -0
  56. data/lib/strava/models/mixins/estimated_moving_time.rb +18 -0
  57. data/lib/strava/models/mixins/http_response.rb +36 -0
  58. data/lib/strava/models/mixins/moving_time.rb +18 -0
  59. data/lib/strava/models/mixins/sport_type.rb +39 -1
  60. data/lib/strava/models/mixins/start_date_local.rb +40 -24
  61. data/lib/strava/models/mixins/time_in_hours.rb +23 -0
  62. data/lib/strava/models/mixins/total_elevation_gain.rb +42 -0
  63. data/lib/strava/models/model.rb +14 -0
  64. data/lib/strava/models/photos_summary.rb +26 -2
  65. data/lib/strava/models/photos_summary_primary.rb +28 -2
  66. data/lib/strava/models/power_zone_ranges.rb +23 -1
  67. data/lib/strava/models/response.rb +18 -0
  68. data/lib/strava/models/route.rb +63 -2
  69. data/lib/strava/models/similar_activities.rb +57 -1
  70. data/lib/strava/models/split.rb +27 -2
  71. data/lib/strava/models/stats_visibility.rb +24 -1
  72. data/lib/strava/models/stream.rb +107 -0
  73. data/lib/strava/models/stream_set.rb +52 -1
  74. data/lib/strava/models/summary_activity.rb +145 -2
  75. data/lib/strava/models/summary_athlete.rb +54 -1
  76. data/lib/strava/models/summary_club.rb +61 -2
  77. data/lib/strava/models/summary_gear.rb +31 -2
  78. data/lib/strava/models/summary_pr_segment_effort.rb +32 -2
  79. data/lib/strava/models/summary_segment.rb +80 -2
  80. data/lib/strava/models/summary_segment_effort.rb +32 -1
  81. data/lib/strava/models/timed_zone_range.rb +24 -1
  82. data/lib/strava/models/token.rb +31 -0
  83. data/lib/strava/models/trend.rb +38 -1
  84. data/lib/strava/models/updatable_activity.rb +36 -1
  85. data/lib/strava/models/upload.rb +50 -1
  86. data/lib/strava/models/waypoint.rb +31 -1
  87. data/lib/strava/models/xoms.rb +35 -1
  88. data/lib/strava/models/zone_range.rb +20 -1
  89. data/lib/strava/models/zones.rb +32 -1
  90. data/lib/strava/oauth/client.rb +107 -19
  91. data/lib/strava/oauth/config.rb +41 -0
  92. data/lib/strava/version.rb +1 -1
  93. data/lib/strava/web/api_response.rb +28 -2
  94. data/lib/strava/web/client.rb +72 -2
  95. data/lib/strava/web/config.rb +40 -0
  96. data/lib/strava/web/connection.rb +39 -0
  97. data/lib/strava/web/raise_response_error.rb +47 -0
  98. data/lib/strava/web/request.rb +56 -0
  99. data/lib/strava/web/response.rb +18 -0
  100. data/lib/strava/webhooks/client.rb +108 -7
  101. data/lib/strava/webhooks/config.rb +44 -0
  102. data/lib/strava/webhooks/models/challenge.rb +40 -0
  103. data/lib/strava/webhooks/models/event.rb +50 -0
  104. data/lib/strava/webhooks/models/subscription.rb +27 -0
  105. data/lib/strava-ruby-client.rb +0 -2
  106. metadata +2 -4
  107. data/lib/strava/models/club_activity.rb +0 -22
  108. data/lib/strava/models/club_athlete.rb +0 -21
@@ -2,27 +2,66 @@
2
2
 
3
3
  module Strava
4
4
  module Models
5
+ #
6
+ # Represents geographic coordinates (latitude/longitude).
7
+ #
8
+ # This is a helper class for working with geographic coordinates in Strava data.
9
+ # Coordinates are stored as an array [latitude, longitude] and can be accessed
10
+ # via helper methods or array syntax.
11
+ #
12
+ # @example Accessing coordinates
13
+ # activity = client.activity(1234567890)
14
+ # start = activity.start_latlng
15
+ # puts "Latitude: #{start.lat}"
16
+ # puts "Longitude: #{start.lng}"
17
+ # puts "Array: #{start.to_a.inspect}" # [40.7128, -74.0060]
18
+ #
19
+ # @example Creating coordinates
20
+ # coords = Strava::Models::LatLng.new([40.7128, -74.0060])
21
+ # coords.lat # => 40.7128
22
+ # coords.lng # => -74.0060
23
+ #
5
24
  class LatLng
25
+ # @return [Array<Float>] Coordinates as [latitude, longitude]
6
26
  attr_accessor :latlng
7
27
 
28
+ #
29
+ # Initialize with coordinates.
30
+ #
31
+ # @param latlng [Array<Float>, nil] Coordinates as [latitude, longitude]
32
+ #
33
+ def initialize(latlng = nil)
34
+ @latlng = latlng
35
+ end
36
+
37
+ # @return [Float, nil] Latitude
8
38
  def lat
9
39
  @latlng[0]
10
40
  end
11
41
 
42
+ # @return [Float, nil] Longitude
12
43
  def lng
13
44
  @latlng[1]
14
45
  end
15
46
 
47
+ # @param value [Float] Latitude value
16
48
  def lat=(value)
17
49
  @latlng ||= [nil, nil]
18
50
  @latlng[0] = value
19
51
  end
20
52
 
53
+ # @param value [Float] Longitude value
21
54
  def lng=(value)
22
55
  @latlng ||= [nil, nil]
23
56
  @latlng[1] = value
24
57
  end
25
58
 
59
+ #
60
+ # Compare with another LatLng or Array.
61
+ #
62
+ # @param other [LatLng, Array] Object to compare with
63
+ # @return [Boolean] true if coordinates are equal
64
+ #
26
65
  def ==(other)
27
66
  case other
28
67
  when LatLng
@@ -34,6 +73,7 @@ module Strava
34
73
  end
35
74
  end
36
75
 
76
+ # @return [Array<Float>] Coordinates as array [latitude, longitude]
37
77
  def to_a
38
78
  @latlng
39
79
  end
@@ -41,19 +81,17 @@ module Strava
41
81
  alias to_h to_a
42
82
  alias as_json to_a
43
83
 
84
+ # @return [String] JSON representation
44
85
  def to_json(*args)
45
86
  as_json.to_json(args)
46
87
  end
47
88
 
89
+ # @return [String] String representation of coordinates
48
90
  def inspect
49
91
  @latlng.inspect
50
92
  end
51
93
 
52
94
  alias to_s inspect
53
-
54
- def initialize(latlng = nil)
55
- @latlng = latlng
56
- end
57
95
  end
58
96
  end
59
97
  end
@@ -2,14 +2,54 @@
2
2
 
3
3
  module Strava
4
4
  module Models
5
- # Undocumented
5
+ #
6
+ # Represents Local Legend status on a segment.
7
+ #
8
+ # Local Legends are athletes with the most efforts on a segment in the last 90 days.
9
+ # This model contains information about the current local legend for a segment.
10
+ # This model is not documented in the official Strava API reference.
11
+ #
12
+ # @note This model is not documented by Strava API documentation. Properties
13
+ # are inferred from API responses.
14
+ #
15
+ # @see Strava::Models::DetailedSegment
16
+ #
17
+ # @example Accessing local legend information
18
+ # segment = client.segment(1234567)
19
+ # if segment.local_legend
20
+ # legend = segment.local_legend
21
+ # puts "Local Legend: #{legend.title}"
22
+ # puts "Athlete ID: #{legend.athlete_id}"
23
+ # puts "#{legend.effort_description} (#{legend.effort_count} efforts)"
24
+ # end
25
+ #
6
26
  class LocalLegend < Strava::Models::Response
27
+ # @return [Integer, nil] ID of the athlete who is the local legend
28
+ # @note Not documented by Strava API
7
29
  property 'athlete_id'
30
+
31
+ # @return [String, nil] Title or name of the local legend
32
+ # @note Not documented by Strava API
8
33
  property 'title'
34
+
35
+ # @return [String, nil] URL to the athlete's profile picture
36
+ # @note Not documented by Strava API
9
37
  property 'profile'
38
+
39
+ # @return [String, nil] Description of the effort count (e.g., "X efforts in the last 90 days")
40
+ # @note Not documented by Strava API
10
41
  property 'effort_description'
42
+
43
+ # @return [Integer, nil] Number of efforts by this athlete in the qualifying period
44
+ # @note Not documented by Strava API
11
45
  property 'effort_count'
46
+
47
+ # @return [Hash, nil] Detailed effort count data
48
+ # @note Not documented by Strava API
12
49
  property 'effort_counts'
50
+
51
+ # @return [Destination, nil] Link to related resource
52
+ # @note Not documented by Strava API
13
53
  property 'destination'
14
54
  end
15
55
  end
@@ -2,10 +2,41 @@
2
2
 
3
3
  module Strava
4
4
  module Models
5
+ #
6
+ # Represents a map with polyline data for an activity, route, or segment.
7
+ #
8
+ # Maps contain encoded polyline data that represents the geographic path.
9
+ # The polyline format uses Google's encoded polyline algorithm which can
10
+ # be decoded using libraries like the 'polylines' gem.
11
+ #
12
+ # @example Decode and use polyline data
13
+ # require 'polylines'
14
+ #
15
+ # activity = client.activity(1234567890)
16
+ # map = activity.map
17
+ #
18
+ # # Decode the summary polyline
19
+ # decoded = Polylines::Decoder.decode_polyline(map.summary_polyline)
20
+ # start_point = decoded.first # [latitude, longitude]
21
+ # end_point = decoded.last
22
+ #
23
+ # # Use with Google Maps Static API
24
+ # url = "https://maps.googleapis.com/maps/api/staticmap?" \
25
+ # "path=enc:#{CGI.escape(map.summary_polyline)}&size=800x800"
26
+ #
27
+ # @see https://developers.google.com/maps/documentation/utilities/polylinealgorithm
28
+ #
5
29
  class Map < Strava::Models::Response
30
+ # @return [String] Map identifier
6
31
  property 'id'
32
+
33
+ # @return [String] Google polyline encoding of the route (lower resolution)
7
34
  property 'summary_polyline'
35
+
36
+ # @return [Integer] Resource state indicator
8
37
  property 'resource_state'
38
+
39
+ # @return [String] Google polyline encoding of the route (higher resolution)
9
40
  property 'polyline'
10
41
  end
11
42
  end
@@ -2,11 +2,24 @@
2
2
 
3
3
  module Strava
4
4
  module Models
5
- # https://developers.strava.com/docs/reference/#api-models-MetaActivity
5
+ #
6
+ # Represents minimal activity information (meta level).
7
+ #
8
+ # Meta models contain only essential identifying information.
9
+ # Used in contexts where only basic activity reference is needed.
10
+ #
11
+ # @see SummaryActivity
12
+ # @see DetailedActivity
13
+ # @see https://developers.strava.com/docs/reference/#api-models-MetaActivity
14
+ #
6
15
  class MetaActivity < Strava::Models::Response
16
+ # @return [Integer] Activity identifier
7
17
  property 'id'
8
- # undocumented
18
+
19
+ # @return [Integer] Resource state indicator (1=meta)
9
20
  property 'resource_state'
21
+
22
+ # @return [String] Activity visibility (e.g., "everyone", "followers_only", "only_me")
10
23
  property 'visibility'
11
24
  end
12
25
  end
@@ -2,12 +2,27 @@
2
2
 
3
3
  module Strava
4
4
  module Models
5
- # https://developers.strava.com/docs/reference/#api-models-MetaAthlete
5
+ #
6
+ # Represents minimal athlete information (meta level).
7
+ #
8
+ # Meta models contain only essential identifying information without
9
+ # detailed data. Used in contexts where only basic athlete reference is needed.
10
+ #
11
+ # @see SummaryAthlete
12
+ # @see DetailedAthlete
13
+ # @see https://developers.strava.com/docs/reference/#api-models-MetaAthlete
14
+ #
6
15
  class MetaAthlete < Strava::Models::Response
16
+ # @return [Integer, nil] Athlete identifier (may be nil for privacy)
7
17
  property 'id'
8
- # undocumented
18
+
19
+ # @return [Integer] Resource state indicator (1=meta)
9
20
  property 'resource_state'
21
+
22
+ # @return [String, nil] Athlete's first name
10
23
  property 'firstname'
24
+
25
+ # @return [String, nil] Athlete's last name
11
26
  property 'lastname'
12
27
  end
13
28
  end
@@ -2,10 +2,24 @@
2
2
 
3
3
  module Strava
4
4
  module Models
5
- # https://developers.strava.com/docs/reference/#api-models-MetaClub
5
+ #
6
+ # Represents minimal club information (meta level).
7
+ #
8
+ # Meta models contain only essential identifying information.
9
+ # Used in contexts where only basic club reference is needed.
10
+ #
11
+ # @see SummaryClub
12
+ # @see DetailedClub
13
+ # @see https://developers.strava.com/docs/reference/#api-models-MetaClub
14
+ #
6
15
  class MetaClub < Strava::Models::Response
16
+ # @return [Integer] Club identifier
7
17
  property 'id'
18
+
19
+ # @return [Integer] Resource state indicator (1=meta)
8
20
  property 'resource_state'
21
+
22
+ # @return [String] Club name
9
23
  property 'name'
10
24
  end
11
25
  end
@@ -3,54 +3,132 @@
3
3
  module Strava
4
4
  module Models
5
5
  module Mixins
6
+ #
7
+ # Provides average speed and pace conversion methods.
8
+ #
9
+ # This mixin adds the average_speed property (stored in meters per second)
10
+ # and provides helper methods to convert it to various speed and pace formats.
11
+ # Pace is the inverse of speed (time per distance) and is commonly used for running.
12
+ #
13
+ # @example Using speed and pace helpers
14
+ # activity = client.activity(1234567890)
15
+ # puts activity.average_speed # => 3.5 (m/s)
16
+ # puts activity.average_speed_s # => "12.6km/h"
17
+ # puts activity.average_speed_miles_per_hour_s # => "7.8mph"
18
+ # puts activity.pace_per_kilometer_s # => "4m45s/km"
19
+ # puts activity.pace_per_mile_s # => "7m40s/mi"
20
+ #
6
21
  module AverageSpeed
7
22
  extend ActiveSupport::Concern
8
23
 
9
24
  included do
25
+ # @return [Float] Average speed in meters per second
10
26
  property 'average_speed'
11
27
  end
12
28
 
13
- # always in meters per second, even in imperial splits
29
+ #
30
+ # Returns average speed in meters per second (same as average_speed).
31
+ #
32
+ # @return [Float] Average speed in m/s
33
+ #
34
+ # @note Always in meters per second, even in imperial splits
35
+ #
14
36
  def average_speed_meters_per_second
15
37
  average_speed
16
38
  end
17
39
 
40
+ #
41
+ # Returns pace per mile.
42
+ #
43
+ # @return [String, nil] Pace formatted as "Xm%Ys/mi" (e.g., "7m30s/mi")
44
+ #
18
45
  def pace_per_mile_s
19
46
  convert_meters_per_second_to_pace average_speed, :mi
20
47
  end
21
48
 
49
+ #
50
+ # Returns pace per 100 yards (swimming).
51
+ #
52
+ # @return [String, nil] Pace formatted as "XmYs/100yd"
53
+ #
22
54
  def pace_per_100_yards_s
23
55
  convert_meters_per_second_to_pace average_speed, :'100yd'
24
56
  end
25
57
 
58
+ #
59
+ # Returns pace per 100 meters (swimming).
60
+ #
61
+ # @return [String, nil] Pace formatted as "XmYs/100m"
62
+ #
26
63
  def pace_per_100_meters_s
27
64
  convert_meters_per_second_to_pace average_speed, :'100m'
28
65
  end
29
66
 
67
+ #
68
+ # Returns pace per kilometer.
69
+ #
70
+ # @return [String, nil] Pace formatted as "XmYs/km" (e.g., "4m30s/km")
71
+ #
30
72
  def pace_per_kilometer_s
31
73
  convert_meters_per_second_to_pace average_speed, :km
32
74
  end
33
75
 
76
+ #
77
+ # Returns average speed in kilometers per hour.
78
+ #
79
+ # @return [String, nil] Speed formatted as "X.Xkm/h"
80
+ #
34
81
  def average_speed_kilometer_per_hour_s
35
82
  return unless average_speed&.positive?
36
83
 
37
84
  format('%.1fkm/h', average_speed * 3.6)
38
85
  end
39
86
 
87
+ #
88
+ # Returns average speed in miles per hour.
89
+ #
90
+ # @return [String, nil] Speed formatted as "X.Xmph"
91
+ #
40
92
  def average_speed_miles_per_hour_s
41
93
  return unless average_speed&.positive?
42
94
 
43
95
  format('%.1fmph', average_speed * 2.23694)
44
96
  end
45
97
 
98
+ #
99
+ # Returns default formatted average speed (kilometers per hour).
100
+ #
101
+ # @return [String, nil] Speed formatted as "X.Xkm/h"
102
+ #
46
103
  def average_speed_s
47
104
  average_speed_kilometer_per_hour_s
48
105
  end
49
106
 
107
+ #
108
+ # Returns default formatted pace (per kilometer).
109
+ #
110
+ # @return [String, nil] Pace formatted as "XmYs/km"
111
+ #
112
+ def pace_s
113
+ pace_per_kilometer_s
114
+ end
115
+
50
116
  private
51
117
 
52
- # Convert speed (m/s) to pace (min/mile or min/km) in the format of 'x:xx'
53
- # http://yizeng.me/2017/02/25/convert-speed-to-pace-programmatically-using-ruby
118
+ #
119
+ # Converts speed in meters per second to pace (time per distance).
120
+ #
121
+ # Pace is calculated as the inverse of speed. For example, running at 3.33 m/s
122
+ # equals a 5:00/km pace (1000m / 3.33m/s = 300 seconds = 5 minutes).
123
+ #
124
+ # @param speed [Float, nil] Speed in meters per second
125
+ # @param unit [Symbol] Unit for pace calculation (:mi, :km, :'100yd', :'100m')
126
+ # @return [String, nil] Formatted pace string or nil if speed is nil/zero
127
+ #
128
+ # @see http://yizeng.me/2017/02/25/convert-speed-to-pace-programmatically-using-ruby
129
+ #
130
+ # @api private
131
+ #
54
132
  def convert_meters_per_second_to_pace(speed, unit = :mi)
55
133
  return unless speed&.positive?
56
134
 
@@ -3,57 +3,82 @@
3
3
  module Strava
4
4
  module Models
5
5
  module Mixins
6
+ #
7
+ # Provides distance conversion and formatting methods.
8
+ #
9
+ # This mixin is included in models that have a distance property, such as
10
+ # activities, laps, segments, and routes. It provides helper methods to
11
+ # convert and format distances in various units.
12
+ #
13
+ # @example Using distance helpers
14
+ # activity = client.activity(1234567890)
15
+ # puts activity.distance # => 10000.0 (meters)
16
+ # puts activity.distance_s # => "10.0km"
17
+ # puts activity.distance_in_miles_s # => "6.21mi"
18
+ # puts activity.distance_in_yards_s # => "10936.1yd"
19
+ #
6
20
  module Distance
7
21
  extend ActiveSupport::Concern
8
22
 
9
23
  included do
24
+ # @return [Float] Distance in meters
10
25
  property 'distance'
11
26
  end
12
27
 
28
+ # @return [Float] Distance in meters
13
29
  def distance_in_meters
14
30
  distance
15
31
  end
16
32
 
33
+ # @return [Float] Distance in feet
17
34
  def distance_in_feet
18
35
  distance * 3.28084
19
36
  end
20
37
 
38
+ # @return [Float] Distance in miles
21
39
  def distance_in_miles
22
40
  distance_in_meters * 0.00062137
23
41
  end
24
42
 
43
+ # @return [String, nil] Formatted distance in miles (e.g., "6.21mi")
25
44
  def distance_in_miles_s
26
45
  return unless distance&.positive?
27
46
 
28
47
  format('%gmi', format('%.2f', distance_in_miles))
29
48
  end
30
49
 
50
+ # @return [Float] Distance in yards
31
51
  def distance_in_yards
32
52
  distance_in_meters * 1.09361
33
53
  end
34
54
 
55
+ # @return [String, nil] Formatted distance in yards (e.g., "10936.1yd")
35
56
  def distance_in_yards_s
36
57
  return unless distance&.positive?
37
58
 
38
59
  format('%gyd', format('%.1f', distance_in_yards))
39
60
  end
40
61
 
62
+ # @return [String, nil] Formatted distance in meters (e.g., "10000m")
41
63
  def distance_in_meters_s
42
64
  return unless distance&.positive?
43
65
 
44
66
  format('%gm', format('%d', distance_in_meters))
45
67
  end
46
68
 
69
+ # @return [Float] Distance in kilometers
47
70
  def distance_in_kilometers
48
71
  distance_in_meters / 1000
49
72
  end
50
73
 
74
+ # @return [String, nil] Formatted distance in kilometers (e.g., "10.0km")
51
75
  def distance_in_kilometers_s
52
76
  return unless distance&.positive?
53
77
 
54
78
  format('%gkm', format('%.2f', distance_in_kilometers))
55
79
  end
56
80
 
81
+ # @return [String, nil] Default formatted distance (same as distance_in_kilometers_s)
57
82
  def distance_s
58
83
  distance_in_kilometers_s
59
84
  end
@@ -3,14 +3,32 @@
3
3
  module Strava
4
4
  module Models
5
5
  module Mixins
6
+ #
7
+ # Provides elapsed time formatting methods.
8
+ #
9
+ # Elapsed time is the total time from start to finish of an activity, including
10
+ # stopped/paused time. This mixin adds the elapsed_time property and a helper
11
+ # method to format it as hours/minutes/seconds.
12
+ #
13
+ # @example Using elapsed time helpers
14
+ # activity = client.activity(1234567890)
15
+ # puts activity.elapsed_time # => 4200 (seconds)
16
+ # puts activity.elapsed_time_in_hours_s # => "1h10m"
17
+ #
6
18
  module ElapsedTime
7
19
  extend ActiveSupport::Concern
8
20
  include TimeInHours
9
21
 
10
22
  included do
23
+ # @return [Integer] Total elapsed time in seconds (includes stopped time)
11
24
  property 'elapsed_time'
12
25
  end
13
26
 
27
+ #
28
+ # Returns formatted elapsed time as hours, minutes, and seconds.
29
+ #
30
+ # @return [String, nil] Formatted time string (e.g., "1h23m45s", "45m30s", "30s")
31
+ #
14
32
  def elapsed_time_in_hours_s
15
33
  time_in_hours_s elapsed_time
16
34
  end
@@ -3,33 +3,75 @@
3
3
  module Strava
4
4
  module Models
5
5
  module Mixins
6
+ #
7
+ # Provides elevation difference conversion and formatting methods.
8
+ #
9
+ # Elevation difference represents the net elevation change (can be positive
10
+ # or negative) for a split or segment. This differs from elevation_gain which
11
+ # only counts upward movement.
12
+ #
13
+ # This mixin adds the elevation_difference property and helper methods to
14
+ # convert and format it in meters or feet.
15
+ #
16
+ # @example Using elevation difference helpers
17
+ # split = activity.splits_metric.first
18
+ # puts split.elevation_difference # => -15.2 (meters, downhill)
19
+ # puts split.elevation_difference_s # => "-15.2m"
20
+ # puts split.elevation_difference_in_feet_s # => "-49.9ft"
21
+ #
6
22
  module ElevationDifference
7
23
  extend ActiveSupport::Concern
8
24
 
9
25
  included do
26
+ # @return [Float] Net elevation difference in meters (positive = uphill, negative = downhill)
10
27
  property 'elevation_difference'
11
28
  end
12
29
 
30
+ #
31
+ # Returns elevation difference in feet.
32
+ #
33
+ # @return [Float] Elevation difference in feet
34
+ #
13
35
  def elevation_difference_in_feet
14
36
  elevation_difference * 3.28084
15
37
  end
16
38
 
39
+ #
40
+ # Returns elevation difference in meters (same as elevation_difference).
41
+ #
42
+ # @return [Float] Elevation difference in meters
43
+ #
17
44
  def elevation_difference_in_meters
18
45
  elevation_difference
19
46
  end
20
47
 
48
+ #
49
+ # Returns formatted elevation difference in meters.
50
+ #
51
+ # @return [String, nil] Formatted elevation (e.g., "15.2m" or "-15.2m")
52
+ #
21
53
  def elevation_difference_in_meters_s
22
54
  return if elevation_difference.nil?
23
55
 
24
56
  format('%gm', format('%.1f', elevation_difference_in_meters))
25
57
  end
26
58
 
59
+ #
60
+ # Returns formatted elevation difference in feet.
61
+ #
62
+ # @return [String, nil] Formatted elevation (e.g., "49.9ft" or "-49.9ft")
63
+ #
27
64
  def elevation_difference_in_feet_s
28
65
  return if elevation_difference.nil?
29
66
 
30
67
  format('%gft', format('%.1f', elevation_difference_in_feet))
31
68
  end
32
69
 
70
+ #
71
+ # Returns default formatted elevation difference (meters).
72
+ #
73
+ # @return [String, nil] Formatted elevation (e.g., "15.2m" or "-15.2m")
74
+ #
33
75
  def elevation_difference_s
34
76
  elevation_difference_in_meters_s
35
77
  end
@@ -3,33 +3,72 @@
3
3
  module Strava
4
4
  module Models
5
5
  module Mixins
6
+ #
7
+ # Provides elevation gain conversion and formatting methods.
8
+ #
9
+ # Elevation gain represents the total upward elevation change during an activity.
10
+ # This mixin adds the elevation_gain property and helper methods to convert
11
+ # and format it in meters or feet.
12
+ #
13
+ # @example Using elevation gain helpers
14
+ # route = client.route(1234567)
15
+ # puts route.elevation_gain # => 450.5 (meters)
16
+ # puts route.elevation_gain_s # => "450.5m"
17
+ # puts route.elevation_gain_in_feet_s # => "1478.0ft"
18
+ #
6
19
  module ElevationGain
7
20
  extend ActiveSupport::Concern
8
21
 
9
22
  included do
23
+ # @return [Float] Elevation gain in meters
10
24
  property 'elevation_gain'
11
25
  end
12
26
 
27
+ #
28
+ # Returns elevation gain in feet.
29
+ #
30
+ # @return [Float] Elevation gain in feet
31
+ #
13
32
  def elevation_gain_in_feet
14
33
  elevation_gain * 3.28084
15
34
  end
16
35
 
36
+ #
37
+ # Returns elevation gain in meters (same as elevation_gain).
38
+ #
39
+ # @return [Float] Elevation gain in meters
40
+ #
17
41
  def elevation_gain_in_meters
18
42
  elevation_gain
19
43
  end
20
44
 
45
+ #
46
+ # Returns formatted elevation gain in meters.
47
+ #
48
+ # @return [String, nil] Formatted elevation (e.g., "450.5m")
49
+ #
21
50
  def elevation_gain_in_meters_s
22
51
  return if elevation_gain.nil?
23
52
 
24
53
  format('%gm', format('%.1f', elevation_gain_in_meters))
25
54
  end
26
55
 
56
+ #
57
+ # Returns formatted elevation gain in feet.
58
+ #
59
+ # @return [String, nil] Formatted elevation (e.g., "1478.0ft")
60
+ #
27
61
  def elevation_gain_in_feet_s
28
62
  return if elevation_gain.nil?
29
63
 
30
64
  format('%gft', format('%.1f', elevation_gain_in_feet))
31
65
  end
32
66
 
67
+ #
68
+ # Returns default formatted elevation gain (meters).
69
+ #
70
+ # @return [String, nil] Formatted elevation (e.g., "450.5m")
71
+ #
33
72
  def elevation_gain_s
34
73
  elevation_gain_in_meters_s
35
74
  end