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')
            });
          
    • http://emberscript.com
  • 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