HopSkipDrive API for drivers.
Unlike many other rideshares, HopSkipDrive is a scheduled ride service and not an on-demand ride service.
In our app for drivers, we show them a list of upcoming rides and they can pick the ones they want.
Our goal is to show them the best rides for each driver first, so to that end we have an internal scoring system.
In this exercise you will be implementing a slimmed down version of it
The driving distance between two addresses is the distance in miles that it would take to drive a reasonably efficient route between them. It is not the straight line distance. It can be calculated by using a routing service
The driving duration between two addresses is the amount of time in hours it would take to drive the driving distance under realistic driving conditions. It can be calculated by using a routing service
The commute distance for a ride is the driving distance from the driver’s home address to the start of the ride, in miles
The commute duration for a ride is the amount of time it is expected to take to drive the commute distance, in hours.
The ride distance for a ride is the driving distance from the start address of the ride to the destination address, in miles
The ride duration for a ride is the amount of time it is expected to take to drive the ride distance, in hours
The ride earnings is how much the driver earns by driving the ride. It takes into account both the amount of time the ride is expected to take and the distance.
For the purposes of this exercise, it is calculated as: $12 + $1.50 per mile beyond 5 miles + (ride duration) * $0.70 per minute beyond 15 minutes
The primary goal of this exercise is for you to demonstrate how you think about testing,
readability, and structuring a Rails application. We are also evaluating your ability to
understand and implement requirements.
-
Create a Rails 7 application, using Ruby 3+
-
Include the following entities:
- Ride
- Has an id, a start address and a destination address. You may end up adding additional information
- Driver
- Has an id and a home address
- Ride
-
Build a RESTful API endpoint that returns a paginated JSON list of rides in descending score order for a given driver
-
Please write up API documentation for this endpoint in MarkDown or alternative
-
Calculate the score of a ride in $ per hour as: (ride earnings) / (commute duration + ride duration). Higher is better
-
Google Maps is expensive. Consider how you can reduce duplicate API calls
-
Include RSpec tests
- Implements the upcoming rides endpoint to be used by the driver's app.
For this project I ended up making some design decisions to keep the project on track while developing the application:
-
I decided to use UUID for all IDs to increase information security.
-
Address table holds addresses for drivers and rides
- Address is a string that is valid for Google Directions API
- Location ID should be used in production to accurately query the same locations
- Google Place ID is an example of an ID that can be used to identify addresses
- Location ID should be used in production to accurately query the same locations
Similar addresses that share a location ID (potentially stored in a long-lived, distributed Solid Cache) can be used by both Driver and Ride and used in the API call to get commute and ride distance and duration.
- Address is a string that is valid for Google Directions API
-
I use the Rails Credentials to keep the Google Directions API Key secure in both development and production.
-
Because I'm working with two entities, Driver and Ride, I decided to use a module for the methods.
-
I decided to create a client class to call the Google Directions API.
-
I decided to use RSwag to provide OpenAPI UI compliance for the endpoint.
-
I use Pagy gem for pagination.
- It handles pagination via the query parameter and headers.
- I set the default to be 5 items per page.
- It handles pagination via the query parameter and headers.
-
I decided to cache the data returned for 5 minutes at a time.
- I think the new Solid Cache can a great candidate for storing data returned from the Google API.
- I think cache can be kept warm between the server restarts to continue to provide fast response from the server.
- I think the new Solid Cache can a great candidate for storing data returned from the Google API.
-
I considered adding the "pickup time" to the upcoming rides but for demo purposes I decided to leave it out so that rides can be retrieved at any time in the future.
I tried to make few assumptions but here they are:
- Ride start and destination addresses cannot be the same.
Libraries provide benefits such as retries, improved error-handling and backoff and should be used in production environments.
I considered using various libraries but ultimately decided to make a simple call instead for this project.
- Ruby on Rails
- PostgreSQL
- OpenAPI
- Clone the repository:
git clone https://github.com/arkadiysudarikov/hopskipdrive.git
- Install dependencies:
bundle install
- Set up the database:
rails db:setup
- Run Rails Credentials to add
google_api_key
:rails credentials:edit
- Run RSpec:
rspec
- Start the server:
rails server
UpcomingRides (RDoc)
GoogleDirectionsApiClient (RDoc)
Please refer to the project's OpenAPI specification for the project.
- I seed the database with a Driver ID
e76885d9-dc50-4616-830e-cd24beefd7d9
that can be used to try out the endpoint.
I offer both /api/v1/drivers/{driver_id}/upcoming_rides
and /api/v1/{driver_id}/upcoming_rides
endpoints.
[
{
"id": "b2d27489-27ab-4334-9940-6ae9eccbe0f1",
"start_address_id": "c23bfaad-5633-4d4b-a36b-9380d6ed0a69",
"destination_address_id": "9bfa1d1b-f07d-44ba-9aa2-18cf8b72a6af",
"created_at": "2024-02-16T04:05:16.517Z",
"updated_at": "2024-02-16T04:05:16.517Z",
"home_address": {
"id": "8bc8c7dd-4977-4655-8892-14143dbc4895",
"address": "1600 Amphitheatre Pkwy",
"created_at": "2024-02-16T04:05:16.451Z",
"updated_at": "2024-02-16T04:05:16.451Z"
},
"start_address": {
"id": "c23bfaad-5633-4d4b-a36b-9380d6ed0a69",
"address": "1588 E Thomspon Blvd",
"created_at": "2024-02-16T04:05:16.435Z",
"updated_at": "2024-02-16T04:05:16.435Z"
},
"commute_distance": 319.3271776007556,
"commute_duration": 4.984444444444445,
"destination_address": {
"id": "9bfa1d1b-f07d-44ba-9aa2-18cf8b72a6af",
"address": "2112 E Thompson Blvd",
"created_at": "2024-02-16T04:05:16.455Z",
"updated_at": "2024-02-16T04:05:16.455Z"
},
"ride_distance": 0.46789367069730453,
"ride_duration": 0.015277777777777777,
"ride_earnings": 12,
"score": 2.4001333407411525
},
{
"id": "0b3e3771-1556-4fa4-981a-693ba8aed182",
"start_address_id": "9bfa1d1b-f07d-44ba-9aa2-18cf8b72a6af",
"destination_address_id": "c23bfaad-5633-4d4b-a36b-9380d6ed0a69",
"created_at": "2024-02-16T04:05:16.502Z",
"updated_at": "2024-02-16T04:05:16.502Z",
"home_address": {
"id": "8bc8c7dd-4977-4655-8892-14143dbc4895",
"address": "1600 Amphitheatre Pkwy",
"created_at": "2024-02-16T04:05:16.451Z",
"updated_at": "2024-02-16T04:05:16.451Z"
},
"start_address": {
"id": "9bfa1d1b-f07d-44ba-9aa2-18cf8b72a6af",
"address": "2112 E Thompson Blvd",
"created_at": "2024-02-16T04:05:16.455Z",
"updated_at": "2024-02-16T04:05:16.455Z"
},
"commute_distance": 319.79444989871627,
"commute_duration": 4.999722222222222,
"destination_address": {
"id": "c23bfaad-5633-4d4b-a36b-9380d6ed0a69",
"address": "1588 E Thomspon Blvd",
"created_at": "2024-02-16T04:05:16.435Z",
"updated_at": "2024-02-16T04:05:16.435Z"
},
"ride_distance": 0.46789367069730453,
"ride_duration": 0.014722222222222222,
"ride_earnings": 12,
"score": 2.3930866385996015
}
]
ride_distance
is in miles.
ride_duration
is in hours.
ride_earnings
is in USD.
score
is in USD/hour.

If you'd like to contribute to this project, please follow these steps:
- Fork the repository
- Create a new branch:
git checkout -b feature/your-feature
- Make your changes and commit them:
git commit -am 'Add some feature'
- Push to the branch:
git push origin feature/your-feature
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
- Arkadiy Sudarikov - arkadiy@arkadiy.com
- Project Link: https://github.com/arkadiysudarikov/hopskipdrive