Skip to content

Folha de Estilo

Adrianne Alves edited this page Nov 17, 2017 · 9 revisions

Histórico de modificações

Data Versão Descrição Autor
27/09/2017 0.1 Adicionando Folha de Estilo Airbnb Álax Alves
27/09/2017 0.2 Adicionando Folha de Estilo Rubocop Álax Alves
27/09/2017 0.3 Adicionando Folha de Estilo Ruby Style Guide Álax Alves
27/09/2017 1.0 Revisão geral do documento Matheus Richard
16/11/2017 1.1 Revisão da escrita Adrianne Alves

Sumário

  1. Introdução
  2. Airbnb JavaScript Style Guide
  3. Rubocop Ruby Style Guide

1. Introdução

Este projeto está divido em dois repositórios(Back-End e Front-End), portanto são utilizadas duas folhas de estilos para as respectivas linguagens (Ruby e Javascript). Para o repositório do back-end utiliza-se o Rubocop e para o front end, a folha de estilo do AirBnB.

2. Airbnb JavaScript Style Guide() {

  • Primitivos: Quando você acessa um tipo primitivo você lida diretamente com seu valor.

    • string
    • number
    • boolean
    • null
    • undefined
    var foo = 1;
    var bar = foo;
    
    bar = 9;
    
    console.log(foo, bar); // => 1, 9
  • Complexos: Quando você acessa um tipo complexo você lida com a referência para seu valor.

    • object
    • array
    • function
    var foo = [1, 2];
    var bar = foo;
    
    bar[0] = 9;
    
    console.log(foo[0], bar[0]); // => 9, 9
  • Use a sintaxe literal para criação de objetos.

    // ruim
    var item = new Object();
    
    // bom
    var item = {};
  • Não use palavras reservadas como chaves. Não irão funcionar no IE8. Leia mais.

    // ruim
    var superman = {
      default: { clark: 'kent' },
      private: true
    };
    
    // bom
    var superman = {
      defaults: { clark: 'kent' },
      hidden: true
    };
  • Use sinônimos legíveis no lugar de palavras reservadas.

    // ruim
    var superman = {
      class: 'alien'
    };
    
    // ruim
    var superman = {
      klass: 'alien'
    };
    
    // bom
    var superman = {
      type: 'alien'
    };
  • Use a sintaxe literal para a criação de Arrays.

    // ruim
    var items = new Array();
    
    // bom
    var items = [];
  • Use Array#push ao inves de atribuir um item diretamente ao array.

    var someStack = [];
    
    
    // ruim
    someStack[someStack.length] = 'abracadabra';
    
    // bom
    someStack.push('abracadabra');
  • Quando precisar copiar um Array utilize Array#slice. jsPerf

    var len = items.length;
    var itemsCopy = [];
    var i;
    
    // ruim
    for (i = 0; i < len; i++) {
      itemsCopy[i] = items[i];
    }
    
    // bom
    itemsCopy = items.slice();
  • Para converter um objeto similar a um array para array, utilize Array#slice.

    function trigger() {
      var args = Array.prototype.slice.call(arguments);
      ...
    }
  • Use aspas simples '' para strings

    // ruim
    var name = "Bob Parr";
    
    // bom
    var name = 'Bob Parr';
    
    // ruim
    var fullName = "Bob " + this.lastName;
    
    // bom
    var fullName = 'Bob ' + this.lastName;
  • Strings maiores que 80 caracteres devem ser escritas em múltiplas linhas e usar concatenação.

  • Nota: Se muito usado, strings longas com concatenação podem impactar na performance. jsPerf & Discussion

    // ruim
    var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
    
    // ruim
    var errorMessage = 'This is a super long error that was thrown because \
    of Batman. When you stop to think about how Batman had anything to do \
    with this, you would get nowhere \
    fast.';
    
    // bom
    var errorMessage = 'This is a super long error that was thrown because ' +
      'of Batman. When you stop to think about how Batman had anything to do ' +
      'with this, you would get nowhere fast.';
  • Quando for construir uma string programaticamente, use Array#join ao invés de concatenação de strings. Principalmente para o IE: jsPerf.

    var items;
    var messages;
    var length;
    var i;
    
    messages = [{
      state: 'success',
      message: 'This one worked.'
    }, {
      state: 'success',
      message: 'This one worked as well.'
    }, {
      state: 'error',
      message: 'This one did not work.'
    }];
    
    length = messages.length;
    
    // ruim
    function inbox(messages) {
      items = '<ul>';
    
      for (i = 0; i < length; i++) {
        items += '<li>' + messages[i].message + '</li>';
      }
    
      return items + '</ul>';
    }
    
    // bom
    function inbox(messages) {
      items = [];
    
      for (i = 0; i < length; i++) {
        items[i] = '<li>' + messages[i].message + '</li>';
      }
    
      return '<ul>' + items.join('') + '</ul>';
    }
  • Declarando Funções:

    // definindo uma função anônima
    var anonymous = function() {
      return true;
    };
    
    // definindo uma função nomeada
    var named = function named() {
      return true;
    };
    
    // função imediatamente invocada (IIFE)
    (function() {
      console.log('Welcome to the Internet. Please follow me.');
    })();
  • Nunca declare uma função em um escopo que não seja de uma função (if, while, etc). Ao invés, atribua a função para uma variavel. Os Browsers irão deixar você fazer isso, mas a interpretação disso não é legal. Fazendo isso você pode ter más notícias a qualquer momento.

  • Nota: A ECMA-262 define um bloco como uma lista de instruções. A declaração de uma função não é uma instrução. Leia em ECMA-262's.

    // ruim
    if (currentUser) {
      function test() {
        console.log('Nope.');
      }
    }
    
    // bom
    if (currentUser) {
      var test = function test() {
        console.log('Yup.');
      };
    }
  • Nunca nomeie um parâmetro como arguments. Isso sobrescrevá o objeto arguments que é passado para cada função.

    // ruim
    function nope(name, options, arguments) {
      // ...outras implementações...
    }
    
    // bom
    function yup(name, options, args) {
      // ...outras implementações...
    }
  • Use ponto . para acessar propriedades.

    var luke = {
      jedi: true,
      age: 28
    };
    
    // ruim
    var isJedi = luke['jedi'];
    
    // bom
    var isJedi = luke.jedi;
  • Use colchetes [] para acessar propriedades através de uma variável.

    var luke = {
      jedi: true,
      age: 28
    };
    
    function getProp(prop) {
      return luke[prop];
    }
    
    var isJedi = getProp('jedi');
  • Sempre use var para declarar variáveis. Não fazer isso irá resultar em variáveis globais. Devemos evitar poluir o namespace global. O Capitão Planeta já nos alertou disso.

    // ruim
    superPower = new SuperPower();
    
    // bom
    var superPower = new SuperPower();
  • Use somente uma declaração var para múltiplas variáveis e declares cada variável em uma nova linha.

    // ruim
    var items = getItems();
    var goSportsTeam = true;
    var dragonball = 'z';
    
    // bom
    var items = getItems(),
        goSportsTeam = true,
        dragonball = 'z';
  • Declare as variáveis que você não vai estipular valor por último. É útil no futuro, quando você precisar atribuir valor para ela dependendo do valor da variável já declarada.

    // ruim
    var i, len, dragonball,
        items = getItems(),
        goSportsTeam = true;
    
    // ruim
    var i, items = getItems(),
        dragonball,
        goSportsTeam = true,
        len;
    
    // bom
    var items = getItems(),
        goSportsTeam = true,
        dragonball,
        length,
        i;
  • Defina variáveis no topo do escopo onde ela se encontra. Isso ajuda a evitar problemas com declaração de variáveis e hoisting.

    // ruim
    function() {
      test();
      console.log('fazendo qualquer coisa..');
    
      //..outras implementações..
    
      var name = getName();
    
      if (name === 'test') {
        return false;
      }
    
      return name;
    }
    
    // bom
    function() {
      var name = getName();
    
      test();
      console.log('fazendo alguma coisa..');
    
      //...outras implmementações...
    
      if (name === 'test') {
        return false;
      }
    
      return name;
    }
    
    // ruim
    function() {
      var name = getName();
    
      if (!arguments.length) {
        return false;
      }
    
      return true;
    }
    
    // bom
    function() {
      if (!arguments.length) {
        return false;
      }
    
      var name = getName();
    
      return true;
    }
  • Declarações de váriaveis durante todo o escopo da função são elevadas ao topo função com valor atribuído undefined. Esse comportamento é chamado de hoisting.

    // sabemos que isso não irá funcionar (assumindo que
    // não exista uma variável global chamada `notDefined`)
    function example() {
      console.log(notDefined); // => lança uma ReferenceError
    }
    
    // Declarar uma variável depois de ter referenciado
    // a mesma irá funcionar pelo comportamento do `hoist`
    // Nota: a atribuição do valor `true` não é afetada por `hoisting`.
    function example() {
      console.log(declaredButNotAssigned); // => undefined
      var declaredButNotAssigned = true;
    }
    
    // O interpretador fez `hoisting` para a declaração
    // da variável. Isso significa que nosso exemplo pode
    // ser reescrito como:
    function example() {
      var declaredButNotAssigned;
      console.log(declaredButNotAssigned); // => undefined
      declaredButNotAssigned = true;
    }
  • Funções anônimas fazem hoist para o nome da sua variável, não para a corpo da função.

    function example() {
      console.log(anonymous); // => undefined
    
      anonymous(); // => TypeError anonymous is not a function
    
      var anonymous = function() {
        console.log('anonymous function expression');
      };
    }
  • Funções nomeadas fazem hoist para o nome da variável, não para o nome ou corpo da função.

    function example() {
      console.log(named); // => undefined
    
      named(); // => TypeError named is not a function
    
      superPower(); // => ReferenceError superPower is not defined
    
      var named = function superPower() {
        console.log('Flying');
      };
    
    
      // O mesmo acontece quando o nome da função
      // é o mesmo da variável.
      function example() {
        console.log(named); // => undefined
    
        named(); // => TypeError named is not a function
    
        var named = function named() {
          console.log('named');
        };
      }
    }
  • Declarações de funções nomeadas fazem hoist do nome da função e do seu corpo.

    function example() {
      superPower(); // => Flying
    
      function superPower() {
        console.log('Flying');
      }
    }
  • Para mais informações veja JavaScript Scoping & Hoisting por Ben Cherry

  • Use === e !== ao invés de == e !=.

  • Expressões condicionais são interpretadas usando coerção de tipos e seguem as seguintes regras:

    • Objeto equivale a true
    • Undefined equivale a false
    • Null equivale a false
    • Booleans equivalem a o valor do boolean
    • Numbers equivalem a false se +0, -0, or NaN, se não true
    • Strings equivalem a false se são vazias '', se não true
    if ([0]) {
      // true
      // Um array é um objeto, objetos equivalem a `true`.
    }
  • Use atalhos.

    // ruim
    if (name !== '') {
      // ...outras implementações...
    }
    
    // bom
    if (name) {
      // ...outras implementações...
    }
    
    // ruim
    if (collection.length > 0) {
      // ...outras implementações...
    }
    
    // bom
    if (collection.length) {
      // ...outras implementações...
    }
  • Para mais informações veja Truth Equality and JavaScript por Angus Croll

  • Use chaves para todos os blocos com mais de uma linha.

    // ruim
    if (test)
      return false;
    
    // bom
    if (test) return false;
    
    // bom
    if (test) {
      return false;
    }
    
    // ruim
    function() { return false; }
    
    // bom
    function() {
      return false;
    }
  • Use /** ... */ para comentários com mais de uma linha. Inclua uma descrição e especifique tipos e valores para todos os parametros e retornos.

    // ruim
    // make() returns a new element
    // based on the passed in tag name
    //
    // @param <String> tag
    // @return <Element> element
    function make(tag) {
    
      // ...outra implementação...
    
      return element;
    }
    
    // bom
    /**
     * make() returns a new element
     * based on the passed in tag name
     *
     * @param <String> tag
     * @return <Element> element
     */
    function make(tag) {
    
      // ...outras implementações...
    
      return element;
    }
  • Use // para comentários de uma linha. Coloque comentários de uma linha acima da expressão. Deixe uma linha em branco antes de cada comentário.

    // ruim
    var active = true;  // is current tab
    
    // bom
    // is current tab
    var active = true;
    
    // ruim
    function getType() {
      console.log('fetching type...');
      // set the default type to 'no type'
      var type = this._type || 'no type';
    
      return type;
    }
    
    // bom
    function getType() {
      console.log('fetching type...');
    
      // set the default type to 'no type'
      var type = this._type || 'no type';
    
      return type;
    }
  • Use prefixos FIXME or TODO nos seus comentários. Isso vai ajudar outros desenvolvedores a entenderem rapidamente se você está indicando um código que precisa ser revisado ou está sugerindo uma solução para o problema e como deve ser implementado. Estes são comentários diferentes dos convencionais, porque eles são acionáveis. As ações são FIXME -- utilizado para comentários de apresentação ou TODO -- necessário a implementação.

  • Use // FIXME: para marcar problemas

    function Calculator() {
    
      // FIXME: não utilizar global aqui
      total = 0;
    
      return this;
    }
  • Use // TODO: para marcar soluções para um problema

    function Calculator() {
    
      // TODO: total deve ser configurado por um parâmetro das opções
      this.total = 0;
    
      return this;
    }
  • Use tabs com 2 espaços

    // ruim
    function() {
    ∙∙∙∙var name;
    }
    
    // ruim
    function() {
    ∙var name;
    }
    
    // bom
    function() {
    ∙∙var name;
    }
  • Coloque um espaço antes da chave que abre o escopo da função.

    // ruim
    function test(){
      console.log('test');
    }
    
    // bom
    function test() {
      console.log('test');
    }
    
    // ruim
    dog.set('attr',{
      age: '1 year',
      breed: 'Bernese Mountain Dog'
    });
    
    // bom
    dog.set('attr', {
      age: '1 year',
      breed: 'Bernese Mountain Dog'
    });
  • Coloque 1 espaço antes do parênteses de abertura de comandos de controle (if, while etc.). Não coloque espaços antes da lista de argumentos em chamadas e declarações de funções.

    // ruim
    if(isJedi) {
      fight ();
    }
    
    // bom
    if (isJedi) {
      fight();
    }
    
    // ruim
    function fight () {
      console.log ('Swooosh!');
    }
    
    // bom
    function fight() {
      console.log('Swooosh!');
    }
  • Colocar espaço entre operadores.

    // ruim
    var x=y+5;
    
    // bom
    var x = y + 5;
  • Coloque uma linha em branco no final do arquivo.

    // ruim
    (function(global) {
      // ...outras implementações...
    })(this);
    
    // ruim
    (function(global) {
      // ...outras implementações...
    })(this);
    
    // bom
    (function(global) {
      // ...outras implementações...
    })(this);
  • Use identação quando encadear vários métodos. Use um ponto à esquerda, o que enfatiza que a linha é uma chamada de método, não uma nova declaração.

      // ruim
      $('#items').find('.selected').highlight().end().find('.open').updateCount();
    
      // ruim
      $('#items').
        find('.selected').
          highlight().
          end().
        find('.open').
          updateCount();
    
      // bom
      $('#items')
        .find('.selected')
          .highlight()
          .end()
        .find('.open')
          .updateCount();
    
      // ruim
      var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
          .attr('width', (radius + margin) * 2).append('svg:g')
          .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
          .call(tron.led);
    
      // bom
      var leds = stage.selectAll('.led')
          .data(data)
        .enter().append('svg:svg')
          .classed('led', true)
          .attr('width', (radius + margin) * 2)
        .append('svg:g')
          .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
          .call(tron.led);
  • Deixar uma linha em branco depois de blocos e antes da próxima declaração

    // ruim
    if (foo) {
      return bar;
    }
    return baz;
    
    // bom
    if (foo) {
      return bar;
    }
    
    return baz;
    
    // ruim
    var obj = {
      foo: function() {
      },
      bar: function() {
      }
    };
    return obj;
    
    // bom
    var obj = {
      foo: function() {
      },
    
      bar: function() {
      }
    };
    
    return obj;
  • Leading commas: Nope.

    // ruim
    var story = [
        once
      , upon
      , aTime
    ];
    
    // bom
    var story = [
      once,
      upon,
      aTime
    ];
    
    // ruim
    var hero = {
        firstName: 'Bob'
      , lastName: 'Parr'
      , heroName: 'Mr. Incredible'
      , superPower: 'strength'
    };
    
    // bom
    var hero = {
      firstName: 'Bob',
      lastName: 'Parr',
      heroName: 'Mr. Incredible',
      superPower: 'strength'
    };
  • Additional trailing comma: Nope. This can cause problems with IE6/7 and IE9 if it's in quirksmode. Also, in some implementations of ES3 would add length to an array if it had an additional trailing comma. This was clarified in ES5 (source):

Edition 5 clarifies the fact that a trailing comma at the end of an ArrayInitialiser does not add to the length of the array. This is not a semantic change from Edition 3 but some implementations may have previously misinterpreted this.

```javascript
// ruim
var hero = {
  firstName: 'Kevin',
  lastName: 'Flynn',
};

var heroes = [
  'Batman',
  'Superman',
];

// bom
var hero = {
  firstName: 'Kevin',
  lastName: 'Flynn'
};

var heroes = [
  'Batman',
  'Superman'
];
```
  • Yup.

    // ruim
    (function() {
      var name = 'Skywalker'
      return name
    })()
    
    // bom
    (function() {
      var name = 'Skywalker';
      return name;
    })();
    
    // bom (guards against the function becoming an argument when two files with IIFEs are concatenated)
    ;(function() {
      var name = 'Skywalker';
      return name;
    })();

    Leia mais.

  • Faça coerção de tipos no inicio da expressão.

  • Strings:

    //  => this.reviewScore = 9;
    
    // ruim
    var totalScore = this.reviewScore + '';
    
    // bom
    var totalScore = '' + this.reviewScore;
    
    // ruim
    var totalScore = '' + this.reviewScore + ' total score';
    
    // bom
    var totalScore = this.reviewScore + ' total score';
  • Use parseInt para Numbers e sempre informe a base de conversão.

    var inputValue = '4';
    
    // ruim
    var val = new Number(inputValue);
    
    // ruim
    var val = +inputValue;
    
    // ruim
    var val = inputValue >> 0;
    
    // ruim
    var val = parseInt(inputValue);
    
    // bom
    var val = Number(inputValue);
    
    // bom
    var val = parseInt(inputValue, 10);
  • Se por alguma razão você está fazendo algo muito underground e o parseInt é o gargalo, se usar deslocamento de bits (Bitshift) por questões de performance, deixe um comentário explicando por que você está fazendo isso.

    // bom
    /**
     * parseInt é a causa do meu código estar lendo.
     * Bitshifting a String para força-lo como um
     * Number faz isso muito mais rápido.
     */
    var val = inputValue >> 0;
  • Nota: Cuidado com operações de bitshift. Numbers são representados por valores 64-bit, mas operações Bitshift sempre retornarão valores inteiros de 32-bit (fonte). Bitshift pode levar a um comportamento inesperado para valores inteiros maiores que 32 bits. Discussão. O mairo valor Integer signed 32-bit é 2.147.483.647:

    2147483647 >> 0 //=> 2147483647
    2147483648 >> 0 //=> -2147483648
    2147483649 >> 0 //=> -2147483647
  • Booleans:

    var age = 0;
    
    // ruim
    var hasAge = new Boolean(age);
    
    // bom
    var hasAge = Boolean(age);
    
    // bom
    var hasAge = !!age;
  • Não use apenas um caracter, seja descritivo.

    // ruim
    function q() {
      // ...outras implementações...
    }
    
    // bom
    function query() {
      // ...outras implementações...
    }
  • Use camelCase quando for nomear objetos, funções e instâncias.

    // ruim
    var OBJEcttsssss = {};
    var this_is_my_object = {};
    function c() {}
    var u = new user({
      name: 'Bob Parr'
    });
    
    // bom
    var thisIsMyObject = {};
    function thisIsMyFunction() {}
    var user = new User({
      name: 'Bob Parr'
    });
  • Use PascalCase quando for nomear construtores ou classes.

    // ruim
    function user(options) {
      this.name = options.name;
    }
    
    var bad = new user({
      name: 'nope'
    });
    
    // bom
    function User(options) {
      this.name = options.name;
    }
    
    var good = new User({
      name: 'yup'
    });
  • Use um underscore _ como primeiro caracter em propriedades privadas.

    // ruim
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    
    // bom
    this._firstName = 'Panda';
  • Quando for guardar referência para this use _this.

    // ruim
    function() {
      var self = this;
      return function() {
        console.log(self);
      };
    }
    
    // ruim
    function() {
      var that = this;
      return function() {
        console.log(that);
      };
    }
    
    // bom
    function() {
      var _this = this;
      return function() {
        console.log(_this);
      };
    }
  • Nomeie suas funções. Ajuda bastante quando for analisar pilhas de erro.

    // ruim
    var log = function(msg) {
      console.log(msg);
    };
    
    // bom
    var log = function log(msg) {
      console.log(msg);
    };
  • Nota: IE8 ou inferior mostra alguns problemas com funções nomeadas. Veja http://kangax.github.io/nfe/ para mais informações.

  • Se seu arquivos exporta apenas uma classes, o nome do arquivo deve conter exatamento o nome da classe.

    // conteúdo do arquivo
    class CheckBox {
      // ...
    }
    module.exports = CheckBox;
    
    // em outro arquivo
    // ruim
    var CheckBox = require('./checkBox');
    
    // ruim
    var CheckBox = require('./check_box');
    
    // bom
    var CheckBox = require('./CheckBox');
  • Métodos acessores de propriedades não são obrigatórios.

  • Se você vai criar métodos acessores utilize getVal() e setVal('hello')

    // ruim
    dragon.age();
    
    // bom
    dragon.getAge();
    
    // ruim
    dragon.age(25);
    
    // bom
    dragon.setAge(25);
  • Se a propriedade é um boolean, use isVal() ou hasVal()

    // ruim
    if (!dragon.age()) {
      return false;
    }
    
    // bom
    if (!dragon.hasAge()) {
      return false;
    }
  • Tudo bem se você criar os métodos get() e set(), mas seja consistente.

    function Jedi(options) {
      options || (options = {});
      var lightsaber = options.lightsaber || 'blue';
      this.set('lightsaber', lightsaber);
    }
    
    Jedi.prototype.set = function(key, val) {
      this[key] = val;
    };
    
    Jedi.prototype.get = function(key) {
      return this[key];
    };
  • Atribua métodos ao objeto prototype ao invés de sobrescrever o prototype com um novo objeto.Overwriting the prototype makes inheritance impossible: by resetting the prototype you'll overwrite the base!

    function Jedi() {
      console.log('new jedi');
    }
    
    // ruim
    Jedi.prototype = {
      fight: function fight() {
        console.log('fighting');
      },
    
      block: function block() {
        console.log('blocking');
      }
    };
    
    // bom
    Jedi.prototype.fight = function fight() {
      console.log('fighting');
    };
    
    Jedi.prototype.block = function block() {
      console.log('blocking');
    };
  • Métodos podem retornar this para encadear novas chamadas.

    // ruim
    Jedi.prototype.jump = function() {
      this.jumping = true;
      return true;
    };
    
    Jedi.prototype.setHeight = function(height) {
      this.height = height;
    };
    
    var luke = new Jedi();
    luke.jump(); // => true
    luke.setHeight(20) // => undefined
    
    // bom
    Jedi.prototype.jump = function() {
      this.jumping = true;
      return this;
    };
    
    Jedi.prototype.setHeight = function(height) {
      this.height = height;
      return this;
    };
    
    var luke = new Jedi();
    
    luke.jump()
      .setHeight(20);
  • Tudo bem em escrever um toString() customizado. Apenas garanta que ele sempre irá funcionar e que não altera nenhum estado.

    function Jedi(options) {
      options || (options = {});
      this.name = options.name || 'no name';
    }
    
    Jedi.prototype.getName = function getName() {
      return this.name;
    };
    
    Jedi.prototype.toString = function toString() {
      return 'Jedi - ' + this.getName();
    };
  • When attaching data payloads to events (whether DOM events or something more proprietary like Backbone events), pass a hash instead of a raw value. This allows a subsequent contributor to add more data to the event payload without finding and updating every handler for the event. For example, instead of:

    // ruim
    $(this).trigger('listingUpdated', listing.id);
    
    ...
    
    $(this).on('listingUpdated', function(e, listingId) {
      // do something with listingId
    });

    prefira:

    // bom
    $(this).trigger('listingUpdated', { listingId : listing.id });
    
    ...
    
    $(this).on('listingUpdated', function(e, data) {
      // do something with data.listingId
    });
  • Um módulo deve começar com !. Isso garante que não haverá erros em produção caso os scripts sejam concatenados e um módulo não termine com ponto e vírgula. Explicação

  • Nomeie o arquivo em formato camelCase, coloque em uma pasta com o mesmo nome e procure o nome da função que é exportada.

  • Adicione um método noConflict() que exporta o módulo antigo e retorna o módulo que foi criado com o mesmo nome.

  • Sempre declare 'use strict'; no topo do módulo.

    // fancyInput/fancyInput.js
    
    !function(global) {
      'use strict';
    
      var previousFancyInput = global.FancyInput;
    
      function FancyInput(options) {
        this.options = options || {};
      }
    
      FancyInput.noConflict = function noConflict() {
        global.FancyInput = previousFancyInput;
        return FancyInput;
      };
    
      global.FancyInput = FancyInput;
    }(this);
  • Nomeie objetos jQuery com o prefixo $.

    // ruim
    var sidebar = $('.sidebar');
    
    // bom
    var $sidebar = $('.sidebar');
  • Guarde as consultas jQuery para reuso.

    // ruim
    function setSidebar() {
      $('.sidebar').hide();
    
      // ...outras implementações...
    
      $('.sidebar').css({
        'background-color': 'pink'
      });
    }
    
    // bom
    function setSidebar() {
      var $sidebar = $('.sidebar');
      $sidebar.hide();
    
      // ...outras implementações...
    
      $sidebar.css({
        'background-color': 'pink'
      });
    }
  • Para pesquisas no DOM use o modo Cascata $('.sidebar ul') ou pai > filho $('.sidebar > ul'). jsPerf

  • Use find em objetos jQuery que estão armazenados em variáveis.

    // ruim
    $('ul', '.sidebar').hide();
    
    // ruim
    $('.sidebar').find('ul').hide();
    
    // bom
    $('.sidebar ul').hide();
    
    // bom
    $('.sidebar > ul').hide();
    
    // bom
    $sidebar.find('ul').hide();

⬆ voltar ao topo

  • Yup.

    function() {
      return true;
    }

};

3. Rubocop Ruby Style Guide <%=

RuboCop Logo

RuboCop é um analizador estático de código Ruby. De maneira que irá implementar várias práticas, ou guidelines, que são difundidas na comunidade do Ruby Style Guide.

No entanto, muitos aspectos do seu comportamento podem ser alterados através de várias opções de configuração.

Além de reportar problemas no seu código, RuboCop pode também automaticamente corrgir certos problemas pra você.

O Ruby Style Guide

Este guia de estilo Ruby recomenda as melhores práticas para que programadores reais de Ruby possam escrever códigos que possam ser mantidos por outros programadores reais de Ruby. Um guia de estilo que reflete o uso que o mundo real está acostumado e um guia de estilo que se prende a um ideal que foi rejeitado pelo povo supõe-se que o melhor é não usá-lo para nada – não importa o quão bom seja.

O guia é separado em várias seções de regras relacionadas. Tentei adicionar a lógica por trás das regras (se estiver omitido, eu assumi que é bastante óbvio).

Eu não vim com todas as regras do nada - são na maioria com base na minha extensa carreira como profissional de engenharia de software, comentários e sugestões dos membros da comunidade Ruby e vários recursos de programação Ruby altamente recomendados, tais como "Programming Ruby 1.9" e "The Ruby Programming Language".

Há algumas áreas em que não há nenhum consenso na Comunidade Ruby em relação um determinado estilo (como estilo de declaração de cadeia de caracteres literais, espaçamento dentro de hashes literais, posição do ponto no método de encadeamento multi-linha, etc.). Em tais situações, todos os estilos populares são reconhecidos e cabe a você escolher um e aplicá-lo de forma consistente.

O guia ainda é um trabalho em andamento - algumas regras possuem exemplos escassos, algumas regras não tem exemplos que as ilustrem claramente. No devido tempo, estas questões serão abordadas - apenas mantenha-los em mente por while.

Você pode gerar um PDF ou uma cópia HTML deste guia usando [Transmuter] https://github.com/TechnoGate/transmuter).

RuboCop é um analisador de código, baseado neste guia de estilo.

Layout do código fonte

Quase todo mundo está convencido que todos os estilos, exceto os seus são > feios e ilegíveis. Deixe de lado o "exceto seus próprios" e eles estarão provavelmente certo...
-- Jerry Coffin (sobre recuo)

  • Use UTF-8 como a codificação do arquivo de fonte.

  • Use dois espaços por nível de recuo (conhecido como soft tabs). Nada de hard tabs.

    # ruim - quatro espaços
    def algum_metodo
        fazer_algo
    end
    
    # bom
    def algum_metodo
      fazer_algo
    end
  • Use os finais de linha no estilo Unix. (* Usuários BSD/Solaris/Linux/OS X estão cobertos por padrão, usuários do Windows precisam ter cuidado extra).

  • Se você estiver usando o Git, você pode querer adicionar a seguinte definição de configuração para proteger o seu projeto do aterrorizante end de linha do Windows:

$ git config --global core.autorcrlf true
  • Não use ';' para separar as declarações e expressões. Como um corolário - use uma expressão por linha.
# ruim
puts 'foobar'; # ponto e vírgula supérfluo

puts 'foo'; puts 'bar' # duas expressões na mesma linha

# bom
puts 'foobar'

puts 'foo'
puts 'bar'

puts 'foo', 'bar' # isto aplica-se ao puts em particular
  • Prefira um formato de linha único para as definições de classe sem corpo.
# ruim
class FooError < StandardError
end

# ok
class FooError < StandardError; end

# bom
FooError = Class.new(StandardError)

Evite os métodos de linha única. Embora sejam um tanto populares no mundo selvagem, existem algumas peculiaridades sobre a sintaxe de definição que torna seu uso indesejável. De qualquer forma - não deveria haver mais do que uma expressão em um método de linha única.

# ruim
def muito; alguma_coisa; alguma_outra_coisa; end

# ok - Observe que o primeiro ; é necessário
def metodo_sem_parenteses; corpo end

# ok - Observe que o segundo ; é opcional
def metodo_sem_parenteses; corpo; end

# okish - sintaxe válida, mas sem o ; é difícil de ler
def metodo() corpo end

# bom
def algum_metodo
  corpo
end

Uma exceção à regra são os métodos de corpo vazio.

# bom
def no_op; end
  • Use espaços em torno de operadores, após vírgulas, dois-pontos e ponto e vírgula, em volta do { e antes do }. Espaço em branco pode ser (na maioria dos casos) irrelevante para o intérprete de Ruby, mas sua utilização correcta é a chave para escrever um código facilmente legível.
soma = 1 + 2
a, b = 1, 2
1 > 2 ? true : false; puts 'Olá'
[1, 2, 3].each { |e| puts e }

A única exceção, sobre os operadores, é o operador de expoente:

# ruim
e = M * c * * 2

# bom
e = M * c**2

{ e } merecem um pouco de esclarecimento, pois eles são usados para blocos e hashes literais, bem como expressões incorporadas em seqüências de caracteres. Para hash, dois estilos literais são considerados aceitáveis.

# bom - espaço após { e antes }
{ um: 1, dois: 2 }

# bom - sem espaço após { e antes }
{um: 1, dois: 2}

A primeira variante é ligeiramente mais legível (e sem dúvida mais popular na comunidade Ruby em geral). A segunda variante tem a vantagem de adicionar a diferença visual entre o bloco e hash literais. Qualquer um que você escolher - aplique-o de forma consistente.

Com as expressões incorporadas, há também duas opções aceitáveis:

# bom - sem espaços
"string #{expr}"

# ok - indiscutivelmente mais legível
"string #{ expr }"

O primeiro estilo é extremamente mais popular e é geralmente aconselhado a ficar com ele. O segundo, por outro lado, é (sem dúvidas) um pouco mais legível. Como com hashes - escolha um estilo e aplique-o de forma consistente.

  • Sem espaços após (, [ ou antes ], ).
some(arg).other
[1, 2, 3].size
  • Sem espaço após !.
# ruim
! algo

# bom
!algo
  • Idente when no nível do case. Eu sei que muitos discordariam com isso, mas é o estilo estabelecido em ambos "The Ruby Programming Language" e "Programming Ruby".
# ruim
case
  when musica.nome == 'Misty'
    puts 'Não!'
  when musica.duracao > 120
    puts 'Muito tempo'!
  when Time.now.hour > 21
    puts "É tarde demais"
  else
    musica.Play
end

# bom
case
when musica.nome == 'Misty'
  puts 'Não!'
when musica.duracao > 120
  puts 'Muito tempo'!
when Time.now.hour > 21
  puts "É tarde demais"
else
  musica.play
end
  • Quando você atribuir o resultado de uma expressão condicional a uma variável, preservar o alinhamento normal dos seus ramos.
# ruim - muito complicado
tipo = case ano
when 1850..1889 then 'Blues'
when 1890..1909 then 'Ragtime'
when 1910..1929 then 'New Orleans Jazz'
when 1930..1939 then 'Swing'
when 1940..1950 then 'Bebop'
else 'Jazz'
end

resultado = if condicao
  calc_algo
else
  calc_outro_algo
end

# bom - é evidente o que está acontecendo
tipo = case ano
       when 1850..1889 then 'Blues'
       when 1890..1909 then 'Ragtime'
       when 1910..1929 then 'New Orleans Jazz'
       when 1930..1939 then 'Swing'
       when 1940..1950 then 'Bebop'
       else 'Jazz'
       end

resultado = if condicao
              calc_algo
            else
              calc_outro_algo
            end


# bom (e um pouco mais largamente eficiente)
tipo =
  case ano
  when 1850..1889 then 'Blues'
  when 1890..1909 then 'Ragtime'
  when 1910..1929 then 'New Orleans Jazz'
  when 1930..1939 then 'Swing'
  when 1940..1950 then 'Bebop'
  else 'Jazz'
  end

resultado =
  if condicao
    calc_algo
  else
    calc_outro_algo
  end
  • Usar linhas vazias entre as definições de método e também para quebrar um método em parágrafos de lógica internas.
def algum_metodo
  data = initialize(options)

  data.manipulate!

  data.result
end

def algum_metodo
  result
end
  • Evitar a vírgula após o último parâmetro em uma chamada de método, especialmente quando os parâmetros não são em linhas separadas.
# ruim - mais fácil de mover ou adicionar/remover parâmetros, mas ainda não preferível
algum_metodo (
              tamanho,
              contagem,
              cor,
             )

# ruim
algum_metodo (tamanho, contagem, cor, )

# bom
algum_metodo (tamanho, contagem, cor)
  • Use espaços em torno do operador = quando para atribuição de valores padrão para os parâmetros do método:
# ruim
def algum_metodo (arg1=:default, arg2=nil, arg3=[])
  # fazer alguma coisa...
end

# bom
def algum_metodo (arg1 =: default, arg2 = nil, arg3 = [])
  # fazer alguma coisa...
end

Enquanto diversos livros de Ruby sugerem o primeiro estilo, o segundo é muito mais proeminente na prática (e, possivelmente, um pouco mais legível).

Evitar a continuação da linha \ onde não é necessário. Na prática, evitar o uso de continuação da linha para nada além de concatenação de seqüência de caracteres.

# ruim
resultado = 1 - \
            2

# bom (mas ainda feio como o inferno)
resultado = 1 \
            - 2

long_string = 'Primeira parte da longa cadeia de caracteres' \
              'e a segunda parte da seqüência de caracteres longa'
  • Adote um método consistente de estilo de encadeamento multi-linha. Existem dois estilos populares na comunidade Ruby, ambos dos quais são considerados bom - iniciando . (Opção A) e finalizando de . (Opção B).

  • (Opção A) Quando continuando uma invocação de método encadeada em outra linha, mantenha o . na segunda linha.

# ruim - precisa consultar a primeira linha para entender a segunda linha
um.dois.tres.
  quatro

# bom - é imediatamente evidente o que está acontecendo a segunda linha
um.dois.tres
  .quatro
  • (Opção B) quando continuando uma invocação de método encadeada na outra linha, incluir o . na primeira linha para indicar que a expressão continua.
# ruim - precisa ler em frente para a segunda linha de saber que a corrente continua
um.dois.tres
  .quatro

# bom - é imediatamente evidente que a expressão continua além da primeira linha
um.dois.tres.
  quatro

Uma discussão sobre os méritos de ambos os estilos alternativos pode ser encontrada aqui.

  • Alinhar os parâmetros de um método chamado se eles se espalham por mais de um linha. Quando alinhar parâmetros não é apropriado devido à restrições de comprimento da linha, identação única para as linhas após a primeira é também aceitável.
# ponto de início (a linha é muito longa)
def enviar_email(source)
  Mailer.deliver(to: '[email protected],' from: '[email protected]', subject: 'Mensagem importante', body: source.text)
end

# ruim (dupla identação)
def send_mail(source)
  Mailer.deliver(
      to: '[email protected]',
      from: '[email protected]',
      subject: 'Mensagem importante',
      body: source.text)
end

# bom
def send_mail(source)
  Mailer.deliver(to: '[email protected]',
                 from: '[email protected]',
                 subject: 'Mensagem importante',
                 body: source.text)
end

# bom (identação única)
def send_mail(source)
  Mailer.deliver(
    to: '[email protected]',
    from: '[email protected]',
    subject: 'Mensagem importante',
    body: source.text
  )
end
  • Alinhe os elementos do array de literais, abrangendo várias linhas.
# ruim - identação única
menu_item = ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
  'Feijão', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']

# bom
menu_item = [
  'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
  'Feijão', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam'
]

# bom
menu_item =
  ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
   'Feijão', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']
  • Adicione sublinhados para literais numéricos grandes para melhorar a sua legibilidade.
# ruim - quantos 0s existem?
num = 1000000

# bom - muito mais fácil de analisar para o cérebro humano
num = 1_000_000
  • Use RDoc e suas convenções para a documentação da API. Não coloque uma linha vazia entre o bloco de comentários e o def.

  • Limitar as linhas à 80 caracteres.

  • Evite o espaço em branco à direita.

  • Termine cada arquivo com uma nova linha.

  • Não use comentários em bloco. Eles não podem ser precedidos por um espaço em branco e não são tão fácil de identificar como os comentários regulares.

# mau
=begin
linha de comentário
outra linha de comentário
=end

# bom
# linha de comentário
# outra linha de comentário

Sintaxe

  • Use :: apenas para referências constantes (isso inclui as classes e módulos) e construtores (como Array() ou Nokogiri::HTML()). Nunca use :: para invocação de método regular.
# mau
AlgumaCasse::algum_metodo
algum_objeto::algum_metodo

# bom
AlgumaClasse.algum_metodo
algum_objeto.algum_metodo
AlgumModulo::AlgumaClasse::SOME_CONST
AlgumModulo::AlgumaClasse()
  • Use def com parênteses, quando há argumentos. Omita os parênteses quando o método não aceita quaisquer argumentos.
# mau
def algum_metodo()
  # corpo omitido
end

# bom
def algum_metodo
  # corpo omitido
end

# mau
def algum_metodo_com_argumentos arg1, arg2
  # corpo omitido
end

# bom
def algum_metodo_com_argumentos(arg1, arg2)
  #corpo omitido
end
  • Nunca use for, à menos que você saiba exatamente o porquê. A maioria dos iteradores de vezes devem ser usado em vez disso. for é implementado em termos de each (assim você está adicionando um nível de indireção), mas com uma reviravolta - for não introduz um novo escopo (ao contrário de each) e as variáveis definidas em seu bloco serão visíveis fora dela.
arr = [1, 2, 3]

# mau
for elem in arr do
  puts elem
end

# note que elem é acessível de fora do loop for
elem #=> 3

# bom
arr.each { |elem| puts elem }

# elem não é acessível fora do cada bloco
elem #=> NameError: undefined local variable or method 'elem'
  • Nunca use then para multi-linhas if/unless.
# mau
if alguma_condicao then
  # corpo omitido
end

# bom
if alguma_condicao
  # corpo omitido
end
  • Sempre colocar a condição na mesma linha que o if / unless em uma condicional de várias linha.
# mau
if
  alguma_condicao
  faca_alguma_coisa
  faca_alguma_outra_coisa
end

# bom
if alguma_condicao
  faca_alguma_coisa
  faca_alguma_outra_coisa
end
  • Favoreça o operador ternário (?:) sobre construções de if/then/else/end. É obviamente mais conciso e mais comum.
# mau
resultado = if alguma_condicao then alguma_coisa else outra_coisa end

# bom
resultado = alguma_condicao ? alguma_coisa : outra_coisa
  • Use uma expressão por ramo em um operador ternário. Isto também significa que os operadores ternários não devem ser aninhados. Prefira construtores if/else nesses casos.
# mau
alguma_condicao ? (condicao_aninhada ? alguma_coisa_aninhada : outra_coisa_aninhada) : outra_coisa

# bom
if alguma_condicao
  condicao_aninhada ? alguma_coisa_aninhada : outra_coisa_aninhada
else
  outra_coisa
end
  • Nunca use if x: ... - a partir de Ruby 1.9, ele foi removido. Use o operador ternário em vez disso.
# mau
resultado = if alguma_condicao : alguma_coisa else outra_coisa end

# bom
resultado = alguma_condicao ? alguma_coisa : outra_coisa
  • Nunca use if x; .... Use o operador ternário.

  • Aproveitar o fato de que, if e case são expressões que retornam um resultado.

# mau
if condicao
  resultado = x
else
  resultado = y
end

# bom
resultado =
  if condicao
    x
  else
    y
  end
  • Use when x then ... para os casos de uma linha. A sintaxe alternativa when x: ... foi removido a partir do Ruby 1.9.

  • Nunca use when x; .... Consulte a regra anterior.

  • Use ! em vez de not.

# mau - chaves são necessárias por causa da precedência de operadores
x = (not algo)

# bom
x = !algo

Evitar a utilização de !!.

# mau
x = 'teste'
# obscura checagem de nil
if !!x
  # corpo omitido
end

x = false
# dupla negação é inútil em booleanos
!!x # => false

# bom
x = 'teste'
unless x.nil?
  # corpo omitido
end
  • As palavras-chave and e or são proibidas. Não valem a pena. Sempre use && e || em vez disso.
# mau
# expressão booleana
if alguma_condicao and alguma_outra_condicao
  faca_alguma_coisa
end

# controle de fluxo
document.saved? or document.save!

# bom
# expressão booleana
if alguma_condicao && alguma_outra_condicao
  faca_alguma_coisa
end

# controle de fluxo
document.saved? || document.save!

Evitar a multi-linha ?: (o operador ternário); Use if/unless.

  • Favorite o uso do modificador if/unless quando você tem um corpo de linha única. Outra boa alternativa é o uso de fluxo de controle &&/||.
# mau
if alguma_condicao
  faca_alguma_coisa
end

# bom
faca_alguma_coisa if alguma_condicao

# outra boa opção
alguma_condicao && faca_alguma_coisa
  • Favorecer unless sobre if para negativo condições (ou controle fluxo ||).
# mau
faca_alguma_coisa if !alguma_condicao

# mau
faca_alguma_coisa if not alguma_condicao

# bom
faca_alguma_coisa unless alguma_condicao

# outra boa opção
alguma_condicao || faca_alguma_coisa
  • Nunca use unless com else. Reescreva estas com o caso positivo primeiro.
# mau
unless sucesso?
  puts 'fracasso'
else
  puts 'sucesso'
end

# bom
if sucesso?
  puts 'sucesso'
else
  puts 'fracasso'
end
  • Não use parênteses em torno da condição de uma if/unless/while/until.
# mau
if (x > 10)
  # corpo omitido
end

# bom
if x > 10
  # corpo omitido
end
  • Nunca utilize while/until condição do para a multi-linha while/until.
# mau
while x > 5 do
  # corpo omitido
end

until x > 5 do
  # corpo omitido
end

# bom
while x > 5
  # corpo omitido
end

until x > 5
  # corpo omitido
end
  • Favoreça o uso do modificador while/until quando você tem uma linha única.
# mau
while alguma_condicao
  faca_alguma_coisa
end

# bom
faca_alguma_coisa while alguma_condicao
  • Favoreça until sobre o while para condições negativas.
# mau
faca_alguma_coisa while !alguma_condicao

# bom
faca_alguma_coisa until alguma_condicao
  • Use Kernel#loop com break ao invés de begin/end/until ou begin/end/while para testes pós-loop.
# mau
being
  puts val
  val += 1
end while val < 0

# bom
loop do
  puts val
  val += 1
  break unless val < 0
end
  • Omitir parênteses em torno de parâmetros para métodos que fazem parte de uma DSL interna (por exemplo, Rake, Rails, RSpec), métodos que têm status de "palavra-chave" em Ruby (por exemplo, attr_reader, puts) e o métodos de acesso de atributos. Use parênteses em torno dos argumentos de todas as outras invocações de método.
class Pessoa
  attr_reader :nome, :idade

  # omitido
end

temperanca = Pessoa.new('Temperança', 30)
temperanca.nome

puts temperanca.idade

x = Math.sin(y)
array.delete(e)

boliche.score.should == 0
  • Omita o aparelho exterior em torno de um hash de opções implícita.
# mau
Usuario.set({nome: 'John', idade: 45, permissoes: {leitura: true}})

# bom
Usuario.set(nome: 'John', idade: 45, permissoes: {leitura: true})
  • Omitir ambas as chaves e parênteses exteriores para métodos que são parte de uma DSL interna.
class Pessoa < ActiveRecord::Base
  # mau
  validates(:nome, { presence: true, length: { within: 1..10 } })

  # bom
  validates :nome, presence: true, length: { within: 1..10 }
end
  • Omita parênteses para chamadas de método sem argumentos.
# mau
Kernel.exit!()
2.even?()
fork()
'test'.upcase()

# bom
Kernel.exit!
2.even?
fork
'teste'.upcase
  • Prefira {...} ao invés de do..end para blocos de linha única. Evite o uso de {...} para a linha multi blocos (encadeamento de várias linhas é sempre feio). Use sempre do..end para "fluxo de controle" e "definições de métodos" (por exemplo, em Rakefiles e certos DSLs). Evite o do..end quando encadeando.
nomes = ['Bozhidar', 'Steve', 'Sarah']

# mau
nomes.each |nome|
  puts nome
end

# bom
nomes.each { |nome| puts nome }

# mau
nomes.select |nome|
  nome.start_with?('S')
end.map { |nome| nome.upcase }

# bom
nomes.select { |nome| nome.start_with?('S') }.map { |nome| nome.upcase }

Alguns argumentam que o encadeamento de várias linhas ficaria bem com o uso de {...}, mas eles devem se perguntar - é esse código realmente legível e podem ser extraídos os conteúdos dos blocos em métodos estilosos?

  • Considere usar argumento explícito de nomes para evitar a escrita de bloco literal que só passa seus argumentos para outro bloco. Cuidado com os impactos no desempenho, assim que, os blocos são convertidos para um Proc.
require 'tempfile'

# mau
def com_dir_tmp
  Dir.mktmpdir do |dir_tmp|
    Dir.chdir(dir_tmp) { |dir| yield dir } # bloco só passa argumentos
  end
end

# bom
def com_dir_tmp(&block)
  Dir.mktmpdir do |dir_tmp|
    Dir.chdir(dir_tmp, &block)
  end
end

com_dir_tmp do |dir|
  puts "dir é acessível como um parâmetro e pwd é definido: #{dir}"
end
  • Evite return, onde não é necessário para o fluxo de controle.
# mau
def algum_metodo(algum_array)
  return algum_array.size
end

# bom
def algum_metodo(algum_array)
  algum_array.size
end
  • Evitar self onde não é necessário. (Só é necessário ao chamar um acessador de auto gravação.)
# bad
def pronto?
  if self.revisado_em > self.atualizado_em
    self.trabalhador.update(self.contudo, self.opcoes)
    self.status = :em_progresso
  end
  self.status == :verificado
end

# good
def pronto?
  if revisado_em > atualizado_em
    trabalhador.update(contudo, opcoes)
    self.status = :em_progresso
  end
  status == :verificado
end

Você pode ler muito mais sobre o RuboCop em seu manual oficial.

Compatibilidade

RuboCop suporta as seguintes implementações Ruby:

  • MRI 2.0
  • MRI 2.1
  • MRI 2.2
  • MRI 2.3
  • MRI 2.4
  • JRuby 9.0+
  • Rubinius 2.0+

%>

Falko

Cronograma Versão 3


Acesso à aplicação


Equipe

Release 02

Sprint 1

Sprint 2

Sprint 3

Sprint 4

Sprint 5

Sprint 6

Sprint 7

Sprint 8

Sprint 9

Release 01

Gerenciamento do Projeto

Artefatos de Desenvolvimento

Encerramento

Clone this wiki locally