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()