A small toy to explore geohashes

For an app I've been building, I've been looking into geohashes. For those who don't know, the geohash format is a simple way to encode latitude and longitude into a single string. As an example, Nelson's Column in London (51.507794, -0.127952) has the geohash gcpvj0dyds.

Geohashes have a couple of interesting features. First, as you remove characters, you lose precision. gcpvj0dyds fairly accurately points to Nelson's Column; gcpvj0d represents the South-West of Trafalgar Square and some of the Mall; and gcpvj covers most of Central London, as well as Islington and King's Cross. A geohash doesn't really represent a point, but rather a bounding area within which a point may lie. The longer the geohash, the smaller that bounding area.

The other interesting property geohashes have is that nearby locations usually (but not always) share similar prefixes. So much of North London is in gcpv, while much of South London is in gcpu. However, due to the Prime Meridian passing through Greenwhich, South East London has the geohash u10h - wildly different than the other two.

This probably sounds a bit complicated. I was having trouble getting my head around the concept, so to try and get to grips with geohashes I've written a toy app that draws them on a map. To try it out, go to http://geohash.gofreerange.com, click the map, zoom and play. If you find it useful, let me know.

Update: This code is now available [on github](https://github.com/tomafro/geohash-explorer).

Tip: Automatic bundle exec for rake and other gems

It's irritating to run gem commands like rake, cap, rspec and others, only to find they needed to be executed via bundle exec. As a simple solution, I use a simple zsh function, combined with aliases for commonly used commands.

Here's the function (which I've named be):

if [[ -a Gemfile ]]; then
  bundle exec $*
else
  command $*
fi

It's very simple. If there's a Gemfile in the pwd, it runs commands through bundle exec. Otherwise it just runs them.

I've combined this with some aliases for much less pain and less frustration:

alias rake='be rake'
alias cap='be cap'
alias rspec='be rspec'

Presenting the #blue api

In building #blue (sign up now!), one of the problems we faced was how to build json data in response to requests to our API. The typical rails solution would be to override #as_json in a model class, then write a controller like this:

    class ContactsController < ApiController
      responds_to :json

      def show
        respond_with Contact.find(params[:id])
      end
    end

I always prefer to keep my controllers as skinny as possible, so this looks like a great solution. The respond_with call takes care of converting the message to json and responding with the right Content-Type, all in a simple call. However it has a number of problems and disadvantages.

The biggest issue for our API is that rather than expose the id of each model, we've tried to encourage the use of the uri instead. So the json returned for a single contact (for example) looks like this:

    {
      "contact": {
        "uri": "https://api.example.com/contacts/ccpwjc",
        "name": "George",
        "email": "george@handmade.org",
        "msisdn": "447897897899",
        "phone_number": "07897897899",
        "messages": "https://api.example.com/contacts/ccpwjc/messages"
      }
    }

It doesn't just have a uri for the actual contact, but also for the messages belonging to that contact (and yes, I regret not calling that attribute messages_uri). Models can't generate uris, and shouldn't really be aware of them, so overriding #as_json doesn't work. In any case, the json structure is really presentation logic, not business logic. It doesn't belong in the model.

Presenting a single model

The solution we've used is to build a presenter for each model, solely responsible for building the json. Here's an example for a contact:

  class ContactPresenter
    include Rails.application.routes.url_helpers

    attr_accessor :controller, :subject
    delegate :params, :url_options, :to => :controller
    delegate :errors, :to => :subject

    def initialize(controller, subject)
      @controller = controller
      @subject = subject
    end

    def as_json(options = {})
      {:contact => {
        :uri => uri,
        :email => subject.email,
        :name => subject.name,
        :msisdn => subject.msisdn,
        :phone_number => subject.phone_number,
        :messages => api_contact_messages_url(:contact_id => subject.id)
      }
    end

    def uri
      api_contact_url(:id => subject.id)
    end
  end

It's now simple to rewrite our controller to use the new presenter:

    class ContactsController < ApiController
      responds_to :json

      def show
        respond_with ContactPresenter.new(self, Contact.find(params[:id]))
      end
    end

Presenting pages of models

The presenter above works well for a single model, but many of our API calls return a page of results. The /contacts for example returns all the contacts belonging to a user (of which there may be hundreds). Luckily it's simple to adapt this pattern to present pages like this. First, we change our original #as_json method slightly:

  def as_json(options = {})
    if options[:partial]
      {
        :uri => uri,
        :email => subject.email,
        :name => subject.name,
        :msisdn => subject.msisdn,
        :phone_number => subject.phone_number,
        :messages => api_contact_messages_url(:contact_id => subject.id)
      }
    else
      {:contact => as_json(:partial => true)}
    end
  end

This change allows us to call as_json with the options :partial. With the option, a hash of data is returned. Without, the same hash is returned, wrapped in another hash.

Next, add a page presenter:

class ContactPagePresenter
  include Rails.application.routes.url_helpers

  attr_accessor :controller, :subject
  delegate :params, :url_options, :to => :controller
  delegate :errors, :to => :subject

  def initialize(controller, subject)
    @controller = controller
    @subject = subject
  end

  def as_json(options = {})
    contacts = subject.map {|o| ContactPresenter.new(controller, o).as_json(:partial => true) }

    {:contacts => contacts}.tap do |result|
      if subject.previous_page
        result[:previous_page_uri] = contacts_url(subject.previous_page)
      end

      if subject.next_page
        result[:next_page_uri] = contacts_url(subject.next_page)
      end
    end
  end
end

Finally, we can add an index action using this presenter:

class ContactsController < ApiController
  responds_to :json

  def index
    respond_with ContactPagePresenter.new(self, Contact.paginate(:page => params[:page], :per_page => 50))
  end
end

Refactoring common logic

The code above is a very much simplified version of what we do in #blue. We have many controllers, and several different models, so in our actual code we've abstracted out as much common logic as possible. In reality, our contacts controller looks more like this:

class ContactsController < ApiController
  before_filter :find_contact, :only => [:show, :update]

  def show
    present @contact
  end

  def index
    present_page_of current_account.contacts
  end

  def create
    @contact = current_account.contacts.build(attributes)
    @contact.save
    present @contact
  end

  def update
    @contact.update_attributes(attributes)
    present @contact
  end

  private

  def find_contact
    @contact = current_account.contacts.where(:_id => params[:id]).first
    head :status => :not_found unless @contact
  end
end

I think the code looks pretty clean. The clever stuff happens in the #present and #present_page_of methods, defined in the superclass:

class ApiController < ApplicationController::Base
  protected

  def present(instance, options = {})
    presenter = presenter_class.new(self, instance)
    options[:location] ||= presenter.uri if request.post? && subject.errors.empty?
    respond_with presenter, options
  end

  def present_page_of(collection, options = {})
    presenter = page_presenter_class.new(self, page_of(collection))
    respond_with presenter, options
  end

  def page_of(collection)
    collection.paginate(:page => params[:page], :per_page => 50)
  end

  def presenter_class
    (self.class.name.gsub!("Controller", "").singularize + "Presenter").constantize
  end

  def page_presenter_class
    (self.class.name.gsub!("Controller", "").singularize + "PagePresenter").constantize
  end
end

The #present and #present_page_of methods handle determining the correct presenter to us, as well as paginating the collection where required. They still use rails build in #respond_with method, which helps provide the correct response headers for each request. As the ContactPresenter delegates #errors to its subject, if there are validation errors, #respond_with correctly returns a 422.

One further motivation for this pattern (other than moving presentation logic out of the model) is that should we want to release a new version of our API, we'll be able to get a lot of the way there simply by swapping which presenter is used. We started using this code about 8 months ago, and I'm still pretty happy with it. I hope you find something useful in it too.

Any comments or suggestions, please get in touch with me on twitter.

#blue opens for business

If you're an O2 customer based in the UK, you might be interested that #blue, a project we've built, has recently (re)opened for business. It's a soft launch, but feel free to tell your friends.

#blue gets copies of every SMS message you send or receive, and makes them available to you on the web. You can search your messages, read whole conversations, and even reply, all from the comfort of your browser.

As well as a nice-looking web UI, there's also an API that allows other apps to read and manipulate your messages and contacts (once you give them permission). Think automatic posting to twitter, or showing new messages as alerts on your desktop. The possibilities are endless.

Oh, and it's all free. All you need is an O2 phone.

If you think it sounds interesting, ask for a beta request. You should get an invitation very quickly. Once on board, please send me any suggestions and feedback. It's going to be interesting to see where we can take this project.