strava-api

Strava (www.strava.com/) allows access to it’s data via a JSON api. This gem wraps that API an allows you to interact with Ruby classes instead.

Installation

gem install strava-api

depending on your installation, you may need to use sudo

Requirements

httparty [github.com/jnunemaker/httparty]

mocha (only if you want to run the tests) [github.com/floehopper/mocha]

Usage

StravaApi is a Ruby wrapper for working with Strava API.

Loading the StravaApi gem classes

Before you can use the gem’s classes and methods, you have to load them

in a Rails app

require 'strava-api'

in a plain irb session

require 'rubygems'
require 'strava-api'

Creating a StravaApi::Base object

In order to interact with the Strava API, you need a StravaApi::Base object

s = StravaApi::Base.new

Clubs

The Strava API allows you to search for clubs by name,

#returns an array of StravaApi::Club objects (each object only has minimal attributes)
s.clubs("part of club name")

get full details on a club,

#returns a StravaApi::Club with full attributes
s.club_show(club_id)

and see the members of a club

#returns an array of StravaApi::Member objects
s.club_members(club_id)

Alternatively, you can work in a more object oriented way. The .clubs(“part of club name”) method returns an array of StravaApi::Club objects. The StravaApi::Club object makes club_show and club_members available as instance methods

my_club = s.clubs("Fred's Riders").first
my_club.show
my_club.members

Rides

Using the API, you can search for rides using any combination of criteria

  • club_id: Optional. Id of the Club for which to search for member’s Rides.

  • athlete_id: Optional. Id of the Athlete for which to search for Rides.

  • athlete_name: Optional. Username of the Athlete for which to search for Rides.

  • start_date: Optional. Day on which to start search for Rides. The date should be formatted YYYY-MM-DD. The date is the local time of when the ride started.

  • end_date: Optional. Day on which to end search for Rides. The date should be formatted YYYY-MM-DD. The date is the local time of when the ride started.

  • start_id: Optional. Only return Rides with an Id greater than or equal to the startId.

  • offset: Optional. Rather than returning the first 50 matching rides, return the rides <offset> from the top of the results.

To get set of rides (by club),

#returns an array of up to 50 StravaApi::Ride objects (each object has minimal attributes)
s.rides(:club_id => club_id)

by athlete and start date

s.rides(:athlete_id => athlete_id, :start_date => Date.civil(2010,9,21))

etc

To see details on a particular ride

#returns a StravaApi::Ride with full attributes
s.ride_show(ride_id)

A ride is composed of a list of efforts (time, etc spent on each segment which makes up part of the ride). To see the efforts for a particular ride,

#returns an array of StravaApi::Effort objects (each object has minimal attributes)
s.ride_efforts(ride_id)

Alternatively, you can work in a more object oriented way. The .rides(:filter => value) method returns an array of StravaApi::Ride objects. The StravaApi::Ride object makes ride_show and ride_efforts available as instance methods

my_ride = s.rides(:club_id => my_club.id).first
my_ride.show
my_ride.efforts

When more than 50 rides match your criteria

In response to a .rides call, Strava will return the first 50 rides that match whatever criteria are supplied. To see all matching rides, you will need to call .rides and also include an :offset parameter

s.rides(:club_id => club_id, :offset => 50)

In order to see all the rides, you will have to nest your .rides call inside of some sort of loop that updates offset after processing each block of rides

offset = 0
while (rides = s.rides(:club_id => club_id, :offset => offset, :start_date => Date.civil(2010,9,15))) && !rides.empty? do
  #work on that set of rides
  offset += rides.size
end
puts "received a total of #{offset} rides"

Segments

Rides can be thought of as a sequence of segments (e.g. my “trip to the kitchen” is “up the stairs” + “down the hall” + “across the living room”). When rides are uploaded to Strava, it breaks them down into the segments they contain based on the route taken. Segments have names, measurements, and keep statistics about who rode them on which date, how long it took, etc.

To find a segment by name,

#returns an array of StravaApi::Segment objects (each object has minimal attributes)
s.segments("part of segment name")

to see measurements / details on a segment,

#returns a StravaApi::Segment with full attributes
s.segment_show(segment_id)

to see who rode a segment and how they did,

#returns an array of up to 50 StravaApi::Effort objects (each object has minimal attributes)
s.segment_efforts(segment_id, {:param => value})

To refine the data returned by segment_efforts, you can pass any combination of optional parameters:

  • club_id: Optional. Id of the Club for which to search for member’s Efforts.

  • athlete_id: Optional. Id of the Athlete for which to search for Efforts.

  • athlete_name: Optional. Username of the Athlete for which to search for Rides.

  • start_date: Optional. Day on which to start search for Efforts. The date should be formatted YYYY-MM-DD. The date is the local time of when the effort started.

  • end_date: Optional. Day on which to end search for Efforts. The date should be formatted YYYY-MM-DD. The date is the local time of when the effort started.

  • start_id: Optional. Only return Effforts with an Id greater than or equal to the startId.

  • best: Optional. Default is true. Whether to only show an athlete’s best effort (shortest elapsed time) vs. all their efforts if there are multiple matching effort for a single athlete.

  • offset: Optional. Rather than returning the first 50 matching rides, return the rides <offset> from the top of the results.

Example

#returns the rides on that segment by members of a specific club on or after 7/1/10.
#only shows each riders best time
s.segment_efforts(segment_id, :club_id => club_id, :start_date => Date.civil(2010,7,1), :best => true)

Alternatively, you can work in a more object oriented way. Given a StravaApi::Segment object returned by another method call, you can get it’s full details and the efforts made on that segment using the .show and .efforts instance methods

#my_segment is a StravaApi::Segment

my_segment.show

my_segment.efforts(:athlete_id => 123)

When more than 50 efforts match your criteria

In response to a .segment_efforts call, Strava will return the first 50 efforts that match whatever criteria are supplied. To see all matching efforts, you will need to call .segment_efforts and also include an :offset parameter.

See the instructions above for an example of how this works with .rides.

Efforts

To get full details on an effort (i.e. specifics on how a rider did on a specific stretch of a ride), you

##returns a StravaApi::Effort with full attributes
s.effort_show(effort_id)

If you have an efforts object (StravaApi::Effort) that has been returned by another call (for example my_ride.efforts), you can use the .show method to fill out the missing details on that effort

my_ride.efforts.first.show

Error Handling

The StravaApi gem raises errors under various conditions.

If an error is raised, check .errors on your StravaApi::Base object to see more details

s.errors

StravaApi::NetworkError

This is raised when the attempt to connect to Strava via httparty raises any of the following:

  • HTTParty::UnsupportedFormat

  • HTTParty::UnsupportedURIScheme

  • HTTParty::ResponseError

  • HTTParty::RedirectionTooDeep

StravaApi::InvalidResponseError

This is raised is the call to Strava returns a result describing an error rather than the data you requested.

StravaApi::CommandError

This is raised if your request causes Strava to return a 500 Error. This is also raised if you attempt to run a command that lacks required parameters. For example, trying to call

.clubs("")

with an empty string or

.rides()

with no options will raise this error.

StravaApi::InternalErrorInternalError

This is raised if you attempt to access an invalid property of a result. For example,

my_seg = s.segment_show(segment_id)
# => returns a <StravaApi::Segment>

my_seg.bogus_property

will raise a StravaApi::InternalErrorInternalError

this returns distance, maximum speed, average speed, etc, etc for that part of the ride.

Contributors

StravaApi is maintained by Steve Chanin.

Note on Patches/Pull Requests

  • Fork the project.

  • Make your feature addition or bug fix.

  • Add tests for it. This is important so I don’t break it in a future version unintentionally.

  • Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)

  • Send me a pull request. Bonus points for topic branches.

Copyright © 2010 Steven Chanin. See LICENSE for details.