Exemplos são importantes.
-- Oficial Alex J. Murphy / RoboCop
O objetivo desse guia é apresentar um conjunto de boas práticas e orientações de estilo para o desenvolvimento em Ruby on Rails 4. Esse guia é complementar ao já existente guia de estilo de programação em Ruby, criado pela comunidade.
Alguns dos conselhos apresentados aqui só se aplicam ao Rails 4.0+.
Você pode gerar uma cópia desse guia em PDF ou HTML usando o Transmuter.
Traduções desse guia estão disponíveis nos seguintes idiomas:
- Chinês simplificado
- Chinês tradicional
- Alemão
- Japonês
- Russo
- Turco
- Coreano
- Português brasileiro
Esse guia de estilo do Rails recomenda boas práticas para que programadores Rails reais escrevam código que possa ser mantido por outros programadores Rails reais. Se um guia de estilo reflete o uso real, ele é usado; porém, um guia de estilo que se apega a um ideal que foi rejeitado pelas pessoas que ele deveria ajudar fica sob risco de cair em desuso – não importa o quão bom ele seja.
Esse guia está dividido em várias seções de regras relacionadas. Eu tentei incluir o motivo por trás das regras (se ele foi omitido, é porque eu assumi que é bastante óbvio).
Eu não inventei essas regras do nada - elas são baseadas principalmente na minha extensa carreira como engenheiro de software profissional, feedback e sugestões de membros da comunidade Rails e vários outros recursos conceituados de programação Rails.
- Configuração
- Roteamento
- Controllers
- Models
- Migrações
- Views
- Internacionalização
- Assets
- Mailers
- Hora
- Bundler
- Gems problemáticas
- Gerenciando processos
-
Coloque suas rotinas de inicialização em
config/initializers
. Os arquivos dessa pasta são executados quando a aplicação é iniciada. [link] -
Deixe o código de inicialização de cada gem num arquivo separado com o mesmo nome da gem, por exemplo,
carrierwave.rb
,active_admin.rb
, etc. [link] -
Ajuste adequadamente as configurações para os ambientes de desenvolvimento, teste e produção (nos respectivos arquivos em
config/environments/
). [link]-
Marque assets adicionais para serem pré-compilados (se houver):
# config/environments/production.rb # Precompile additional assets (application.js, application.css, #and all non-JS/CSS are already added) config.assets.precompile += %w( rails_admin/rails_admin.css rails_admin/rails_admin.js )
-
-
Deixe as configurações que se aplicam a todos os ambientes no arquivo
config/application.rb
. [link] -
Crie um ambiente adicional chamado
staging
que seja muito parecido com o ambienteproduction
. [link] -
Deixe quaisquer configurações adicionais em arquivos YAML na pasta
config/
. [link]Desde o Rails 4.2, é muito simples carregar arquivos de configuração YAML com o novo método
config_for
:Rails::Application.config_for(:yaml_file)
-
Quando você precisar adicionar mais ações a um recurso RESTful (você precisa mesmo delas?) use as rotas
member
ecollection
. [link]# ruim get 'subscriptions/:id/unsubscribe' resources :subscriptions # bom resources :subscriptions do get 'unsubscribe', on: :member end # ruim get 'photos/search' resources :photos # bom resources :photos do get 'search', on: :collection end
-
Se você quiser definir múltiplas rotas
member/collection
, use a sintaxe alternativa que recebe um bloco. [link]resources :subscriptions do member do get 'unsubscribe' # mais rotas end end resources :photos do collection do get 'search' # mais rotas end end
-
Use rotas aninhadas para expressar melhor o relacionamento entre models do ActiveRecord. [link]
class Post < ActiveRecord::Base has_many :comments end class Comments < ActiveRecord::Base belongs_to :post end # routes.rb resources :posts do resources :comments end
-
Se você precisar aninhar rotas com mais de 1 nível de profundidade, use a opção
shallow: true
. Isso vai poupar seu usuário de urls longasposts/1/comments/5/versions/7/edit
e também vai te poupar de helpers longosedit_post_comment_version
.resources :posts, shallow: true do resources :comments do resources :versions end end
-
Defina rotas dentro de um
namespace
para agrupar ações relacionadas. [link]namespace :admin do # Direciona /admin/products/* para Admin::ProductsController # (app/controllers/admin/products_controller.rb) resources :products end
-
Nunca use a antiga rota 'wild'. Essa rota vai deixar todas as ações em todos os controllers acessíveis via requisições GET. [link]
# muito ruim match ':controller(/:action(/:id(.:format)))'
-
Não use
match
para definir nenhuma rota, a não ser que você precise mapear múltiplos métodos de requisição dentre[:get, :post, :patch, :put, :delete]
para uma mesma ação através da opção:via
. [link]
-
Mantenha os controllers enxutos - eles só devem obter dados para a camada de view e não devem conter nenhuma lógica de negócio (toda a lógica de negócio deve ficar naturalmente no model). [link]
-
Cada ação de um controller deve (idealmente) invocar apenas um método além de um
find
ounew
inicial. [link] -
Não compartilhe mais que duas variáveis de instância entre um controller e uma view. [link]
-
Introduza classes não-ActiveRecord livremente. [link]
-
Dê nomes significativos (porém curtos) para os models, sem abreviações. [link]
-
Se você precisar de objetos do model que suportem comportamento do ActiveRecord, mas sem a funcionalidade de banco de dados, use a gem [ActiveAttr(https://github.com/cgriego/active_attr)]. [link]
class Message include ActiveAttr::Model attribute :name attribute :email attribute :content attribute :priority attr_accessible :name, :email, :content validates :name, presence: true validates :email, format: { with: /\A[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}\z/i } validates :content, length: { maximum: 500 } end
Para um exemplo mais completo, veja o RailsCast sobre isso.
-
Evite alterar os padrões do ActiveRecord (nomes de tabelas, chave primária, etc) a não ser que você tenha uma razão muito boa (como um banco de dados que você não controla). [link]
# ruim - não faça isso se você puder modificar o schema class Transaction < ActiveRecord::Base self.table_name = 'order' ... end
-
Agrupe métodos no estilo macro (
has_many
,validates
, etc) no topo da definição da classe. [link]class User < ActiveRecord::Base # deixe a default_scope primeiro (se houver) default_scope { where(active: true) } # depois vêm as constantes COLORS = %w(red green blue) # depois nós colocamos macros relacionadas a attr attr_accessor :formatted_date_of_birth attr_accessible :login, :first_name, :last_name, :email, :password # seguidas de macros de associações belongs_to :country has_many :authentications, dependent: :destroy # e macros de validação validates :email, presence: true validates :username, presence: true validates :username, uniqueness: { case_sensitive: false } validates :username, format: { with: /\A[A-Za-z][A-Za-z0-9._-]{2,19}\z/ } validates :password, format: { with: /\A\S{8,128}\z/, allow_nil: true} # depois nós colocamos os callbacks before_save :cook before_save :update_username_lower # outras macros (como as do devise) devem ser colocadas depois dos callbacks ... end
-
Prefira
has_many :through
ahas_and_belongs_to_many
. Usandohas_many :through
você pode adicionar atributos e validações adicionais no modelo de junção. [link]# não muito bom - usando has_and_belongs_to_many class User < ActiveRecord::Base has_and_belongs_to_many :groups end class Group < ActiveRecord::Base has_and_belongs_to_many :users end # jeito melhor - usando has_many :through class User < ActiveRecord::Base has_many :memberships has_many :groups, through: :memberships end class Membership < ActiveRecord::Base belongs_to :user belongs_to :group end class Group < ActiveRecord::Base has_many :memberships has_many :users, through: :memberships end
-
Prefira
self[:attribute]
aread_attribute(:attribute)
. [link]# ruim def amount read_attribute(:amount) * 100 end # bom def amount self[:amount] * 100 end
-
Prefira
self[:attribute] = value
awrite_attribute(:attribute, value)
. [link]# ruim def amount write_attribute(:amount, 100) end # bom def amount self[:amount] = 100 end
-
Sempre use as novas validações "sexy". [link]
# ruim validates_presence_of :email validates_length_of :email, maximum: 100 # bom validates :email, presence: true, length: { maximum: 100 }
-
Quando uma validação personalizada é usada mais que uma vez, ou a validação é um mapeamento de expressões regulares, crie uma nova classe de validação. [link]
# ruim class Person validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i } end # bom class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) record.errors[attribute] << (options[:message] || 'is not a valid email') unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i end end class Person validates :email, email: true end
-
Deixe os seus validadores em
app/validators/
. [link] -
Considere extrair seus validadores próprios para uma gem compartilhada se você estiver mantendo vários apps relacionados ou se os validadores forem genéricos o suficiente. [link]
-
Use escopos nomeados livremente. [link]
class User < ActiveRecord::Base scope :active, -> { where(active: true) } scope :inactive, -> { where(active: false) } scope :with_orders, -> { joins(:orders).select('distinct(users.id)') } end
-
Quando um escopo nomeado definido com um lambda e parâmetros ficar muito complicado, é preferível fazer ao invés dele um método de classe que sirva para o mesmo propósito e retorne uma instância de
ActiveRecord::Relation
. Você pode até definir escopos mais simples desse jeito.
[link]
class User < ActiveRecord::Base
def self.with_orders
joins(:orders).select('distinct(users.id)')
end
end
Perceba que esse estilo de escopo não pode ser colocado em chain do mesmo jeito que escopos nomeados. Por exemplo:
# não dá pra fazer chain
class User < ActiveRecord::Base
def User.old
where('age > ?', 80)
end
def User.heavy
where('weight > ?', 200)
end
end
Nesse estilo, tanto old
como heavy
funcionam separadamente, mas você não pode chamar User.old.heavy
; para fazer chain nesses escopos, use:
# dá pra fazer chain
class User < ActiveRecord::Base
scope :old, -> { where('age > 60') }
scope :heavy, -> { where('weight > 200') }
end
-
Tome cuidado com o comportamento do método
update_attribute
. Ele não executa as validações do model (diferente deupdate_attributes
) e pode facilmente corromper o estado do model. [link] -
Use URLs amigáveis. Mostre algum atributo descritivo do model na URL ao invés do
id
dele. Há várias formas de conseguir isso: [link]-
Sobrescrever o método
to_param
do model. Esse método é usado pelo Rails para construir a URL do objeto. A implementação padrão retorna oid
do registro como String. Ela poderia ser sobrescrita para incluir outro atributo mais legível.class Person def to_param "#{id} #{name}".parameterize end end
Para deixar esse valor num formato compatível com URLs, você deve chamar
parameterize
na string. Oid
do objeto precisa estar no começo para que ele possa ser encontrado pelo métodofind
do ActiveRecord.-
Usar a gem
friendly_id
. Ela permite a criação de URLs legíveis através do uso de algum atributo descritivo do model ao invés do seuid
.class Person extend FriendlyId friendly_id :name, use: :slugged end
Veja a documentação da gem para mais informações sobre como usá-la.
-
-
Use
find_each
para iterar sobre uma coleção de objetos AR. Iterar sobre uma coleção de registros do banco de dados (usando o métodoall
, por exemplo) é muito ineficiente, porque o AR tentará instanciar todos os objetos de uma vez. Nesse caso, métodos de processamento em batch permitem que você trabalhe com os registros em batches, dessa forma reduzindo muito o consumo de memória. [link]# ruim Person.all.each do |person| person.do_awesome_stuff end Person.where('age > 21').each do |person| person.party_all_night! end # bom Person.find_each do |person| person.do_awesome_stuff end Person.where('age > 21').find_each do |person| person.party_all_night! end
-
Como o Rails cria callbacks para associações dependentes, sempre chame callbacks
before_destroy
que realizem validações comprepend: true
. [link]# ruim (roles vão ser deletados automaticamente mesmo que super_admin? seja true) has_many :roles, dependent: :destroy before_destroy :ensure_deletable def ensure_deletable fail "Cannot delete super admin." if super_admin? end # bom has_many :roles, dependent: :destroy before_destroy :ensure_deletable, prepend: true def ensure_deletable fail "Cannot delete super admin." if super_admin? end
-
Evite usar interpolação de strings em queries, porque isso deixa seu código suscetível a SQL injection. [link]
# ruim - param será interpolado sem ser escapado Client.where("orders_count = #{params[:orders]}") # bom - param será escapado adequadamente Client.where('orders_count = ?', params[:orders])
-
Considere usar placeholders nomeados ao invés de placeholders posicionais quando você tiver mais de 1 placeholder na sua query. [link]
# mais ou menos Client.where( 'created_at >= ? AND created_at <= ?', params[:start_date], params[:end_date] ) # bom Client.where( 'created_at >= :start_date AND created_at <= :end_date', start_date: params[:start_date], end_date: params[:end_date] )
-
Prefira usar
find
ao invés dewhere
quando você precisar obter um único registro pelo seu id. [link]# ruim User.where(id: id).take # bom User.find(id)
-
Prefira usar
find_by
ao invés dewhere
quando você precisar obter um único registro por alguns atributos. [link]# ruim User.where(first_name: 'Bruce', last_name: 'Wayne').first # bom User.find_by(first_name: 'Bruce', last_name: 'Wayne')
-
Use
find_each
quando você precisar processar muitos registros. [link]# ruim - carrega todos os registros de uma vez # Isso é muito ineficiente caso a tabela de users tenha milhares de linhas. User.all.each do |user| NewsMailer.weekly(user).deliver_now end # bom - registros são obtidos em batches User.find_each do |user| NewsMailer.weekly(user).deliver_now end
-
Prefira usar
where.not
ao invés de SQL. [link]# ruim User.where("id != ?", id) # bom User.where.not(id: id)
-
Quando especificar uma query explícita num método como
find_by_sql
, use heredocs comsquish
. Isso permite que você formate o SQL de forma legível com quebras de linha e identações, ao mesmo tempo em que suporta o realce de sintaxe em diversas ferramentas (inclusive o GitHub, Atom e RubyMine). [link]User.find_by_sql(<<SQL.squish) SELECT users.id, accounts.plan FROM users INNER JOIN accounts ON accounts.user_id = users.id # outras complexidades... SQL
O método
String#squish
remove a identação e quebras de linha; dessa forma o log do seu servidor vai mostrar uma string fluida de SQL ao invés de algo assim:SELECT\n users.id, accounts.plan\n FROM\n users\n INNER JOIN\n acounts\n ON\n accounts.user_id = users.id
-
Mantenha o arquivo
schema.rb
(oustructure.sql
) sob controle de versão. [link] -
Use
rake db:schema:load
ao invés derake db:migrate
para inicializar um banco de dados vazio. [link] -
Imponha valores default nas próprias migrações ao invés de fazê-lo na camada de aplicação. [link]
# ruim - valor default definido na aplicação def amount self[:amount] or 0 end
Apesar de muitos desenvolvedores sugerirem a definição de padrões de tabelas apenas no Rails, isso é uma abordagem extremamente frágil, que deixa seus dados vulneráveis a diversos bugs da aplicação. E há de se considerar o fato de que a maioria dos apps não-triviais compartilha um banco de dados com outras aplicações, então impor a integridade dos dados a partir do app Rails é impossível.
-
Imponha restrições de chave estrangeira. A partir da versão 4.2, o ActiveRecord suporta restrições de chave estrangeira nativamente. [link]
-
When writing constructive migrations (adding tables or columns), use the
change
method instead ofup
anddown
methods. Quando estiver escrevendo migrações construtivas (acrescentando tabelas ou colunas), use o métodochange
ao invés dos métodosup
edown
. [link]# o jeito antigo class AddNameToPeople < ActiveRecord::Migration def up add_column :people, :name, :string end def down remove_column :people, :name end end # o novo jeito preferencial class AddNameToPeople < ActiveRecord::Migration def change add_column :people, :name, :string end end
-
Não use classes do model em migrações. As classes do model estão constantemente evoluindo e em algum ponto do futuro as migrações que funcionavam podem parar de funcionar, por causa de mudanças nos models usados. [link]
-
Nunca invoque a camada do model diretamente de uma view. [link]
-
Nunca faça formatações complexas nas views; exporte a formatação para um método no helper da view ou no model. [link]
-
Reduza a duplicação de código através do uso de templates e layouts parciais. [link]
-
Nenhuma string ou outras configurações específicas da região devem ser usadas nas views, models e controllers. Esses textos devem ser movidos para os arquivos regionais na pasta
config/locales
. [link] -
Quando os labels de um model do ActiveRecord precisarem ser traduzidos, use o escopo
activerecord
: [link]en: activerecord: models: user: Member attributes: user: name: 'Full name'
Dessa forma,
User.model_name.human
vai retornar "Member" eUser.human_attribute_name("name")
vai retornar "Full name". Essas traduções dos atributos serão utilizadas como labels nas views. -
Separe os textos usados nas views das traduções de atributos do ActiveRecord. Coloque os arquilos regionais dos models numa pasta
models
e os textos usados nas views numa pastaviews
. [link]-
Quando a organização dos arquivos regionais é feita com diretórios adicionais, esses diretórios devem ser descritos no arquivo
application.rb
para poderem ser carregados.# config/application.rb config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')]
-
-
Coloque as opções compartilhadas de localização, como data ou formatos de moeda, em arquivos no root da pasta
locales
. [link] -
Use a forma abreviada dos métodos de I18n:
I18n.t
ao invés deI18n.translate
eI18n.l
ao invés deI18n.localize
. [link] -
Use a busca "preguiçosa" por textos usados nas views. Vamos supor que nós temos a seguinte estrutura: [link]
en: users: show: title: 'User details page'
O valor de
users.show.title
pode ser buscado no templateapp/views/users/show.html.haml
assim:= t '.title'
-
Use as chaves separadas por pontos nos controllers e models ao invés de especificar a opção
:scope
. A chamada separada por pontos é mais fácil de ler e de traçar a hierarquia. [link]# ruim I18n.t :record_invalid, :scope => [:activerecord, :errors, :messages] # bom I18n.t 'activerecord.errors.messages.record_invalid'
-
Mais informações sobre o I18n do Rails pode ser encontrada nos Guias do Rails. [link]
Use o pipeline de assets para maximizar a organização dentro da sua aplicação.
-
Reserve a pasta
app/assets
para stylesheets, javascripts ou imagens customizadas. [link] -
Use a pasta
lib/assets
para suas próprias bibliotecas que não se encaixam muito bem no escopo da aplicação. [link] -
Códigos de terceiros, como o jQuery ou bootstrap, devem ser colocados na pasta
vendor/assets
. [link] -
Quando possível, use versões "gemificadas" dos assets (i.e., jquery-rails, jquery-ui-rails, bootstrap-sass, zurb-foundation). [link]
-
Nomeie os mailers como
SomethingMailer
. Sem o sufixo "Mailer", não fica imediatamente aparente o que é um mailer e quais views são relacionadas ao mailer. [link] -
Forneça templates tanto em HTML quando em texto puro. [link]
-
Habilite os erros lançados quando há falhas no envio de emails no seu ambiente de desenvolvimento. Esses erros são desabilitados por padrão. [link]
# config/environments/development.rb config.action_mailer.raise_delivery_errors = true
-
Use um servidor SMTP local como o Mailcatcher no ambiente de desenvolvimento. [link]
# config/environments/development.rb config.action_mailer.smtp_settings = { address: 'localhost', port: 1025, # mais configurações }
-
Forneça configurações padrão para o nome do host. [link]
# config/environments/development.rb config.action_mailer.default_url_options = { host: "#{local_ip}:3000" } # config/environments/production.rb config.action_mailer.default_url_options = { host: 'your_site.com' } # na sua classe mailer default_url_options[:host] = 'your_site.com'
-
Se você precisar usar um link para o seu site num e-mail, sempre use os métodos
_url
, não os métodos_path
. Os métodos_url
incluem o nome do host e os métodos_path
não. [link]# ruim You can always find more info about this course <%= link_to 'here', course_path(@course) %> # bom You can always find more info about this course <%= link_to 'here', course_url(@course) %>
-
Formate os endereços
de
epara
adequadamente. Use o seguinte formato: [link]# na sua classe mailer default from: 'Your Name <info@your_site.com>'
-
Certifique-se de que o método de envio de e-mails para o seu ambiente de desenvolvimento está definido como
test
: [link]# config/environments/test.rb config.action_mailer.delivery_method = :test
-
O método de envio para desenvolvimento e produção deve ser
smtp
: [link]# config/environments/development.rb, config/environments/production.rb config.action_mailer.delivery_method = :smtp
-
Quando enviar e-mails HTML, todos os estilos devem estar inline, porque alguns clientes de e-mail têm problemas com estilos externos. No entanto, isso torna os templates mais difíceis de manter e leva a duplicação de código. Existem duas gems similares que transformam os estilos e os colocam nas tags HTML correspondentes: premailer-rails e roadie. [link]
-
Enviar e-mails enquanto gera páginas de resposta deve ser evitado. Isso causa atrados no carregamento da página e a requisição pode exceder o tempo limite se múltiplos e-mails estão sendo enviados. Para superar isso, os e-mails podem ser enviados em processos de segundo placo com a ajuda da gem sidekiq. [link]
-
Configure seu fuso horário adequadamente em
application.rb
. [link]config.time_zone = 'Eastern European Time' # opcional - note que isso pode ser apenas :utc ou :local (o padrão é :utc) config.active_record.default_timezone = :local
-
Não use
Time.parse
. [link]# ruim Time.parse('2015-03-02 19:05:37') # => Vai assumir que a string dada está no fuso horário do sistema. # bom Time.zone.parse('2015-03-02 19:05:37') # => Mon, 02 Mar 2015 19:05:37 EET +02:00
-
Não use
Time.now
. [link]# ruim Time.now # => Retorna o horário do sistema e ignora o fuso horário configurado. # bom Time.zone.now # => Fri, 12 Mar 2014 22:04:47 EET +02:00 Time.current # Mesma coisa, mas mais curto.
-
Coloque as gems usadas só para desenvolvimento ou teste nos grupos apropriados no Gemfile. [link]
-
Use apenas gems estabelecidas nos seus projetos. Se você estiver considerando incluir alguma gem pouco conhecida, você deve fazer uma revisão cuidadosa do código fonte dela primeiro. [link]
-
Gems específicas de um sistema operacional vão por padrão resultar num
Gemfile.lock
constantemente mudando para múltiplos desenvolvedores usando diferentes sistemas operacionais. Coloque todas as gems específicas do OS X num grupo chamadodarwin
no Gemfile, e todas as gems específicas do Linux num grupo chamadolinux
: [link]# Gemfile group :darwin do gem 'rb-fsevent' gem 'growl' end group :linux do gem 'rb-inotify' end
Para carregar as gems apropriadas no ambiente certo, coloque o sequinte em
config/application.rb
:platform = RUBY_PLATFORM.match(/(linux|darwin)/)[0].to_sym Bundler.require(platform)
-
Não tire o
Gemfile.lock
do controle de versão. Esse arquivo não é um arquivo qualquer gerado aleatoriamente - ele garante que todos os membros da sua equipe tenham as mesmas versões das gem quando eles fizerembundle install
. [link]
Essa é uma lista de gem que, ou são problemáticas, ou foram suplantadas por outras gems. Você deve evitar usá-las nos seus projetos.
-
rmagick - essa gem é famosa por seu consumo de memória. Use minimagick ao invés dela.
-
autotest - antiga solução para rodar testes automaticamente. Muito inferior a guard ou watchr.
-
rcov - ferramenta de cobertura de código, não compatível com Ruby 1.9. Use SimpleCov no lugar dela.
-
therubyracer - o uso dessa gem em produção é fortemente desencorajado porque ela usa uma grante quantidade de memória. Eu sugeriria usar
node.js
ao invés dela.
Essa lista também é um trabalho em andamento. Por favor, me fale caso você saiba de outras gem populares, mas problemáticas.
Existem alguns recursos excelentes sobre o estilo em Rails, que você deveria considerar olhar caso tenha tempo livre:
- The Rails 4 Way
- Ruby on Rails Guides
- The RSpec Book
- The Cucumber Book
- Everyday Rails Testing with RSpec
- Better Specs for RSpec
Nada que está escrito nesse guia é imutável. É meu desejo trabalhar junto com todos que estejam interessados no estilo de codificação em Rails, para que nós possamos ultimamente criar um recurso que será benéfico para a comunidade Ruby inteira.
Sinta-se livre para abrir tickets ou pull requests com melhorias. Obrigado desde já pela sua ajuda!
Você também pode apoiar o projeto (e o RuboCop) com contribuições financeiras através do gittip.
É simples, basta seguir as diretrizes de contribuição.
This work is licensed under a Creative Commons Attribution 3.0 Unported License
Um guia de estilo criado pela comunidade não serve pra muita coisa numa comunidade que não sabe que ele existe. Tuite sobre o guia, compartilhe com seus amigos e colegas. Cada comentário, sugestão ou opinião que nós recebemos torna o guia um pouquinho melhor. E nós queremos ter o melhor guia possível, não é?
Cheers,
Bozhidar