diff --git a/CHANGELOG.md b/CHANGELOG.md index 562d578..184bfcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ -### 3.0.1 (Next) +### 3.1.0 (Next) +* [#100](https://github.com/dblock/strava-ruby-client/pull/100): Adds comprehensive rdoc documentation - [@dblock](https://github.com/dblock). * Your contribution here. ### 3.0.0 (2025/10/24) diff --git a/lib/strava/api/client.rb b/lib/strava/api/client.rb index 4968a40..b5de1f9 100644 --- a/lib/strava/api/client.rb +++ b/lib/strava/api/client.rb @@ -2,6 +2,33 @@ module Strava module Api + # + # Main API client for interacting with the Strava API v3. + # + # This class provides a complete Ruby interface to the Strava API, including support for: + # * Activities (create, read, update, list) + # * Athletes (profile, stats, zones) + # * Clubs (details, members, activities) + # * Gear (equipment details) + # * Routes (details, GPX/TCX export) + # * Segments and Segment Efforts + # * Streams (activity, route, segment data) + # * Uploads (activity file uploads) + # * OAuth (token refresh, deauthorization) + # + # @example Create a client with an access token + # client = Strava::Api::Client.new(access_token: "your_access_token") + # athlete = client.athlete + # activities = client.athlete_activities(per_page: 10) + # + # @example Configure globally + # Strava::Api::Client.configure do |config| + # config.access_token = "your_access_token" + # end + # client = Strava::Api::Client.new + # + # @see https://developers.strava.com/docs/reference/ Strava API Documentation + # class Client < Strava::Web::Client include Endpoints::Activities include Endpoints::Athletes @@ -16,6 +43,26 @@ class Client < Strava::Web::Client attr_accessor(*Config::ATTRIBUTES) + # + # Initialize a new API client. + # + # @param [Hash] options Configuration options for the client + # @option options [String] :access_token OAuth access token for API authentication (required) + # @option options [String] :endpoint API endpoint URL (defaults to https://www.strava.com/api/v3) + # @option options [String] :user_agent User agent string for HTTP requests + # @option options [Logger] :logger Logger instance for request/response logging + # @option options [Integer] :timeout HTTP request timeout in seconds + # @option options [Integer] :open_timeout HTTP connection timeout in seconds + # @option options [String] :proxy HTTP proxy URL + # @option options [String] :ca_path Path to SSL CA certificates + # @option options [String] :ca_file Path to SSL CA certificate file + # + # @example + # client = Strava::Api::Client.new( + # access_token: "your_access_token", + # user_agent: "My Strava App/1.0" + # ) + # def initialize(options = {}) Config::ATTRIBUTES.each do |key| send("#{key}=", options[key] || Strava::Api.config.send(key)) @@ -23,15 +70,37 @@ def initialize(options = {}) super end + # + # Returns HTTP headers for API requests. + # + # @return [Hash] Headers including OAuth bearer token authorization + # @api private + # def headers { 'Authorization' => "Bearer #{access_token}" } end class << self + # + # Configure the API client globally. + # + # @yield [Config] Configuration object + # @return [Strava::Api::Config] Configuration object + # + # @example + # Strava::Api::Client.configure do |config| + # config.access_token = "your_access_token" + # end + # def configure block_given? ? yield(Config) : Config end + # + # Returns the configuration object. + # + # @return [Strava::Api::Config] Configuration object + # def config Config end diff --git a/lib/strava/api/config.rb b/lib/strava/api/config.rb index d8c1eb9..659d058 100644 --- a/lib/strava/api/config.rb +++ b/lib/strava/api/config.rb @@ -2,9 +2,25 @@ module Strava module Api + # + # Configuration module for the Strava API client. + # + # This module manages configuration settings for the API client, including + # the API endpoint URL and access token for authentication. + # + # @example Configure the API client + # Strava::Api.configure do |config| + # config.access_token = 'your_access_token_here' + # end + # + # @example Access current configuration + # Strava::Api.config.endpoint + # # => 'https://www.strava.com/api/v3' + # module Config extend self + # @return [Array] List of configurable attributes ATTRIBUTES = %i[ endpoint access_token @@ -12,6 +28,14 @@ module Config attr_accessor(*Config::ATTRIBUTES) + # + # Reset configuration to default values. + # + # Sets the endpoint to the default Strava API v3 URL and + # clears the access token. + # + # @return [void] + # def reset self.endpoint = 'https://www.strava.com/api/v3' self.access_token = nil @@ -19,10 +43,26 @@ def reset end class << self + # + # Configure the API client with a block. + # + # @yield [Config] Yields the configuration module for setup + # @return [Module] The Config module + # + # @example + # Strava::Api.configure do |config| + # config.access_token = ENV['STRAVA_ACCESS_TOKEN'] + # end + # def configure block_given? ? yield(Config) : Config end + # + # Returns the current API configuration. + # + # @return [Module] The Config module + # def config Config end diff --git a/lib/strava/api/cursor.rb b/lib/strava/api/cursor.rb index 73302a7..2d18e27 100644 --- a/lib/strava/api/cursor.rb +++ b/lib/strava/api/cursor.rb @@ -2,11 +2,42 @@ module Strava module Api + # + # Handles paginated iteration through API endpoints. + # + # This class provides an Enumerable interface for iterating through + # paginated API responses. It supports both traditional page-based + # pagination and cursor-based pagination. + # + # @see Strava::Api::Pagination + # + # @example Iterating through pages + # cursor = Strava::Api::Cursor.new(client, 'athlete/activities', per_page: 30) + # cursor.each do |page| + # page.each do |activity| + # puts activity.name + # end + # end + # class Cursor include Enumerable - attr_reader :client, :path, :params + # @return [Strava::Api::Client] API client instance + attr_reader :client + # @return [String] API endpoint path + attr_reader :path + + # @return [Hash] Query parameters for the request + attr_reader :params + + # + # Initialize a new Cursor for paginated iteration. + # + # @param client [Strava::Api::Client] API client to use for requests + # @param path [String] API endpoint path to paginate + # @param params [Hash] Query parameters (excluding :limit which is removed) + # def initialize(client, path, params = {}) @client = client @path = path diff --git a/lib/strava/api/endpoints/activities.rb b/lib/strava/api/endpoints/activities.rb index 8ec85b2..fc23aee 100644 --- a/lib/strava/api/endpoints/activities.rb +++ b/lib/strava/api/endpoints/activities.rb @@ -3,19 +3,70 @@ module Strava module Api module Endpoints + # + # API endpoints for Strava activities. + # + # Activities represent workouts, rides, runs, and other athletic pursuits recorded on Strava. + # This module provides methods for creating, retrieving, updating, and listing activities, + # as well as accessing related data like comments, kudos, laps, and zones. + # + # @see https://developers.strava.com/docs/reference/#api-Activities + # module Activities # - # Create an activity. + # Create a manual activity. + # + # Creates a new manual activity for an athlete. Requires write access. + # + # @param [Hash] options Activity attributes + # @option options [String] :name Activity name (required) + # @option options [String] :sport_type Activity type (required, e.g., 'Run', 'Ride', 'Swim') + # @option options [Time, String] :start_date_local Local start date and time (required) + # @option options [Integer] :elapsed_time Activity duration in seconds (required) + # @option options [String] :description Activity description + # @option options [Float] :distance Distance in meters + # @option options [Boolean] :trainer Whether activity was on a trainer + # @option options [Boolean] :commute Whether activity was a commute + # + # @return [Strava::Models::DetailedActivity] The created activity + # + # @example + # activity = client.create_activity( + # name: 'Morning Run', + # sport_type: 'Run', + # start_date_local: Time.now, + # elapsed_time: 3600, + # distance: 10000, + # description: 'Easy recovery run' + # ) + # + # @see https://developers.strava.com/docs/reference/#api-Activities-createActivity # def create_activity(options = {}) Strava::Models::DetailedActivity.new(post('activities', options)) end # - # Get activity. + # Get detailed information about a specific activity. + # + # Returns the given activity that is owned by the authenticated athlete. + # Includes all activity details such as splits, laps, segment efforts, and photos. # - # @option options [String] :id - # Activity id. + # @param [Integer, Hash] id_or_options Activity ID or options hash with :id key + # @param [Hash] options Additional options + # @option options [Boolean] :include_all_efforts Include all segment efforts + # + # @return [Strava::Models::DetailedActivity] The activity + # + # @example Get activity by ID + # activity = client.activity(1234567890) + # puts activity.name + # puts activity.distance_s + # + # @example Get activity with options hash + # activity = client.activity(id: 1234567890, include_all_efforts: true) + # + # @see https://developers.strava.com/docs/reference/#api-Activities-getActivityById # def activity(id_or_options, options = {}) id, options = parse_args(id_or_options, options) @@ -23,17 +74,29 @@ def activity(id_or_options, options = {}) end # - # List activity comments. + # List comments for an activity. + # + # Returns the comments on the given activity. Supports cursor-based pagination. # - # @option options [String] :id - # Activity id. - # @option options [Integer] :page - # Page number. - # @option options [Integer] :page_size - # Number of items per page. Defaults to 30. - # @option options [Integer] :per_page - # Number of items per page. Defaults to 30. - # @deprecated use {page_size} + # @param [Integer, Hash] id_or_options Activity ID or options hash with :id key + # @param [Hash] options Pagination options + # @option options [Integer] :page_size Number of items per page (default: 30) + # @option options [String] :after_cursor Cursor for pagination + # @option options [Integer] :limit Maximum number of items to return + # @option options [Integer] :per_page (Deprecated) Use :page_size instead + # + # @yield [Strava::Models::Comment] Yields each comment if block given + # @return [Array] Array of comments + # + # @example Get all comments + # comments = client.activity_comments(1234567890) + # + # @example Paginate through comments + # client.activity_comments(1234567890, page_size: 50) do |comment| + # puts "#{comment.athlete.username}: #{comment.text}" + # end + # + # @see https://developers.strava.com/docs/reference/#api-Activities-getCommentsByActivityId # def activity_comments(id_or_options, options = {}, &block) id, options = parse_args(id_or_options, options) @@ -41,14 +104,26 @@ def activity_comments(id_or_options, options = {}, &block) end # - # List activity photos. + # List photos for an activity. + # + # Returns the photos on the given activity. This is an undocumented Strava API endpoint. + # By default, retrieves full-size photos (5000px). # - # @option options [String] :id - # Activity id. - # @option options [Integer] :page - # Page number. - # @option options [Integer] :per_page - # Number of items per page. Defaults to 30. + # @param [Integer, Hash] id_or_options Activity ID or options hash with :id key + # @param [Hash] options Pagination options + # @option options [Integer] :size Photo size in pixels (default: 5000 for full size) + # @option options [Integer] :page Page number + # @option options [Integer] :per_page Number of items per page (default: 30) + # + # @yield [Strava::Models::DetailedPhoto] Yields each photo if block given + # @return [Array] Array of photos + # + # @example Get all photos + # photos = client.activity_photos(1234567890) + # photos.each { |photo| puts photo.urls } + # + # @example Get specific size + # photos = client.activity_photos(1234567890, size: 1920) # def activity_photos(id_or_options, options = {}, &block) id, options = parse_args(id_or_options, options) @@ -57,14 +132,23 @@ def activity_photos(id_or_options, options = {}, &block) end # - # List activity kudoers. + # List athletes who kudoed an activity. + # + # Returns the athletes who kudoed an activity identified by an identifier. + # + # @param [Integer, Hash] id_or_options Activity ID or options hash with :id key + # @param [Hash] options Pagination options + # @option options [Integer] :page Page number + # @option options [Integer] :per_page Number of items per page (default: 30) # - # @option options [String] :id - # Activity id. - # @option options [Integer] :page - # Page number. - # @option options [Integer] :per_page - # Number of items per page. Defaults to 30. + # @yield [Strava::Models::SummaryAthlete] Yields each athlete if block given + # @return [Array] Array of athletes + # + # @example Get all kudoers + # kudoers = client.activity_kudos(1234567890) + # kudoers.each { |athlete| puts athlete.username } + # + # @see https://developers.strava.com/docs/reference/#api-Activities-getKudoersByActivityId # def activity_kudos(id_or_options, options = {}, &block) id, options = parse_args(id_or_options, options) @@ -72,10 +156,21 @@ def activity_kudos(id_or_options, options = {}, &block) end # - # Get activity laps. + # List laps for an activity. + # + # Returns the laps of an activity identified by an identifier. + # Laps are split segments either created manually or auto-generated. + # + # @param [Integer, Hash] id_or_options Activity ID or options hash with :id key + # @param [Hash] options Additional options + # + # @return [Array] Array of laps + # + # @example Get activity laps + # laps = client.activity_laps(1234567890) + # laps.each { |lap| puts "#{lap.name}: #{lap.distance_s}" } # - # @option options [String] :id - # Activity id. + # @see https://developers.strava.com/docs/reference/#api-Activities-getLapsByActivityId # def activity_laps(id_or_options, options = {}) id, options = parse_args(id_or_options, options) @@ -85,16 +180,36 @@ def activity_laps(id_or_options, options = {}) end # - # List logged-in athlete activities. + # List activities for the authenticated athlete. + # + # Returns the currently logged-in athlete's activities. Supports pagination and time-based filtering. + # Activities are returned in descending order by start date. + # + # @param [Hash] options Pagination and filtering options + # @option options [Time, Integer] :before Epoch timestamp or Time object for filtering activities before this time + # @option options [Time, Integer] :after Epoch timestamp or Time object for filtering activities after this time + # @option options [Integer] :page Page number + # @option options [Integer] :per_page Number of items per page (default: 30) + # @option options [Integer] :limit Maximum number of items to return + # + # @yield [Strava::Models::SummaryActivity] Yields each activity if block given + # @return [Array] Array of activities + # + # @example Get recent activities + # activities = client.athlete_activities(per_page: 10) + # + # @example Get activities with time filter + # activities = client.athlete_activities( + # after: Time.now - 7.days, + # per_page: 50 + # ) # - # @option options [Integer] :before - # An epoch timestamp to use for filtering activities that have taken place before a certain time. - # @option options [Integer] :after - # An epoch timestamp to use for filtering activities that have taken place after a certain time. - # @option options [Integer] :page - # Page number. - # @option options [Integer] :per_page - # Number of items per page. Defaults to 30. + # @example Paginate through all activities + # client.athlete_activities(per_page: 100) do |activity| + # puts "#{activity.name}: #{activity.distance_s}" + # end + # + # @see https://developers.strava.com/docs/reference/#api-Activities-getLoggedInAthleteActivities # def athlete_activities(options = {}, &block) options = options.dup if options.key?(:after) || options.key?(:before) @@ -104,10 +219,24 @@ def athlete_activities(options = {}, &block) end # - # Get activity zones. + # Get zones for an activity. + # + # Returns the zones of a given activity. Summit feature required. + # Zones include heart rate and power zones with distribution buckets. + # + # @param [Integer, Hash] id_or_options Activity ID or options hash with :id key + # @param [Hash] options Additional options + # + # @return [Array] Array of activity zones + # + # @example Get activity zones + # zones = client.activity_zones(1234567890) + # zones.each do |zone| + # puts "Type: #{zone.type}" + # zone.distribution_buckets.each { |bucket| puts "#{bucket.min}-#{bucket.max}: #{bucket.time}s" } + # end # - # @option options [String] :id - # Activity id. + # @see https://developers.strava.com/docs/reference/#api-Activities-getZonesByActivityId # def activity_zones(id_or_options, options = {}) id, options = parse_args(id_or_options, options) @@ -119,20 +248,31 @@ def activity_zones(id_or_options, options = {}) # # Update an activity. # - # @option options [String] :id - # Activity id. - # @option options [Boolean] :commute - # Whether this activity is a commute. - # @option options [Boolean] :trainer - # Whether this activity was recorded on a training machine. - # @option options [String] :description - # The description of the activity. - # @option options [String] :name - # The name of the activity. - # @option options [String] :sport_type - # Activity type. - # @option options [String] :gear_id - # Identifier for the gear associated with the activity. Specifying "none" clears gear from activity. + # Updates the given activity that is owned by the authenticated athlete. + # Requires write access. + # + # @param [Integer, Hash] id_or_options Activity ID or options hash with :id key + # @param [Hash] options Activity attributes to update + # @option options [Boolean] :commute Whether this activity is a commute + # @option options [Boolean] :trainer Whether this activity was recorded on a training machine + # @option options [String] :description Activity description + # @option options [String] :name Activity name + # @option options [String] :sport_type Activity type (e.g., 'Run', 'Ride') + # @option options [String] :gear_id Equipment ID (specify "none" to clear gear) + # + # @return [Strava::Models::DetailedActivity] The updated activity + # + # @example Update activity name and description + # activity = client.update_activity( + # id: 1234567890, + # name: 'Updated Activity Name', + # description: 'New description' + # ) + # + # @example Mark as commute + # activity = client.update_activity(1234567890, commute: true) + # + # @see https://developers.strava.com/docs/reference/#api-Activities-updateActivityById # def update_activity(id_or_options, options = {}) id, options = parse_args(id_or_options, options) diff --git a/lib/strava/api/endpoints/athletes.rb b/lib/strava/api/endpoints/athletes.rb index b90757c..4c2b69a 100644 --- a/lib/strava/api/endpoints/athletes.rb +++ b/lib/strava/api/endpoints/athletes.rb @@ -3,26 +3,72 @@ module Strava module Api module Endpoints + # + # API endpoints for Strava athletes. + # + # Athletes represent Strava users. This module provides methods for retrieving + # athlete profiles, stats, zones, and updating athlete information. + # + # @see https://developers.strava.com/docs/reference/#api-Athletes + # module Athletes # - # Returns the currently authenticated athlete. + # Get the authenticated athlete's profile. + # + # Returns the currently authenticated athlete with detailed information. + # Requires profile:read_all scope for full profile access. + # + # @return [Strava::Models::DetailedAthlete] The authenticated athlete + # + # @example Get athlete profile + # athlete = client.athlete + # puts "#{athlete.firstname} #{athlete.lastname}" + # puts "Location: #{athlete.city}, #{athlete.state}" + # + # @see https://developers.strava.com/docs/reference/#api-Athletes-getLoggedInAthlete # def athlete Strava::Models::DetailedAthlete.new(get('athlete')) end # - # Returns the the authenticated athlete's heart rate and power zones. + # Get the authenticated athlete's heart rate and power zones. + # + # Returns the athlete's configured training zones for heart rate and power. + # These zones are used for training analysis and activity classification. + # + # @param [Hash] options Additional options + # + # @return [Strava::Models::Zones] Athlete zones including heart rate and power + # + # @example Get athlete zones + # zones = client.athlete_zones + # heart_rate = zones.heart_rate + # heart_rate.zones.each { |zone| puts "#{zone.min}-#{zone.max} bpm" } + # + # @see https://developers.strava.com/docs/reference/#api-Athletes-getLoggedInAthleteZones # def athlete_zones(options = {}) Strava::Models::Zones.new(get('athlete/zones', options)) end # - # Returns the activity stats of an athlete. + # Get activity statistics for an athlete. # - # @option options [String] :id - # Athlete id. + # Returns the activity stats of an athlete including recent totals, year-to-date totals, + # and all-time totals for different activity types. + # + # @param [Integer, Hash] id_or_options Athlete ID or options hash with :id key + # @param [Hash] options Additional options + # + # @return [Strava::Models::ActivityStats] Activity statistics + # + # @example Get athlete stats + # stats = client.athlete_stats(12345) + # recent_run = stats.recent_run_totals + # puts "Recent runs: #{recent_run.count} totaling #{recent_run.distance_s}" + # + # @see https://developers.strava.com/docs/reference/#api-Athletes-getStats # def athlete_stats(id_or_options, options = {}) id, options = parse_args(id_or_options, options) @@ -30,10 +76,20 @@ def athlete_stats(id_or_options, options = {}) end # - # Update the currently authenticated athlete. + # Update the authenticated athlete's profile. + # + # Updates the currently authenticated athlete. Only weight can be updated. + # Requires profile:write scope. + # + # @param [Hash] options Athlete attributes to update + # @option options [Float] :weight Athlete weight in kilograms + # + # @return [Strava::Models::DetailedAthlete] The updated athlete profile + # + # @example Update athlete weight + # athlete = client.update_athlete(weight: 70.5) # - # @option options [Float] :weight - # The weight of the athlete in kilograms. + # @see https://developers.strava.com/docs/reference/#api-Athletes-updateLoggedInAthlete # def update_athlete(options = {}) Strava::Models::DetailedAthlete.new(put('athlete', options)) diff --git a/lib/strava/api/endpoints/clubs.rb b/lib/strava/api/endpoints/clubs.rb index f13c0e3..473164a 100644 --- a/lib/strava/api/endpoints/clubs.rb +++ b/lib/strava/api/endpoints/clubs.rb @@ -3,16 +3,29 @@ module Strava module Api module Endpoints + # + # API endpoints for Strava clubs. + # + # Clubs represent groups of athletes on Strava. Club API endpoints allow + # you to retrieve club information, list club members and administrators, + # and view recent club activities and events. + # + # @see https://developers.strava.com/docs/reference/#api-Clubs + # module Clubs # # List club activities. # - # @option options [String] :id - # Club id. - # @option options [Integer] :page - # Page number. - # @option options [Integer] :per_page - # Number of items per page. Defaults to 30. + # @param id_or_options [String, Integer, Hash] Either a club ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) + # @option options [Integer] :page Page number + # @option options [Integer] :per_page Number of items per page. Defaults to 30 + # + # @example With ID and options as separate arguments + # client.club_activities('12345', per_page: 10) + # + # @example With ID in options hash + # client.club_activities(id: '12345', per_page: 10) # def club_activities(id_or_options, options = {}, &block) id, options = parse_args(id_or_options, options) @@ -22,12 +35,10 @@ def club_activities(id_or_options, options = {}, &block) # # List club / group events. # - # @option options [String] :id - # Club id. - # @option options [Integer] :page - # Page number. - # @option options [Integer] :per_page - # Number of items per page. Defaults to 30. + # @param id_or_options [String, Integer, Hash] Either a club ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) + # @option options [Integer] :page Page number + # @option options [Integer] :per_page Number of items per page. Defaults to 30 # def club_events(id_or_options, options = {}, &block) id, options = parse_args(id_or_options, options) @@ -37,12 +48,10 @@ def club_events(id_or_options, options = {}, &block) # # List club administrators. # - # @option options [String] :id - # Club id. - # @option options [Integer] :page - # Page number. - # @option options [Integer] :per_page - # Number of items per page. Defaults to 30. + # @param id_or_options [String, Integer, Hash] Either a club ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) + # @option options [Integer] :page Page number + # @option options [Integer] :per_page Number of items per page. Defaults to 30 # def club_admins(id_or_options, options = {}, &block) id, options = parse_args(id_or_options, options) @@ -52,8 +61,8 @@ def club_admins(id_or_options, options = {}, &block) # # Get club. # - # @option options [String] :id - # Club id. + # @param id_or_options [String, Integer, Hash] Either a club ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) # def club(id_or_options, options = {}) id, options = parse_args(id_or_options, options) @@ -63,12 +72,10 @@ def club(id_or_options, options = {}) # # List club members. # - # @option options [String] :id - # Club id. - # @option options [Integer] :page - # Page number. - # @option options [Integer] :per_page - # Number of items per page. Defaults to 30. + # @param id_or_options [String, Integer, Hash] Either a club ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) + # @option options [Integer] :page Page number + # @option options [Integer] :per_page Number of items per page. Defaults to 30 # def club_members(id_or_options, options = {}, &block) id, options = parse_args(id_or_options, options) diff --git a/lib/strava/api/endpoints/gears.rb b/lib/strava/api/endpoints/gears.rb index a463147..1e84000 100644 --- a/lib/strava/api/endpoints/gears.rb +++ b/lib/strava/api/endpoints/gears.rb @@ -3,12 +3,21 @@ module Strava module Api module Endpoints + # + # API endpoints for Strava gears. + # + # Gears (also known as equipment) represent bikes and shoes used by athletes in + # their activities. Gears track usage statistics like total distance and can be + # associated with specific activities. + # + # @see https://developers.strava.com/docs/reference/#api-Gears + # module Gears # # Returns an equipment using its identifier. # - # @option options [String] :id - # Gear id. + # @param id_or_options [String, Integer, Hash] Either a gear ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) # def gear(id_or_options, options = {}) id, options = parse_args(id_or_options, options) diff --git a/lib/strava/api/endpoints/oauth.rb b/lib/strava/api/endpoints/oauth.rb index f387c6c..a4dd510 100644 --- a/lib/strava/api/endpoints/oauth.rb +++ b/lib/strava/api/endpoints/oauth.rb @@ -3,10 +3,32 @@ module Strava module Api module Endpoints + # + # API endpoints for OAuth operations. + # + # OAuth endpoints provide functionality for managing OAuth access tokens, + # including the ability to revoke an athlete's access token and deauthorize + # an application's access to their data. + # + # @see https://developers.strava.com/docs/authentication/ + # module OAuth # # Revoke access to an athlete's data. # + # Deauthorizes the application and revokes the athlete's access token. + # After calling this method, the access token will no longer be valid + # and the athlete will need to reauthorize the application. + # + # @param options [Hash] Additional options for the deauthorization request + # + # @return [Authorization] Authorization response with revocation confirmation + # + # @example Deauthorize the current athlete + # client.deauthorize + # + # @see https://developers.strava.com/docs/authentication/#deauthorization + # def deauthorize(options = {}) Strava::Models::Authorization.new(post('deauthorize', { endpoint: Strava::OAuth.config.endpoint }.merge(options))) end diff --git a/lib/strava/api/endpoints/routes.rb b/lib/strava/api/endpoints/routes.rb index cb43ce8..cd3e8fc 100644 --- a/lib/strava/api/endpoints/routes.rb +++ b/lib/strava/api/endpoints/routes.rb @@ -3,12 +3,21 @@ module Strava module Api module Endpoints + # + # API endpoints for Strava routes. + # + # Routes are manually-created paths on Strava that can be used for planning. + # Routes can be exported in GPX or TCX format and can include waypoints, + # elevation data, and other metadata. + # + # @see https://developers.strava.com/docs/reference/#api-Routes + # module Routes # # Returns a GPX file of the route. # - # @option options [String] :id - # Route id. + # @param id_or_options [String, Integer, Hash] Either a route ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) # def export_route_gpx(id_or_options, options = {}) id, options = parse_args(id_or_options, options) @@ -16,10 +25,10 @@ def export_route_gpx(id_or_options, options = {}) end # - # Returns a TCS file of the route. + # Returns a TCX file of the route. # - # @option options [String] :id - # Route id. + # @param id_or_options [String, Integer, Hash] Either a route ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) # def export_route_tcx(id_or_options, options = {}) id, options = parse_args(id_or_options, options) @@ -29,8 +38,8 @@ def export_route_tcx(id_or_options, options = {}) # # Returns a route using its identifier. # - # @option options [String] :id - # Route id. + # @param id_or_options [String, Integer, Hash] Either a route ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) # def route(id_or_options, options = {}) id, options = parse_args(id_or_options, options) @@ -40,12 +49,10 @@ def route(id_or_options, options = {}) # # Returns a list of the routes created by the authenticated athlete using their athlete ID. # - # @option options [Integer] :id - # The identifier of the athlete. - # @option options [Integer] :page - # Page number. - # @option options [Integer] :per_page - # Number of items per page. Defaults to 30. + # @param id_or_options [String, Integer, Hash] Either an athlete ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) + # @option options [Integer] :page Page number + # @option options [Integer] :per_page Number of items per page. Defaults to 30 # def athlete_routes(id_or_options, options = {}, &block) id, options = parse_args(id_or_options, options) diff --git a/lib/strava/api/endpoints/segment_efforts.rb b/lib/strava/api/endpoints/segment_efforts.rb index 4366f3e..3fd298d 100644 --- a/lib/strava/api/endpoints/segment_efforts.rb +++ b/lib/strava/api/endpoints/segment_efforts.rb @@ -3,12 +3,21 @@ module Strava module Api module Endpoints + # + # API endpoints for Strava segment efforts. + # + # Segment efforts represent an athlete's attempt on a specific segment during an + # activity. Each time an activity passes through a segment, a segment effort is + # created with details like elapsed time, distance, and ranking information. + # + # @see https://developers.strava.com/docs/reference/#api-SegmentEfforts + # module SegmentEfforts # # Returns a segment effort from an activity that is owned by the authenticated athlete. # - # @option options [String] :id - # The identifier of the segment effort. + # @param id_or_options [String, Integer, Hash] Either a segment effort ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) # def segment_effort(id_or_options, options = {}) id, options = parse_args(id_or_options, options) @@ -18,12 +27,10 @@ def segment_effort(id_or_options, options = {}) # # Returns a set of the authenticated athlete's segment efforts for a given segment. # - # @option options [Integer] :id - # The identifier of the segment. - # @option options [Integer] :page - # Page number. - # @option options [Integer] :per_page - # Number of items per page. Defaults to 30. + # @param id_or_options [String, Integer, Hash] Either a segment ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) + # @option options [Integer] :page Page number + # @option options [Integer] :per_page Number of items per page. Defaults to 30 # def segment_efforts(id_or_options, options = {}, &block) id, options = parse_args(id_or_options, options) diff --git a/lib/strava/api/endpoints/segments.rb b/lib/strava/api/endpoints/segments.rb index e69676c..f2cd5a8 100644 --- a/lib/strava/api/endpoints/segments.rb +++ b/lib/strava/api/endpoints/segments.rb @@ -3,18 +3,36 @@ module Strava module Api module Endpoints + # + # API endpoints for Strava segments. + # + # Segments are specific sections of road or trail where athletes can compete for time. + # This module provides methods for exploring, retrieving, and starring segments. + # + # @see https://developers.strava.com/docs/reference/#api-Segments + # module Segments # - # Returns the top 10 segments matching a specified query. + # Explore segments in a geographic area. # - # @option options [Array[Float]] :bounds - # The latitude and longitude for two points describing a rectangular boundary for the search: [southwest corner latitude, southwest corner longitude, northeast corner latitude, northeast corner longitude]. - # @option options [String] :activity_type - # Desired activity type. May take one of the following values: running, riding. - # @option options [Integer] :min_cat - # The minimum climbing category. - # @option options [Integer] :max_cat - # The maximum climbing category. + # Returns the top 10 segments matching a specified query within a rectangular + # geographic boundary. Useful for discovering popular segments in an area. + # + # @param [Hash] options Search parameters + # @option options [Array] :bounds Required. Rectangular boundary as [sw_lat, sw_lng, ne_lat, ne_lng] + # @option options [String] :activity_type Activity type: 'running' or 'riding' + # @option options [Integer] :min_cat Minimum climbing category (0-5) + # @option options [Integer] :max_cat Maximum climbing category (0-5) + # + # @return [Array] Array of segments + # + # @example Explore segments in an area + # segments = client.explore_segments( + # bounds: [40.7,-74.0,40.8,-73.9], + # activity_type: 'running' + # ) + # + # @see https://developers.strava.com/docs/reference/#api-Segments-exploreSegments # def explore_segments(options = {}) throw ArgumentError.new('Required argument :bounds missing') if options[:bounds].nil? @@ -28,10 +46,21 @@ def explore_segments(options = {}) # # List of the authenticated athlete's starred segments. # - # @option options [Integer] :page - # Page number. - # @option options [Integer] :per_page - # Number of items per page. Defaults to 30. + # Returns a paginated list of segments that the authenticated athlete has starred. + # Starred segments are favorites that the athlete wants to track their performance on. + # + # @param options [Hash] Pagination options + # @option options [Integer] :page Page number + # @option options [Integer] :per_page Number of items per page. Defaults to 30 + # + # @yield [SummarySegment] Yields each segment in the paginated results + # @return [Array, Cursor] Array of starred segments or Cursor for iteration + # + # @example List all starred segments + # starred = client.starred_segments + # starred.each do |segment| + # puts "#{segment.name}: #{segment.distance_s}" + # end # def starred_segments(options = {}, &block) paginate 'segments/starred', options, Strava::Models::SummarySegment, &block @@ -40,8 +69,17 @@ def starred_segments(options = {}, &block) # # Returns the specified segment. # - # @option options [String] :id - # The identifier of the segment. + # Retrieves detailed information about a specific segment including location, + # elevation profile, and the athlete's personal records on that segment. + # + # @param id_or_options [String, Integer, Hash] Either a segment ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) + # + # @return [DetailedSegment] The detailed segment information + # + # @example Get a segment by ID + # segment = client.segment(229781) + # puts "#{segment.name}: #{segment.distance_s} with #{segment.average_grade}% grade" # def segment(id_or_options, options = {}) id, options = parse_args(id_or_options, options) @@ -51,10 +89,23 @@ def segment(id_or_options, options = {}) # # Stars/Unstars the given segment for the authenticated athlete. # - # @option options [String] :id - # The identifier of the segment to star. - # @option options [Boolean] :starred - # If true, star the segment; if false, unstar the segment. + # Adds or removes a segment from the athlete's starred/favorite segments. + # Starred segments appear in the athlete's starred segments list and can + # be used to track performance over time. + # + # @param id_or_options [String, Integer, Hash] Either a segment ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) + # @option options [Boolean] :starred If true, star the segment; if false, unstar the segment (required) + # + # @return [DetailedSegment] The updated segment with new starred status + # + # @raise [ArgumentError] If :starred option is not provided + # + # @example Star a segment + # segment = client.star_segment(229781, starred: true) + # + # @example Unstar a segment + # segment = client.star_segment(229781, starred: false) # def star_segment(id_or_options, options = {}) id, options = parse_args(id_or_options, options) diff --git a/lib/strava/api/endpoints/streams.rb b/lib/strava/api/endpoints/streams.rb index 4c6fa8e..f3c9b58 100644 --- a/lib/strava/api/endpoints/streams.rb +++ b/lib/strava/api/endpoints/streams.rb @@ -3,16 +3,24 @@ module Strava module Api module Endpoints + # + # API endpoints for Strava streams. + # + # Streams represent the raw data of uploaded activities. They can be thought of + # as a collection of time series data, where each data point is sampled at a + # specific time or distance. Available stream types include altitude, cadence, + # distance, heartrate, latlng, moving, power, temp, time, and velocity. + # + # @see https://developers.strava.com/docs/reference/#api-Streams + # module Streams # # Returns the given activity's streams. # - # @option options [String] :id - # The identifier of the activity. - # @option options [Array[String]] :keys - # Desired stream types. - # @option options [Boolean] :key_by_type - # Must be true. + # @param id_or_options [String, Integer, Hash] Either an activity ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) + # @option options [Array] :keys Desired stream types (e.g., ['time', 'latlng', 'distance', 'altitude']) + # @option options [Boolean] :key_by_type Must be true # def activity_streams(id_or_options, options = {}) id, options = parse_args(id_or_options, options) @@ -25,12 +33,10 @@ def activity_streams(id_or_options, options = {}) # # Returns a set of streams for a segment effort completed by the authenticated athlete. # - # @option options [String] :id - # The identifier of the segment effort. - # @option options [Array[String]] :keys - # The types of streams to return. - # @option options [Boolean] :key_by_type - # Must be true. + # @param id_or_options [String, Integer, Hash] Either a segment effort ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) + # @option options [Array] :keys The types of streams to return + # @option options [Boolean] :key_by_type Must be true # def segment_effort_streams(id_or_options, options = {}) id, options = parse_args(id_or_options, options) @@ -43,12 +49,10 @@ def segment_effort_streams(id_or_options, options = {}) # # Returns the given segment's streams. # - # @option options [String] :id - # The identifier of the segment. - # @option options [Array[String]] :keys - # The types of streams to return. - # @option options [Boolean] :key_by_type - # Must be true. + # @param id_or_options [String, Integer, Hash] Either a segment ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) + # @option options [Array] :keys The types of streams to return + # @option options [Boolean] :key_by_type Must be true # def segment_streams(id_or_options, options = {}) id, options = parse_args(id_or_options, options) diff --git a/lib/strava/api/endpoints/uploads.rb b/lib/strava/api/endpoints/uploads.rb index 09b1006..9b7e201 100644 --- a/lib/strava/api/endpoints/uploads.rb +++ b/lib/strava/api/endpoints/uploads.rb @@ -3,6 +3,16 @@ module Strava module Api module Endpoints + # + # API endpoints for Strava uploads. + # + # Uploads allow you to create activities by uploading activity files in various + # formats such as FIT, TCX, and GPX. After uploading, the file is processed + # asynchronously and you can check the upload status to determine when the + # activity has been created. + # + # @see https://developers.strava.com/docs/reference/#api-Uploads + # module Uploads # # Uploads a new data file to create an activity from. @@ -29,8 +39,8 @@ def create_upload(options = {}) # # Returns an upload for a given identifier. # - # @option options [String] :id - # The identifier of the upload. + # @param id_or_options [String, Integer, Hash] Either an upload ID or a hash of options including :id + # @param options [Hash] Additional options (if first parameter is an ID) # def upload(id_or_options, options = {}) id, options = parse_args(id_or_options, options) diff --git a/lib/strava/api/pagination.rb b/lib/strava/api/pagination.rb index c10609e..692e8f4 100644 --- a/lib/strava/api/pagination.rb +++ b/lib/strava/api/pagination.rb @@ -41,16 +41,41 @@ def size @collection.size end + # + # Iterates over each item in the paginated collection. + # + # @yield [Object] Yields each item in the collection + # @return [void] + # def each @collection.each { |c| yield c if block_given? } end private + # + # Delegates missing methods to the underlying collection. + # + # Allows the Pagination object to respond to array methods by + # forwarding them to the internal collection array. + # + # @api private + # @param method_symbol [Symbol] The method name to forward + # @param args [Array] Arguments to pass to the method + # @return [Object] Result from the delegated method + # def method_missing(method_symbol, ...) @collection.send(method_symbol, ...) end + # + # Checks if the underlying collection responds to a method. + # + # @api private + # @param method_name [Symbol] The method name to check + # @param include_private [Boolean] Whether to include private methods + # @return [Boolean] true if the collection responds to the method + # def respond_to_missing?(method_name, include_private = false) super end diff --git a/lib/strava/api/ratelimit.rb b/lib/strava/api/ratelimit.rb index ea0daef..c229dff 100644 --- a/lib/strava/api/ratelimit.rb +++ b/lib/strava/api/ratelimit.rb @@ -2,7 +2,41 @@ module Strava module Api + # + # Handles Strava API rate limit information from HTTP responses. + # + # Strava enforces rate limits on API requests with two windows: + # - 15-minute window: 600 requests per 15 minutes (short-term limit) + # - Daily window: 30,000 requests per day (long-term limit) + # + # This class extracts and provides easy access to rate limit data from + # HTTP response headers. + # + # @see Strava::Web::ApiResponse + # @see https://developers.strava.com/docs/rate-limits/ Strava API Rate Limiting documentation + # + # @example Checking rate limits after an API call + # activity = client.activity(1234567890) + # ratelimit = activity.http_response.ratelimit + # + # puts "15-minute limit: #{ratelimit.fifteen_minutes_limit}" + # puts "15-minute usage: #{ratelimit.fifteen_minutes_usage}" + # puts "15-minute remaining: #{ratelimit.fifteen_minutes_remaining}" + # + # puts "Daily limit: #{ratelimit.total_day}" + # puts "Daily usage: #{ratelimit.total_day_usage}" + # puts "Daily remaining: #{ratelimit.total_day_remaining}" + # + # if ratelimit.exceeded? + # puts "Rate limit exceeded!" + # end + # class Ratelimit + # + # Initialize a new Ratelimit instance from an HTTP response. + # + # @param response [Faraday::Response] HTTP response containing rate limit headers + # def initialize(response) @response = response @headers = response.headers diff --git a/lib/strava/deep_copyable.rb b/lib/strava/deep_copyable.rb index 55290a9..02154c4 100644 --- a/lib/strava/deep_copyable.rb +++ b/lib/strava/deep_copyable.rb @@ -1,9 +1,28 @@ # frozen_string_literal: true module Strava + # + # Mixin module for deep copying objects. + # + # This module provides a helper method for creating deep copies of objects + # using Ruby's Marshal serialization. Unlike shallow cloning (which only + # duplicates the top-level object), deep copying recursively duplicates all + # nested objects and collections. + # + # This is particularly useful when working with model objects that contain + # nested hashes or arrays, ensuring modifications to the copy don't affect + # the original object. + # + # @example Deep copying a nested hash + # include Strava::DeepCopyable + # original = {a: {b: [1, 2, 3]}} + # copy = deep_copy(original) + # copy[:a][:b] << 4 + # original[:a][:b] # => [1, 2, 3] (unchanged) + # module DeepCopyable # - # Ruby's way of creating a true deep copy/clone + # Ruby's way of creating a true deep copy/clone. # # @param [Object] obj of any kind # diff --git a/lib/strava/errors/fault.rb b/lib/strava/errors/fault.rb index f36990e..482f74b 100644 --- a/lib/strava/errors/fault.rb +++ b/lib/strava/errors/fault.rb @@ -2,15 +2,47 @@ module Strava module Errors + # + # Base error class for Strava API client errors. + # + # This exception is raised when the Strava API returns an error response. + # It extends Faraday::ClientError and provides convenient access to the + # error message, response headers, and detailed error information from + # the API response body. + # + # @see Faraday::ClientError + # class Fault < ::Faraday::ClientError + # + # Returns the error message from the API response. + # + # Extracts the message from the response body if available, + # otherwise falls back to the parent class message. + # + # @return [String] The error message + # def message response[:body]['message'] || super end + # + # Returns the HTTP response headers. + # + # @return [Hash] The response headers hash + # def headers response[:headers] end + # + # Returns detailed error information from the API response. + # + # The Strava API may return an 'errors' array in the response body + # containing detailed information about validation errors or other + # specific error conditions. + # + # @return [Array, nil] Array of error details, or nil if not present + # def errors response[:body]['errors'] end diff --git a/lib/strava/errors/ratelimit_error.rb b/lib/strava/errors/ratelimit_error.rb index df7188b..e9ac478 100644 --- a/lib/strava/errors/ratelimit_error.rb +++ b/lib/strava/errors/ratelimit_error.rb @@ -2,22 +2,57 @@ module Strava module Errors + # + # Exception raised when API rate limits are exceeded. + # + # Strava enforces rate limits on API requests to prevent abuse. When your + # application exceeds these limits (either per 15 minutes or per day), the + # API returns a 429 Too Many Requests response, which triggers this exception. + # + # This error includes rate limit information extracted from the response headers, + # allowing you to determine current usage and when limits will reset. + # + # @see Strava::Api::Ratelimit + # @see https://developers.strava.com/docs/rate-limits/ + # class RatelimitError < ::Faraday::ClientError + # @return [Strava::Api::Ratelimit] Rate limit information from response headers attr_reader :ratelimit + # + # Initialize a new rate limit error. + # + # @param env [Faraday::Env] The Faraday request environment + # @param response [Hash] The HTTP response hash + # def initialize(env, response) @ratelimit = Strava::Api::Ratelimit.new(env.response) super(response) end + # + # Returns the error message from the API response. + # + # @return [String] The error message + # def message response[:body]['message'] || super end + # + # Returns the HTTP response headers. + # + # @return [Hash] The response headers hash + # def headers response[:headers] end + # + # Returns detailed error information from the API response. + # + # @return [Array, nil] Array of error details, or nil if not present + # def errors response[:body]['errors'] end diff --git a/lib/strava/errors/upload_error.rb b/lib/strava/errors/upload_error.rb index c8709d4..0b69d62 100644 --- a/lib/strava/errors/upload_error.rb +++ b/lib/strava/errors/upload_error.rb @@ -2,29 +2,84 @@ module Strava module Errors + # + # Exception raised when an activity upload fails. + # + # When uploading activity files (FIT, TCX, GPX) to Strava, the API processes + # the file asynchronously and may encounter errors during processing. This + # exception is raised when the upload fails, and includes information about + # the upload status and error details. + # + # The error provides access to the upload model, which contains detailed + # information about what went wrong during the upload processing. + # + # @see Strava::Models::Upload + # @see https://developers.strava.com/docs/reference/#api-Uploads + # class UploadError < ::Faraday::ClientError + # + # Returns the HTTP response status code. + # + # @return [Integer] The HTTP status code + # def status response[:status] end + # + # Returns the HTTP response headers. + # + # @return [Hash] The response headers hash + # def headers response[:headers] end + # + # Returns the error message from the upload response. + # + # Extracts the error message from the response body's 'error' field, + # or falls back to the parent class message if not available. + # + # @return [String] The error message + # def message body[:error] || super end + # + # Returns the upload processing status from the response. + # + # The upload status indicates where in the processing pipeline the + # upload failed (e.g., 'error', 'processing', 'ready'). + # + # @return [String] The upload status + # def error_status body[:status] end + # + # Returns the upload model containing detailed upload information. + # + # The upload model includes fields like id, external_id, error, + # status, and activity_id (if the upload succeeded before encountering + # an error). + # + # @return [Strava::Models::Upload] The upload model + # def upload @upload ||= Strava::Models::Upload.new(body) end private + # + # Returns the response body with indifferent access. + # + # @api private + # @return [HashWithIndifferentAccess] The response body hash + # def body (response[:body] || {}).with_indifferent_access end diff --git a/lib/strava/logger.rb b/lib/strava/logger.rb index a3e2013..bf3eb9a 100644 --- a/lib/strava/logger.rb +++ b/lib/strava/logger.rb @@ -3,7 +3,34 @@ require 'logger' module Strava + # + # Default logger for the Strava client library. + # + # Extends the standard Ruby Logger class and provides a default logger + # instance configured to output warnings and errors to STDOUT. The logger + # can be customized via the Web configuration to control HTTP request/response + # logging. + # + # @example Using the default logger + # Strava::Logger.logger.info('Making API request') + # + # @example Customizing the logger + # Strava::Web.configure do |config| + # config.logger = Logger.new('strava.log') + # end + # + # @see Logger + # class Logger < ::Logger + # + # Returns the default logger instance. + # + # Creates a memoized logger that outputs to STDOUT with a default + # log level of WARN. This logger is used by default for HTTP request + # logging unless overridden in the configuration. + # + # @return [Strava::Logger] The default logger instance + # def self.logger @logger ||= begin logger = new $stdout diff --git a/lib/strava/models/achievement.rb b/lib/strava/models/achievement.rb index 54c0e00..a9aecb7 100644 --- a/lib/strava/models/achievement.rb +++ b/lib/strava/models/achievement.rb @@ -2,10 +2,37 @@ module Strava module Models - # Undocumented + # + # Represents an achievement earned during a segment effort. + # + # Achievements include overall rank on a segment, yearly/monthly/weekly records, + # and PR (personal record) designations. This model is not documented in the + # official Strava API reference. + # + # @note This model is not documented by Strava API documentation. Properties + # are inferred from API responses. + # + # @see Strava::Models::DetailedSegmentEffort + # + # @example Accessing achievements from a segment effort + # activity = client.activity(1234567890) + # activity.segment_efforts.each do |effort| + # effort.achievements.each do |achievement| + # puts "#{achievement.type}: Rank #{achievement.rank}" + # end + # end + # class Achievement < Strava::Models::Response + # @return [Integer, nil] Rank or position for this achievement (e.g., 1 for first place) + # @note Not documented by Strava API property 'rank' + + # @return [String, nil] Type of achievement (e.g., "overall", "pr", "year_pr") + # @note Not documented by Strava API property 'type' + + # @return [Integer, nil] Numeric identifier for the achievement type + # @note Not documented by Strava API property 'type_id' end end diff --git a/lib/strava/models/activity_stats.rb b/lib/strava/models/activity_stats.rb index cad96c3..bc77edc 100644 --- a/lib/strava/models/activity_stats.rb +++ b/lib/strava/models/activity_stats.rb @@ -2,18 +2,64 @@ module Strava module Models - # https://developers.strava.com/docs/reference/#api-models-ActivityStats + # + # Represents aggregated activity statistics for an athlete. + # + # Contains totals broken down by activity type (ride, run, swim) and time period + # (recent, year-to-date, all-time). Recent totals cover the last 4 weeks. + # + # @example Get athlete statistics + # stats = client.athlete_stats(12345) + # + # # Recent activity + # recent = stats.recent_run_totals + # puts "Recent runs: #{recent.count} activities" + # puts "Distance: #{recent.distance_s}" + # puts "Time: #{recent.moving_time_in_hours_s}" + # + # # Year-to-date + # ytd = stats.ytd_ride_totals + # puts "YTD rides: #{ytd.count} totaling #{ytd.distance_s}" + # + # # All-time records + # puts "Biggest ride: #{stats.biggest_ride_distance}m" + # puts "Biggest climb: #{stats.biggest_climb_elevation_gain}m" + # + # @see ActivityTotal + # @see https://developers.strava.com/docs/reference/#api-models-ActivityStats + # class ActivityStats < Strava::Models::Response + # @return [Float] Longest ride distance in meters property 'biggest_ride_distance' + + # @return [Float] Most elevation gain in a single activity (meters) property 'biggest_climb_elevation_gain' + + # @return [ActivityTotal] Recent ride totals (last 4 weeks) property 'recent_ride_totals', transform_with: ->(v) { Strava::Models::ActivityTotal.new(v) } + + # @return [ActivityTotal] Recent run totals (last 4 weeks) property 'recent_run_totals', transform_with: ->(v) { Strava::Models::ActivityTotal.new(v) } + + # @return [ActivityTotal] Recent swim totals (last 4 weeks) property 'recent_swim_totals', transform_with: ->(v) { Strava::Models::ActivityTotal.new(v) } + + # @return [ActivityTotal] Year-to-date ride totals property 'ytd_ride_totals', transform_with: ->(v) { Strava::Models::ActivityTotal.new(v) } + + # @return [ActivityTotal] Year-to-date run totals property 'ytd_run_totals', transform_with: ->(v) { Strava::Models::ActivityTotal.new(v) } + + # @return [ActivityTotal] Year-to-date swim totals property 'ytd_swim_totals', transform_with: ->(v) { Strava::Models::ActivityTotal.new(v) } + + # @return [ActivityTotal] All-time ride totals property 'all_ride_totals', transform_with: ->(v) { Strava::Models::ActivityTotal.new(v) } + + # @return [ActivityTotal] All-time run totals property 'all_run_totals', transform_with: ->(v) { Strava::Models::ActivityTotal.new(v) } + + # @return [ActivityTotal] All-time swim totals property 'all_swim_totals', transform_with: ->(v) { Strava::Models::ActivityTotal.new(v) } end end diff --git a/lib/strava/models/activity_total.rb b/lib/strava/models/activity_total.rb index 1527888..276af59 100644 --- a/lib/strava/models/activity_total.rb +++ b/lib/strava/models/activity_total.rb @@ -2,13 +2,37 @@ module Strava module Models - # https://developers.strava.com/docs/reference/#api-models-ActivityTotal + # + # Represents totals for a specific activity type and time period. + # + # Contains aggregated counts, distance, time, and elevation for activities. + # Used within ActivityStats to break down statistics by activity type + # (ride, run, swim) and period (recent, YTD, all-time). + # + # Includes helper mixins for formatting distance, time, and elevation. + # + # @example Access activity totals + # stats = client.athlete_stats(12345) + # totals = stats.recent_run_totals + # puts "#{totals.count} runs" + # puts "Total distance: #{totals.distance_s}" + # puts "Moving time: #{totals.moving_time_in_hours_s}" + # puts "Elevation: #{totals.elevation_gain_s}" + # puts "Achievements: #{totals.achievement_count}" + # + # @see ActivityStats + # @see https://developers.strava.com/docs/reference/#api-models-ActivityTotal + # class ActivityTotal < Strava::Models::Response + # @return [Integer] Number of activities property 'count' + include Mixins::Distance include Mixins::MovingTime include Mixins::ElapsedTime include Mixins::ElevationGain + + # @return [Integer] Total number of achievements/trophies earned property 'achievement_count' end end diff --git a/lib/strava/models/activity_zone.rb b/lib/strava/models/activity_zone.rb index 53e0f6b..837bf41 100644 --- a/lib/strava/models/activity_zone.rb +++ b/lib/strava/models/activity_zone.rb @@ -2,16 +2,53 @@ module Strava module Models - # https://developers.strava.com/docs/reference/#api-models-ActivityZone + # + # Represents time spent in heart rate or power zones during an activity. + # + # Activity zones show how much time was spent at different intensity levels, + # which helps analyze training load and effort distribution. Each zone bucket + # contains the time spent in that zone range. + # + # @see https://developers.strava.com/docs/reference/#api-models-ActivityZone Strava API ActivityZone reference + # @see Strava::Models::TimedZoneRange + # @see Strava::Api::Client#activity_zones + # + # @example Analyzing time in heart rate zones + # zones = client.activity_zones(1234567890) + # hr_zone = zones.find { |z| z.type == 'heartrate' } + # + # if hr_zone + # puts "Max HR: #{hr_zone.max}" + # puts "Sensor-based: #{hr_zone.sensor_based}" + # hr_zone.distribution_buckets.each_with_index do |bucket, i| + # puts "Zone #{i + 1}: #{bucket.time} seconds (#{bucket.min}-#{bucket.max} bpm)" + # end + # end + # class ActivityZone < Strava::Models::Response + # @return [Integer, nil] Relative effort score for this activity property 'score' + + # @return [Array] Array of time spent in each zone bucket property 'distribution_buckets', transform_with: ->(v) { v.map { |r| Strava::Models::TimedZoneRange.new(r) } } + + # @return [String] Type of zone data ("heartrate" or "power") property 'type' + + # @return [Boolean] Whether data comes from a sensor (vs. estimated) property 'sensor_based' + + # @return [Integer, nil] Points or score accumulated in this zone property 'points' + + # @return [Boolean] Whether custom zones are being used property 'custom_zones' + + # @return [Integer, nil] Maximum value recorded (e.g., max heart rate in bpm) property 'max' - # undocumented + + # @return [Integer] Resource state indicator + # @note Not documented by Strava API property 'resource_state' end end diff --git a/lib/strava/models/authorization.rb b/lib/strava/models/authorization.rb index 57d92d5..4d57e71 100644 --- a/lib/strava/models/authorization.rb +++ b/lib/strava/models/authorization.rb @@ -2,7 +2,20 @@ module Strava module Models + # + # Represents an authorization deauthorization response. + # + # This model is returned when revoking access to an athlete's data. + # It contains the access token that was revoked. + # + # @example Deauthorize an athlete + # authorization = client.deauthorize + # puts "Revoked token: #{authorization.access_token}" + # + # @see Strava::Api::Client#deauthorize + # class Authorization < Strava::Models::Response + # @return [String] The access token that was revoked property 'access_token' end end diff --git a/lib/strava/models/base_stream.rb b/lib/strava/models/base_stream.rb index ec14e0a..601f0fa 100644 --- a/lib/strava/models/base_stream.rb +++ b/lib/strava/models/base_stream.rb @@ -2,10 +2,31 @@ module Strava module Models - # https://developers.strava.com/docs/reference/#api-models-BaseStream + # + # Represents base stream metadata from the Strava API. + # + # Streams are time-series data for activities such as GPS coordinates, heart rate, + # power, cadence, etc. The BaseStream provides metadata about how the stream data + # was sampled and processed. + # + # @see https://developers.strava.com/docs/reference/#api-models-BaseStream Strava API BaseStream reference + # @see Strava::Models::Stream + # @see Strava::Models::StreamSet + # + # @example Accessing stream metadata + # streams = client.activity_streams(1234567890, keys: 'heartrate,watts') + # puts streams.heartrate.original_size # => 3600 + # puts streams.heartrate.resolution # => "high" + # puts streams.heartrate.series_type # => "distance" + # class BaseStream < Strava::Models::Response + # @return [Integer] Total number of data points in the original stream before any resampling property 'original_size' + + # @return [String] Resolution of the stream data ("low", "medium", or "high") property 'resolution' + + # @return [String] Type of series - how data points are indexed ("time" or "distance") property 'series_type' end end diff --git a/lib/strava/models/club_activity.rb b/lib/strava/models/club_activity.rb index c80cac0..de975d5 100644 --- a/lib/strava/models/club_activity.rb +++ b/lib/strava/models/club_activity.rb @@ -2,20 +2,45 @@ module Strava module Models - # https://developers.strava.com/docs/reference/#api-models-ClubActivity + # + # Represents an activity posted to a club feed. + # + # Club activities are a simplified view of activities shared with a club. + # Note that Strava does not return activity or athlete IDs via this API endpoint, + # so you cannot link directly to the full activity or athlete profile. + # + # Includes helper mixins for formatting distance, time, elevation, and sport type. + # + # @example List club activities + # activities = client.club_activities(12345) + # activities.each do |activity| + # puts "#{activity.athlete.name}: #{activity.name}" + # puts "#{activity.distance_s} in #{activity.moving_time_in_hours_s}" + # puts "#{activity.sport_type_emoji} #{activity.workout_type}" + # end + # + # @see https://developers.strava.com/docs/reference/#api-models-ClubActivity + # class ClubActivity < Strava::Models::Response include Mixins::ElevationGain include Mixins::SportType + # @return [MetaAthlete] Athlete who posted the activity (without ID) property 'athlete', transform_with: ->(v) { Strava::Models::MetaAthlete.new(v) } + + # @return [String] Activity name property 'name' + include Mixins::Distance include Mixins::MovingTime include Mixins::ElapsedTime include Mixins::TotalElevationGain include Mixins::SportType + + # @return [Integer, nil] Workout type (0=default, 1=race, 2=long run, 3=intervals, etc.) property 'workout_type' - # undocumented + + # @return [Integer] Resource state indicator property 'resource_state' end end diff --git a/lib/strava/models/club_athlete.rb b/lib/strava/models/club_athlete.rb index 339c749..9760792 100644 --- a/lib/strava/models/club_athlete.rb +++ b/lib/strava/models/club_athlete.rb @@ -2,17 +2,49 @@ module Strava module Models - # https://developers.strava.com/docs/reference/#api-models-ClubAthlete + # + # Represents an athlete within a club context. + # + # This model provides athlete information along with their club membership + # status, showing whether they are a regular member, administrator, or owner. + # + # @example List club members + # members = client.club_members(12345) + # members.each do |member| + # puts member.name + # puts "Admin" if member.admin + # puts "Owner" if member.owner + # end + # + # @see https://developers.strava.com/docs/reference/#api-models-ClubAthlete + # class ClubAthlete < Strava::Models::Response + # @return [Integer] Resource state indicator property 'resource_state' + + # @return [String] Athlete's first name property 'firstname' + + # @return [String] Athlete's last name property 'lastname' + + # @return [String, nil] Membership type (e.g., "member") property 'member' + + # @return [Boolean] Whether athlete is a club administrator property 'admin' + + # @return [Boolean] Whether athlete is the club owner property 'owner' - # undocumented + + # @return [String, nil] Membership status or type property 'membership' + # + # Returns the athlete's full name. + # + # @return [String, nil] Full name combining firstname and lastname, or nil if neither exists + # def name [firstname, lastname].compact.join(' ') if firstname || lastname end diff --git a/lib/strava/models/club_event.rb b/lib/strava/models/club_event.rb index 9a69e4f..1e480da 100644 --- a/lib/strava/models/club_event.rb +++ b/lib/strava/models/club_event.rb @@ -2,29 +2,92 @@ module Strava module Models - # Unodcumented + # + # Represents a club group event or ride. + # + # Club events are organized group activities where club members can join + # to ride, run, or participate together. Events can be recurring and may + # include a specific route. + # + # @example List club events + # events = client.club_events(12345) + # events.each do |event| + # puts event.title + # puts event.description + # puts "Organized by: #{event.organizing_athlete.name}" + # puts "Activity type: #{event.activity_type}" + # puts "Next occurrence: #{event.upcoming_occurrences.first}" + # puts event.strava_url + # end + # + # @see Strava::Api::Client#club_events + # class ClubEvent < Strava::Models::Response + # @return [Integer] Event identifier property 'id' + + # @return [Integer] Resource state indicator property 'resource_state' + + # @return [String] Event title property 'title' + + # @return [String] Event description property 'description' + + # @return [Integer] Club identifier property 'club_id' + + # @return [SummaryClub] The club hosting this event property 'club', transform_with: ->(c) { Strava::Models::SummaryClub.new(c) } + + # @return [SummaryAthlete] Athlete organizing the event property 'organizing_athlete', transform_with: ->(oa) { Strava::Models::SummaryAthlete.new(oa) } + + # @return [String] Type of activity (e.g., "Ride", "Run") property 'activity_type' + + # @return [Time] When the event was created property 'created_at', transform_with: ->(v) { Time.parse(v) if v&.length&.positive? } + + # @return [Integer, nil] Associated route identifier property 'route_id' + + # @return [Route, nil] Associated route details property 'route', transform_with: ->(r) { Strava::Models::Route.new(r) } + + # @return [Boolean] Whether event is for women only property 'women_only' + + # @return [Boolean] Whether event is private property 'private' + + # @return [Array] Skill levels for participants (0-5 scale) property 'skill_levels' + + # @return [Array] Terrain types (e.g., road, trail, mixed) property 'terrain' + + # @return [Array