SlideShare a Scribd company logo
Raimonds Simanovskis

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

       github.com/rsim




         @rsim

             .com
The Problem
Ruby code in Rails
JavaScript code in
   Rails 3.0.x
application.js
// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults
Which leads to...
(example from Redmine)
application.js
                  (example from Redmine)
/* redMine - project management software
   Copyright (C) 2006-2008 Jean-Philippe Lang */

function checkAll (id, checked) {
  var els = Element.descendants(id);
  for (var i = 0; i < els.length; i++) {
    if (els[i].disabled==false) {
      els[i].checked = checked;
    }
  }
}

function toggleCheckboxesBySelector(selector) {
  boxes = $$(selector);
  var all_checked = true;
  for (i = 0; i < boxes.length; i++) { if (boxes[i].checked == false) { all_checked = false; } }
  for (i = 0; i < boxes.length; i++) { boxes[i].checked = !all_checked; }
}

function setCheckboxesBySelector(checked, selector) {
  var boxes = $$(selector);
  boxes.each(function(ele) {
    ele.checked = checked;
  });
}

function showAndScrollTo(id, focus) {
  Element.show(id);
  if (focus!=null) { Form.Element.focus(focus); }
/*
 * 1 - registers a callback which copies the csrf token into the
 * X-CSRF-Token header with each ajax request. Necessary to


                           application.js
 * work with rails applications which have fixed
 * CVE-2011-0447
 * 2 - shows and hides ajax indicator
 */
Ajax.Responders.register({

                      (example from Redmine)
    onCreate: function(request){
        var csrf_meta_tag = $$('meta[name=csrf-token]')[0];

         if (csrf_meta_tag) {
             var header = 'X-CSRF-Token',
                 token = csrf_meta_tag.readAttribute('content');

                if (!request.options.requestHeaders) {
                  request.options.requestHeaders = {};
                }
                request.options.requestHeaders[header] = token;
            }

         if ($('ajax-indicator') && Ajax.activeRequestCount > 0) {
             Element.show('ajax-indicator');
         }
      },
      onComplete: function(){
          if ($('ajax-indicator') && Ajax.activeRequestCount == 0) {
              Element.hide('ajax-indicator');
          }
      }
});

function hideOnLoad() {
  $$('.hol').each(function(el) {
    el.hide();
  });
}

Event.observe(window, 'load', hideOnLoad);
The Problem #2
Do we really know
    (and love?)
   JavaScript?
Sample JavaScript
               (from RailsCasts #267)
var CreditCard = {
  cleanNumber: function(number) {
    return number.replace(/[- ]/g, "");
  },

     validNumber: function(number) {
       var total = 0;
       number = this.cleanNumber(number);
       for (var i=number.length-1; i >= 0; i--) {
         var n = parseInt(number[i]);
         if ((i+number.length) % 2 == 0) {
            n = n*2 > 9 ? n*2 - 9 : n*2;
          }
          total += n;
       };
       return total % 10 == 0;
     }
};

console.log(CreditCard.validNumber('4111 1111-11111111')); // true
console.log(CreditCard.validNumber('4111111111111121'));   // false
We see as this
                  ā€œuglyā€ Ruby
CreditCard = {
  :cleanNumber => lambda { |number|
    return number.gsub(/[- ]/, "");
  },

     :validNumber => lambda { |number|
       total = 0;
       number = CreditCard[:cleanNumber].call(number);
       for i in 0..(number.length-1)
         n = number[i].to_i;
         if ((i+number.length) % 2 == 0)
            n = n*2 > 9 ? n*2 - 9 : n*2;
         end
         total += n;
       end;
       return total % 10 == 0;
     }
};

puts(CreditCard[:validNumber].call('4111 1111-11111111')); # true
puts(CreditCard[:validNumber].call('4111111111111121'));   # false
Or as this ā€œnormalā€ Ruby
module CreditCard
  def self.clean_number(number)
    number.gsub(/[- ]/, "")
  end

  def self.valid_number?(number)
    total = 0
    number = clean_number(number)
    for i in 0...number.length
      n = number[i].to_i
      if i+number.length % 2 == 0
        n = n*2 > 9 ? n*2 - 9 : n*2
      end
      total += n
    end
    total % 10 == 0
  end
end

puts CreditCard.valid_number?('4111 1111-11111111') # true
puts CreditCard.valid_number?('4111111111111121')   # false
ā€œBest practicesā€ Ruby
class CreditCard
  def initialize(number)
    @number = clean_number(number)
  end

  def valid?
    total = 0
    for i in 0...@number.length
      n = @number[i].to_i
      if i+@number.length % 2 == 0
        n = n*2 > 9 ? n*2 - 9 : n*2
      end
      total += n
    end
    total % 10 == 0
  end

  private

  def clean_number(number)
    number.gsub(/[- ]/, "")
  end
end

puts CreditCard.new('4111 1111-11111111').valid? # true
puts CreditCard.new('4111111111111121').valid?   # false
JavaScript has objects too!
var CreditCard = function(number) {
   function cleanNumber(number) {
     return number.replace(/[- ]/g, "");
   }
   this.number = cleanNumber(number);
};

CreditCard.prototype = {
   isValid: function() {
     var total = 0;
     for (var i=this.number.length-1; i >= 0; i--) {
       var n = parseInt(this.number[i]);
       if ((i+this.number.length) % 2 == 0) {
          n = n*2 > 9 ? n*2 - 9 : n*2;
        }
        total += n;
     };
     return total % 10 == 0;
   }
};

console.log( (new CreditCard('4111 1111-11111111')).isValid() ); // true
console.log( (new CreditCard('4111111111111121')).isValid() );   // false
But this would be much
        more Ruby-like!
class CreditCard
  cleanNumber = (number) -> number.replace /[- ]/g, ""

 constructor: (number) ->
   @number = cleanNumber number

 isValid: (number) ->
   total = 0
   for i in [0...@number.length]
     n = +@number[i]
     if (i+@number.length) % 2 == 0
       n = if n*2 > 9 then n*2 - 9 else n*2
     total += n
   total % 10 == 0

console.log (new CreditCard '4111 1111-11111111').isValid() # true
console.log (new CreditCard '4111111111111121').isValid()   # false
Rails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
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
Functions
square = (x) -> x * x
cube   = (x) -> square(x) * x

fill = (container, liquid = "coffee") ->
  "Filling the #{container} with #{liquid}..."

awardMedals = (first, second, others...) ->
  gold   = first
  silver = second
  rest   = others

contenders = [
  "Michael Phelps"
  "Liu Xiang"
  "Yao Ming"
  "Allyson Felix"
  "Shawn Johnson"
]

awardMedals contenders...
Objects and Arrays
 song = ["do", "re", "mi", "fa", "so"]

 singers = {Jagger: "Rock", Elvis: "Roll"}

 bitlist   = [
   1, 0,   1
   0, 0,   1
   1, 1,   0
 ]

 kids =
   brother:
     name: "Max"
     age: 11
   sister:
     name: "Ida"
     age: 9
Variable Scope

                          var changeNumbers, inner, outer;
outer = 1                 outer = 1;
changeNumbers = ->        changeNumbers = function() {
                             var inner;
  inner = -1                 inner = -1;
  outer = 10                 return outer = 10;
inner = changeNumbers()   };
                          inner = changeNumbers();
Existential Operator
 solipsism = true if mind? and not world?

 speed ?= 75

 footprints = yeti ? "bear"

 zip = lottery.drawWinner?().address?.zipcode
Conditionals
mood = greatlyImproved if singing

if happy and knowsIt
  clapsHands()
  chaChaCha()
else
  showIt()

date = if friday then sue else jill

options or= defaults
Loops

eat food for food in ['toast', 'cheese', 'wine']
countdown = (num for num in [10..1])

earsOld = max: 10, ida: 9, tim: 11
ages = for child, age of yearsOld
  child + " is " + age
Classes, Inheritance
     and super
  class Animal
    constructor: (@name) ->

    move: (meters) ->
      alert @name + " moved " + meters + "m."

  class Snake extends Animal
    move: ->
      alert "Slithering..."
      super 5

  class Horse extends Animal
    move: ->
      alert "Galloping..."
      super 45

  sam = new Snake "Sammy the Python"
  tom = new Horse "Tommy the Palomino"

  sam.move()
  tom.move()
Function Binding

Account = (customer, cart) ->
  @customer = customer
  @cart = cart

  $('.shopping_cart').bind 'click', (event) =>
    @customer.purchase @cart
And many other
nice features...
How to install?


brew install node # or install node.js otherwise
curl http://npmjs.org/install.sh | sh
npm install -g coffee-script
Back to the
Problem #1
Dynamic single page
    application
Identifying
components
    AppView
Identifying
components
    AppView



              TodoView
              TodoView
              TodoView
Identifying
components
          AppView

keypress event
click event          TodoView
      dblclick event TodoView
                     TodoView
                     click event
Browser-side
       Views and Models
          AppView
                                   TodoList
keypress event
click event          TodoView       Todo
      dblclick event TodoView       Todo
                     TodoView       Todo
                     click event
Browser-side
       Views and Models
          AppView
                                   new, fetch
                                                  TodoList
keypress event
click event          TodoView      create, save
                                                   Todo
      dblclick event TodoView                      Todo
                     TodoView                      Todo
                     click event
Browser-side
      Views and Models
         AppView
                               refresh, add    TodoList
keypress event
click event          TodoView                   Todo
      dblclick event TodoViewchange, destroy    Todo
                     TodoView                   Todo
                     click event
Browser-side Models
and RESTful resources
Browser                  Rails
             GET
 TodoList            TodosController
            POST          index
  Todo       PUT          show
  Todo                    create
            DELETE       update
  Todo
                         destroy
              JSON
Rails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
Organize CoffeeScript
and JavaScript Code




 using http://github.com/Sutto/barista
application.coffee

    # main namespace
    window.TodoApp = {}
Todo model
class TodoApp.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 @get "content"
     @set content: @EMPTY

 # Toggle the `done` state of this todo item.
 toggle: ->
   @save done: not @get "done"
TodoList collection
class TodoApp.TodoList extends Backbone.Collection

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

 # Save all of the todo items under the `"todos"` namespace.
 url: '/todos'

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

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

 # We keep the Todos in sequential order, despite being saved by unordered
 # GUID in the database. This generates the next order number for new items.
 nextOrder: ->
   if @length then @last().get('order') + 1 else 1

 # Todos are sorted by their original insertion order.
 comparator: (todo) ->
   todo.get 'order'
Todo item view
class TodoApp.TodoView extends Backbone.View
  # ... is a list tag.
  tagName: "li"

 # Cache the template function for a single item.
 template: TodoApp.template '#item-template'

 # The DOM events specific to an   item.
 events:
   "click .check"              :   "toggleDone"
   "dblclick div.todo-content" :   "edit"
   "click span.todo-destroy"   :   "destroy"
   "keypress .todo-input"      :   "updateOnEnter"

 # The TodoView listens for changes to its model, re-rendering. Since there's
 # a one-to-one correspondence between a **Todo** and a **TodoView** in this
 # app, we set a direct reference on the model for convenience.
 initialize: ->
   _.bindAll this, 'render', 'close'
   @model.bind 'change', @render
   @model.bind 'destroy', => @remove()

 # Re-render the contents of the todo item.
 render: ->
   $(@el).html @template @model.toJSON()
   @setContent()
   this
# Re-render the contents of the todo item.
render: ->
  $(@el).html @template @model.toJSON()
  @setContent()


            Todo item view
  this

# To avoid XSS (not that it would be harmful in this particular app),
# we use `jQuery.text` to set the contents of the todo item.
setContent: ->
  content = @model.get 'content'
  @$('.todo-content').text content
  @input = @$('.todo-input')
  @input.blur @close
  @input.val content

# Toggle the `"done"` state of the model.
toggleDone: ->
  @model.toggle()

# Switch this view into `"editing"` mode, displaying the input field.
edit: ->
  $(@el).addClass "editing"
  @input.focus()

# Close the `"editing"` mode, saving changes to the todo.
close: ->
  @model.save content: @input.val()
  $(@el).removeClass "editing"

# If you hit `enter`, we're through editing the item.
updateOnEnter: (e) ->
  @close() if e.keyCode == 13

# Destroy the model.
destroy: ->
  @model.destroy()
Application view
class TodoApp.AppView extends Backbone.View

 # Instead of generating a new element, bind to the existing skeleton of
 # the App already present in the HTML.
 el: "#todoapp"

 # Our template for the line of statistics at the bottom of the app.
 statsTemplate: TodoApp.template '#stats-template'

 # Delegated events for creating new items, and clearing completed ones.
 events:
   "keypress #new-todo" : "createOnEnter"
   "keyup #new-todo"     : "showTooltip"
   "click .todo-clear a" : "clearCompleted"

 # At initialization we bind to the relevant events on the `Todos`
 # collection, when items are added or changed. Kick things off by
 # loading any preexisting todos that might be saved.
 initialize: ->
   _.bindAll this, 'addOne', 'addAll', 'renderStats'

   @input = @$("#new-todo")

   @collection.bind 'add',     @addOne
   @collection.bind 'refresh', @addAll
   @collection.bind 'all',     @renderStats

   @collection.fetch()
@collection.bind 'add',     @addOne
 @collection.bind 'refresh', @addAll
 @collection.bind 'all',     @renderStats



       Application view
 @collection.fetch()

# Re-rendering the App just means refreshing the statistics -- the rest
# of the app doesn't change.
renderStats: ->
  @$('#todo-stats').html @statsTemplate
    total:      @collection.length
    done:       @collection.done().length
    remaining: @collection.remaining().length

# Add a single todo item to the list by creating a view for it, and
# appending its element to the `<ul>`.
addOne: (todo) ->
  view = new TodoApp.TodoView model: todo
  @$("#todo-list").append view.render().el

# Add all items in the collection at once.
addAll: ->
  @collection.each @addOne

# Generate the attributes for a new Todo item.
newAttributes: ->
  content: @input.val()
  order:   @collection.nextOrder()
  done:    false

# If you hit return in the main input field, create new **Todo** model,
# persisting it to server.
createOnEnter: (e) ->
  if e.keyCode == 13
    @collection.create @newAttributes()
    @input.val ''
view = new TodoApp.TodoView model: todo
 @$("#todo-list").append view.render().el

# Add all items in the collection at once.


       Application view
addAll: ->
  @collection.each @addOne

# Generate the attributes for a new Todo item.
newAttributes: ->
  content: @input.val()
  order:   @collection.nextOrder()
  done:    false

# If you hit return in the main input field, create new **Todo** model,
# persisting it to server.
createOnEnter: (e) ->
  if e.keyCode == 13
    @collection.create @newAttributes()
    @input.val ''

# Clear all done todo items, destroying their views and models.
clearCompleted: ->
  todo.destroy() for todo in @collection.done()
  false

# Lazily show the tooltip that tells you to press `enter` to save
# a new todo item, after one second.
showTooltip: (e) ->
  tooltip = @$(".ui-tooltip-top")
  val = @input.val()
  tooltip.fadeOut()
  clearTimeout @tooltipTimeout if @tooltipTimeout
  unless val == '' or val == @input.attr 'placeholder'
    @tooltipTimeout = _.delay ->
      tooltip.show().fadeIn()
    , 1000
#todoapp
                  index.html.haml
  .title
    %h1 Todos
  .content
    #create-todo
      %input#new-todo{:placeholder => "What needs to be done?", :type => "text"}/
      %span.ui-tooltip-top{:style => "display:none;"} Press Enter to save this task
    #todos
      %ul#todo-list
    #todo-stats
%ul#instructions
  %li Double-click to edit a todo.

:coffeescript
  $ ->
    TodoApp.appView = new TodoApp.AppView collection: new TodoApp.TodoList

%script#item-template{:type => "text/html"}
  .todo{:class => "{{#done}}done{{/done}}"}
    .display
      %input{:class => "check", :type => "checkbox", :"{{#done}}checked{{/done}}" => true}
      .todo-content
      %span.todo-destroy
    .edit
      %input.todo-input{:type => "text", :value => ""}

%script#stats-template{:type => "text/html"}
  {{#if total}}
  %span.todo-count
%input#new-todo{:placeholder => "What needs to be done?", :type => "text"}/
      %span.ui-tooltip-top{:style => "display:none;"} Press Enter to save this task
    #todos
      %ul#todo-list


                  index.html.haml
    #todo-stats
%ul#instructions
  %li Double-click to edit a todo.

:coffeescript
  $ ->
    TodoApp.appView = new TodoApp.AppView collection: new TodoApp.TodoList

%script#item-template{:type => "text/html"}
  .todo{:class => "{{#done}}done{{/done}}"}
    .display
      %input{:class => "check", :type => "checkbox", :"{{#done}}checked{{/done}}" => true}
      .todo-content
      %span.todo-destroy
    .edit
      %input.todo-input{:type => "text", :value => ""}

%script#stats-template{:type => "text/html"}
  {{#if total}}
  %span.todo-count
    %span.number {{remaining}}
    %span.word {{pluralize remaining "item"}}
    left.
  {{/if}}
  {{#if done}}
  %span.todo-clear
    %a{:href => "#"}
      Clear
      %span.number-done {{done}}
      completed
      %span.word-done {{pluralize done "item"}}
  {{/if}}
One more thing:
Backbone Controllers
          Routers
class Workspace extends Backbone.Controller Router

  routes:
    "help"                : "help"     #help
    "search/:query"       : "search"   #search/kiwis
    "search/:query/p:page": "search"   #search/kiwis/p7

  help: ->
    ...

  search: (query, page) ->
    ...
How do you test it?
RSpec-like testing for
    JavaScript
Together with all
  other tests
Testing Todo model
describe "Todo", ->
  todo = null
  ajaxCall = (param) -> jQuery.ajax.mostRecentCall.args[0][param]

  beforeEach ->
    todo = new TodoApp.Todo
    todos = new TodoApp.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()
and TodoList
                  collection
describe "TodoList", ->
  attributes = [
    content: "First"
    done: true
  ,
    content: "Second"
  ]
  todos = null

  beforeEach ->
    todos = new TodoApp.TodoList attributes

  it "should return done todos", ->
    expect(_.invoke todos.done(), "toJSON").toEqual [attributes[0]]

  it "should return remaining todos", ->
    expect(_.invoke todos.remaining(), "toJSON").toEqual [attributes[1]]
Rails 3.1
Asset Pipeline
application.js.coffee
    using Sprockets

     #=   require jquery
     #=   require underscore
     #=   require backbone
     #=   require handlebars
     #=   require ./todo_app
     #=   require_tree ./models
     #=   require ./views/helpers
     #=   require_tree ./views
Watch RailsConf
           DHH keynote
http://en.oreilly.com/rails2011/public/schedule/detail/19068
References
     http://jashkenas.github.com/coffee-script/

    http://documentcloud.github.com/backbone/

         http://pivotal.github.com/jasmine/



https://github.com/rsim/backbone_coffeescript_demo
Used in eazybi.com

More Related Content

What's hot (20)

Scala ActiveRecord
Scala ActiveRecordScala ActiveRecord
Scala ActiveRecord
scalaconfjp
Ā 
Rust ⇋ JavaScript
Rust ⇋ JavaScriptRust ⇋ JavaScript
Rust ⇋ JavaScript
Ingvar Stepanyan
Ā 
Say Hello To Ecmascript 5
Say Hello To Ecmascript 5Say Hello To Ecmascript 5
Say Hello To Ecmascript 5
Juriy Zaytsev
Ā 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Domenic Denicola
Ā 
5 Tips for Better JavaScript
5 Tips for Better JavaScript5 Tips for Better JavaScript
5 Tips for Better JavaScript
Todd Anglin
Ā 
Node Boot Camp
Node Boot CampNode Boot Camp
Node Boot Camp
Troy Miles
Ā 
Scala active record
Scala active recordScala active record
Scala active record
鉄平 土佐
Ā 
AST - the only true tool for building JavaScript
AST - the only true tool for building JavaScriptAST - the only true tool for building JavaScript
AST - the only true tool for building JavaScript
Ingvar Stepanyan
Ā 
Mastering Java Bytecode With ASM - 33rd degree, 2012
Mastering Java Bytecode With ASM - 33rd degree, 2012Mastering Java Bytecode With ASM - 33rd degree, 2012
Mastering Java Bytecode With ASM - 33rd degree, 2012
Anton Arhipov
Ā 
JavaScript and the AST
JavaScript and the ASTJavaScript and the AST
JavaScript and the AST
Jarrod Overson
Ā 
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
Fernando Hamasaki de Amorim
Ā 
G*ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ— in 仙台 Grails(とことん)å…„é–€
G*ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ— in 仙台 Grails(とことん)å…„é–€G*ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ— in 仙台 Grails(とことん)å…„é–€
G*ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ— in 仙台 Grails(とことん)å…„é–€
Tsuyoshi Yamamoto
Ā 
Your code is not a string
Your code is not a stringYour code is not a string
Your code is not a string
Ingvar Stepanyan
Ā 
Ten useful JavaScript tips & best practices
Ten useful JavaScript tips & best practicesTen useful JavaScript tips & best practices
Ten useful JavaScript tips & best practices
Ankit Rastogi
Ā 
RubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - KeynoteRubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - Keynote
Dr Nic Williams
Ā 
Xlab #1: Advantages of functional programming in Java 8
Xlab #1: Advantages of functional programming in Java 8Xlab #1: Advantages of functional programming in Java 8
Xlab #1: Advantages of functional programming in Java 8
XSolve
Ā 
Http4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web StackHttp4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web Stack
GaryCoady
Ā 
Workshop 5: JavaScript testing
Workshop 5: JavaScript testingWorkshop 5: JavaScript testing
Workshop 5: JavaScript testing
Visual Engineering
Ā 
Art of Javascript
Art of JavascriptArt of Javascript
Art of Javascript
Tarek Yehia
Ā 
Workshop 10: ECMAScript 6
Workshop 10: ECMAScript 6Workshop 10: ECMAScript 6
Workshop 10: ECMAScript 6
Visual Engineering
Ā 
Scala ActiveRecord
Scala ActiveRecordScala ActiveRecord
Scala ActiveRecord
scalaconfjp
Ā 
Rust ⇋ JavaScript
Rust ⇋ JavaScriptRust ⇋ JavaScript
Rust ⇋ JavaScript
Ingvar Stepanyan
Ā 
Say Hello To Ecmascript 5
Say Hello To Ecmascript 5Say Hello To Ecmascript 5
Say Hello To Ecmascript 5
Juriy Zaytsev
Ā 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Domenic Denicola
Ā 
5 Tips for Better JavaScript
5 Tips for Better JavaScript5 Tips for Better JavaScript
5 Tips for Better JavaScript
Todd Anglin
Ā 
Node Boot Camp
Node Boot CampNode Boot Camp
Node Boot Camp
Troy Miles
Ā 
AST - the only true tool for building JavaScript
AST - the only true tool for building JavaScriptAST - the only true tool for building JavaScript
AST - the only true tool for building JavaScript
Ingvar Stepanyan
Ā 
Mastering Java Bytecode With ASM - 33rd degree, 2012
Mastering Java Bytecode With ASM - 33rd degree, 2012Mastering Java Bytecode With ASM - 33rd degree, 2012
Mastering Java Bytecode With ASM - 33rd degree, 2012
Anton Arhipov
Ā 
JavaScript and the AST
JavaScript and the ASTJavaScript and the AST
JavaScript and the AST
Jarrod Overson
Ā 
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
Fernando Hamasaki de Amorim
Ā 
G*ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ— in 仙台 Grails(とことん)å…„é–€
G*ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ— in 仙台 Grails(とことん)å…„é–€G*ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ— in 仙台 Grails(とことん)å…„é–€
G*ćƒÆćƒ¼ć‚Æć‚·ćƒ§ćƒƒćƒ— in 仙台 Grails(とことん)å…„é–€
Tsuyoshi Yamamoto
Ā 
Your code is not a string
Your code is not a stringYour code is not a string
Your code is not a string
Ingvar Stepanyan
Ā 
Ten useful JavaScript tips & best practices
Ten useful JavaScript tips & best practicesTen useful JavaScript tips & best practices
Ten useful JavaScript tips & best practices
Ankit Rastogi
Ā 
RubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - KeynoteRubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - Keynote
Dr Nic Williams
Ā 
Xlab #1: Advantages of functional programming in Java 8
Xlab #1: Advantages of functional programming in Java 8Xlab #1: Advantages of functional programming in Java 8
Xlab #1: Advantages of functional programming in Java 8
XSolve
Ā 
Http4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web StackHttp4s, Doobie and Circe: The Functional Web Stack
Http4s, Doobie and Circe: The Functional Web Stack
GaryCoady
Ā 
Workshop 5: JavaScript testing
Workshop 5: JavaScript testingWorkshop 5: JavaScript testing
Workshop 5: JavaScript testing
Visual Engineering
Ā 
Art of Javascript
Art of JavascriptArt of Javascript
Art of Javascript
Tarek Yehia
Ā 
Workshop 10: ECMAScript 6
Workshop 10: ECMAScript 6Workshop 10: ECMAScript 6
Workshop 10: ECMAScript 6
Visual Engineering
Ā 

Similar to Rails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine (20)

A piece of sugar in your client-side development
A piece of sugar in your client-side developmentA piece of sugar in your client-side development
A piece of sugar in your client-side development
Nicolas Blanco
Ā 
Damn Fine CoffeeScript
Damn Fine CoffeeScriptDamn Fine CoffeeScript
Damn Fine CoffeeScript
niklal
Ā 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Paulo Ragonha
Ā 
CoffeeScript
CoffeeScriptCoffeeScript
CoffeeScript
Scott Leberknight
Ā 
CoffeeScript
CoffeeScriptCoffeeScript
CoffeeScript
Ryan McGeary
Ā 
CoffeeScript - JavaScript in a simple way
CoffeeScript - JavaScript in a simple wayCoffeeScript - JavaScript in a simple way
CoffeeScript - JavaScript in a simple way
Lim Chanmann
Ā 
JavaScript 101
JavaScript 101JavaScript 101
JavaScript 101
ygv2000
Ā 
CoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairCoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love Affair
Mark
Ā 
Maintainable JavaScript 2011
Maintainable JavaScript 2011Maintainable JavaScript 2011
Maintainable JavaScript 2011
Nicholas Zakas
Ā 
Maintainable JavaScript 2012
Maintainable JavaScript 2012Maintainable JavaScript 2012
Maintainable JavaScript 2012
Nicholas Zakas
Ā 
2 coding101 fewd_lesson2_programming_overview 20210105
2 coding101 fewd_lesson2_programming_overview 202101052 coding101 fewd_lesson2_programming_overview 20210105
2 coding101 fewd_lesson2_programming_overview 20210105
John Picasso
Ā 
Javascript variables and datatypes
Javascript variables and datatypesJavascript variables and datatypes
Javascript variables and datatypes
Varun C M
Ā 
WD programs descriptions.docx
WD programs descriptions.docxWD programs descriptions.docx
WD programs descriptions.docx
anjani pavan kumar
Ā 
The Future of JavaScript (Ajax Exp '07)
The Future of JavaScript (Ajax Exp '07)The Future of JavaScript (Ajax Exp '07)
The Future of JavaScript (Ajax Exp '07)
jeresig
Ā 
JavaScript The Definitive Guide 7th Edition David Flanagan
JavaScript The Definitive Guide 7th Edition David FlanaganJavaScript The Definitive Guide 7th Edition David Flanagan
JavaScript The Definitive Guide 7th Edition David Flanagan
nohelardif
Ā 
Good practices for Developers
Good practices for DevelopersGood practices for Developers
Good practices for Developers
Piotr Miazga
Ā 
Good Coding Practices with JavaScript
Good Coding Practices with JavaScriptGood Coding Practices with JavaScript
Good Coding Practices with JavaScript
šŸ Pierre-Henry Soria šŸ’”
Ā 
JavaScript from Scratch: Getting Your Feet Wet
JavaScript from Scratch: Getting Your Feet WetJavaScript from Scratch: Getting Your Feet Wet
JavaScript from Scratch: Getting Your Feet Wet
Michael Girouard
Ā 
Maintainable JavaScript
Maintainable JavaScriptMaintainable JavaScript
Maintainable JavaScript
Nicholas Zakas
Ā 
Wakanday JS201 Best Practices
Wakanday JS201 Best PracticesWakanday JS201 Best Practices
Wakanday JS201 Best Practices
Juergen Fesslmeier
Ā 
A piece of sugar in your client-side development
A piece of sugar in your client-side developmentA piece of sugar in your client-side development
A piece of sugar in your client-side development
Nicolas Blanco
Ā 
Damn Fine CoffeeScript
Damn Fine CoffeeScriptDamn Fine CoffeeScript
Damn Fine CoffeeScript
niklal
Ā 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Paulo Ragonha
Ā 
CoffeeScript
CoffeeScriptCoffeeScript
CoffeeScript
Ryan McGeary
Ā 
CoffeeScript - JavaScript in a simple way
CoffeeScript - JavaScript in a simple wayCoffeeScript - JavaScript in a simple way
CoffeeScript - JavaScript in a simple way
Lim Chanmann
Ā 
JavaScript 101
JavaScript 101JavaScript 101
JavaScript 101
ygv2000
Ā 
CoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairCoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love Affair
Mark
Ā 
Maintainable JavaScript 2011
Maintainable JavaScript 2011Maintainable JavaScript 2011
Maintainable JavaScript 2011
Nicholas Zakas
Ā 
Maintainable JavaScript 2012
Maintainable JavaScript 2012Maintainable JavaScript 2012
Maintainable JavaScript 2012
Nicholas Zakas
Ā 
2 coding101 fewd_lesson2_programming_overview 20210105
2 coding101 fewd_lesson2_programming_overview 202101052 coding101 fewd_lesson2_programming_overview 20210105
2 coding101 fewd_lesson2_programming_overview 20210105
John Picasso
Ā 
Javascript variables and datatypes
Javascript variables and datatypesJavascript variables and datatypes
Javascript variables and datatypes
Varun C M
Ā 
WD programs descriptions.docx
WD programs descriptions.docxWD programs descriptions.docx
WD programs descriptions.docx
anjani pavan kumar
Ā 
The Future of JavaScript (Ajax Exp '07)
The Future of JavaScript (Ajax Exp '07)The Future of JavaScript (Ajax Exp '07)
The Future of JavaScript (Ajax Exp '07)
jeresig
Ā 
JavaScript The Definitive Guide 7th Edition David Flanagan
JavaScript The Definitive Guide 7th Edition David FlanaganJavaScript The Definitive Guide 7th Edition David Flanagan
JavaScript The Definitive Guide 7th Edition David Flanagan
nohelardif
Ā 
Good practices for Developers
Good practices for DevelopersGood practices for Developers
Good practices for Developers
Piotr Miazga
Ā 
JavaScript from Scratch: Getting Your Feet Wet
JavaScript from Scratch: Getting Your Feet WetJavaScript from Scratch: Getting Your Feet Wet
JavaScript from Scratch: Getting Your Feet Wet
Michael Girouard
Ā 
Maintainable JavaScript
Maintainable JavaScriptMaintainable JavaScript
Maintainable JavaScript
Nicholas Zakas
Ā 
Wakanday JS201 Best Practices
Wakanday JS201 Best PracticesWakanday JS201 Best Practices
Wakanday JS201 Best Practices
Juergen Fesslmeier
Ā 
Ad

More from Raimonds Simanovskis (20)

Profiling Mondrian MDX Requests in a Production Environment
Profiling Mondrian MDX Requests in a Production EnvironmentProfiling Mondrian MDX Requests in a Production Environment
Profiling Mondrian MDX Requests in a Production Environment
Raimonds Simanovskis
Ā 
Improve Mondrian MDX usability with user defined functions
Improve Mondrian MDX usability with user defined functionsImprove Mondrian MDX usability with user defined functions
Improve Mondrian MDX usability with user defined functions
Raimonds Simanovskis
Ā 
Analyze and Visualize Git Log for Fun and Profit - DevTernity 2015
Analyze and Visualize Git Log for Fun and Profit - DevTernity 2015Analyze and Visualize Git Log for Fun and Profit - DevTernity 2015
Analyze and Visualize Git Log for Fun and Profit - DevTernity 2015
Raimonds Simanovskis
Ā 
Data Warehouses and Multi-Dimensional Data Analysis
Data Warehouses and Multi-Dimensional Data AnalysisData Warehouses and Multi-Dimensional Data Analysis
Data Warehouses and Multi-Dimensional Data Analysis
Raimonds Simanovskis
Ā 
mondrian-olap JRuby library
mondrian-olap JRuby librarymondrian-olap JRuby library
mondrian-olap JRuby library
Raimonds Simanovskis
Ā 
eazyBI Overview - Embedding Mondrian in other applications
eazyBI Overview - Embedding Mondrian in other applicationseazyBI Overview - Embedding Mondrian in other applications
eazyBI Overview - Embedding Mondrian in other applications
Raimonds Simanovskis
Ā 
Atvērto datu izmantoÅ”anas pieredze Latvijā
Atvērto datu izmantoÅ”anas pieredze LatvijāAtvērto datu izmantoÅ”anas pieredze Latvijā
Atvērto datu izmantoÅ”anas pieredze Latvijā
Raimonds Simanovskis
Ā 
JavaScript Unit Testing with Jasmine
JavaScript Unit Testing with JasmineJavaScript Unit Testing with Jasmine
JavaScript Unit Testing with Jasmine
Raimonds Simanovskis
Ā 
JRuby - Programmer's Best Friend on JVM
JRuby - Programmer's Best Friend on JVMJRuby - Programmer's Best Friend on JVM
JRuby - Programmer's Best Friend on JVM
Raimonds Simanovskis
Ā 
Agile Operations or How to sleep better at night
Agile Operations or How to sleep better at nightAgile Operations or How to sleep better at night
Agile Operations or How to sleep better at night
Raimonds Simanovskis
Ā 
TDD - Why and How?
TDD - Why and How?TDD - Why and How?
TDD - Why and How?
Raimonds Simanovskis
Ā 
Analyze and Visualize Git Log for Fun and Profit
Analyze and Visualize Git Log for Fun and ProfitAnalyze and Visualize Git Log for Fun and Profit
Analyze and Visualize Git Log for Fun and Profit
Raimonds Simanovskis
Ā 
PL/SQL Unit Testing Can Be Fun
PL/SQL Unit Testing Can Be FunPL/SQL Unit Testing Can Be Fun
PL/SQL Unit Testing Can Be Fun
Raimonds Simanovskis
Ā 
opendata.lv Case Study - Promote Open Data with Analytics and Visualizations
opendata.lv Case Study - Promote Open Data with Analytics and Visualizationsopendata.lv Case Study - Promote Open Data with Analytics and Visualizations
opendata.lv Case Study - Promote Open Data with Analytics and Visualizations
Raimonds Simanovskis
Ā 
Extending Oracle E-Business Suite with Ruby on Rails
Extending Oracle E-Business Suite with Ruby on RailsExtending Oracle E-Business Suite with Ruby on Rails
Extending Oracle E-Business Suite with Ruby on Rails
Raimonds Simanovskis
Ā 
RailsWayCon: Multidimensional Data Analysis with JRuby
RailsWayCon: Multidimensional Data Analysis with JRubyRailsWayCon: Multidimensional Data Analysis with JRuby
RailsWayCon: Multidimensional Data Analysis with JRuby
Raimonds Simanovskis
Ā 
Multidimensional Data Analysis with JRuby
Multidimensional Data Analysis with JRubyMultidimensional Data Analysis with JRuby
Multidimensional Data Analysis with JRuby
Raimonds Simanovskis
Ā 
Rails-like JavaScript using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript using CoffeeScript, Backbone.js and JasmineRails-like JavaScript using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript using CoffeeScript, Backbone.js and Jasmine
Raimonds Simanovskis
Ā 
How to Adopt Agile at Your Organization
How to Adopt Agile at Your OrganizationHow to Adopt Agile at Your Organization
How to Adopt Agile at Your Organization
Raimonds Simanovskis
Ā 
Multidimensional Data Analysis with Ruby (sample)
Multidimensional Data Analysis with Ruby (sample)Multidimensional Data Analysis with Ruby (sample)
Multidimensional Data Analysis with Ruby (sample)
Raimonds Simanovskis
Ā 
Profiling Mondrian MDX Requests in a Production Environment
Profiling Mondrian MDX Requests in a Production EnvironmentProfiling Mondrian MDX Requests in a Production Environment
Profiling Mondrian MDX Requests in a Production Environment
Raimonds Simanovskis
Ā 
Improve Mondrian MDX usability with user defined functions
Improve Mondrian MDX usability with user defined functionsImprove Mondrian MDX usability with user defined functions
Improve Mondrian MDX usability with user defined functions
Raimonds Simanovskis
Ā 
Analyze and Visualize Git Log for Fun and Profit - DevTernity 2015
Analyze and Visualize Git Log for Fun and Profit - DevTernity 2015Analyze and Visualize Git Log for Fun and Profit - DevTernity 2015
Analyze and Visualize Git Log for Fun and Profit - DevTernity 2015
Raimonds Simanovskis
Ā 
Data Warehouses and Multi-Dimensional Data Analysis
Data Warehouses and Multi-Dimensional Data AnalysisData Warehouses and Multi-Dimensional Data Analysis
Data Warehouses and Multi-Dimensional Data Analysis
Raimonds Simanovskis
Ā 
eazyBI Overview - Embedding Mondrian in other applications
eazyBI Overview - Embedding Mondrian in other applicationseazyBI Overview - Embedding Mondrian in other applications
eazyBI Overview - Embedding Mondrian in other applications
Raimonds Simanovskis
Ā 
Atvērto datu izmantoÅ”anas pieredze Latvijā
Atvērto datu izmantoÅ”anas pieredze LatvijāAtvērto datu izmantoÅ”anas pieredze Latvijā
Atvērto datu izmantoÅ”anas pieredze Latvijā
Raimonds Simanovskis
Ā 
JavaScript Unit Testing with Jasmine
JavaScript Unit Testing with JasmineJavaScript Unit Testing with Jasmine
JavaScript Unit Testing with Jasmine
Raimonds Simanovskis
Ā 
JRuby - Programmer's Best Friend on JVM
JRuby - Programmer's Best Friend on JVMJRuby - Programmer's Best Friend on JVM
JRuby - Programmer's Best Friend on JVM
Raimonds Simanovskis
Ā 
Agile Operations or How to sleep better at night
Agile Operations or How to sleep better at nightAgile Operations or How to sleep better at night
Agile Operations or How to sleep better at night
Raimonds Simanovskis
Ā 
Analyze and Visualize Git Log for Fun and Profit
Analyze and Visualize Git Log for Fun and ProfitAnalyze and Visualize Git Log for Fun and Profit
Analyze and Visualize Git Log for Fun and Profit
Raimonds Simanovskis
Ā 
PL/SQL Unit Testing Can Be Fun
PL/SQL Unit Testing Can Be FunPL/SQL Unit Testing Can Be Fun
PL/SQL Unit Testing Can Be Fun
Raimonds Simanovskis
Ā 
opendata.lv Case Study - Promote Open Data with Analytics and Visualizations
opendata.lv Case Study - Promote Open Data with Analytics and Visualizationsopendata.lv Case Study - Promote Open Data with Analytics and Visualizations
opendata.lv Case Study - Promote Open Data with Analytics and Visualizations
Raimonds Simanovskis
Ā 
Extending Oracle E-Business Suite with Ruby on Rails
Extending Oracle E-Business Suite with Ruby on RailsExtending Oracle E-Business Suite with Ruby on Rails
Extending Oracle E-Business Suite with Ruby on Rails
Raimonds Simanovskis
Ā 
RailsWayCon: Multidimensional Data Analysis with JRuby
RailsWayCon: Multidimensional Data Analysis with JRubyRailsWayCon: Multidimensional Data Analysis with JRuby
RailsWayCon: Multidimensional Data Analysis with JRuby
Raimonds Simanovskis
Ā 
Multidimensional Data Analysis with JRuby
Multidimensional Data Analysis with JRubyMultidimensional Data Analysis with JRuby
Multidimensional Data Analysis with JRuby
Raimonds Simanovskis
Ā 
Rails-like JavaScript using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript using CoffeeScript, Backbone.js and JasmineRails-like JavaScript using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript using CoffeeScript, Backbone.js and Jasmine
Raimonds Simanovskis
Ā 
How to Adopt Agile at Your Organization
How to Adopt Agile at Your OrganizationHow to Adopt Agile at Your Organization
How to Adopt Agile at Your Organization
Raimonds Simanovskis
Ā 
Multidimensional Data Analysis with Ruby (sample)
Multidimensional Data Analysis with Ruby (sample)Multidimensional Data Analysis with Ruby (sample)
Multidimensional Data Analysis with Ruby (sample)
Raimonds Simanovskis
Ā 
Ad

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

  • 1. Raimonds Simanovskis Rails-like JavaScript using CoffeeScript, Backbone.js and Jasmine
  • 2. Raimonds Simanovskis github.com/rsim @rsim .com
  • 4. Ruby code in Rails
  • 5. JavaScript code in Rails 3.0.x
  • 6. application.js // Place your application-specific JavaScript functions and classes here // This file is automatically included by javascript_include_tag :defaults
  • 8. application.js (example from Redmine) /* redMine - project management software Copyright (C) 2006-2008 Jean-Philippe Lang */ function checkAll (id, checked) { var els = Element.descendants(id); for (var i = 0; i < els.length; i++) { if (els[i].disabled==false) { els[i].checked = checked; } } } function toggleCheckboxesBySelector(selector) { boxes = $$(selector); var all_checked = true; for (i = 0; i < boxes.length; i++) { if (boxes[i].checked == false) { all_checked = false; } } for (i = 0; i < boxes.length; i++) { boxes[i].checked = !all_checked; } } function setCheckboxesBySelector(checked, selector) { var boxes = $$(selector); boxes.each(function(ele) { ele.checked = checked; }); } function showAndScrollTo(id, focus) { Element.show(id); if (focus!=null) { Form.Element.focus(focus); }
  • 9. /* * 1 - registers a callback which copies the csrf token into the * X-CSRF-Token header with each ajax request. Necessary to application.js * work with rails applications which have fixed * CVE-2011-0447 * 2 - shows and hides ajax indicator */ Ajax.Responders.register({ (example from Redmine) onCreate: function(request){ var csrf_meta_tag = $$('meta[name=csrf-token]')[0]; if (csrf_meta_tag) { var header = 'X-CSRF-Token', token = csrf_meta_tag.readAttribute('content'); if (!request.options.requestHeaders) { request.options.requestHeaders = {}; } request.options.requestHeaders[header] = token; } if ($('ajax-indicator') && Ajax.activeRequestCount > 0) { Element.show('ajax-indicator'); } }, onComplete: function(){ if ($('ajax-indicator') && Ajax.activeRequestCount == 0) { Element.hide('ajax-indicator'); } } }); function hideOnLoad() { $$('.hol').each(function(el) { el.hide(); }); } Event.observe(window, 'load', hideOnLoad);
  • 11. Do we really know (and love?) JavaScript?
  • 12. Sample JavaScript (from RailsCasts #267) var CreditCard = { cleanNumber: function(number) { return number.replace(/[- ]/g, ""); }, validNumber: function(number) { var total = 0; number = this.cleanNumber(number); for (var i=number.length-1; i >= 0; i--) { var n = parseInt(number[i]); if ((i+number.length) % 2 == 0) { n = n*2 > 9 ? n*2 - 9 : n*2; } total += n; }; return total % 10 == 0; } }; console.log(CreditCard.validNumber('4111 1111-11111111')); // true console.log(CreditCard.validNumber('4111111111111121')); // false
  • 13. We see as this ā€œuglyā€ Ruby CreditCard = { :cleanNumber => lambda { |number| return number.gsub(/[- ]/, ""); }, :validNumber => lambda { |number| total = 0; number = CreditCard[:cleanNumber].call(number); for i in 0..(number.length-1) n = number[i].to_i; if ((i+number.length) % 2 == 0) n = n*2 > 9 ? n*2 - 9 : n*2; end total += n; end; return total % 10 == 0; } }; puts(CreditCard[:validNumber].call('4111 1111-11111111')); # true puts(CreditCard[:validNumber].call('4111111111111121')); # false
  • 14. Or as this ā€œnormalā€ Ruby module CreditCard def self.clean_number(number) number.gsub(/[- ]/, "") end def self.valid_number?(number) total = 0 number = clean_number(number) for i in 0...number.length n = number[i].to_i if i+number.length % 2 == 0 n = n*2 > 9 ? n*2 - 9 : n*2 end total += n end total % 10 == 0 end end puts CreditCard.valid_number?('4111 1111-11111111') # true puts CreditCard.valid_number?('4111111111111121') # false
  • 15. ā€œBest practicesā€ Ruby class CreditCard def initialize(number) @number = clean_number(number) end def valid? total = 0 for i in [email protected] n = @number[i].to_i if [email protected] % 2 == 0 n = n*2 > 9 ? n*2 - 9 : n*2 end total += n end total % 10 == 0 end private def clean_number(number) number.gsub(/[- ]/, "") end end puts CreditCard.new('4111 1111-11111111').valid? # true puts CreditCard.new('4111111111111121').valid? # false
  • 16. JavaScript has objects too! var CreditCard = function(number) { function cleanNumber(number) { return number.replace(/[- ]/g, ""); } this.number = cleanNumber(number); }; CreditCard.prototype = { isValid: function() { var total = 0; for (var i=this.number.length-1; i >= 0; i--) { var n = parseInt(this.number[i]); if ((i+this.number.length) % 2 == 0) { n = n*2 > 9 ? n*2 - 9 : n*2; } total += n; }; return total % 10 == 0; } }; console.log( (new CreditCard('4111 1111-11111111')).isValid() ); // true console.log( (new CreditCard('4111111111111121')).isValid() ); // false
  • 17. But this would be much more Ruby-like! class CreditCard cleanNumber = (number) -> number.replace /[- ]/g, "" constructor: (number) -> @number = cleanNumber number isValid: (number) -> total = 0 for i in [[email protected]] n = +@number[i] if ([email protected]) % 2 == 0 n = if n*2 > 9 then n*2 - 9 else n*2 total += n total % 10 == 0 console.log (new CreditCard '4111 1111-11111111').isValid() # true console.log (new CreditCard '4111111111111121').isValid() # false
  • 20. 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
  • 21. Functions square = (x) -> x * x cube = (x) -> square(x) * x fill = (container, liquid = "coffee") -> "Filling the #{container} with #{liquid}..." awardMedals = (first, second, others...) -> gold = first silver = second rest = others contenders = [ "Michael Phelps" "Liu Xiang" "Yao Ming" "Allyson Felix" "Shawn Johnson" ] awardMedals contenders...
  • 22. Objects and Arrays song = ["do", "re", "mi", "fa", "so"] singers = {Jagger: "Rock", Elvis: "Roll"} bitlist = [ 1, 0, 1 0, 0, 1 1, 1, 0 ] kids = brother: name: "Max" age: 11 sister: name: "Ida" age: 9
  • 23. Variable Scope var changeNumbers, inner, outer; outer = 1 outer = 1; changeNumbers = -> changeNumbers = function() { var inner; inner = -1 inner = -1; outer = 10 return outer = 10; inner = changeNumbers() }; inner = changeNumbers();
  • 24. Existential Operator solipsism = true if mind? and not world? speed ?= 75 footprints = yeti ? "bear" zip = lottery.drawWinner?().address?.zipcode
  • 25. Conditionals mood = greatlyImproved if singing if happy and knowsIt clapsHands() chaChaCha() else showIt() date = if friday then sue else jill options or= defaults
  • 26. Loops eat food for food in ['toast', 'cheese', 'wine'] countdown = (num for num in [10..1]) earsOld = max: 10, ida: 9, tim: 11 ages = for child, age of yearsOld child + " is " + age
  • 27. Classes, Inheritance and super class Animal constructor: (@name) -> move: (meters) -> alert @name + " moved " + meters + "m." class Snake extends Animal move: -> alert "Slithering..." super 5 class Horse extends Animal move: -> alert "Galloping..." super 45 sam = new Snake "Sammy the Python" tom = new Horse "Tommy the Palomino" sam.move() tom.move()
  • 28. Function Binding Account = (customer, cart) -> @customer = customer @cart = cart $('.shopping_cart').bind 'click', (event) => @customer.purchase @cart
  • 29. And many other nice features...
  • 30. How to install? brew install node # or install node.js otherwise curl http://npmjs.org/install.sh | sh npm install -g coffee-script
  • 32. Dynamic single page application
  • 34. Identifying components AppView TodoView TodoView TodoView
  • 35. Identifying components AppView keypress event click event TodoView dblclick event TodoView TodoView click event
  • 36. Browser-side Views and Models AppView TodoList keypress event click event TodoView Todo dblclick event TodoView Todo TodoView Todo click event
  • 37. Browser-side Views and Models AppView new, fetch TodoList keypress event click event TodoView create, save Todo dblclick event TodoView Todo TodoView Todo click event
  • 38. Browser-side Views and Models AppView refresh, add TodoList keypress event click event TodoView Todo dblclick event TodoViewchange, destroy Todo TodoView Todo click event
  • 39. Browser-side Models and RESTful resources Browser Rails GET TodoList TodosController POST index Todo PUT show Todo create DELETE update Todo destroy JSON
  • 41. Organize CoffeeScript and JavaScript Code using http://github.com/Sutto/barista
  • 42. application.coffee # main namespace window.TodoApp = {}
  • 43. Todo model class TodoApp.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 @get "content" @set content: @EMPTY # Toggle the `done` state of this todo item. toggle: -> @save done: not @get "done"
  • 44. TodoList collection class TodoApp.TodoList extends Backbone.Collection # Reference to this collection's model. model: TodoApp.Todo # Save all of the todo items under the `"todos"` namespace. url: '/todos' # Filter down the list of all todo items that are finished. done: -> @filter (todo) -> todo.get 'done' # Filter down the list to only todo items that are still not finished. remaining: -> @without this.done()... # We keep the Todos in sequential order, despite being saved by unordered # GUID in the database. This generates the next order number for new items. nextOrder: -> if @length then @last().get('order') + 1 else 1 # Todos are sorted by their original insertion order. comparator: (todo) -> todo.get 'order'
  • 45. Todo item view class TodoApp.TodoView extends Backbone.View # ... is a list tag. tagName: "li" # Cache the template function for a single item. template: TodoApp.template '#item-template' # The DOM events specific to an item. events: "click .check" : "toggleDone" "dblclick div.todo-content" : "edit" "click span.todo-destroy" : "destroy" "keypress .todo-input" : "updateOnEnter" # The TodoView listens for changes to its model, re-rendering. Since there's # a one-to-one correspondence between a **Todo** and a **TodoView** in this # app, we set a direct reference on the model for convenience. initialize: -> _.bindAll this, 'render', 'close' @model.bind 'change', @render @model.bind 'destroy', => @remove() # Re-render the contents of the todo item. render: -> $(@el).html @template @model.toJSON() @setContent() this
  • 46. # Re-render the contents of the todo item. render: -> $(@el).html @template @model.toJSON() @setContent() Todo item view this # To avoid XSS (not that it would be harmful in this particular app), # we use `jQuery.text` to set the contents of the todo item. setContent: -> content = @model.get 'content' @$('.todo-content').text content @input = @$('.todo-input') @input.blur @close @input.val content # Toggle the `"done"` state of the model. toggleDone: -> @model.toggle() # Switch this view into `"editing"` mode, displaying the input field. edit: -> $(@el).addClass "editing" @input.focus() # Close the `"editing"` mode, saving changes to the todo. close: -> @model.save content: @input.val() $(@el).removeClass "editing" # If you hit `enter`, we're through editing the item. updateOnEnter: (e) -> @close() if e.keyCode == 13 # Destroy the model. destroy: -> @model.destroy()
  • 47. Application view class TodoApp.AppView extends Backbone.View # Instead of generating a new element, bind to the existing skeleton of # the App already present in the HTML. el: "#todoapp" # Our template for the line of statistics at the bottom of the app. statsTemplate: TodoApp.template '#stats-template' # Delegated events for creating new items, and clearing completed ones. events: "keypress #new-todo" : "createOnEnter" "keyup #new-todo" : "showTooltip" "click .todo-clear a" : "clearCompleted" # At initialization we bind to the relevant events on the `Todos` # collection, when items are added or changed. Kick things off by # loading any preexisting todos that might be saved. initialize: -> _.bindAll this, 'addOne', 'addAll', 'renderStats' @input = @$("#new-todo") @collection.bind 'add', @addOne @collection.bind 'refresh', @addAll @collection.bind 'all', @renderStats @collection.fetch()
  • 48. @collection.bind 'add', @addOne @collection.bind 'refresh', @addAll @collection.bind 'all', @renderStats Application view @collection.fetch() # Re-rendering the App just means refreshing the statistics -- the rest # of the app doesn't change. renderStats: -> @$('#todo-stats').html @statsTemplate total: @collection.length done: @collection.done().length remaining: @collection.remaining().length # Add a single todo item to the list by creating a view for it, and # appending its element to the `<ul>`. addOne: (todo) -> view = new TodoApp.TodoView model: todo @$("#todo-list").append view.render().el # Add all items in the collection at once. addAll: -> @collection.each @addOne # Generate the attributes for a new Todo item. newAttributes: -> content: @input.val() order: @collection.nextOrder() done: false # If you hit return in the main input field, create new **Todo** model, # persisting it to server. createOnEnter: (e) -> if e.keyCode == 13 @collection.create @newAttributes() @input.val ''
  • 49. view = new TodoApp.TodoView model: todo @$("#todo-list").append view.render().el # Add all items in the collection at once. Application view addAll: -> @collection.each @addOne # Generate the attributes for a new Todo item. newAttributes: -> content: @input.val() order: @collection.nextOrder() done: false # If you hit return in the main input field, create new **Todo** model, # persisting it to server. createOnEnter: (e) -> if e.keyCode == 13 @collection.create @newAttributes() @input.val '' # Clear all done todo items, destroying their views and models. clearCompleted: -> todo.destroy() for todo in @collection.done() false # Lazily show the tooltip that tells you to press `enter` to save # a new todo item, after one second. showTooltip: (e) -> tooltip = @$(".ui-tooltip-top") val = @input.val() tooltip.fadeOut() clearTimeout @tooltipTimeout if @tooltipTimeout unless val == '' or val == @input.attr 'placeholder' @tooltipTimeout = _.delay -> tooltip.show().fadeIn() , 1000
  • 50. #todoapp index.html.haml .title %h1 Todos .content #create-todo %input#new-todo{:placeholder => "What needs to be done?", :type => "text"}/ %span.ui-tooltip-top{:style => "display:none;"} Press Enter to save this task #todos %ul#todo-list #todo-stats %ul#instructions %li Double-click to edit a todo. :coffeescript $ -> TodoApp.appView = new TodoApp.AppView collection: new TodoApp.TodoList %script#item-template{:type => "text/html"} .todo{:class => "{{#done}}done{{/done}}"} .display %input{:class => "check", :type => "checkbox", :"{{#done}}checked{{/done}}" => true} .todo-content %span.todo-destroy .edit %input.todo-input{:type => "text", :value => ""} %script#stats-template{:type => "text/html"} {{#if total}} %span.todo-count
  • 51. %input#new-todo{:placeholder => "What needs to be done?", :type => "text"}/ %span.ui-tooltip-top{:style => "display:none;"} Press Enter to save this task #todos %ul#todo-list index.html.haml #todo-stats %ul#instructions %li Double-click to edit a todo. :coffeescript $ -> TodoApp.appView = new TodoApp.AppView collection: new TodoApp.TodoList %script#item-template{:type => "text/html"} .todo{:class => "{{#done}}done{{/done}}"} .display %input{:class => "check", :type => "checkbox", :"{{#done}}checked{{/done}}" => true} .todo-content %span.todo-destroy .edit %input.todo-input{:type => "text", :value => ""} %script#stats-template{:type => "text/html"} {{#if total}} %span.todo-count %span.number {{remaining}} %span.word {{pluralize remaining "item"}} left. {{/if}} {{#if done}} %span.todo-clear %a{:href => "#"} Clear %span.number-done {{done}} completed %span.word-done {{pluralize done "item"}} {{/if}}
  • 52. One more thing: Backbone Controllers Routers class Workspace extends Backbone.Controller Router routes: "help" : "help" #help "search/:query" : "search" #search/kiwis "search/:query/p:page": "search" #search/kiwis/p7 help: -> ... search: (query, page) -> ...
  • 53. How do you test it?
  • 55. Together with all other tests
  • 56. Testing Todo model describe "Todo", -> todo = null ajaxCall = (param) -> jQuery.ajax.mostRecentCall.args[0][param] beforeEach -> todo = new TodoApp.Todo todos = new TodoApp.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()
  • 57. and TodoList collection describe "TodoList", -> attributes = [ content: "First" done: true , content: "Second" ] todos = null beforeEach -> todos = new TodoApp.TodoList attributes it "should return done todos", -> expect(_.invoke todos.done(), "toJSON").toEqual [attributes[0]] it "should return remaining todos", -> expect(_.invoke todos.remaining(), "toJSON").toEqual [attributes[1]]
  • 59. application.js.coffee using Sprockets #= require jquery #= require underscore #= require backbone #= require handlebars #= require ./todo_app #= require_tree ./models #= require ./views/helpers #= require_tree ./views
  • 60. Watch RailsConf DHH keynote http://en.oreilly.com/rails2011/public/schedule/detail/19068
  • 61. References http://jashkenas.github.com/coffee-script/ http://documentcloud.github.com/backbone/ http://pivotal.github.com/jasmine/ https://github.com/rsim/backbone_coffeescript_demo