Skip to content

caiogranero/ruby-airbnb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 

Repository files navigation

Ruby Style Guide

O guia de estilo para Ruby do Airbnb.

Inspirado por GitHub's guide e Bozhidar Batsov's guide.

Airbnb também mantém o Guia de estilo para JavaScript.

Índice

  1. Espaços em branco 1. Indentação 1. Linha única 1. Novas linhas
  2. Tamanho das linhas
  3. Comentários 1. Comentários de arquivos/classes 1. Comentários de funções 1. Comentários de blocos e linhas únicas 1. Pontuação, ortografia e gramática 1. Comentários de pendências 1. Códigos comentado
  4. Métodos 1. Definição de métodos 1. Chamada de métodos
  5. Expressões condicionais 1. Palavras condicionais 1. Operador ternário 1. Condições longas
  6. Sintaxe
  7. Nomenclatura
  8. Classes
  9. Exceções
  10. Coleções
  11. Textos
  12. Expressões regulares
  13. Notação de porcentagem
  14. Rails 1. Escopo
  15. Seja consistente
  16. Tradução

Espaços em branco

Indentação

  • Use tabs com dois espaçamentos.[link]

  • Indente when da mesma forma que case. [link]

    case
    when song.name == 'Misty'
      puts 'Not again!'
    when song.duration > 120
      puts 'Too long!'
    when Time.now.hour > 21
      puts "It's too late"
    else
      song.play
    end
    
    kind = case year
           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
  • Alinhe todos os parâmetros de função ou na mesma linha ou um por linha.[link]

    # ruim
    def self.create_translation(phrase_id, phrase_key, target_locale,
                                value, user_id, do_xss_check, allow_verification)
      ...
    end
    
    # bom
    def self.create_translation(phrase_id,
                                phrase_key,
                                target_locale,
                                value,
                                user_id,
                                do_xss_check,
                                allow_verification)
      ...
    end
    
    # bom
    def self.create_translation(
      phrase_id,
      phrase_key,
      target_locale,
      value,
      user_id,
      do_xss_check,
      allow_verification
    )
      ...
    end
  • Indente continuamente múltiplas linhas de expressões condicionais.[link]

    # ruim
    def is_eligible?(user)
      Trebuchet.current.launch?(ProgramEligibilityHelper::PROGRAM_TREBUCHET_FLAG) &&
      is_in_program?(user) &&
      program_not_expired
    end
    
    # bom
    def is_eligible?(user)
      Trebuchet.current.launch?(ProgramEligibilityHelper::PROGRAM_TREBUCHET_FLAG) &&
        is_in_program?(user) &&
        program_not_expired
    end

Linha única

  • Nunca deixe espaços em branco soltos. [link]

  • Quando fizer comentários em apenas uma, inclua um espaço entre o fim do código e o começo do comentário. [link]

    # ruim
    result = func(a, b)# Nós podemos alterar b para c
    
    # bom
    result = func(a, b) # Nós podemos alterar b para c
  • Use espaço ao redor de operadores e depois de virgulas, dois pontos, ponto e virgula e ao redor de { e antes de }. [link]

    sum = 1 + 2
    a, b = 1, 2
    1 > 2 ? true : false; puts 'Hi'
    [1, 2, 3].each { |e| puts e }
  • Nunca inclua um espaço antes de uma virgula. [link]

    result = func(a, b)
  • Não inclua espaço dentro de blocos de parâmetros. Inclua um espaço entre parâmetros dentro de um bloco. Inclua um espaço fora do bloco de parâmetro. [link]

    # ruim
    {}.each { | x,  y |puts x }
    
    # bom
    {}.each { |x, y| puts x }
  • Não deixe espaço entre ! e a variável. [link]

    !something
  • Não use espaço depois de (,[ ou antes de ], ). [link]

    some(arg).other
    [1, 2, 3].length
  • Não use espaços quando utilizar textos com variáveis.[link]

    # ruim
    var = "This #{foobar} is interpolated."
    
    # bom
    var = "This #{foobar} is interpolated."
  • Não use espaços em range númericos. [link]

    # ruim
    (0 ... coll).each do |item|
    
    # bom
    (0...coll).each do |item|

Novas linhas

  • Adiciona uma nova linha depois da condição if usada com múltiplas linhas para ajudar diferenciar entre condições e o conteúdo. [link]

    if @reservation_alteration.checkin == @reservation.start_date &&
       @reservation_alteration.checkout == (@reservation.start_date + @reservation.nights)
    
      redirect_to_alteration @reservation_alteration
    end
  • Adiciona uma nova linha depois de condicionais, blocos, switch-case, etc.[link]

    if robot.is_awesome?
      send_robot_present
    end
    
    robot.add_trait(:human_like_intelligence)
  • Não quebre linhas entre diferentes conteúdos (Como classes ou módulos). [link]

    # ruim
    class Foo
    
      def bar
        # conteúdo omitido
      end
    
    end
    
    # bom
    class Foo
      def bar
        # conteúdo omitido
      end
    end
  • Inclua APENAS uma nova linha entre métodos. [link]

    def a
    end
    
    def b
    end
  • Use apenas uma linha como espaçamento entre blocos lógicos. [link]

    def transformorize_car
      car = manufacture(options)
      t = transformer(robot, disguise)
    
      car.after_market_mod!
      t.transform(car)
      car.assign_cool_name!
    
      fleet.add(car)
      car
    end
  • Finalize cada linha com uma nova linha. Não inclua múltiplas linhas no fim do seu arquivo. [link]

Tamanho das linhas

  • Mantenha cada linha do código com um tamanho viável para leitura. A não ser que você tenha uma razão para não fazer, mantenha linhas com menos de 100 caracteres. (rationale) [link]

Comentários

Apesar de ser chato de escrever, comentários são extremamente importantes para manter o código viável para leitura. As regras a seguir descrevem o que você devería comentar e aonde. Mas lembre-se: Enquanto comentários são muito importantes, um bom código é auto-documentável. Definir nomes com sentido para variáveis e métodos são melhores do que usar nomes sem significado que você precise explicar através de comentários.

Enquanto estiver escrevendo seus comentários, escreva para o seu público: O próximo colaborador que irá precisar entender seu código. Seja generoso - o próximo pode ser você!

Google C++ Style Guide

Partes dessa seção foram fortemente emprestados dos guias de estilo do Google C++ e Python.

Comentários de arquivos/classes

Toda definição de classe deve ter um comentário que descreve o que ela faz e como usa-la.

Um arquivo que contém nenhuma ou alguma classe deve ter um comentário no seu ínicio descrevendo o seu conteúdo.

# Conversão automática de uma localização para outra que seja possível, como 
# América para Inglês britanico.
module Translation
  # Classe para conversão entre textos entre localizações similares.
  # Por enquanto, somente conversões entre inglês americano -> Britânico,
  # Canadense, Australiano, Nova Zelandia e variações são possíveis.
  class PrimAndProper
    def initialize
      @converters = { :en => { :"en-AU" => AmericanToAustralian.new,
                               :"en-CA" => AmericanToCanadian.new,
                               :"en-GB" => AmericanToBritish.new,
                               :"en-NZ" => AmericanToKiwi.new,
                             } }
    end

  ...

  # Permite transformar em inglês americano o que é comum entre 
  # todas as outras colonias inglesas.
  class AmericanToColonial
    ...
  end

  # Converte inglês americano para inglês britânico.
  # Para outras variações de colonias inglesas, altere "apartment" para "flat".
  class AmericanToBritish < AmericanToColonial
    ...
  end

Todos os arquivos, incluindo arquivos de configuração e base de dados, devem ter comentários.

# Lista de variações de pronuncias de America-para-Britânico.
#
# Essa lista é feita com
# lib/tasks/list_american_to_british_spelling_variants.rake.
#
# Ela contém palavras com variações de pronuncias:
#   [trave]led/lled, [real]ize/ise, [flav]or/our, [cent]er/re, plus
# and these extras:
#   learned/learnt, practices/practises, airplane/aeroplane, ...

sectarianizes: sectarianises
neutralization: neutralisation
...

Comentários de funções

Toda declaração de função deve ter comentários imediatamente antes de sua definição, que descreva o que a função faz e como deve ser usada. Esse comentário deve ser preferivel ("Abre os arquivos") ao invés de ("Abra o arquivo"); o comentário descreve a função, não diz para a função o que fazer. No geral, esse comentário não descreve como a função funciona. Por outro lado, isso deve ser deixado para comentários dentro do código da função.

Toda função deve mencionar o que são as variáveis de entrada e saída, a não ser que siga todos os critérios abaixo:

  • Não é visivel externamente
  • Muito pequena
  • Óbvio

Você pode usar o formato que desejaar. Em Ruby, duas documentações populares de função são: TomDoc e YARD. Você também pode apenas escrever algo conciso:

# Retorna o localização de fallback para a variável the_locale.
# Se opts[:exclude_default] estiver setado, a localização padrão, 
# que sempre é a ultima posição da lista, será excluída.
#
# Por exemplo:
#   fallbacks_for(:"pt-BR")
#     => [:"pt-BR", :pt, :en]
#   fallbacks_for(:"pt-BR", :exclude_default => true)
#     => [:"pt-BR", :pt]
def fallbacks_for(the_locale, opts = {})
  ...
end

Comentários de blocos e linhas únicas

Outro lugar para ter comentários é em partes importantes do seu código. Se você vai ter que explicar durante o code review, é melhor comentar agora. Operações complicadas merecem algumas linhas de comentários antes do seu começo. Operações não óbvias, ganham comentários no fim da linha.

def fallbacks_for(the_locale, opts = {})
  # dup() produz um array que podemos alterar
  ret = @fallbacks[the_locale].dup

  # Temos que assumir duas coisas aqui:
  # 1) Há apenas uma localização padrão.
  # 2) O localização padrão é um idioma. (Como :pt, e não :"pt-BR".)
  if opts[:exclude_default] &&
      ret.last == default_locale &&
      ret.last != language_from_locale(the_locale)
    ret.pop
  end

  ret
end

Por outro lado, nunca descreva seu comentário. Assuma que a pessoa lendo o código conhece a linguagem (e não o que você está fazendo) melhor que você.

Relacionado: Não use blocos de comentários. Eles não podem ser precedidos por espaços e não são fáceis de encaixar no código se comparado a comentários padrões. [link]

# ruim
=begin
comment line
another comment line
=end

# bom
# comentários aqui
# mais comentários

Pontuação, ortografia e gramática

Preste atenção para pontuação, ortografia e gramática; é mais fácil ler comentários bem escritos do que comentários mal escritos.

Comentários devem ser tão fáceis de ler quanto textos de narrativa, com suas definidas pontuações e letras maiusculas. Em muitos casos, completar as frases é mais fácil de ler do que pedaços de textos. Comentários pequenos, como comentários no fim da linha do código, podem muitas vezes serem menos formais, mas você deve ser consistente com seu padrão.

Apesar que pode ser frustante ter passar por um code review que aponte que você está usando virgula ao invés de ponto e virgula, é muito importante que seu código mantenha um nível alto de clareza e leitura. Pontuação, gramática e ortografia adequadas, ajudam a alcançar o objetivo.

Comentários de pendências

Comentários de pendências no código são temporários, uma solução a curto prazo.

Comentários de pendências devem incluir a palavra TODO, seguido pelo nome completo da pessoa que conhece mais do problema. Dois pontos é opcional. Um comentário explicando qual o requisito dessa tarefa é obrigatório. A principal motivação de ter um comentário de pendência é ser fácil de encontrar a pessoa que está sendo mencionada. Esse comentário não é um compromisso para a pessoa resolver o problema. Tanto que, quando você cria um comentário de pendência, costuma ser o seu nome que será mencionado.

  # ruim
  # TODO(RS): Use o devido nome para essa variável.

  # ruim
  # TODO(drumm3rz4lyfe): Use o devido nome para essa variável.

  # bom
  # TODO(Ringo Starr): Use o devido nome para essa variável.

Códigos comentado

  • NUNCA deixe trechos de códigos comentados no seu código. [link]

Métodos

Definição de métodos

  • Crie def com parênteses quando contém parâmetros. Não use parênteses quando o metodo não aceita parâmetros. [link]

    def metodo
      # sem conteudo
    end
    
    def metodo_com_parametros(arg1, arg2)
      # sem conteudo
    end
  • Não use argumentos de definição de valores. Use argumentos (se disponível - Ruby 2.0 ou posterior) ou um hash de opções.[link]

    # ruim
    def obliterate(things, gently = true, except = [], at = Time.now)
      ...
    end
    
    # bom
    def obliterate(things, gently: true, except: [], at: Time.now)
      ...
    end
    
    # bom
    def obliterate(things, options = {})
      options = {
        :gently => true, # remove com um soft-delete
        :except => [], # não remove esses itens
        :at => Time.now, # define o horário de remoção
      }.merge(options)
    
      ...
    end
  • Evite escrever metodos em uma linha só. Apesar de ser populares, existe algumas pecularidades que fazem dessa abordagem indesejável. [link]

    # ruim
    def too_much; something; something_else; end
    
    # bom
    def some_method
      # conteudo
    end

Chamada de métodos

Uso do parênteses para uma chamada de método:

  • Se o método retorna um valor. [link]

    # ruim
    @current_user = User.find_by_id 1964192
    
    # bom
    @current_user = User.find_by_id(1964192)
  • Se o primeiro argumento do método necessita de parenteses.[link]

    # ruim
    put! (x + y) % len, value
    
    # bom
    put!((x + y) % len, value)
  • Nunca coloque espaço entre o nome do método e parenteses.[link]

    # bad
    f (3 + 2) + 1
    
    # good
    f(3 + 2) + 1
  • Não use parênteses para uma chamada de método se o método aceita argumentos.[link]

    # ruim
    nil?()
    
    # bom
    nil?
  • Se o método não retorna um valor (ou não nos importamos com o retorno), o parênteses é opcional. (Com exceção se o argumento ocupa múltiplas linhas, parênteses podem ajudar a leitura.) [link]

    # bom
    render(:partial => 'foo')
    
    # bom
    render :partial => 'foo'

Em ambos os casos:

  • Se o método aceita um hash de opções como último argumento, não use { } durante a chamada. [link]

    # ruim
    get '/v1/reservations', { :id => 54875 }
    
    # bom
    get '/v1/reservations', :id => 54875

Expressões condicionais

Palavras condicionais

  • Nunca use then com if/unless em múltiplas linhas. [link]

    # ruim
    if some_condition then
      ...
    end
    
    # bom
    if some_condition
      ...
    end
  • Nunca use do com while ou until em múltiplas linhas. [link]

    # ruim
    while x > 5 do
      ...
    end
    
    until x > 5 do
      ...
    end
    
    # bom
    while x > 5
      ...
    end
    
    until x > 5
      ...
    end
  • As palavras and, or, e not estão banidas. Não as use. Ao invés, sempre use &&, ||, e !. [link]

  • Modificar if/unless é válido quando o conteúdo é pequeno, a condição é simples e tudo se encaixa em uma linha. Caso contrário, evite modificar if/unless. [link]

    # ruim - isso não se encaixa em uma linha
    add_trebuchet_experiments_on_page(request_opts[:trebuchet_experiments_on_page]) if request_opts[:trebuchet_experiments_on_page] && !request_opts[:trebuchet_experiments_on_page].empty?
    
    # okay
    if request_opts[:trebuchet_experiments_on_page] &&
         !request_opts[:trebuchet_experiments_on_page].empty?
    
      add_trebuchet_experiments_on_page(request_opts[:trebuchet_experiments_on_page])
    end
    
    # ruim - isso é complexo e merece multiplas linhas e comentários
    parts[i] = part.to_i(INTEGER_BASE) if !part.nil? && [0, 2, 3].include?(i)
    
    # okay
    return if reconciled?
  • Nunca use unless com else. Reescreva com o uso de if.[link]

    # ruim
    unless success?
      puts 'failure'
    else
      puts 'success'
    end
    
    # bom
    if success?
      puts 'success'
    else
      puts 'failure'
    end
  • Evite usar `unless``com múltiplas condições.[link]

      # bad
      unless foo? && bar?
        ...
      end
    
      # okay
      if !(foo? && bar?)
        ...
      end
  • Evite usar unless com operadores de comparação. Use if nesse caso.[link]

      # ruim
      unless x == 10
        ...
      end
      
      # bom
      if x != 10
        ...
      end
      
      # ruim
      unless x < 10
        ...
      end
      
      # bom
      if x >= 10
        ...
      end
      
      # ok
      unless x === 10
        ...
      end
  • Não use parênteses nas condições em if/unless/while. [link]

    # ruim
    if (x > 10)
      ...
    end
    
    # bom
    if x > 10
      ...
    end

Operador ternário

  • Evite utilizar operadores ternários (?:) a não ser que a condição seja extremamente trivial. Por outro lado, use o operador ternário (?:) ao invés de if/then/else/end para condições de linhas únicas.[link]

    # ruim
    result = if some_condition then something else something_else end
    
    # bom
    result = some_condition ? something : something_else
  • Use uma expressão por bloco em operadores ternários. Isso significa que operadores ternários não devem ser longos. Prefira if/else nesses casos.[link]

    # ruim
    some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else
    
    # bom
    if some_condition
      nested_condition ? nested_something : nested_something_else
    else
      something_else
    end
  • Evite multiplas condições em operadores ternários. Operadores ternários são melhor usados em condições simples. [link]

  • Evite multiplas linhas com o operador ternário ?:, nesses casos, use if/then/else/end. [link]

    # ruim
    some_really_long_condition_that_might_make_you_want_to_split_lines ?
      something : something_else
    
    # bom
    if some_really_long_condition_that_might_make_you_want_to_split_lines
      something
    else
      something_else
    end

Condições longas

  • Evite utilizar condições muito longas para controle do fluxo. (Leia mais sobre isso.) [link]

    Prefira utilizar uma clause guard quando pode receber um dado inválida. Uma guard clause é uma condição no topo de uma função que retorna assim que possível.

    Os principios dessa clausula, são:

    • Retorna assim que sua função sabe que não pode fazer mais nada.
    • Diminui o tamanho do código. Isso faz o código ser mais fácil de ler e necessita menos atenção em relação a blocos if/else.
    • O fluxo principal deve ser o último a ser validado.
    # ruim
    def compute
      server = find_server
      if server
        client = server.client
        if client
          request = client.make_request
          if request
             process_request(request)
          end
        end
      end
    end
    
    # bom
    def compute
      server = find_server
      return unless server
      client = server.client
      return unless client
      request = client.make_request
      return unless request
      process_request(request)  
    end

    Durante os laços, prefira utilizar next ao invés de blocos condicionais.

    # ruim
    [0, 1, 2, 3].each do |item|
      if item > 1
        puts item
      end
    end
    
    # bom
    [0, 1, 2, 3].each do |item|
      next unless item > 1
      puts item
    end

    Veja também a seção "Guard Clause", p68-70 in Beck, Kent. Implementation Patterns. Upper Saddle River: Addison-Wesley, 2008, que inspirou o conteúdo acima.

Sintaxe

  • Nunca use for, a não ser que você saiba o por que. Utilize outros interatores. for é implementado da mesma forma que o each, porém com uma divergência, for não introduz um novo escopo e variáveis definidas dentro do seu bloco serão vistas fora dele.[link]

    arr = [1, 2, 3]
    
    # ruim
    for elem in arr do
      puts elem
    end
    
    # bom
    arr.each { |elem| puts elem }
  • Prefira {...} ao invés de do...end para blocos únicos. Evite usar {...} para blocos de múltiplas linhas (Chaves com multiplas linhas sempre são mal vistos). Sempre use do...end para "controle do fluxo" e "definição de métodos" (Exemplo: em Rakefiles e alguns DSLs). Evite do...end com encadeamento.[link]

    names = ["Bozhidar", "Steve", "Sarah"]
    
    # bom
    names.each { |name| puts name }
    
    # ruim
    names.each do |name| puts name end
    
    # bom
    names.each do |name|
      puts name
      puts 'yay!'
    end
    
    # ruim
    names.each { |name|
      puts name
      puts 'yay!'
    }
    
    # bom
    names.select { |name| name.start_with?("S") }.map { |name| name.upcase }
    
    # ruim
    names.select do |name|
      name.start_with?("S")
    end.map { |name| name.upcase }

    Algumas pessoas irão argumentar que chaves com multiplas linhas são bem vistas com o uso do {...}, mas elas devem perguntar a si mesmo se esse código é de fácil leitura e também se o conteúdo pode ser extraído em algum método.

  • Sempre que possível, use a forma abreviada dos operadores.[link]

    # ruim
    x = x + y
    x = x * y
    x = x**y
    x = x / y
    x = x || y
    x = x && y
    
    # bom
    x += y
    x *= y
    x **= y
    x /= y
    x ||= y
    x &&= y
  • Evite usar ponto e vírgula, com exceção de declarações de classes em uma linha. Quando for apropriado utilizar ponto e vírgula, utilize apenas quando o bloco lógico estiver terminado: Não deve ter espaço antes do ponto e vírgula.[link]

    # ruim
    puts 'foobar'; # Desnecessário uso do ponto e vírgula
    puts 'foo'; puts 'bar' # Duas expressões na mesma linha.
    
    # bom
    puts 'foobar'
    
    puts 'foo'
    puts 'bar'
    
    puts 'foo', 'bar' # Isso se aplica a puts em particular
  • Use :: apenas para referenciar constantes (Isso inclui classes e módulos) e construtores (Array() ou Nakogiri::HTML()). Não use :: para inicialização de métodos. [link]

    # ruim
    SomeClass::some_method
    some_object::some_method
    
    # bom
    SomeClass.some_method
    some_object.some_method
    SomeModule::SomeClass::SOME_CONST
    SomeModule::SomeClass()
  • Evite usar return quando não for necessário. [link]

    # ruim
    def some_method(some_arr)
      return some_arr.size
    end
    
    # bom
    def some_method(some_arr)
      some_arr.size
    end
  • Não faça condições com o uso de =.[link]

    # ruim - deixa explicito o uso da condição
    if (v = array.grep(/foo/))
      ...
    end
    
    # ruim
    if v = array.grep(/foo/)
      ...
    end
    
    # bom
    v = array.grep(/foo/)
    if v
      ...
    end
  • Está liberado o uso de ||= na inicialização das variáveis. [link]

    # Define o nome para Bozhidar, somente se ele for nil ou false
    name ||= 'Bozhidar'
  • Não use ||= para inicializar variáveis do tipo boolean, ao invés disso, considere o que deve ser feito caso a variável esteja definida como false)[link]

    # ruim - vai definir como true, caso seja false
    enabled ||= true
    
    # bom
    enabled = true if enabled.nil?
  • Use .call explicitamente quando for usar lambdas. [link]

    # ruim
    lambda.(x, y)
    
    # bom
    lambda.call(x, y)
  • Evite usar o estilo Perl para variáveis especiais (como, $0-9, $, etc. ). Seu uso é meio enigmático e desincorajado. Prefira formas mais completas, como: $PROGRAM_NAME.[link]

  • Quando um metodo contém apenas um argumento, e seu conteúdo é apenas uma leitura ou chamada de outro método sem argumentos, use a abreviação &:. [link]

    # ruim
    bluths.map { |bluth| bluth.occupation }
    bluths.select { |bluth| bluth.blue_self? }
    
    # bom
    bluths.map(&:occupation)
    bluths.select(&:blue_self?)
  • Prefira utilizar some_method do que self.some_method quando usar outro método da mesma instancia.[link]

    # ruim
    def end_date
      self.start_date + self.nights
    end
    
    # bom
    def end_date
      start_date + nights
    end

    Nos casos de uso a seguir, self. é obrigatório na linguagem e uma boa prática:

    1. Quando está definindo um método de uma classe: def self.some_method.
    2. O lado esquerdo da igualdade, quando chamar o método de atribuição, incluindo atribuição de um atributo e self sendo uma model do ActiveRecord: self.guest = user.
    3. Referenciando a instance da classe atual: self.class.
  • Quando for definir algum objeto de um tipo que pode ser alterado, mas com a intenção de ser uma constante, não esqueça de utilizar o método freeze. Exemplos comuns são: strings, arrays, e hashes. (Leia mais sobre isso.)[link]

    Isso acontece por que no Ruby, constantes não tem um tipo único. Utilizando freeze nós garantimos que o tipo não será mudado e portanto será uma constante de fato, qualquer tentativa de mudança irá gerar uma exceção.

    # ruim
    class Color
      RED = 'red'
      BLUE = 'blue'
      GREEN = 'green'
      
      ALL_COLORS = [
        RED,
        BLUE,
        GREEN,
      ]
    
      COLOR_TO_RGB = {
        RED => 0xFF0000,
        BLUE => 0x0000FF,
        GREEN => 0x00FF00,
      }
    end
    
    # bom    
    class Color
      RED = 'red'.freeze
      BLUE = 'blue'.freeze
      GREEN = 'green'.freeze
    
      ALL_COLORS = [
        RED,
        BLUE,
        GREEN,
      ].freeze
    
      COLOR_TO_RGB = {
        RED => 0xFF0000,
        BLUE => 0x0000FF,
        GREEN => 0x00FF00,
      }.freeze
    end

Nomenclatura

  • Para métodos e variáveis, utilize o padrão snake_case. [link]

  • Para classes ou módulos, utilize o padrão CamelCase. (Mantenha singlas, como: HTTP, RFC, XML UPPERCASE.) [link]

  • Para outras constantes, utilize o padrão: SCREAMING_SNAKE_CASE.[link]

  • Nome dos predicados dos métodos (Método que retorna um valor boolean) devem terminar com ponto de interrogação (exemplo: Array#empty?).[link]

  • Nome de métodos que são potenciallmente "perigosos" (exemplo: métodos que modificam self ou argumentos, exit!, etc.) devem terminar com um ponto de exclamação. Um bang method só deve existir caso haja um non-bang correspondente. (Leia mais sobre isso.) [link]

  • Nomeie variáveis temporarias com _. [link]

    version = '3.2.1'
    major_version, minor_version, _ = version.split('.')

Classes

  • Evite usar (@@) em classes devido ao seu comportamento desagradável em heranças. [link]

    class Parent
      @@class_var = 'parent'
    
      def self.print_class_var
        puts @@class_var
      end
    end
    
    class Child < Parent
      @@class_var = 'child'
    end
    
    Parent.print_class_var # => Irá imprimir "child"

    Como você pode ver, todas as classes em um sistema de hierarquia irão compartilhar uma variável em comum. As variáveis da classe instanciada são preferíveis a variáveis da classe atual.

  • Use def self.method para definir métodos com o padrão singleton. Isso faz com que o método seja mais resistente a mudanças. [link]

    class TestClass
      # ruim
      def TestClass.some_method
        ...
      end
    
      # bom
      def self.some_other_method
        ...
      end
  • Evite class << self, exceto quando necessário. exemplo: Atributos auxiliares. [link]

    class TestClass
      # ruim
      class << self
        def first_method
          ...
        end
    
        def second_method_etc
          ...
        end
      end
    
      # bom
      class << self
        attr_accessor :per_page
        alias_method :nwo, :find_by_name_with_owner
      end
    
      def self.first_method
        ...
      end
    
      def self.second_method_etc
        ...
      end
    end
  • Indente métodos public, protected, e private tanto quanto a definição do método é aplicada. Deixe uma linha vazia antes e depois.[link]

    class SomeClass
      def public_method
        # ...
      end
    
      private
    
      def private_method
        # ...
      end
    end

Exceções

  • Não use exceções para controle de fluxo. [link]

    # ruim
    begin
      n / d
    rescue ZeroDivisionError
      puts "Cannot divide by 0!"
    end
    
    # bom
    if d.zero?
      puts "Cannot divide by 0!"
    else
      n / d
    end
  • Evite capturar a classe Exception. [link]

    # ruim
    begin
      # uma exceção acontece aqui
    rescue Exception
      # lidando com a exceção
    end
    
    # bom
    begin
      # uma exceção acontece aqui
    rescue StandardError
      # lidando com a exceção
    end
    
    # aceitável
    begin
      # uma exceção acontece aqui
    rescue
      # lidando com a exceção
    end
  • Não declare a exceção RuntimeError com dois argumentos. Prefira erros de sub-classes e erros explicítos de criação.[link]

    # ruim
    raise RuntimeError, 'message'
    
    # melhor - RuntimeError está implicito aqui
    raise 'message'
    
    # ótimo
    class MyExplicitError < RuntimeError; end
    raise MyExplicitError
  • Ao invés de uma instancia para tratar a exceção,'prefira fornecer uma classe e uma mensagem para exceção em dois argumentos separados com raise. [link]

    # ruim
    raise SomeException.new('message')
    # Note que não há uma forma de fazer: `raise SomeException.new('message'), backtrace`.
    
    # bom
    raise SomeException, 'message'
    # Consistente com `raise SomeException, 'message', backtrace`.
  • Evite usar rescue na sua forma modificada. [link]

    # ruim
    read_file rescue handle_error($!)
    
    # bom
    begin
      read_file
    rescue Errno:ENOENT => ex
      handle_error(ex)
    end

Coleções

  • Prefira o método map ao collect.[link]

  • Prefira o método detect ao invés de find. find tem um uso ambiguo por causa do método do ActiveRecord's find - Utilizar detect deixa claro que você está trabalhando com uma coleção em Ruby, não um objeto ActiveRecord. [link]

  • Prefira o método reduce ao invés de inject. [link]

  • Prefira o método size ao invés de length ou count por motivos performáticos.[link]

  • Prefira criar array e hash de forma literal, a não ser que você passe parâmetros para seus construtores. [link]

    # ruim
    arr = Array.new
    hash = Hash.new
    
    # bom
    arr = []
    hash = {}
    
    # bom por que o construtor necessita de parâmetros
    x = Hash.new { |h, k| h[k] = {} }
  • Prefira Array#join do que Array#* por clareza. [link]

    # ruim
    %w(one two three) * ', '
    # => 'one, two, three'
    
    # bom
    %w(one two three).join(', ')
    # => 'one, two, three'
  • Nas hash key, use simbolos ao invés de strings. [link]

    # ruim
    hash = { 'one' => 1, 'two' => 2, 'three' => 3 }
    
    # bom
    hash = { :one => 1, :two => 2, :three => 3 }
  • Use simbolos ao invés de strings sempre que possível.[link]

    # ruim
    :"symbol"
    
    # bom
    :symbol
  • Use Hash#key? ao invés de Hash#has_key? e Hash#value? ao invés de Hash#has_value?. De acordo com Matz, as formas longas são consideradas desatualizadas. [link]

    # ruim
    hash.has_key?(:test)
    hash.has_value?(value)
    
    # bom
    hash.key?(:test)
    hash.value?(value)
  • Declare hashes em multiplas linhas quando for deixar o código mais fácil para leitura e utilize virgula para garantir que o parâmetro mudou. [link]

    hash = {
      :protocol => 'https',
      :only_path => false,
      :controller => :users,
      :action => :set_password,
      :redirect => @redirect_url,
      :secret => @secret,
    }
  • Use vírgula em Array que abrange mais de uma linha [link]

    # bom
    array = [1, 2, 3]
    
    # bom
    array = [
      "car",
      "bear",
      "plane",
      "zoo",
    ]

Textos

  • Prefira interpolar strings ao invés de concatena-las: [link]

    # ruim
    email_with_name = user.name + ' <' + user.email + '>'
    
    # bom
    email_with_name = "#{user.name} <#{user.email}>"

    Além disso, mantenha em mente que Ruby 1.9 liberou a interpolação. Digamos que você está usando cache keys como este:

    CACHE_KEY = '_store'
    
    cache.write(@user.id + CACHE_KEY)

    Prefira interpolar strings ao invés de concatena-las

    CACHE_KEY = '%d_store'
    
    cache.write(CACHE_KEY % @user.id)
  • Evite usar String#+ quando você precisa criar grandes pedaços de dado. Ao invés disso, use String#<<. Concatenação altera o conteúdo da string e é sempre mais rápido que String#+, que cria um novo objeto de strings.[link]

    # bom e rápido
    html = ''
    html << '<h1>Page title</h1>'
    
    paragraphs.each do |paragraph|
      html << "<p>#{paragraph}</p>"
    end
  • Para concatenar multiplas linhas de string use \ no fim das linhas ao invés de + ou << [link]

    # ruim
    "Some string is really long and " +
      "spans multiple lines."
    
    "Some string is really long and " <<
      "spans multiple lines."
    
    # bom
    "Some string is really long and " \
      "spans multiple lines."

Expressões regulares

  • Evite usar $1-9 por que pode ser difícil de localizar o seu conteṕudo. Prefira usar variáveis. [link]

    # ruim
    /(regexp)/ =~ string
    ...
    process $1
    
    # bom
    /(?<meaningful_var>regexp)/ =~ string
    ...
    process meaningful_var
  • Tenha cuidado com ^ e $ quando eles se encaixam no começo o no fim das linhas e não fim das strings. Se você quer localizar todo o texto, use: \A e \z.[link]

    string = "some injection\nusername"
    string[/^username$/]   # encontrou
    string[/\Ausername\z/] # não encontrou
  • Para regex complexos, use x. Isso faz com que seja mais fácil de ler e você pode adicionar alguns comentários. Só tenha cuidado com os espaços que podem ser ignorados. [link]

    regexp = %r{
      start         # algum texto
      \s            # caracter de espaço em branco
      (group)       # primeiro grupo
      (?:alt1|alt2) # alguma alternativa
      end
    }x

Notação de porcentagem

  • Prefira parenteses ao invés de chaves, colchetes ou traço quando utilizar % para consistencia e pelo comportamento do % ser mais próximos da chamada de métodos.[link]

    # ruim
    %w[date locale]
    %w{date locale}
    %w|date locale|
    
    # bom
    %w(date locale)
  • Use %w livremente.[link]

    STATES = %w(draft open closed)
  • Use %() para textos de linhas únicas que necessite tanto interpolação quando aspas duplas. Para multiplas linhas, prefira heredoc.[link]

    # ruim - sem necessidade de interpolação
    %(<div class="text">Some text</div>)
    # devería ser '<div class="text">Some text</div>'
    
    # ruim - sem aspas duplas
    %(This is #{quality} style)
    # Devería ser "This is #{quality} style"
    
    # ruim - multiplas linhas
    %(<div>\n<span class="big">#{exclamation}</span>\n</div>)
    # devería ser em heredoc.
    
    # bom - necessita de interpolação, tem aspas duplas e está em linha única.
    %(<tr><td class="name">#{name}</td>)
  • Use %r somente para expressões regulares localizando mais de um caracter '/'.[link]

    # ruim
    %r(\s+)
    
    # ainda ruim
    %r(^/(.*)$)
    # devería ser /^\/(.*)$/
    
    # bom
    %r(^/blog/2011/(.*)$)
  • Evite o uso de %x, a não ser que você esteja invocando um comando com aspas invertidas (O que é indesejável). [link]

    # ruim
    date = %x(date)
    
    # bom
    date = `date`
    echo = %x(echo `date`)

Rails

  • Quando retornar imediatamente depois de render ou redirect_to, coloque return na próxima linha, não na mesma. [link]

    # ruim
    render :text => 'Howdy' and return
    
    # bom
    render :text => 'Howdy'
    return
    
    # ainda ruim
    render :text => 'Howdy' and return if foo.present?
    
    # bom
    if foo.present?
      render :text => 'Howdy'
      return
    end

Escopo

  • Quando definir model no ActiveRecord, encaixe a relação em lambda. Sem isso, a relação força o banco de dados a estabelezer a conexão quando a classe é carregada, não no ínicio. [link]

    # ruim
    scope :foo, where(:bar => 1)
    
    # bom
    scope :foo, -> { where(:bar => 1) }

Seja consistente.

Se você está editando o código, invista um tempo olhando-o e determinando seu estio. Se ele usa espaço ao redor de todos os operadores aritméticos, você deve fazer também. Se os comentários contém pequenas caixas cercadas de barras, faça seus comentários da mesma forma.

A motivação de ter um guia de estilo é ter um vocabulário comum para que as pessoas possam se concentrar no que está sendo tido e não como está sendo tido. Nós apresentamos um estilo global para que as pessoas saibam esse vocabulário, mas vocabulários regionais também são importantes. Se você fizer um código completamente diferente dos códigos ao seu redor, irá causar intriga entre os leitores que forem ler ele. Evite isso.

Google C++ Style Guide

Tradução

Este guia também está disponível em outros idiomas:

About

Versão pt-BR do guia de estilo do Airbnb - https://github.com/airbnb/ruby

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published