Sebastian Hoitz Blog Archive

Spine.JS and Chicago Boss

28 January 2012

This is a follow up to Chicago Boss and Backbone.js.

We use Chicago Boss as an MVC framework and SpineJS on the client side to bind the models to the DOM. We think this is a really great setup and are building our future projects with this architecture.

In this blog post we want to show you how to get up and running with CB and Spine!

Set up a new Chicago Boss project

First of all, make sure you have Chicago Boss installed on your computer. Then create a new CB app:

make app PROJECT="todomanager"

It should now set up a skeleton project under ../todomanager.

This is what my output looks like:

==> ChicagoBoss-0.6.10 (create)
Writing ../todomanager/
Writing ../todomanager/
Writing ../todomanager/start-server.bat
Writing ../todomanager/Makefile
Writing ../todomanager/
Writing ../todomanager/boss.config
Writing ../todomanager/src/mail/todomanager_outgoing_mail_controller.erl
Writing ../todomanager/src/mail/todomanager_incoming_mail_controller.erl
Writing ../todomanager/priv/init/todomanager_01_news.erl
Writing ../todomanager/priv/static/chicago-boss.png
Writing ../todomanager/priv/static/favicon.ico
Writing ../todomanager/priv/todomanager.routes

Install Spine

Cool. Now we are ready to start setting up spine. The easiest way to get Spine is to use npm (Node Package Manager). If you don’t have npm yet, you can get it with this sweet one-liner:

curl | sh

Once we have that, we should install It takes care for us of setting up all the required skeletons for our JS app. We can also already install hem, we will need that later:

npm install -g hem

After npm has done its magic, set up a spine project. Execute this in the todomanager folder:

spine app frontend
cd frontend
npm install

This creates a lot more files and installs all npm dependencies. But bear with me.

We will use Hem to bundle and package all these files created by Spine. Hem is a pretty neat little packaging tool written by Alex MacCaw, the creator of Spine. What it does is bundle all your Javascript and CSS resources and puts them into one single JS and CSS file. Pretty neat, huh?

When you look at the files created, you should see a file called slug.json. Open it.

This is the configuration file for hem. We now need to change some settings, so that spine plays along nicely with our Chicago Boss setup.

It should look like this:

  "dependencies": [
  "libs": []

Change the file, so that it looks like this:

      "dependencies": [
      "libs": [],
      "public": "../priv/static"

This tells hem to put the generated javascript and css file into the publicly accessible folder of Chicago Boss. Give it a try by running

hem build

You should see those files being added at the right location.

Let’s start building something!

Let’s see what we want to achieve:

We will start by setting up the Chicago Boss model.

Chicago Boss

Create a file src/model/todo.erl:

-module(todo, [Id, Subject, Done]).

That’s our model. Chicago Boss takes care of all the remaining magical functions like persisting etc. Cool, huh?

Now we create the todo REST resource.

Create the controller src/controller/todomanager_todo_controller.erl:

-module(todomanager_todo_controller, [Req]).

%% List
%% GET todo/index
index('GET', []) ->
  Todos = boss_db:find(todo, []),
  case Todos of
    [] ->
      {output, <<"[]">>, [{"Content-Type", "application/json"}]};
    _Else ->
      {json, Todos}

%% Read
%% GET todo/index/todo-1
index('GET', [Id]) ->
  Todo = boss_db:find(Id),
  % TODO for some reason, when we have a non-existent todo, we still output the json
  % data and don't jump to the not_found section.
  case Todo of
    Todo ->
      {json, [{todo, Todo}]};
    [] ->

%% Create
%% POST todo/index
index('POST', []) ->
  Body = element(2, mochijson:decode(Req:request_body())),
  io:format("~p", [proplists:get_value("subject", Body)]),
  Todo = todo:new(id, proplists:get_value("subject", Body), false),
  %io:format("~p", [Todo]),
  {json, [{todo, element(2, Todo:save())}]};

%% Update
%% PUT todo/index/123
index('PUT', [Id]) ->
  Todo = boss_db:find(Id),
  Body = element(2, mochijson:decode(Req:request_body())),
  %% Set the new values
  NewTodo = Todo:attributes([
      {subject, proplists:get_value("subject", Body)},
      {done, proplists:get_value("done", Body)}
  {json, [{todo, element(2, NewTodo:save())}]}.

This creates all our required end points for our REST controller.

However, we also need an index controller in Chicago Boss to deliver our required initial HTML, so that our client-side Spine app can actually start working.

So create this index controller in src/controller/todomanager_index_controller.erl:

-module(todomanager_index_controller, [Req]).

index('GET', []) ->
  {ok, []}.

We also need the view script. It is placed in src/view/index/index.html:

<!DOCTYPE html>
    <link rel="stylesheet" href="/static/application.css">
    <title>My sweet Erlang Todo app</title>
    <div id="app">
      <form action="/todo/create" method="post">
        <input type="text" name="subject" placeholder="Enter your todo item">
        <input type="submit" value="Save">
      <ul class="items">
        <span class="countVal">0</span> todos left.
    <script type="text/javascript" src="/static/application.js"></script>

    <script type="text/javascript">
      $ = require("jqueryify");
      App = require("index");

      $(function() {
        new App({el: $("#app")});


This will deliver our initial HTML, include the files generated by hem and bootstrap our app.

Cool! By now we are pretty much done with our Erlang setup.

So now we can start writing our Spine app.


Spine apps are built in a pretty similar fashion. You have models, views and controllers.

So let’s start by writing our client-side model. We will place this file in frontend/app/models/

Spine = require("spine")

class Todo extends Spine.Model
  @configure "Todo", "id", "subject", "done"

  @extend Spine.Model.Ajax
  @url: "/todo/index"

  @active: ->
    @select (item) -> !item.done

  @done: ->
    @select (item) -> !!item.done

module.exports = Todo

By calling

@extend Spine.Model.Ajax
@url: "/todo/index"

we tell Spine to persist via AJAX to a REST resource located at “/todo/index”.

Let’s create our todo controller /frontend/app/controllers/

In Spine it is best practice when you create a controller for each “widget” that we have on our side.

When looking at our todo app, we have two “widgets”. One to contain the list of todos, and then one widget per each todo, that takes care of all the events for this single todo.

So we have to create two controllers in our todo controller file. First we have to import some dependencies:

Spine = require("spine")
$ = Spine.$
Model = require("models/todo")

This is our TodoItem widget controller:

class TodoItem extends Spine.Controller
    "change input[type=checkbox]": "toggle"
    "dblclick": "edit"
    "blur input[type=text]": "close"
    "keypress input[type=text]": "blurOnEnter"

    "input[type=text]": "input"

  constructor: ->
    @item.bind("update", @render)
    @item.bind("destroy", @remove)

  render: =>
    @replace $(require("views/todo/todo")(@item))

  toggle: ->
    @item.done = !@item.done

  edit: ->

  blurOnEnter: (e) ->
    if e.keyCode is 13 then

  close: ->
    @item.updateAttributes({subject: @input.val()})

  remove: =>

Let’s look at some interesting parts here:

  "change input[type=checkbox]": "toggle"
  "dblclick": "edit"
  "blur input[type=text]": "close"
  "keypress input[type=text]": "blurOnEnter"

This binds dom events to certain actions in our controller. Spine takes care for us of attaching these events to our dom.

render: =>
  @replace $(require("views/todo/todo")(@item))

This renders our todo view and passes it our todo item as parameter so we can display some of its data.

class Todo extends Spine.Controller
    "submit form": "create"
    "click .clear": "clear"

    ".items": "items"
    "form input": "input"
    ".countVal": "count"

  constructor: ->

    @log "Initialited"

    Model.bind "create", @addOne
    Model.bind "refresh", @addAll
    Model.bind "refresh change", @renderCount

  # Add a single todo item
  addOne: (todo) =>
    view = new TodoItem(item: todo)

    @items.append view.render().el

  # After a refresh
  addAll: =>
    Model.each @addOne

  # Create a new todo
  create: (e) ->
    @log @input.val()
    Model.create(subject: @input.val())
    @input.val ""

  renderCount: =>
    active =

module.exports = Todo

This is the remaining controller that takes care of managing all our TodoItem instances.

We also need our todo view script /frontend/app/views/todo/

<li id="<%= @id %>">
  <input type="checkbox" name="todo[]" value="<%= @id %>"
    id="checkbox-<%= @id %>" <% if @done: %>checked="checked"<% end %>>
  <label for="checkbox-<%= @id %>"><%= @subject %></label>
  <div class="edit">
    <input type="text" value="<%= @subject %>">

Remaining bootstrap code /frontend/app/


Spine = require('spine')
Todo = require("controllers/todo")

module.exports = Todo

Some basic CSS /frontend/css/index.styl:

@import './mixin'

body, html

  margin:50px auto
  box-shadow:0px 0px 10px rgba(0,0,0,0.4)


    padding:8px 0



  ul li
    padding:10px 0
    border-bottom:solid 1px #ddd

  ul li input


  li .edit


  li.editing .edit

  li.editing label,
  li.editing input[type=checkbox]

And we are ready to give it a test run!

Execute hem watch in our frontend folder, and ./ in the todomanager root dir.

Then open a browser and navigate to “localhost:8001” and you should see our todo app. You can add entries, double click existing entries to edit them and mark todos as done.

You can find the existing source code on Github.

I hope this was helpful for you! I appreciate any feedback :-)

blog comments powered by Disqus
Fork me on GitHub