CoffeeScript
A Rubyist’s Love Affair
Mark Bates
Ruby/Rails
Consultant
Author of
“Distributed
Programming with
Ruby”
“Insert Book Name
       Here”
What CoffeeScript is
       not!
No Just “Lack of
 Punctuation!
NOT RJS!!!
RJS Example
page.replace_html('tasks-completed',
Task.completed_count)
page.replace_html('tasks-remaining',
Task.remaining_count)
page.replace_html('task-status-' + @task.id.to_s,
@task.status.description)
page.visual_effect(:highlight, 'task-item-' +
@task.id.to_s, :duration => 1.0)
So what is
CoffeeScript?
CoffeeScript is...
“a little language that compiles into
JavaScript”
Easier to read and write
A hybrid of Ruby and Python
Helpful
So why should I
embrace (and love)
  CoffeeScript?
String Interpolation

     name = 'Mark'
     "Hello #{name}"
var name;
name = 'Mark';
"Hello " + name;
Heredoc
html = """
  <div class="comment" id="tweet-#{tweet.id_str}">
    <hr>
    <div class='tweet'>
      <span class="imgr"><img
src="#{tweet.profile_image_url}"></span>
      <span class="txtr">
         <h5><a href="http://twitter.com/#{tweet.from_user}"
target="_blank">@#{tweet.from_user}</a></h5>
         <p>#{tweet.text}</p>
         <p class="comment-posted-on">#{tweet.created_at}</p>
      </span>
    </div>
  </div>
"""
var html;
html = "<div class="comment" id="tweet-"
+ tweet.id_str + "">n <hr>n <div
class='tweet'>n    <span class="imgr
"><img src="" + tweet.profile_image_url +
""></span>n    <span class="txtr">n
<h5><a href="http://twitter.com/" +
tweet.from_user + "" target="_blank">@"
+ tweet.from_user + "</a></h5>n      <p>"
+ tweet.text + "</p>n      <p class=
"comment-posted-on">" + tweet.created_at
+ "</p>n    </span>n </div>n</div>";
Ranges
# 1, 2, 3, 4, 5
[1..5]

# 1,..., 4
[1...5]

#1,...,500
[1..500]

#5, 4, 3, 2, 1
[5..1]

#500,...,1
[500..1]
var _i, _j, _results, _results2;
[1, 2, 3, 4, 5];
[1, 2, 3, 4];
(function() {
  _results = [];
  for (_i = 1; _i <= 500; _i++)
{ _results.push(_i); }
  return _results;
}).apply(this, arguments);
[5, 4, 3, 2, 1];
(function() {
  _results2 = [];
  for (_j = 500; _j >= 1; _j--)
{ _results2.push(_j); }
  return _results2;
}).apply(this, arguments);
Ruby Style
    Conditionals

Unless
In-line Conditionals
Conditionals
unless a
  console.log "a is false"

console.log "a is false" unless a
if (!a) {
  console.log("a is false");
}
if (!a) {
  console.log("a is false");
}
Functions

Bound Functions
Default Arguments
Splatted Arguments
Regular Functions
p = (name)->
  console.log "hello: #{name}"

p 'mark'
var p;
p = function(name) {
   return console.log("hello: " + name);
};
p('mark');
Regular Functions
p = (name)->
  console.log "hello: #{name}"

p 'mark'
Regular Functions
p = (name)->
  console.log "hello: #{name}"
                                 CoffeeScript
p 'mark'




p = ->(name) {
  puts "hello: #{name}"
}
                                 Ruby 1.9
p.call 'mark'
Bound Functions
   class Person

     def initialize(name)
       @name = name
     end

     def bind
       binding
     end

   end

   p = Person.new('mark')
   eval "puts @name", p.bind
Bound Functions
fetchData: =>
  url = "http://search.twitter.com/search.json?
q=#{encodeURIComponent(@hash_tag)}&rpp=#{@max_results}&callba
ck=?"
  $.get url, {}, @handleData, 'json'

handleData: (data) =>
  if data['error']?
    @handleError()
  if data.results.length is 0 and @fall_back
    @handleEmptyData()
  else if data.results.length > 0
    setTimeout (=> @fetchData()), 10000
    @render data
var __bind = function(fn, me){ return function(){ return fn.apply(me,
arguments); }; };
({
   fetchData: __bind(function() {
     var url;
     url = "http://search.twitter.com/search.json?q=" +
(encodeURIComponent(this.hash_tag)) + "&rpp=" + this.max_results + "&callback=?";
     return $.get(url, {}, this.handleData, 'json');
   }, this),
   handleData: __bind(function(data) {
     if (data['error'] != null) {
       this.handleError();
     }
     if (data.results.length === 0 && this.fall_back) {
       return this.handleEmptyData();
     } else if (data.results.length > 0) {
       setTimeout((__bind(function() {
         return this.fetchData();
       }, this)), 10000);
       return this.render(data);
     }
   }, this)
});
Default Arguments
createElement: (name, attributes = {}) ->
  obj = document.createElement name
  for key, val of attributes
    obj.setAttribute key, val
  obj
({
  createElement: function(name, attributes) {
    var key, obj, val;
    if (attributes == null) {
      attributes = {};
    }
    obj = document.createElement(name);
    for (key in attributes) {
      val = attributes[key];
      obj.setAttribute(key, val);
    }
    return obj;
  }
});
Splatted Arguments
splatter = (first, second, others...) ->
  console.log "First: #{first}"
  console.log "Second: #{second}"
  console.log "Others: #{others.join(', ')}"

splatter [1..5]...
var splatter;
var __slice = Array.prototype.slice;
splatter = function() {
   var first, others, second;
  first = arguments[0], second = arguments[1], others = 3 <=
arguments.length ? __slice.call(arguments, 2) : [];
   console.log("First: " + first);
   console.log("Second: " + second);
   return console.log("Others: " + (others.join(', ')));
};
splatter.apply(null, [1, 2, 3, 4, 5]);
Array Loop
arr = [1..5]

for num in arr
  console.log num
var arr, num, _i, _len;
arr = [1, 2, 3, 4, 5];
for (_i = 0, _len = arr.length; _i < _len; _i++) {
  num = arr[_i];
  console.log(num);
}
Hash Loop
for key, val of attributes
  console.log "key: #{key}"
  console.log "val: #{val}"
var key, val;
for (key in attributes) {
  val = attributes[key];
  console.log("key: " + key);
  console.log("val: " + val);
}
Classes

Easy To Define
Easy To Extend
Keep Code Clean
Classes
class Employee

  constructor: (@name, @options = {})->

  job: ->
    @options.job

mark = new Employee('Mark', job: 'Developer')

mark.job() # => 'Developer'
mark.name # => 'Mark'
var Employee, mark;
Employee = (function() {
  function Employee(name, options) {
     this.name = name;
     this.options = options != null ? options : {};
  }
  Employee.prototype.job = function() {
     return this.options.job;
  };
  return Employee;
})();
mark = new Employee('Mark', {
  job: 'Developer'
});
mark.job();
mark.name;
Extending Classes
class Manager extends Employee

  job: ->
    "$$$ #{super}"

steve = new Manager('Steve', job: 'CEO')

steve.job() # => '$$$ CEO'
steve.name # => 'Steve'
var Manager, steve;
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
   for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
   function ctor() { this.constructor = child; }
   ctor.prototype = parent.prototype;
   child.prototype = new ctor;
   child.__super__ = parent.prototype;
   return child;
};
Manager = (function() {
   __extends(Manager, Employee);
   function Manager() {
      Manager.__super__.constructor.apply(this, arguments);
   }
   Manager.prototype.job = function() {
      return "$$$ " + Manager.__super__.job.apply(this, arguments);
   };
   return Manager;
})();
steve = new Manager('Steve', {
   job: 'CEO'
});
steve.job();
steve.name;
@
class User

  constructor: (@name)->
    @new_record = true

  @find: (id)->
    # do some work
@
class User

  constructor: (name)->
    @name = name
    @new_record = true

  @find: (id)->
    # do some work
var User;
User = (function() {
  function User(name) {
    this.name = name;
    this.new_record = true;
  }
  User.find = function(id) {};
  return User;
})();
Thank You
        @markbates
http://www.markbates.com

CoffeeScript - A Rubyist's Love Affair

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
    No Just “Lackof Punctuation!
  • 6.
  • 7.
    RJS Example page.replace_html('tasks-completed', Task.completed_count) page.replace_html('tasks-remaining', Task.remaining_count) page.replace_html('task-status-' +@task.id.to_s, @task.status.description) page.visual_effect(:highlight, 'task-item-' + @task.id.to_s, :duration => 1.0)
  • 8.
  • 9.
    CoffeeScript is... “a littlelanguage that compiles into JavaScript” Easier to read and write A hybrid of Ruby and Python Helpful
  • 10.
    So why shouldI embrace (and love) CoffeeScript?
  • 11.
    String Interpolation name = 'Mark' "Hello #{name}"
  • 12.
    var name; name ='Mark'; "Hello " + name;
  • 13.
    Heredoc html = """ <div class="comment" id="tweet-#{tweet.id_str}"> <hr> <div class='tweet'> <span class="imgr"><img src="#{tweet.profile_image_url}"></span> <span class="txtr"> <h5><a href="http://twitter.com/#{tweet.from_user}" target="_blank">@#{tweet.from_user}</a></h5> <p>#{tweet.text}</p> <p class="comment-posted-on">#{tweet.created_at}</p> </span> </div> </div> """
  • 14.
    var html; html ="<div class="comment" id="tweet-" + tweet.id_str + "">n <hr>n <div class='tweet'>n <span class="imgr "><img src="" + tweet.profile_image_url + ""></span>n <span class="txtr">n <h5><a href="http://twitter.com/" + tweet.from_user + "" target="_blank">@" + tweet.from_user + "</a></h5>n <p>" + tweet.text + "</p>n <p class= "comment-posted-on">" + tweet.created_at + "</p>n </span>n </div>n</div>";
  • 15.
    Ranges # 1, 2,3, 4, 5 [1..5] # 1,..., 4 [1...5] #1,...,500 [1..500] #5, 4, 3, 2, 1 [5..1] #500,...,1 [500..1]
  • 16.
    var _i, _j,_results, _results2; [1, 2, 3, 4, 5]; [1, 2, 3, 4]; (function() { _results = []; for (_i = 1; _i <= 500; _i++) { _results.push(_i); } return _results; }).apply(this, arguments); [5, 4, 3, 2, 1]; (function() { _results2 = []; for (_j = 500; _j >= 1; _j--) { _results2.push(_j); } return _results2; }).apply(this, arguments);
  • 17.
    Ruby Style Conditionals Unless In-line Conditionals
  • 18.
    Conditionals unless a console.log "a is false" console.log "a is false" unless a
  • 19.
    if (!a) { console.log("a is false"); } if (!a) { console.log("a is false"); }
  • 20.
  • 21.
    Regular Functions p =(name)-> console.log "hello: #{name}" p 'mark'
  • 22.
    var p; p =function(name) { return console.log("hello: " + name); }; p('mark');
  • 23.
    Regular Functions p =(name)-> console.log "hello: #{name}" p 'mark'
  • 24.
    Regular Functions p =(name)-> console.log "hello: #{name}" CoffeeScript p 'mark' p = ->(name) { puts "hello: #{name}" } Ruby 1.9 p.call 'mark'
  • 25.
    Bound Functions class Person def initialize(name) @name = name end def bind binding end end p = Person.new('mark') eval "puts @name", p.bind
  • 26.
    Bound Functions fetchData: => url = "http://search.twitter.com/search.json? q=#{encodeURIComponent(@hash_tag)}&rpp=#{@max_results}&callba ck=?" $.get url, {}, @handleData, 'json' handleData: (data) => if data['error']? @handleError() if data.results.length is 0 and @fall_back @handleEmptyData() else if data.results.length > 0 setTimeout (=> @fetchData()), 10000 @render data
  • 27.
    var __bind =function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; ({ fetchData: __bind(function() { var url; url = "http://search.twitter.com/search.json?q=" + (encodeURIComponent(this.hash_tag)) + "&rpp=" + this.max_results + "&callback=?"; return $.get(url, {}, this.handleData, 'json'); }, this), handleData: __bind(function(data) { if (data['error'] != null) { this.handleError(); } if (data.results.length === 0 && this.fall_back) { return this.handleEmptyData(); } else if (data.results.length > 0) { setTimeout((__bind(function() { return this.fetchData(); }, this)), 10000); return this.render(data); } }, this) });
  • 28.
    Default Arguments createElement: (name,attributes = {}) -> obj = document.createElement name for key, val of attributes obj.setAttribute key, val obj
  • 29.
    ({ createElement:function(name, attributes) { var key, obj, val; if (attributes == null) { attributes = {}; } obj = document.createElement(name); for (key in attributes) { val = attributes[key]; obj.setAttribute(key, val); } return obj; } });
  • 30.
    Splatted Arguments splatter =(first, second, others...) -> console.log "First: #{first}" console.log "Second: #{second}" console.log "Others: #{others.join(', ')}" splatter [1..5]...
  • 31.
    var splatter; var __slice= Array.prototype.slice; splatter = function() { var first, others, second; first = arguments[0], second = arguments[1], others = 3 <= arguments.length ? __slice.call(arguments, 2) : []; console.log("First: " + first); console.log("Second: " + second); return console.log("Others: " + (others.join(', '))); }; splatter.apply(null, [1, 2, 3, 4, 5]);
  • 32.
    Array Loop arr =[1..5] for num in arr console.log num
  • 33.
    var arr, num,_i, _len; arr = [1, 2, 3, 4, 5]; for (_i = 0, _len = arr.length; _i < _len; _i++) { num = arr[_i]; console.log(num); }
  • 34.
    Hash Loop for key,val of attributes console.log "key: #{key}" console.log "val: #{val}"
  • 35.
    var key, val; for(key in attributes) { val = attributes[key]; console.log("key: " + key); console.log("val: " + val); }
  • 36.
    Classes Easy To Define EasyTo Extend Keep Code Clean
  • 37.
    Classes class Employee constructor: (@name, @options = {})-> job: -> @options.job mark = new Employee('Mark', job: 'Developer') mark.job() # => 'Developer' mark.name # => 'Mark'
  • 38.
    var Employee, mark; Employee= (function() { function Employee(name, options) { this.name = name; this.options = options != null ? options : {}; } Employee.prototype.job = function() { return this.options.job; }; return Employee; })(); mark = new Employee('Mark', { job: 'Developer' }); mark.job(); mark.name;
  • 39.
    Extending Classes class Managerextends Employee job: -> "$$$ #{super}" steve = new Manager('Steve', job: 'CEO') steve.job() # => '$$$ CEO' steve.name # => 'Steve'
  • 40.
    var Manager, steve; var__hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; }; Manager = (function() { __extends(Manager, Employee); function Manager() { Manager.__super__.constructor.apply(this, arguments); } Manager.prototype.job = function() { return "$$$ " + Manager.__super__.job.apply(this, arguments); }; return Manager; })(); steve = new Manager('Steve', { job: 'CEO' }); steve.job(); steve.name;
  • 41.
    @ class User constructor: (@name)-> @new_record = true @find: (id)-> # do some work
  • 42.
    @ class User constructor: (name)-> @name = name @new_record = true @find: (id)-> # do some work
  • 43.
    var User; User =(function() { function User(name) { this.name = name; this.new_record = true; } User.find = function(id) {}; return User; })();
  • 44.
    Thank You @markbates http://www.markbates.com