This is Portabilis Ruby Style Guide.
It was inspired by GitHub's guide and Rubocop guide.
- Whitespace 1. Indentation 1. Inline 1. Newlines
- Line Length
- Commenting 1. File/class-level comments 1. Function comments 1. Block and inline comments 1. Punctuation, spelling, and grammar 1. TODO comments 1. Commented-out code
- Methods 1. Method definitions 1. Method calls
- Conditional Expressions 1. Conditional keywords 1. Ternary operator
- Syntax
- Naming
- Classes
- Exceptions
- Collections
- Strings
- Regular Expressions
- Percent Literals
- Rails 1. Scopes
- Be Consistent
- Translation
-
Use soft-tabs with a two-space indent.[link]
-
Indent
when
as deep ascase
. [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
-
Align function parameters either all on the same line or one per line.[link]
# bad def self.create_translation(phrase_id, phrase_key, target_locale, value, user_id, do_xss_check, allow_verification) ... end # bad def self.create_translation(phrase_id, phrase_key, target_locale, value, user_id, do_xss_check, allow_verification) ... end # good def self.create_translation( phrase_id, phrase_key, target_locale, value, user_id, do_xss_check, allow_verification ) ... end
-
Indent succeeding lines in multi-line boolean expressions.[link]
# bad def is_eligible?(user) Trebuchet.current.launch?(ProgramEligibilityHelper::PROGRAM_TREBUCHET_FLAG) && is_in_program?(user) && program_not_expired end # good def is_eligible?(user) Trebuchet.current.launch?(ProgramEligibilityHelper::PROGRAM_TREBUCHET_FLAG) && is_in_program?(user) && program_not_expired end
-
Never leave trailing whitespace. [link]
-
When making inline comments, include a space between the end of the code and the start of your comment. [link]
# bad result = func(a, b)# we might want to change b to c # good result = func(a, b) # we might want to change b to c
-
Use spaces around operators; after commas, colons, and semicolons; and around
{
and before}
. [link]sum = 1 + 2 a, b = 1, 2 1 > 2 ? true : false; puts 'Hi' [1, 2, 3].each { |e| puts e }
-
Never include a space before a comma. [link]
result = func(a, b)
-
Do not include space inside block parameter pipes. Include one space between parameters in a block. Include one space outside block parameter pipes. [link]
# bad {}.each { | x, y |puts x } # good {}.each { |x, y| puts x }
-
Do not leave space between
!
and its argument.[link]!something
-
No spaces after
(
,[
or before]
,)
. [link]some(arg).other [1, 2, 3].length
-
Omit whitespace when doing string interpolation.[link]
# bad var = "This #{ foobar } is interpolated." # good var = "This #{foobar} is interpolated."
-
Don't use extra whitespace in range literals.[link]
# bad (0 ... coll).each do |item| # good (0...coll).each do |item|
-
Add a new line after
if
conditions spanning multiple lines to help differentiate between the conditions and the body. [link]if @reservation_alteration.checkin == @reservation.start_date && @reservation_alteration.checkout == (@reservation.start_date + @reservation.nights) redirect_to_alteration @reservation_alteration end
-
Add a new line before and after conditionals, blocks, case statements, etc.[link]
if robot.is_awesome? send_robot_present end robot.add_trait(:human_like_intelligence)
-
Don’t include newlines between areas of different indentation (such as around class or module bodies). [link]
# bad class Foo def bar # body omitted end end # good class Foo def bar # body omitted end end
-
Include one, but no more than one, new line between methods.[link]
def a end def b end
-
Use a single empty line to break between statements to break up methods into logical paragraphs internally. [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
-
End each file with a newline. Don't include multiple newlines at the end of a file. [link]
- Keep each line of code to a readable length. Unless you have a reason to, keep lines to fewer than 115 characters. (rationale) [link]
Though a pain to write, comments are absolutely vital to keeping our code readable. The following rules describe what you should comment and where. But remember: while comments are very important, the best code is self-documenting. Giving sensible names to types and variables is much better than using obscure names that you must then explain through comments.
When writing your comments, write for your audience: the next contributor who will need to understand your code. Be generous — the next one may be you!
Portions of this section borrow heavily from the Google C++ and Python style guides.
-
Use
def
with parentheses when there are parameters. Omit the parentheses when the method doesn't accept any parameters.[link]def some_method # body omitted end def some_method_with_parameters(arg1, arg2) # body omitted end
-
Do not use default positional arguments. Use keyword arguments (if available - in Ruby 2.0 or later) or an options hash instead.[link]
# bad def obliterate(things, gently = true, except = [], at = Time.now) ... end # bad def obliterate(things, options = {}) options = { gently: true, # obliterate with soft-delete :except => [], # skip obliterating these things :at => Time.now, # don't obliterate them until later }.merge(options) ... end # good def obliterate(things, gently: true, except: [], at: Time.now) ... end
-
Avoid single-line methods. Although they are somewhat popular in the wild, there are a few peculiarities about their definition syntax that make their use undesirable. [link]
# bad def too_much; something; something_else; end # good def some_method # body end
Use parentheses for a method call:
-
If the method returns a value. [link]
# bad @current_user = User.find_by_id 1964192 # good @current_user = User.find_by_id(1964192)
-
If the first argument to the method uses parentheses.[link]
# bad put! (x + y) % len, value # good put!((x + y) % len, value)
-
Never put a space between a method name and the opening parenthesis.[link]
# bad f (3 + 2) + 1 # good f(3 + 2) + 1
-
Omit parentheses for a method call if the method accepts no arguments.[link]
# bad nil?() # good nil?
-
If the method doesn't return a value (or we don't care about the return), parentheses are optional. (Especially if the arguments overflow to multiple lines, parentheses may add readability.) [link]
# okay render(:partial => 'foo') # okay render partial: 'foo'
In either case:
-
If a method accepts an options hash as the last argument, do not use
{
}
during invocation. [link]# bad get '/v1/reservations', { :id => 54875 } # good get '/v1/reservations', id: 54875
-
Never use
then
for multi-lineif/unless
. [link]# bad if some_condition then ... end # good if some_condition ... end
-
Never use
do
for multi-linewhile
oruntil
.[link]# bad while x > 5 do ... end until x > 5 do ... end # good while x > 5 ... end until x > 5 ... end
-
The
and
,or
, andnot
keywords are banned. It's just not worth it. Always use&&
,||
, and!
instead. [link] -
Modifier
if/unless
usage is okay when the body is simple, the condition is simple, and the whole thing fits on one line. Otherwise, avoid modifierif/unless
. [link]# bad - this doesn't fit on one line 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 # bad - this is complex and deserves multiple lines and a comment parts[i] = part.to_i(INTEGER_BASE) if !part.nil? && [0, 2, 3].include?(i) # okay return if reconciled?
-
Never use
unless
withelse
. Rewrite these with the positive case first.[link]# bad unless success? puts 'failure' else puts 'success' end # good if success? puts 'success' else puts 'failure' end
-
Avoid
unless
with multiple conditions.[link]# bad if !(foo? && bar?) ... end # good unless foo? && bar? ... end
-
Avoid
unless
with comparison operators if you can useif
with an opposing comparison operator.[link]# bad unless x == 10 ... end # good if x != 10 ... end # bad unless x < 10 ... end # good if x >= 10 ... end # ok unless x === 10 ... end
-
Don't use parentheses around the condition of an
if/unless/while
. [link]# bad if (x > 10) ... end # good if x > 10 ... end
-
Avoid the ternary operator (
?:
) except in cases where all expressions are extremely trivial. However, do use the ternary operator(?:
) overif/then/else/end
constructs for single line conditionals.[link]# bad result = if some_condition then something else something_else end # good result = some_condition ? something : something_else
-
Use one expression per branch in a ternary operator. This also means that ternary operators must not be nested. Prefer
if/else
constructs in these cases.[link]# bad some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else # good if some_condition nested_condition ? nested_something : nested_something_else else something_else end
-
Avoid multiple conditions in ternaries. Ternaries are best used with single conditions. [link]
-
Avoid multi-line
?:
(the ternary operator), useif/then/else/end
instead. [link]# bad some_really_long_condition_that_might_make_you_want_to_split_lines ? something : something_else # good if some_really_long_condition_that_might_make_you_want_to_split_lines something else something_else end
-
Avoid the use of nested conditionals for flow of control. (More on this.) [link]
Prefer a guard clause when you can assert invalid data. A guard clause is a conditional statement at the top of a function that returns as soon as it can.
The general principles boil down to:
- Return immediately once you know your function cannot do anything more.
- Reduce nesting and indentation in the code by returning early. This makes
the code easier to read and requires less mental bookkeeping on the part
of the reader to keep track of
else
branches. - The core or most important flows should be the least indented.
# bad 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 # good 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 # good def compute return unless server return unless client return unless request process_request(request) end
Prefer
next
in loops instead of conditional blocks.# bad [0, 1, 2, 3].each do |item| if item > 1 puts item end end # good [0, 1, 2, 3].each do |item| next unless item > 1 puts item end
See also the section "Guard Clause", p68-70 in Beck, Kent. Implementation Patterns. Upper Saddle River: Addison-Wesley, 2008, which has inspired some of the content above.
-
Never use
for
, unless you know exactly why. Most of the time iterators should be used instead.for
is implemented in terms ofeach
(so you're adding a level of indirection), but with a twist -for
doesn't introduce a new scope (unlikeeach
) and variables defined in its block will be visible outside it.[link]arr = [1, 2, 3] # bad for elem in arr do puts elem end # good arr.each { |elem| puts elem }
-
Prefer
{...}
overdo...end
for single-line blocks. Avoid using{...}
for multi-line blocks (multiline chaining is always ugly). Always usedo...end
for "control flow" and "method definitions" (e.g. in Rakefiles and certain DSLs). Avoiddo...end
when chaining.[link]names = ["Bozhidar", "Steve", "Sarah"] # good names.each { |name| puts name } # bad names.each do |name| puts name end # good names.each do |name| puts name puts 'yay!' end # bad names.each { |name| puts name puts 'yay!' } # good names.select { |name| name.start_with?("S") }.map { |name| name.upcase } # bad names.select do |name| name.start_with?("S") end.map { |name| name.upcase }
Some will argue that multiline chaining would look okay with the use of
{...}
, but they should ask themselves if this code is really readable and whether the block's content can be extracted into nifty methods. -
Use shorthand self assignment operators whenever applicable.[link]
# bad x = x + y x = x * y x = x**y x = x / y x = x || y x = x && y # good x += y x *= y x **= y x /= y x ||= y x &&= y
-
Avoid semicolons except for in single line class definitions. When it is appropriate to use a semicolon, it should be directly adjacent to the statement it terminates: there should be no space before the semicolon.[link]
# bad puts 'foobar'; # superfluous semicolon puts 'foo'; puts 'bar' # two expressions on the same line # good puts 'foobar' puts 'foo' puts 'bar' puts 'foo', 'bar' # this applies to puts in particular
-
Use :: only to reference constants(this includes classes and modules) and constructors (like Array() or Nokogiri::HTML()). Do not use :: for regular method invocation.[link]
# bad SomeClass::some_method some_object::some_method # good SomeClass.some_method some_object.some_method SomeModule::SomeClass::SOME_CONST SomeModule::SomeClass()
-
Avoid
return
where not required. [link]# bad def some_method(some_arr) return some_arr.size end # good def some_method(some_arr) some_arr.size end
-
Don't use the return value of
=
in conditionals[link]# bad - shows intended use of assignment if (v = array.grep(/foo/)) ... end # bad if v = array.grep(/foo/) ... end # good v = array.grep(/foo/) if v ... end
-
Use
||=
freely to initialize variables. [link]# set name to Bozhidar, only if it's nil or false name ||= 'Bozhidar'
-
Don't use
||=
to initialize boolean variables. (Consider what would happen if the current value happened to befalse
.)[link]# bad - would set enabled to true even if it was false enabled ||= true # good enabled = true if enabled.nil?
-
Use
.call
explicitly when calling lambdas. [link]# bad lambda.(x, y) # good lambda.call(x, y)
-
Avoid using Perl-style special variables (like
$0-9
,$
, etc. ). They are quite cryptic and their use in anything but one-liner scripts is discouraged. Prefer long form versions such as$PROGRAM_NAME
.[link] -
When a method block takes only one argument, and the body consists solely of reading an attribute or calling one method with no arguments, use the
&:
shorthand. [link]# bad bluths.map { |bluth| bluth.occupation } bluths.select { |bluth| bluth.blue_self? } # good bluths.map(&:occupation) bluths.select(&:blue_self?)
-
Prefer
some_method
overself.some_method
when calling a method on the current instance.[link]# bad def end_date self.start_date + self.nights end # good def end_date start_date + nights end
In the following three common cases,
self.
is required by the language and is good to use:- When defining a class method:
def self.some_method
. - The left hand side when calling an assignment method, including assigning
an attribute when
self
is an ActiveRecord model:self.guest = user
. - Referencing the current instance's class:
self.class
.
- When defining a class method:
-
When defining an object of any mutable type meant to be a constant, make sure to call
freeze
on it. Common examples are strings, arrays, and hashes. (More on this.)[link]The reason is that Ruby constants are actually mutable. Calling
freeze
ensures they are not mutated and are therefore truly constant and attempting to modify them will raise an exception. For strings, this allows older versions of Ruby below 2.2 to intern them.# bad class Color RED = 'red' BLUE = 'blue' GREEN = 'green' ALL_COLORS = [ RED, BLUE, GREEN, ] COLOR_TO_RGB = { RED => 0xFF0000, BLUE => 0x0000FF, GREEN => 0x00FF00, } end # good 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
-
Use
snake_case
for methods and variables. [link] -
Use
CamelCase
for classes and modules. (Keep acronyms like HTTP, RFC, XML uppercase.) [link] -
Use
SCREAMING_SNAKE_CASE
for other constants.[link] -
The names of predicate methods (methods that return a boolean value) should end in a question mark. (i.e.
Array#empty?
).[link] -
The names of potentially "dangerous" methods (i.e. methods that modify
self
or the arguments,exit!
, etc.) should end with an exclamation mark. Bang methods should only exist if a non-bang method exists. (More on this.) [link] -
Name throwaway variables
_
. [link]version = '3.2.1' major_version, minor_version, _ = version.split('.')
-
Avoid the usage of class (
@@
) variables due to their "nasty" behavior in inheritance. [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 # => will print "child"
As you can see all the classes in a class hierarchy actually share one class variable. Class instance variables should usually be preferred over class variables.
-
Use
def self.method
to define singleton methods. This makes the methods more resistant to refactoring changes. [link]class TestClass # bad def TestClass.some_method ... end # good def self.some_other_method ... end
-
Avoid
class << self
except when necessary, e.g. single accessors and aliased attributes. [link]class TestClass # bad class << self def first_method ... end def second_method_etc ... end end # good 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
-
Indent the
public
,protected
, andprivate
methods as much the method definitions they apply to. Leave one blank line above and below them.[link]class SomeClass def public_method # ... end private def private_method # ... end end
-
Don't use exceptions for flow of control. [link]
# bad begin n / d rescue ZeroDivisionError puts "Cannot divide by 0!" end # good if d.zero? puts "Cannot divide by 0!" else n / d end
-
Avoid rescuing the
Exception
class. [link]# bad begin # an exception occurs here rescue Exception # exception handling end # bad begin # an exception occurs here rescue # exception handling end # good begin # an exception occurs here rescue StandardError # exception handling end
-
Don't specify
RuntimeError
explicitly in the two argument version of raise. Prefer error sub-classes for clarity and explicit error creation.[link]# bad raise RuntimeError, 'message' # better - RuntimeError is implicit here raise 'message' # best class MyExplicitError < RuntimeError; end raise MyExplicitError
-
Prefer supplying an exception class and a message as two separate arguments to
raise
, instead of an exception instance. [link]# bad raise SomeException.new('message') # Note that there is no way to do `raise SomeException.new('message'), backtrace`. # good raise SomeException, 'message' # Consistent with `raise SomeException, 'message', backtrace`.
-
Avoid using rescue in its modifier form. [link]
# bad read_file rescue handle_error($!) # good begin read_file rescue Errno:ENOENT => ex handle_error(ex) end
-
Prefer
map
overcollect
.[link] -
Prefer
detect
overfind
. The use offind
is ambiguous with regard to ActiveRecord'sfind
method -detect
makes clear that you're working with a Ruby collection, not an AR object. [link] -
Prefer
reduce
overinject
. [link] -
Prefer
size
over eitherlength
orcount
for performance reasons.[link] -
Prefer literal array and hash creation notation unless you need to pass parameters to their constructors. [link]
# bad arr = Array.new hash = Hash.new # good arr = [] hash = {} # good because constructor requires parameters x = Hash.new { |h, k| h[k] = {} }
-
Favor
Array#join
overArray#*
for clarity. [link]# bad %w(one two three) * ', ' # => 'one, two, three' # good %w(one two three).join(', ') # => 'one, two, three'
-
Use symbols instead of strings as hash keys. [link]
# bad hash = { 'one' => 1, 'two' => 2, 'three' => 3 } # good hash = { one: 1, two: 2, three: 3 }
-
Relatedly, use plain symbols instead of string symbols when possible.[link]
# bad :"symbol" # good :symbol
-
Use
Hash#key?
instead ofHash#has_key?
andHash#value?
instead ofHash#has_value?
. According to Matz, the longer forms are considered deprecated. [link]# bad hash.has_key?(:test) hash.has_value?(value) # good hash.key?(:test) hash.value?(value)
-
Use multi-line hashes when it makes the code more readable, and use trailing commas to ensure that parameter changes don't cause extraneous diff lines when the logic has not otherwise changed. [link]
hash = { protocol: 'https', only_path: false, controller: :users, action: :set_password, redirect: @redirect_url, secret: @secret, }
-
Use a trailing comma in an
Array
that spans more than 1 line[link]# good array = [1, 2, 3] # good array = [ 'car', 'bear', 'plane', 'zoo', ]
-
Prefer string interpolation instead of string concatenation:[link]
# bad email_with_name = user.name + ' <' + user.email + '>' # good email_with_name = "#{user.name} <#{user.email}>"
-
Avoid using
String#+
when you need to construct large data chunks. Instead, useString#<<
. Concatenation mutates the string instance in-place and is always faster thanString#+
, which creates a bunch of new string objects.[link]# good and also fast html = '' html << '<h1>Page title</h1>' paragraphs.each do |paragraph| html << "<p>#{paragraph}</p>" end
-
Use
\
at the end of the line instead of+
or<<
to concatenate multi-line strings. [link]# bad "Some string is really long and " + "spans multiple lines." "Some string is really long and " << "spans multiple lines." # good "Some string is really long and " \ "spans multiple lines."
-
Avoid using
$1-9
as it can be hard to track what they contain. Named groups can be used instead. [link]# bad /(regexp)/ =~ string ... process $1 # good /(?<meaningful_var>regexp)/ =~ string ... process meaningful_var
-
Be careful with
^
and$
as they match start/end of line, not string endings. If you want to match the whole string use:\A
and\z
.[link]string = "some injection\nusername" string[/^username$/] # matches string[/\Ausername\z/] # don't match
-
Use
x
modifier for complex regexps. This makes them more readable and you can add some useful comments. Just be careful as spaces are ignored.[link]regexp = %r{ start # some text \s # white space char (group) # first group (?:alt1|alt2) # some alternation end }x
-
Prefer parentheses over curly braces, brackets, or pipes when using
%
-literal delimiters for consistency, and because the behavior of%
-literals is closer to method calls than the alternatives.[link]# bad %w[date locale] %w{date locale} %w|date locale| # good %w(date locale)
-
Use
%w
freely.[link]STATES = %w(draft open closed)
-
Use
%()
for single-line strings which require both interpolation and embedded double-quotes. For multi-line strings, prefer heredocs.[link]# bad - no interpolation needed %(<div class="text">Some text</div>) # should be '<div class="text">Some text</div>' # bad - no double-quotes %(This is #{quality} style) # should be "This is #{quality} style" # bad - multiple lines %(<div>\n<span class="big">#{exclamation}</span>\n</div>) # should be a heredoc. # good - requires interpolation, has quotes, single line %(<tr><td class="name">#{name}</td>)
-
Use
%r
only for regular expressions matching more than one '/' character.[link]# bad %r(\s+) # still bad %r(^/(.*)$) # should be /^\/(.*)$/ # good %r(^/blog/2011/(.*)$)
-
Avoid the use of %x unless you're going to invoke a command with backquotes in it (which is rather unlikely). [link]
# bad date = %x(date) # good date = `date` echo = %x(echo `date`)
-
When immediately returning after calling
render
orredirect_to
, putreturn
on the next line, not the same line. [link]# bad render text: 'Howdy' && return # good render text: 'Howdy' return # still bad render text: 'Howdy' && return if foo.present? # good if foo.present? render text: 'Howdy' return end
-
When defining ActiveRecord model scopes, wrap the relation in a
lambda
. A naked relation forces a database connection to be established at class load time (instance startup). [link]# bad scope :foo, where(bar: 1) # good scope :foo, -> { where(bar: 1) } # bad scope :foo, -> (user_id) where(user_id: user_id) # good scope :foo, ->(user_id) { where(user_id: user_id) }
If you're editing code, take a few minutes to look at the code around you and determine its style. If they use spaces around all their arithmetic operators, you should too. If their comments have little boxes of hash marks around them, make your comments have little boxes of hash marks around them too.
The point of having style guidelines is to have a common vocabulary of coding so people can concentrate on what you're saying rather than on how you're saying it. We present global style rules here so people know the vocabulary, but local style is also important. If code you add to a file looks drastically different from the existing code around it, it throws readers out of their rhythm when they go to read it. Avoid this.
TODO
[ ] Adicionar o texto Clean Code
[ ] Adicionar os Design Patterns