A Simple OpenLayers App with Yeoman, Sinatra, MongoDB and Backbone - Part 2
Introduction
In the previous part we set up the solution structure. In this installment we’ll get our API using MongoDB to persist/retrieve data and begin visualising this data with OpenLayers.
Setting up MongoDB
There are several options available to us for using MongoDB. The easiest way to run locally is just to install via a package manager. The default Ubuntu repositories have MongoDB, but it’s a little behind the latest version. If you must have the latest, you can add the 10gen repository and install it from there. Just follow these instructions.
The easiest way, and the way we will use, is to use a cloud provider. A couple of well known options are MongoHQ and MongoLab. This example uses the latter.
MongoLab also have an great open source tool for managing servers and replica sets called Mongoctl. I have this on all my Linux installations. I won’t go into it here, but if you’re inclined, check it out on GitHub here.
Getting the API to Talk to MongoDB
In a previous post, I explored a method of creating dynamic Ruby models suitable for use with MongoDB, but this time we’re going to use one of the Ruby object document mappers (ODMs) - Mongoid. We already added this gem in part one.
First we need for Mongoid to be able to connect to a database. Just create a file call mongoid.yml in the solution root. The simplest configuration for connecting to a local database looks like the example below. If you go with a hosted service, you just have to replace the host, user and password as appropriate.
Naturally we need a model that represents a point of interest (POI) on the map. To keep it simple, I just created poi.rb in the solution root folder. Here’s what it looks like.
Next we need some routes for our API, so let’s set up a typical suite for handling CRUD operations:
- A GET method for retrieving a list of POIs.
- A GET method for retrieving a single POI by its ID.
- A POST method for creating a POI.
- A PUT method for updating a POI.
- A DELETE method for (surprise) deleting a POI.
As can be seen in our new API code below, Mongoid allows us to make short work of this.
Let’s give it a quick test using cURL.
> curl -X POST -d 'poi={"name":"test","desc":"test feature","pos":[5,5]}'
-v http://localhost:4567/poi
About to connect() to localhost port 4567 (#0)
Trying 127.0.0.1... connected
POST /poi HTTP/1.1
User-Agent: curl/7.22.0 (i686-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1
zlib/1.2.3.4 libidn/1.23 librtmp/2.3
Host: localhost:4567
Accept: */*
Content-Length: 53
Content-Type: application/x-www-form-urlencoded
upload completely sent off: 53out of 53 bytes
HTTP/1.1 201 Created
Content-Type: application/json;charset=utf-8
Location: /poi/50f719e18cbaec991c000001
Content-Length: 0
Connection: keep-alive
Server: thin 1.5.0 codename Knife
Connection #0 to host localhost left intact
Closing connection #0
Sweet. We’ve got a 201 and location header telling us where the newly created resource can be found. Let’s retrieve it. Don’t worry about the -v (verbose) flag this time.
> curl http://localhost:4567/poi/50f719e18cbaec991c000001
{"_id":"50f719e18cbaec991c000001","desc":"test feature",
"name":"test","pos":[5,5]}
And just because we’re thorough testers…
> curl -X PUT -d 'poi={"_id":"50f719e18cbaec991c000001","desc":"updated",
"name":"test","pos":[5,5]}' -i http://localhost:4567/poi
HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8
Content-Length: 0
Connection: keep-alive
Server: thin 1.5.0 codename Knife
> curl http://localhost:4567/poi/50f719e18cbaec991c000001
{"_id":"50f719e18cbaec991c000001","desc":"updated","name":"test","pos":[5,5]}
> curl -X DELETE http://localhost:4567/poi/50f719e18cbaec991c000001
> curl http://localhost:4567/poi
[]
That’s it. Our API is functional and MongoDB is holding our data.
Talking to the API with Backbone.
First we need a Backbone model for our POIs. Let’s kick things off with Yeoman’s Backbone generator:
> yeoman init backbone:model poi
This creates a model skeleton in app/scripts/models/poi-model.js. For brevity I’m just going to keep the default names that Yeoman creates. It’s important to set the idAttribute to the name of the MongoDB generated ID field so that Backbone can uniquely identify the model instances. Adding some defaults and a URL where instances of the model can be persisted make it look like this.
Next we need a collection. Collections are ordered sets of model instances. Via a collection we can fetch all of our POIs from the API, listen for certain events and (my favourite) use all the Underscore iterator methods on the set. We’ll see one of these in action shortly.
> yeoman init backbone:collection poi
Yeoman creates this collection in app/scripts/collections/poi-collection.js. We then need to indicate the type of models in the collection and specify the URL where the collection can access data. This is the result.
This is all we need right now. In the third and final part, we’ll be asking more of Backbone.
Showing Data on the Map
We need to include these new scripts along with their dependencies - Backbone and Underscore, in index.html. It now looks like this:
The final piece of the puzzle for this part is main.js. What we need is:
- A module to encapsulate our mapping functionality including rendering POIs.
- Code to fetch all the POIs from the API via our Backbone collection.
In the code below, we first set up some namespacing (used in our Yeoman generated Backbone files). Then we declare a mapping module with an init method that creates a base layer and a vector layer for POIs with some simple default styling. This module also includes the addPois method that takes our collection and uses Underscore’s map method to transform the set into an array of OpenLayers features, which are added to the vector layer.
After the mapping module is instantiated, all we do is then fetch our collection and pass it to the mapping module for display.
Now we haven’t yet added any front end UI for creating/editing/deleting POIs - this will come in the next part. To test that our JavaScript is working let’s add a POI via cURL.
> curl -X POST -d 'poi={"name":"test","desc":"test feature","pos":[145, -37.8]}'
http://localhost:4567/poi
Now if we view index.html via the base route of our application we should see this POI rendered in the center of our map - something like this. 
That wraps up this part. In the third and final part, we’ll add interactivity to our map and use Backbone views to edit our POI data.