Coffee Script
Coffee Script
Coffee Script
level 1
A Beautiful Programming Language
!
Least amount of code to solve problems
!
Readable and Understandable
!
Easy to Maintain
?
!
Least amount of code to solve problems
!
Readable and Understandable
!
Easy to Maintain
compiles into
http://jashkenas.github.com/coffee-script/
http://jashkenas.github.com/coffee-script/
CoffeeScript JavaScript
CoffeeScript
JavaScript
Variables
No semicolons
var message;
message = "Ready for some Coffee?";
alert(message);
message = "Ready for some Coffee?"
alert(message)
No variable declarations
Variables and Functions
function coffee() {
return confirm("Ready for some Coffee?");
}
Variables and Functions
!
Named Functions
2 ways to create Functions in JS
var coffee = function() {
return confirm("Ready for some Coffee?");
}
!
Function Expressions
coffee();
Both called with
Variables and Functions
We only use Function Expressions
coffee = ->
confirm "Ready for some Coffee?"
var coffee = function() {
return confirm("Ready for some Coffee?");
}
1 tab or 2 spaces indented
-> converts to function() {
Always has a return value
Variables and Functions
Returning a String
answer = confirm "Ready for some Coffee?"
coffee = ->
"Your answer is " + answer
var answer;
var coffee;
coffee = function() {
answer = confirm("Ready for some Coffee?");
return "Your answer is " + answer;
}
"Your answer is #{answer}"
same as
Variables and Functions
Function Parameters
answer = confirm message
coffee = (message) ->
var answer;
var coffee;
return "Your answer is " + answer;
}
"Your answer is #{answer}"
coffee = function( ) {
answer = confirm( );
message
message
Variables and Functions
Calling Functions
coffee = (message) ->
coffee = ->
coffee = (message, other) ->
coffee()
coffee("Yo")
coffee "Yo"
coffee("Yo", 2)
coffee "Yo", 2
parenthesis optional
Variables and Functions
Function Parameters
coffee = (message) ->
answer = confirm message
"Your answer is #{answer}"
parenthesis on everything
but the outermost call
coffee alert "Ready for some Coffee?" ( )
Variables and Functions
Optional Parameters
answer = confirm message
"Your answer is #{answer}"
coffee alert
"Ready for some Coffee?"
()
If we want a default message
coffee = (message ) -> =
coffee alert ( ) "Want some Decaf?"
Variables and Functions
Optional Parameters
answer = confirm message
"Your answer is #{answer}"
"Ready for some Coffee?" coffee = (message ) -> =
var coffee;
coffee = function(message) {
var answer;
if (message == null) {
message = "Ready for some Coffee?";
}
answer = confirm(message);
return "Your answer is " + answer;
}
Variables and Functions
The CoffeeScript Compiler (optional)
$ npm install -g coffee-script
1. Install Node.js http://nodejs.org/
2. Install npm http://npmjs.org/
3.
$ coffee -h
Usage: coffee [options] path/to/script.coffee
-c, --compile compile to JavaScript and save as .js files
-i, --interactive run an interactive CoffeeScript REPL
-o, --output set the directory for compiled JavaScript
-w, --watch watch scripts for changes, and recompile
-p, --print print the compiled JavaScript to stdout
-e, --eval compile a string from the command line
Variables and Functions
Command Line Examples
$ coffee -c test.coffee
Creates test.js
$ coffee -cw test.coffee
Every time test.coffee is
updated re-compile.
$ coffee -c src -o js
Compile all .coffee files in the
src dir into the js dir.
$ coffee -wc src -o js
Every time a file is updated
re-compile.
Variables and Functions
CoffeeScript TextMate Bundle
https://github.com/jashkenas/coffee-script-tmbundle
&
S
u
b
l
i
m
e
T
e
x
t
2
name author URL
Information density Arenamontanus
http://www.ickr.com/photos/arenamontanus/535807315
Paper Writings: Quotes Lucia..
http://www.ickr.com/photos/angelic0devil6/2104607220
Stterlin ninastoessinger
http://www.ickr.com/photos/ninastoessinger/4713004390
FF DIN Round: Round Pieces FontFont
http://www.ickr.com/photos/fontfont/4840584146
Sonnet 18 Jinx!
http://www.ickr.com/photos/span112/3041263205
Creative Commons
download the slides
use the hints
Applied jQuery
level 2
jQuery to CoffeeScript
Applied jQuery
jQuery(function($) {
function changeTab(e) {
e.preventDefault();
$("#tabs li a.active").removeClass("active");
$(this).addClass("active");
$("#tabs ul li a").click(changeTab);
});
}
jQuery ($) ->
$ ->
or
* If no other libraries are using $
*
jQuery to CoffeeScript
Applied jQuery
function changeTab(e) {
e.preventDefault()
$("#tabs li a.active").removeClass("active")
$(this).addClass("active")
$("#tabs ul li a").click(changeTab)
})
}
$ ->
changeTab = (e) ->
;
;
;
;
;
jQuery to CoffeeScript
Applied jQuery
e.preventDefault()
$("#tabs li a.active").removeClass("active")
$(this).addClass("active")
$("#tabs ul li a").click(changeTab)
})
}
$ ->
changeTab = (e) ->
Remove all semicolons and curly brackets
;
;
;
;
;
jQuery to CoffeeScript
Applied jQuery
$ ->
changeTab = (e) ->
e.preventDefault()
$("#tabs li a.active").removeClass("active")
$(this).addClass("active")
$("#tabs ul li a").click(changeTab)
Optionally remove parenthesis
jQuery to CoffeeScript
Applied jQuery
$ ->
changeTab = (e) ->
e.preventDefault()
$("#tabs li a.active").removeClass "active"
$(this).addClass "active"
$("#tabs ul li a").click changeTab
$(@).addClass "active"
same as
@ = this
jQuery to CoffeeScript
Applied jQuery
$ ->
changeTab = (e) ->
e.preventDefault()
$("#tabs li a.active").removeClass "active"
$("#tabs ul li a").click changeTab
$(@).addClass "active"
jQuery(function($) {
function changeTab(e) {
e.preventDefault();
$("#tabs li a.active").removeClass("active");
$(this).addClass("active");
$("#tabs ul li a").click(changeTab);
});
}
jQuery to CoffeeScript
Applied jQuery
$("#tabs #error a").click(function (e){
e.preventDefault();
});
$("#tabs #error a").click (e) ->
e.preventDefault()
$('#confirm').queue(function() {
$(this).dequeue();
});
$("#confirm").queue ->
$(@).dequeue()
jQuery to CoffeeScript
Applied jQuery
function showNumberOfFlights(e) {
var num_flights = $(this).data('flights');
$(this).append("<span>" + num_flights +"</span>");
$("#tabs span.tooltip").show();
}
showNumberOfFlights = (e) ->
num_flights = $(@).data 'flights'
$(@).append "<span>#{flights}</span>"
$("#tabs span.tooltip").show()
Conditionals & Operators
level 3
If Statement
Parenthesis
if age < 18
alert 'Under age'
if (age < 18) {
alert('Under age');
}
Conditionals & Operators
Optional
alert 'Under age' if age < 18
if age < 18 then alert 'Under age'
If Else Statement
if age < 18
alert 'Under age'
if (age < 18) {
alert('Under age');
} else {
alert('of age');
}
else
alert 'of age'
if age < 18 then alert 'Under age' else alert 'of age'
Conditionals & Operators
No Ternary Operator
Operators
CoffeeScript JavaScript
===
== is
!==
isnt
not
!
!=
and
&&
or
||
true yes on true
false no off false
Conditionals & Operators
Operator Examples & Unless
addCaffeine() if not Decaf()
if paid() and coffee() is on then pour()
addCaffeine() unless Decaf()
if (paid() && coffee() === true) {
pour();
}
Conditionals & Operators
Chained Comparisons
if (2 < newLevel && newLevel < 5) {
alert("In Range!");
}
if 2 < newLevel < 5
alert "In Range!"
Conditionals & Operators
Switch Statements
var message = (function() {
switch (cupsOfCoffee) {
case 0:
return 'Asleep';
case 1:
return 'Eyes Open';
case 2:
return 'Buzzed';
default:
return 'Dangerous';
}
})();
message = switch cupsOfCoffee
when 0 then 'Asleep'
when 1 then 'Eyes Open'
when 2 then 'Buzzed'
else 'Dangerous
Conditionals & Operators
Existential Operators
cupsOfCoffee
How do we check to see that
isnt defined and isnt null?
if (typeof cupsOfCoffee !== "undefined" && cupsOfCoffee !== null) {
alert('it exists!');
}
if cupsOfCoffee?
alert 'it exists!'
alert 'it exists!' if cupsOfCoffee?
Conditionals & Operators
Existential Operators
cupsOfCoffee
Set
to Zero unless previously set
if not cupsOfCoffee?
cupsOfCoffee = 0
cupsOfCoffee = 0 unless cupsOfCoffee?
cupsOfCoffee ?= 0
Conditionals & Operators
Existential Operators
brew()
Call
on
if coffeePot?
coffeePot.brew()
coffeePot
only if it exists
coffeePot?.brew()
vehicle.start_engine?().shift_gear?()
Only call function if it exists
in Ruby try()
Conditionals & Operators
Arrays, Objects, Iteration
level 4
Ranges
Arrays, Objects, Iteration
range = [1..4]
var range = [1, 2, 3, 4];
range = [1...4]
var range = [1, 2, 3];
With three dots excludes the end
start = 5
end = 10
range = [start..end]
Variables & Subsets
[5, 6, 7, 8, 9, 10]
range[1..4]
[6, 7, 8, 9]
range[1...range.length]
[6, 7, 8, 9, 10]
range[1..-1]
Arrays
storeLocations = [
'Orlando'
'Winter Park'
'Sanford'
]
storeLocations = ['Orlando', 'Winter Park', 'Sanford']
Can use new lines instead of commas
Arrays, Objects, Iteration
Loops
storeLocations = ['Orlando', 'Winter Park', 'Sanford']
storeLocations.forEach (location, index) ->
alert "Location: #{location}"
Arrays, Objects, Iteration
storeLocations.forEach(function(location, index) {
return alert("Location: " + location);
});
Loops
storeLocations = ['Orlando', 'Winter Park', 'Sanford']
storeLocations.forEach (location, index) ->
alert "Location: #{location}"
for location in storeLocations
alert "Location: #{location}"
alert "Location: #{location}" for location in storeLocations
This is a list comprehension
Arrays, Objects, Iteration
List Comprehensions
storeLocations = ['Orlando', 'Winter Park', 'Sanford']
Add , FL to each storeLocation
its an expression
"#{loc}, FL" for loc in storeLocations
['Orlando, FL', 'Winter Park, FL', 'Sanford, FL']
storeLocations = ("#{loc}, FL" for loc in storeLocations)
the parenthesis are important
geoLocate(loc) for loc in storeLocations when loc isnt 'Sanford'
filter
(expression)
List Comprehensions
storeLocations = ['Orlando', 'Winter Park', 'Sanford']
its an expression
Create new array without Sanford
['Orlando', 'Winter Park']
newLocs = []
for loc in storeLocations
newLocs.push loc if loc isnt 'Sanford'
same as
newLocs = (loc for loc in storeLocations when loc isnt 'Sanford')
Splats
'Looking for Starducks in Orlando'
same as
searchLocations = (brand, cities...) ->
"looking for #{brand} in #{cities.join(',')}"
For a variable number of arguments
searchLocations 'Starducks', 'Orlando'
searchLocations 'Starducks', 'Orlando', 'Winter Park'
'Looking for Starducks in Orlando, Winter Park'
params = ['Starducks', 'Orlando', 'Winter Park']
searchLocations(params...)
curly braces optional
Objects
Objects are lists of keys & values
(hash)
coffee = { name: 'French', strength: 1 }
coffee = name: 'French', strength: 1
coffee =
name: 'French'
strength: 1
commas optional
Arrays, Objects, Iteration
Objects
coffee =
name: 'French'
strength: 1
brew: -> alert("brewing #{@name}")
var coffee = {
name: 'French',
strength: 1,
brew: function() {
return alert("brewing " + this.name);
}
};
coffee.brew()
called with
Arrays, Objects, Iteration
Objects
coffee =
name: 'French'
strength: 1
brew: -> alert("brewing #{@name}")
pour: (amount=1) ->
if amount is 1
"Poured a single cup"
else
"Poured #{amount} cups"
pour: function(amount) {
if (amount == null) amount = 1;
if (amount === 1) {
return "Poured a single cup";
} else {
return "Poured " + amount + " cups";
}
Arrays, Objects, Iteration
Careful with your Indenting!
coffee =
name: 'French'
strength: 1
coffee = {
name: 'French'
};
({
strength: 1
})
indent issues!
Arrays, Objects, Iteration
Complex Objects
coffees =
french:
strength: 1
in_stock: 20
italian:
strength: 2
in_stock: 12
decaf:
strength: 0
in_stock: 8
var coffees = {
french: {
strength: 1,
in_stock: 20
},
italian: {
strength: 2,
in_stock: 12
},
decaf: {
strength: 0,
in_stock: 0
}
};
coffees.italian.in_stock
coffees.italian.in_stock
12
Arrays, Objects, Iteration
Object Iteration with of
coffees =
french:
strength: 1
in_stock: 20
italian:
strength: 2
in_stock: 12
decaf:
strength: 0
in_stock: 0
coffees.italian.in_stock
"#{coffee} has #{attrs.in_stock}" for coffee, attrs of coffees
["french has 20", "italian has 12", "decaf has 0"]
KEY VALUE
iterating over object
Object Iteration with of
coffees.italian.in_stock
"#{coffee} has #{attrs.in_stock}" for coffee, attrs of coffees
["french has 20", "italian has 12", "decaf has 0"]
for coffee, attrs of coffees
"#{coffee} has #{attrs.in_stock}"
"french has 20, italian has 12"
to_print.join ", "
same as
"#{coffee} has #{attrs.in_stock}"
to_print = for coffee, attrs of coffees when attrs.in_stock > 0
Arrays, Objects, Iteration
Object Iteration with of
coffees.italian.in_stock
"#{coffee} has #{attrs.in_stock}"
to_print = for coffee, attrs of coffees when attrs.in_stock > 0
var attrs, coffee, to_print;
to_print = (function() {
var _results;
_results = [];
for (coffee in coffees) {
attrs = coffees[coffee];
if (attrs.in_stock > 0) _results.push("" + coffee + " has "
+ attrs.in_stock);
}
return _results;
})();
to_print.join(", ")
to_print.join ", "
Applied jQuery, Part 2
level 5
Object Simplicity
Applied jQuery, Part 2
$("#tabs ul li a").bind({
click: changeTab,
mouseenter: showNumberOfFlights,
mouseleave: hideNumberOfFlights
});
$("#tabs ul li a").bind
click: changeTab
mouseenter: showNumberOfFlights
mouseleave: hideNumberOfFlights
A Complex Example
showFlights = (activeDiv) ->
$("#tabs div").hide()
function showFlights(activeDiv) {
$("#tabs div").hide();
if (fetchingFlights) {
fetchingFlights.abort();
}
fetchingFlights = $.ajax('/flights', {
data: { date: activeDiv },
cache: false,
error: function(result) {
if (result.statusText != "abort") {
$('#tabs #error').show();
}
}
});
}
{
Applied jQuery, Part 2
showFlights = (activeDiv) ->
$("#tabs div").hide()
if (fetchingFlights) {
fetchingFlights.abort();
}
fetchingFlights = $.ajax('/flights', {
data: { date: activeDiv },
cache: false,
error: function(result) {
if (result.statusText != "abort") {
$('#tabs #error').show();
}
}
});
}
if fetchingFlights
fetchingFlights.abort()
A Complex Example
{
Applied jQuery, Part 2
showFlights = (activeDiv) ->
$("#tabs div").hide()
fetchingFlights = $.ajax('/flights', {
data: { date: activeDiv },
cache: false,
error: function(result) {
if (result.statusText != "abort") {
$('#tabs #error').show();
}
}
});
}
if fetchingFlights
fetchingFlights.abort()
fetchingFlights = $.ajax '/flights'
data:
date: activeDiv
cache: false
A Complex Example
{
Applied jQuery, Part 2
showFlights = (activeDiv) ->
$("#tabs div").hide()
error: function(result) {
if (result.statusText != "abort") {
$('#tabs #error').show();
}
}
});
}
if fetchingFlights
fetchingFlights.abort()
fetchingFlights = $.ajax '/flights'
data:
date: activeDiv
cache: false
A Complex Example
{
error: (result) ->
if result.statusText isnt "abort"
$('#tabs #error').show()
Applied jQuery, Part 2
showFlights = (activeDiv) ->
$("#tabs div").hide()
if fetchingFlights
fetchingFlights.abort()
fetchingFlights = $.ajax '/flights'
data:
date: activeDiv
cache: false
A Complex Example
error: (result) ->
if result.statusText isnt "abort"
$('#tabs #error').show()
46 Less Characters!
Applied jQuery, Part 2
Mind Bending Comprehensions
if (stops == '2+' || flight.routing == 0) {
filteredFlights.push(flight);
}
});
var filteredFlights = [];
$.each(currentFlights, function(index, flight) {
$.each currentFlights, (index, flight) ->
if stops is '2+' or flight.routing is 0
filteredFlights.push flight
filteredFlights = []
Applied jQuery, Part 2
Mind Bending Comprehensions
$.each currentFlights, (index, flight) ->
if stops is '2+' or flight.routing is 0
filteredFlights.push flight
filteredFlights =
(flight for flight in currentFlights when stops is '2+' or
flight.routing is 0)
Applied jQuery, Part 2
filteredFlights = []
Object Orientation
level 6
Remember the Coffee Object?
Object Orientation
coffee =
name: 'French'
strength: 1
brew: -> alert "brewing #{@name}"
pour: (amount=1) ->
if amount is 1
"Poured a single cup"
else
"Poured #{amount} cups"
Constructors & Properties
class Coffee
constructor: (name, strength=1) ->
class Coffee
constructor: (@name, @strength=1) ->
same as
@name = name
@strength = strength
Object Orientation
called when instantiated
Constructors & Properties
class Coffee
constructor: (@name, @strength=1) ->
brew: -> alert "brewing #{@name}"
pour: (amount=1) ->
if amount is 1
"Poured a single cup"
else
"Poured #{amount} cups"
french = new Coffee("French", 2)
french.brew()
Object Orientation
Inheritance
class Coffee
constructor: (@name, @strength=1) ->
brew: -> alert "brewing #{@name}"
class MaxgoodHouse extends Coffee
constructor: (@name, @strength=0) ->
@brand = "Maxgood House"
boring = new MaxgoodHouse("Boring")
boring.brew()
Object Orientation
Inheritance
class MaxgoodHouse extends Coffee
constructor: (@name, @strength=0) ->
@brand = "Maxgood House"
boring = new MaxgoodHouse("Boring")
boring.brew()
brew: -> alert "Brewing #{@brand} #{@name}"
Object Orientation
Inheritance
class MaxgoodHouse extends Coffee
constructor: (@name, @strength=0) ->
@brand = "Maxgood House"
boring = new MaxgoodHouse("Boring")
boring.pour()
brew: -> alert "Brewing #{@brand} #{@name}"
pour: (amount=1) ->
"#{super(amount)}, but it sucks"
Object Orientation
The Fat Arrow
@inventory @name
and
Looking for property of the dom element
called
Error!
$("#pour-#{@name}").click (event)
class Coffee
constructor: (@name, @strength=1, @inventory=0) ->
pourClick: ->
if @inventory isnt 0
@inventory -= 1
alert "Poured a cup of #{@name}"
->
Object Orientation
The Fat Arrow
Binds to current value of this
$("#pour-#{@name}").click (event)
class Coffee
constructor: (@name, @strength=1, @inventory=0) ->
pourClick: ->
if @inventory isnt 0
@inventory -= 1
alert "Poured a cup of #{@name}"
=>
Object Orientation
Using a Class for Encapsulation
class SelectFlights
{
var selectFlights = {
fetchingFlights : null,
init : function() {
$("#tabs ul li a").bind({
click: this.changeTab
});
$("#tabs #error a").click(function (event){
e.preventDefault();
this.showFlights($("#tabs li a.active").attr("href"));
});
},
showFlights : function(activeDiv) { },
changeTab : function(event) { }
});
Object Orientation
Using a Class for Encapsulation
class SelectFlights
{
fetchingFlights : null,
init : function() {
$("#tabs ul li a").bind({
click: this.changeTab
});
$("#tabs #error a").click(function (event){
e.preventDefault();
this.showFlights($("#tabs li a.active").attr("href"));
});
},
showFlights : function(activeDiv) { },
changeTab : function(event) { }
constructor: (@fetchingFlights=null) ->
});
Object Orientation
Using a Class for Encapsulation
class SelectFlights
{
$("#tabs ul li a").bind({
click: this.changeTab
});
$("#tabs #error a").click(function (event){
e.preventDefault();
this.showFlights($("#tabs li a.active").attr("href"));
});
},
showFlights : function(activeDiv) { },
changeTab : function(event) { }
$("#tabs ul li a").bind
click: @changeTab
});
Object Orientation
constructor: (@fetchingFlights=null) ->
Using a Class for Encapsulation
class SelectFlights
{
$("#tabs #error a").click(function (event){
e.preventDefault();
this.showFlights($("#tabs li a.active").attr("href"));
});
},
showFlights : function(activeDiv) { },
changeTab : function(event) { }
$("#tabs ul li a").bind
click: @changeTab
$("#tabs #error a").click (event) =>
event.preventDefault()
@showFlights $("#tabs li a.active").attr("href")
});
Object Orientation
constructor: (@fetchingFlights=null) ->
Using a Class for Encapsulation
class SelectFlights
{
showFlights : function(activeDiv) { },
changeTab : function(event) { }
$("#tabs ul li a").bind
click: @changeTab
$("#tabs #error a").click (event) =>
event.preventDefault()
@showFlights $("#tabs li a.active").attr("href")
changeTab : (event) =>
showFlights : (activeDiv) ->
});
Object Orientation
constructor: (@fetchingFlights=null) ->
Using a Class for Encapsulation
class SelectFlights
$("#tabs ul li a").bind
click: @changeTab
$("#tabs #error a").click (event) =>
event.preventDefault()
@showFlights $("#tabs li a.active").attr("href")
changeTab : (event) =>
showFlights : (activeDiv) ->
selectFlights = new SelectFlights()
Object Orientation
constructor: (@fetchingFlights=null) ->