Addicted to Coffee(script)
Aurélio A. Heckert (AKA Aurium)
http://SoftwareLivre.org/aurium
FLISOL — Salvador/BA — Abril/2013
http://coffeescript.org
“It's just Javascript”
-- Jeremy Ashkenas
Sintaxe sucinta: Escreva ⅓ a menos
Literate programming: Legibilidade em 1º lugar
Menos problemas: comparação adequada, for each
eficiente, prototype chain,...
Escreva o que você quer,
não “meta assuntos”.
for item in list
process item
for (var i = 0, l = list.length; i < l; i++) {
var item = list[i];
process(item);
}
Tudo é uma expressão
list.push name for name of window
list = (name for name of window)
alert(
try
nonexistent / undefined
catch error
"And the error is ... #{error}"
)
Filtrando listas
list = (name for name of window)[0..10]
name for name of window when name[0] is 's'
par for par in [0..10] when par%2 is 0
frutas = ['maçã', 'pera', 'uva', 'umbu']
log( f for f,i in frutas when i%2 )
Rola um “list comprehension”?
Só Fibonacci garante:
l=j=0
log (l=j;j=i) for i in [1..40] by l
E com objetos?
criancas =
Ana: 10
Bia: 9
Caio: 11
for nome, idade of criancas when idade >= 10
log "#{nome} tem #{idade} anos."
E algo mais realista?
criancas =
Ana: idade: 10, peso: 20
Bia: idade: 9 , peso: 16
Caio: idade: 11, peso: 32
for nome, dados of criancas \
when dados.idade >= 10
log "#{nome} pesa #{dados.peso}Kg."
Isso existe?
“I ♥ feature detection”
if (anArray && anArray.push)
anArray.push(aValue);
anArray?.push? aValue
Modificadores
alert "ok" if "test" in array
# esse "in" é coffee!
alert "ok" if ~ string.indexOf "test"
# esse "~" é js!
x=0; log ++x while x<10
Funções
(exemplo do livro "CoffeeScript - Accelerated JavaScript Development")
ehImpar = (num) ->
if typeof num is 'number'
if num is Math.round num
if num > 0
num % 2 is 1
else
throw "#{num} não é positivo"
else
throw "#{num} não é inteiro"
else
throw "#{num} não é número"
Uma melhoradinha...
ehImpar = (num) ->
throw "#{num} não é número" \
unless typeof num is 'number'
throw "#{num} não é inteiro" \
unless num is Math.round num
throw "#{num} não é positivo" \
unless num > 0
num % 2 is 1
Sua assinatura é sua personalidade
umaFunc = (arg1, arg2, arg3) ->
umaFunc = (arg1, arg2...) ->
umaFunc = (arg1, arg2..., arg3) ->
umaFunc = (arg1, arg2=123, arg3={}) ->
Chamando uma função
umaFunc arg1, arg2, arg3
numeros = [14, 35, -7, 46, 98]
Math.max numeros...
$("#myId").css color:"red", cursor:"pointer"
do ->
Mantenha suas origens
carrinhoDeCompras =
lista: []
url: "http://dominio.com/pega-produto?id="
adicionarProduto: (id)->
$.getJSON @url+id, (data)=>
@lista.push data
do @atualizar
atualizar: ->
# atualiza o html
Atribuição desestruturante ou “Magica!”
{ a, b } = umObjeto
[ a, b ] = umaLista
{join, resolve} = require 'path'
join '/base/path', 'other/path'
Usando no mundo “real”(?) Facebook Graph
{
"name": "Aurélio A. Heckert",
"link": "https://www.facebook.com/aurelio.heckert",
"location": {
"id": "111386048885164",
"name": "Salvador, Bahia, Brazil"
},
"bio": "I'm a vegan and free (as in freedom) software developer.",
...
"sports": [
{"name": "Bike riding"},
{"name": "Natação"}
],
"inspirational_people": [
{"name": "Róbson Véio"},
{"name": "Richard Stallman"}
],
"gender": "male",
"relationship_status": "In a Relationship",
"significant_other": {
"name": "Ilza Santos"
},
"religion": "Agnostico",
"political": "Libertarian",
"languages": [
{"name": "Brazilian Portuguese"},
{"name": "English"}
],
"verified": true,
}
Acessando dados do Facebook Graph
url = 'https://graph.facebook.com/me' +
'?format=json&access_token=XXXXXX'
$.getJSON url, (data)->
{
name, religion:fe,
inspirational_people:[{name:primeiro}],
languages:[lang,{name:segLing}]
} = data
console.log """
#{name} é #{fe}, foi influenciado
por #{primeiro} e fala #{segLing}.
"""
OO não precisa de Classes!
(mas assim é mais gostoso)
class Animal
constructor: (@name) ->
move: (metros) ->
log "#{@name} andou #{metros}m."
class Cavalo extends Animal
move: ->
log "Trotando..."
super 5
O construtor que você sempre quis
class Ponto
constructor: ({@x, @y, @cor})->
throw new Error 'bad x' if @x<0
throw new Error 'bad y' if @y<0
log new Ponto x:11, y:22, cor:'red' # Ok
log new Ponto x:-1, y:22, cor:'F00' # Erro
Estendendo um protótipo
Number::times = (bl)-> do bl for n in [1..@]
3.times -> log 'yeah!'
Number::times = (bl)-> bl(n) for n in [1..@]
3.times (n)-> log ">> #{n} yeah!"
Do café à meta-anfetamina
Coco - o radical
meuObj{a, b} = objetoEstrangeiro
$('#bloco').textContent = "(#<>)"
export x, y # browser, node.js, etc...
- http://github.com/satyr/coco
- http://satyr.github.io/cup
Do café à meta-anfetamina (2)
IcedCoffeeScript - o assíncrono
search = (keyword, callback) ->
url = "http://search.com/json?q="+keyword
await $.getJSON url, defer json
callback json.results
- http://maxtaco.github.io/coffee-script
Do café à meta-anfetamina (2.5)
IcedCoffeeScript - o assíncrono (em série)
serialSearch = (keywords, callback) ->
out = []
for k,i in keywords
await search k, defer out[i]
callback out
IcedCoffeeScript - o assíncrono (em paralelo)
parallelSearch = (keywords, callback) ->
out = []
await
for k,i in keywords
search k, defer out[i]
callback out
IcedCoffee provando o seu valor...
CoffeeScript em paralelo
parallelSearch = (keywords, cb) ->
results = []
n_out = 0
cb_generator = (i) ->
n_out++
(json) ->
results[i] = json
if n_out-- is 0
cb results
for k,i in keywords
search k, cb_generator i
Do café à meta-anfetamina (3)
Contracts.coffee - o rigoroso
square :: (Num) -> Num
square = (x) -> x * x
square "a string" # throws a run-time error
Error: Contract violation: expected <Num>,
actual: "a string"
Value guarded in: id_module:42
-- blame is on: client_code:104
Parent contracts:
(Num) -> Num
- http://disnetdev.com/contracts.coffee
Do café à meta-anfetamina (4)
EmberScript - o... viabilizador?
class PostsController extends Ember.ArrayController
trimmedPosts: ~>
@content.slice(0, 3)
var PostsController;
var get$ = Ember.get;
PostsController = Ember.ArrayController.extend({
trimmedPosts: Ember.computed(function () {
return get$(this, 'content').slice(0, 3);
}).property('content.@each')
});
Tudo junto e mais um pouco
CoffeeScript II: a ira Khan
Otimizadores
Geradores de código
Customização de output
Compiladores
source map, relatório de error melhor, sem colisão de nomes, ...
http://michaelficarra.github.com/CoffeeScriptRedux
http://www.ustream.tv/recorded/29301270
Teste... 1, 2, 3... Teste!
Usando mocha
e chai
assert = require("chai").assert
meuModulo = require("meu-modulo")
suite "Coleção de testes", ->
test "meu primeiro teste", ->
assert.equal meuModulo.quadrado(3), 9
test "um método assíncrono", (done)->
meuModulo.download url, (err, data)->
assert.isNull err, "can read"
assert.equal data, "meu conteúdo"
do done
Rodaâando!
$ npm test
> omnifs@0.0.1 test /home/aurium/Projetos/omnifs
> mocha --compilers coffee:coffee-script --ui tdd
․․․․․․․․․․․․․․․․․
✖ 1 of 17 tests failed:
1) splitURI method recognize undefined protocol as "file":
actual expected
filego
npm ERR! Test failed. See above for more details.
npm ERR! not ok code 0
Documentação (aquela parte chata)
Usando codo
(http://coffeedoc.info)
# Base class for all animals.
#
# @example How to subclass an animal
# class Lion extends Animal
# move: (direction, speed): ->
#
class Example.Animal
# The Answer to the Ultimate Question
@ANSWER = 42
# Construct a new animal.
#
# @param [String] name the name of the animal
#
constructor: (@name, @birthDate = new Date()) ->
# Move the animal.
#
# @param [Object] options the moving options
# @option options [String] direction the moving direction
# @option options [Number] speed the speed in mph
#
move: (options = {}) ->
O amargo do café
White-space significante
- a+b diferente de a + b
- f(a) diferente de f (a)
Conflito de nomes (não use _var
!)
Bolo — remake do jogo para a web
http://stephank.github.io/orona/bolo.html
nack — Node powered Rack server
http = require 'http'
nack = require 'nack'
app = nack.createProcess "/path/to/config.ru"
(http.createServer (req, res)->
app.proxy req, res
).listen 8124, "127.0.0.1"
http://github.com/josh/nack
Apiary — prototipagem e debug de APIs
http://apiary.io
AnnotateIt — marque e comente qualquer site
http://annotateit.org
Hubot — o bot plugável do GitHub
http://hubot.github.com
Zombie.js — um pseudo browser para... testes
Browser = require "zombie"
assert = require "assert"
# Visita uma página local
browser = new Browser
browser.visit "http://localhost:3000/", ->
# Preenche e-mail, senha e envia p form
browser
.fill("email", "zombie@underworld.dead")
.fill("password", "eat-the-living")
.pressButton "Sign Me Up!", ->
# Form enviado, nova página carregada.
assert.ok browser.success
assert.equal browser.text("title"),
"Welcome To Brains Depot"
http://zombie.labnotes.org
# npm install -g coffee-script