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
Streams
The Strava API also has a Streams API call as well. This isn’t documented on the Strava API wiki, so use it at your own risk.
The Steam for a ride provides the raw data from your ride
-
altitude
-
cadence
-
distance
-
heartrate
-
latlng
-
time
-
watts
-
watts_calc
To get a the underlying stream for a ride, you can call
##returns a StravaApi::Streams
s.ride_streams(ride_id)
Or you can start from a ride (StravaApi::Ride) and call
my_ride.streams
Thanks to Andrew Olson (github.com/anolson) for this feature.
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
Copyright © 2010 Steven Chanin. See LICENSE for details.