Skip to content

Instantly share code, notes, and snippets.

@kavunshiva
Last active July 11, 2017 20:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kavunshiva/14720485f982730431963584f728fa04 to your computer and use it in GitHub Desktop.
Save kavunshiva/14720485f982730431963584f728fa04 to your computer and use it in GitHub Desktop.
persisting position to a database
class Api::V1::PositionsController < ApplicationController
before_action :authorize_device!
def index
positions = Position.where(device_id: params[:id]).order(time: :desc)
render json: positions
end
def create
position = Position.new(
position_params(
:lat,
:long,
:alt,
:time,
:prev_pos,
:next_pos,
:device_id
)
)
position.prev_pos = Position.where(device_id: device_id).order(time: :desc).first.id
if position.save
update_last_pos(position)
render json: position
else
render json: [{}], status: 404
end
end
private
def position_params(*args)
params.require(:position).permit(*args)
end
def update_last_pos(current_pos)
last_pos = Position.where(device_id: device_id).order(time: :desc).second
last_pos.next_pos = current_pos.id
last_pos.save
end
end
@kavunshiva
Copy link
Author

The gist of what I was trying to do

Basically, this is taken from the back end of a Rails API geolocation service that allows for a client (a device) to post its (current or cached) position to a database (using ActiveRecord).

What the code does

Starting from the top, the PositionsController is invoked when the Rails router receives a request from a route pointing to it (with a particular associated action invoking a method in the controller, e.g. #create). As the PositionsController inherits from the ApplicationController, it has access to the (hopefully self-explanatory) methods contained therein of #authorize_device! and #device_id (which returns the id of the device associated with the position as ascertained by its decoded security token1).

The #index method merely returns an ActiveRecord object (resembling an array), listing out positions in reverse chronological order (by the position's :time parameter).

The #create method accepts a position post from a device, validates its parameters1, links it to the previous positional post from the same device (and vice-versa)2, and attempts to save it. If the save is successful and the position is persisted to the database, the position is rendered back to the client in json format.

1 Security

The device is assigned a JWT token after it signs in and must pass it along in its headers with each POST request to authenticate it (see: the before_action :authorize_device! on line 2). Position parameters are validated before Ruby objects are instantiated from them via the #position_params method to prevent injection of unpermitted, possibly malicious parameters.

2 Data structures

The position posts contain information about latitude, longitude, altitude, time3, and the database id of the device to which they belong as well as the database ids of the (chronologically) previous and next (null for the most recent) positions; these last parameters make positions a doubly-linked list for convenient traversal of the position chain, e.g. for the purposes of plotting routes on a map. A bonus feature to improve lookup times for large sets of time-bound positional search results could be realized by storing these positions in a balanced (e.g. red-black or AVL) binary search tree, which allows for lookup in O(log n) time instead of the O(n) time associated with traversing a list.

3 Time

Time of GPS reading, which differs from the time of insertion into the database due to:

  1. the asynchronous nature of the server calls over an e.g. cellular network, and
  2. device caching (in the case of e.g. devices with poor connectivity or without e.g. cellular network connectivity)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment