Rails-like JavaScript
using CoffeeScript,
 Backbone.js and
      Jasmine
     Raimonds Simanovskis
Abstract
Ruby on Rails provides good conventions and structure for your Ruby code so that from the
first day you can start to build your application. But there are practically no conventions and
structure how to develop your JavaScript code and historically Rails was trying to hide any
generated JavaScript from you. Nowadays if you want to build rich UI and efficient web
applications you need to learn and use JavaScript and after some time you understand that
you can't just write a bunch of functions in application.js, you need some conventions and
structure as well.

If you are Ruby developer then you should take a look on CoffeeScript which is programming
language that compiles into JavaScript but with syntax inspired by Ruby and Python. As it is
more similar to Ruby it will be easier to switch between CoffeeScript and Ruby during
development as well as your CoffeeScript code will be smaller and more readable than
JavaScript.

And if you are building rich UI web application in browser then it is worth to try Backbone.js
which provides MVC pattern for browser side UI development which will be more
understandable and easier to maintain than bunch of JavaScript functions that are bound to
bunch of DOM events. And as Backbone.js is made by the same author Jeremy Ashkenas as
CoffeeScript then you can develop your browser side UI in CoffeeScript and Backbone.js.

If you are Ruby developer then testing culture is in your blood and you should feel
uncomfortable if you will not test your JavaScript code. If you prefer RSpec testing framework
in Ruby then in JavaScript (or CoffeeScript) you might like Jasmine testing framework which
syntax is inspired by RSpec.
Example slides
Sample CoffeeScript
# Assignment:                   # Splats:
number   = 42                   race = (winner, runners...) ->
opposite = true                   print winner, runners

# Conditions:                   # Existence:
number = -42 if opposite        alert "I knew it!" if elvis?

# Functions:                    # Array comprehensions:
square = (x) -> x * x           cubes = (math.cube num for num in list)

# Arrays:
list = [1, 2, 3, 4, 5]

# Objects:
math =
  root:    Math.sqrt
  square: square
  cube:   (x) -> x * square x
Backbone.js model
     in CoffeeScript
class window.Todo extends Backbone.Model

 # If you don't provide a todo, one will be provided for you.
 EMPTY: "empty todo..."

 # Ensure that each todo created has `content`.
 initialize: ->
   unless this.get "content"
     this.set content: this.EMPTY

 # Toggle the `done` state of this todo item.
 toggle: ->
   this.save done: !this.get "done"
Collection of models
class window.TodoList extends Backbone.Collection

 # Reference to this collection's model.
 model: Todo

  url: '/todos'

 # Filter down the list of all todo items that are finished.
 done: ->
   this.filter (todo) -> todo.get 'done'

 # Filter down the list to only todo items that are still not finished.
 remaining: ->
   this.without this.done()...

 nextOrder: ->
   return 1 unless @length
   this.last().get('order') + 1

 # Todos are sorted by their original insertion order.
 comparator: (todo) ->
   todo.get 'order'
class window.TodoView extends Backbone.View
           tagName: "li"
           template: Handlebars.compile $('#item-template').html()
           events:
             "click .check"              : "toggleDone"
             "dblclick div.todo-content" : "edit"
             "click span.todo-destroy"   : "clear"
             "keypress .todo-input"      : "updateOnEnter"
           initialize: ->
             _.bindAll this, 'render', 'close', 'clear'
             @model.bind 'change', @render
             @model.bind 'clear', @clear
           render: ->



Sample
             $(@el).html @template @model.toJSON()
             this.setContent()
             this
           setContent: ->
             content = @model.get 'content'


 view
             this.$('.todo-content').text content
             @input = this.$('.todo-input')
             @input.bind 'blur', @close
             @input.val content
           toggleDone: ->
             @model.toggle()
           edit: ->
             $(@el).addClass "editing"
             @input.focus()
           close: ->
             @model.save content: @input.val()
             $(@el).removeClass "editing"
           updateOnEnter: (e) ->
             this.close() if e.keyCode == 13
           clear: ->
             $(@el).remove()
             @model.destroy()
Sample Jasmine
            tests
describe "Todo", ->
  todo = null
  ajaxCall = (param) -> jQuery.ajax.mostRecentCall.args[0][param]

  beforeEach ->
    todo = new Todo
    todos = new TodoList [todo]

  it "should initialize with empty content", ->
    expect(todo.get "content").toEqual "empty todo..."

  it "should initialize as not done", ->
    expect(todo.get "done").toBeFalsy()

  it "should save after toggle", ->
    spyOn jQuery, "ajax"
    todo.toggle()
    expect(ajaxCall "url").toEqual "/todos"
    expect(todo.get "done").toBeTruthy()

Rails-like JavaScript using CoffeeScript, Backbone.js and Jasmine

  • 1.
    Rails-like JavaScript using CoffeeScript, Backbone.js and Jasmine Raimonds Simanovskis
  • 2.
    Abstract Ruby on Railsprovides good conventions and structure for your Ruby code so that from the first day you can start to build your application. But there are practically no conventions and structure how to develop your JavaScript code and historically Rails was trying to hide any generated JavaScript from you. Nowadays if you want to build rich UI and efficient web applications you need to learn and use JavaScript and after some time you understand that you can't just write a bunch of functions in application.js, you need some conventions and structure as well. If you are Ruby developer then you should take a look on CoffeeScript which is programming language that compiles into JavaScript but with syntax inspired by Ruby and Python. As it is more similar to Ruby it will be easier to switch between CoffeeScript and Ruby during development as well as your CoffeeScript code will be smaller and more readable than JavaScript. And if you are building rich UI web application in browser then it is worth to try Backbone.js which provides MVC pattern for browser side UI development which will be more understandable and easier to maintain than bunch of JavaScript functions that are bound to bunch of DOM events. And as Backbone.js is made by the same author Jeremy Ashkenas as CoffeeScript then you can develop your browser side UI in CoffeeScript and Backbone.js. If you are Ruby developer then testing culture is in your blood and you should feel uncomfortable if you will not test your JavaScript code. If you prefer RSpec testing framework in Ruby then in JavaScript (or CoffeeScript) you might like Jasmine testing framework which syntax is inspired by RSpec.
  • 3.
  • 4.
    Sample CoffeeScript # Assignment: # Splats: number = 42 race = (winner, runners...) -> opposite = true print winner, runners # Conditions: # Existence: number = -42 if opposite alert "I knew it!" if elvis? # Functions: # Array comprehensions: square = (x) -> x * x cubes = (math.cube num for num in list) # Arrays: list = [1, 2, 3, 4, 5] # Objects: math = root: Math.sqrt square: square cube: (x) -> x * square x
  • 5.
    Backbone.js model in CoffeeScript class window.Todo extends Backbone.Model # If you don't provide a todo, one will be provided for you. EMPTY: "empty todo..." # Ensure that each todo created has `content`. initialize: -> unless this.get "content" this.set content: this.EMPTY # Toggle the `done` state of this todo item. toggle: -> this.save done: !this.get "done"
  • 6.
    Collection of models classwindow.TodoList extends Backbone.Collection # Reference to this collection's model. model: Todo url: '/todos' # Filter down the list of all todo items that are finished. done: -> this.filter (todo) -> todo.get 'done' # Filter down the list to only todo items that are still not finished. remaining: -> this.without this.done()... nextOrder: -> return 1 unless @length this.last().get('order') + 1 # Todos are sorted by their original insertion order. comparator: (todo) -> todo.get 'order'
  • 7.
    class window.TodoView extendsBackbone.View tagName: "li" template: Handlebars.compile $('#item-template').html() events: "click .check" : "toggleDone" "dblclick div.todo-content" : "edit" "click span.todo-destroy" : "clear" "keypress .todo-input" : "updateOnEnter" initialize: -> _.bindAll this, 'render', 'close', 'clear' @model.bind 'change', @render @model.bind 'clear', @clear render: -> Sample $(@el).html @template @model.toJSON() this.setContent() this setContent: -> content = @model.get 'content' view this.$('.todo-content').text content @input = this.$('.todo-input') @input.bind 'blur', @close @input.val content toggleDone: -> @model.toggle() edit: -> $(@el).addClass "editing" @input.focus() close: -> @model.save content: @input.val() $(@el).removeClass "editing" updateOnEnter: (e) -> this.close() if e.keyCode == 13 clear: -> $(@el).remove() @model.destroy()
  • 8.
    Sample Jasmine tests describe "Todo", -> todo = null ajaxCall = (param) -> jQuery.ajax.mostRecentCall.args[0][param] beforeEach -> todo = new Todo todos = new TodoList [todo] it "should initialize with empty content", -> expect(todo.get "content").toEqual "empty todo..." it "should initialize as not done", -> expect(todo.get "done").toBeFalsy() it "should save after toggle", -> spyOn jQuery, "ajax" todo.toggle() expect(ajaxCall "url").toEqual "/todos" expect(todo.get "done").toBeTruthy()