Basic "bump" API using Rails

This post will show how to create a basic "bump" API, it does have several shortcomings but will demonstrate the essential components required to get started. The code can be found on github.

How does bump work?

The basic operation of the bump protocol breaks down into 2 parts an app running on a mobile device capable of sensing a "bump" using its sensors and a server that receives these bump events via some mechanism like an API. In the event of a bump the device sends basic information like device id and coordinates to a server. The server in turn uses an algorithm to match the bump to other recent bump events in the vicinity of the received bump and proceeds to pair up the devices. This post will focus on the server part of the protocol.

Environment

Rvm, Ruby & Rails

It is not required but a good idea to setup rvm to help you manage different ruby versions. Another good practice is to create gemsets per project, it has the benefit of keeping dependencies isolated.

rvm use 2.2.1@rails_bump_protocol --create
gem install rails

Postgres & PostGIS

I chose Postgres because of PostGIS which is a spatial database extender for Postgres. This will form the backbone of the solution which is to detect bumps in close proximity. Most of the instructions I got out of the ubuntu wiki page but the gist of it is:

Rails app

Generate a new rails app in your project directory usingrails new rails-bump-protocol - I'm not sure about the rails app naming convention, after quick google search I could find examples with both "-" and "_" to separate terms.

Next step is to add the required gems to the project. RGeo is an Active Record extension that provides spatial data types and spacial queries. Puma is used to ensure that the app can handle concurrent requests.

The generated migration files will need some specifics applied. First up the CreateBumpEvents migration. The :lonlat column needs to be marked as :geographic to indicate that it will contain longitude and latitude data. We also need to add a spatial index to :lonlat since most queries would be around the location.

After doing some googling, I came across a great tutorial on self-referential associations and extracted just the parts I needed from it. As the diagram indicates, a BumpEventhas manyBumpEventthroughBumpEventMatches. The other parts to note are the named scopes which makes for code that reads better.

Controller & Routes

The controller only needs to support a subset of the RESTFul actions, POST to create a BumpEvent and GET to retrieve it. It's a good idea to separate API controllers from the rest of the application controllers, hence I namespaced and versioned the resource.

Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :bump_events, only: [:create, :show]
end
end
end

The generated bump_events_controller.rb needs to be moved into controllers/api/v1 to match the namespace defined in the routes file. We also need to tell rails that API is an acronym to facilitate defining the module accordingly instead of Api like convention would dictate.

ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym 'API'
end

The basic implementation of the controller is rudimentary and would not scale well. The create method saves the BumpEvent after decoding the JSON representation of the latitude/longitude which follows GeojSON format. Then follows the goofy bit, loop 10 times, sleeping for a second between loops and then linking any BumpEvents that the system might have received in the last second.

Conclusion

Although this solution has a few weaknesses, it does provide the basic components of a working prototype. To get past the 10s sleep one might consider a polling solution from the device or maybe some other mechanism for pushing the matched events back to each device.

An alternative approach may be to return immediately and let each bump event take care for linking both ways during creation. Each device can then request all the matches some time after the initial bump, thereby moving the delay to the client device.