Docs, guidelines and compromising photos
To make changes to these pages, checkout the wego.github.io project and the GitHub Pages docs.
UTF-8
as the source file encoding.Use two spaces per indentation level. No hard tabs.
# bad - four spaces
def some_method
do_something
end
# good
def some_method
do_something
end
Use spaces around operators, after commas, colons and semicolons, around {
and before }
.
sum = 1 + 2
a, b = 1, 2
1 > 2 ? true : false; puts 'Hi'
[1, 2, 3].each { |e| puts e }
The only exception is when using the exponent operator:
# bad
e = M * c ** 2
# good
e = M * c**2
No spaces after (
, [
or before ]
, )
.
some(arg).other
[1, 2, 3].length
Indent when
as deep as case
.
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
Use empty lines between def
s and to break up a method into logical
paragraphs.
def some_method
data = initialize(options)
data.manipulate!
data.result
end
def some_method
result
end
Use def
with parentheses when there are arguments. Omit the
parentheses when the method doesn't accept any arguments.
def some_method
# body omitted
end
def some_method_with_arguments(arg1, arg2)
# body omitted
end
Never use for
, unless you know exactly why. Most of the time iterators
should be used instead. for
is implemented in terms of each
(so
you're adding a level of indirection), but with a twist - for
doesn't introduce a new scope (unlike each
) and variables defined
in its block will be visible outside it.
arr = [1, 2, 3]
# bad
for elem in arr do
puts elem
end
# good
arr.each { |elem| puts elem }
Never use then
for multi-line if/unless
.
# bad
if some_condition then
# body omitted
end
# good
if some_condition
# body omitted
end
Avoid the ternary operator (?:
) except in cases where all expressions
are extremely trivial. However, do use the ternary operator over
if/then/else/end
constructs for single line conditionals.
# 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.
# 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
Use &&/||
for boolean expressions, and/or
for control flow. (Rule
of thumb: If you have to use outer parentheses, you are using the
wrong operators.)
# boolean expression
if some_condition && some_other_condition
do_something
end
# control flow
document.saved? or document.save!
Avoid multi-line ?:
(the ternary operator); use if/unless
instead.
Favor modifier if/unless
usage when you have a single-line
body. Another good alternative is the usage of control flow and/or
.
# bad
if some_condition
do_something
end
# good
do_something if some_condition
# another good option
some_condition and do_something
Favor unless
over if
for negative conditions (or control
flow or
).
# bad
do_something if !some_condition
# good
do_something unless some_condition
# another good option
some_condition or do_something
Never use unless
with else
. Rewrite these with the positive case first.
# bad
unless success?
puts 'failure'
else
puts 'success'
end
# good
if success?
puts 'success'
else
puts 'failure'
end
Don't use parentheses around the condition of an if/unless/while
,
unless the condition contains an assignment.
# bad
if (x > 10)
# body omitted
end
# good
if x > 10
# body omitted
end
# ok
if (x = self.next_value)
# body omitted
end
Prefer {...}
over do...end
for single-line blocks. Avoid using
{...}
for multi-line blocks (multiline chaining is always
ugly). Always use do...end
for "control flow" and "method
definitions" (e.g. in Rakefiles and certain DSLs). Avoid do...end
when chaining.
names = ['Bozhidar', 'Steve', 'Sarah']
# bad
names.each do |name|
puts name
end
# good
names.each { |name| puts name }
# bad
names.select do |name|
name.start_with?('S')
end.map { |name| name.upcase }
# good
names.select { |name| name.start_with?('S') }.map { |name| name.upcase }
Some will argue that multiline chaining would look OK with the use of {...}, but they should ask themselves - is this code really readable and can the blocks' contents be extracted into nifty methods?
Avoid return
where not required for flow of control.
# bad
def some_method(some_arr)
return some_arr.size
end
# good
def some_method(some_arr)
some_arr.size
end
Avoid self
where not required. (It is only required when calling a self write accessor.)
# bad
def ready?
if self.last_reviewed_at > self.last_updated_at
self.worker.update(self.content, self.options)
self.status = :in_progress
end
self.status == :verified
end
# good
def ready?
if last_reviewed_at > last_updated_at
worker.update(content, options)
self.status = :in_progress
end
status == :verified
end
As a corollary, avoid shadowing methods with local variables unless they are both equivalent.
class Foo
attr_accessor :options
# ok
def initialize(options)
self.options = options
# both options and self.options are equivalent here
end
# bad
def do_something(options = {})
unless options[:when] == :later
output(self.options[:message])
end
end
# good
def do_something(params = {})
unless params[:when] == :later
output(options[:message])
end
end
end
Use spaces around the =
operator when assigning default values to method parameters:
# bad
def some_method(arg1=:default, arg2=nil, arg3=[])
# do something...
end
# good
def some_method(arg1 = :default, arg2 = nil, arg3 = [])
# do something...
end
Avoid line continuation (\) where not required. In practice, avoid using line continuations at all.
# bad
result = 1 - \
2
# good (but still ugly as hell)
result = 1 \
- 2
When using =
(an assignment) in a conditional expression, surround
it in parentheses to indicate that the assignment is intentional.
if (v = next_value) == "hello"
do_something(v)
...
end
Use ||=
freely to initialize variables.
# 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 be false
.)
# bad - would set enabled to true even if it was false
enabled ||= true
# good
enabled = true if enabled.nil?
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.
Never put a space between a method name and the opening parenthesis.
# bad
f (3 + 2) + 1
# good
f(3 + 2) + 1
If the first argument to a method begins with an open parenthesis,
always use parentheses in the method invocation. For example, write
f((3 + 2) + 1)
.
Use the new lambda literal syntax.
# bad
lambda = lambda { |a, b| a + b }
lambda.call(1, 2)
# good
lambda = ->(a, b) { a + b }
lambda.(1, 2)
Use _
for unused block parameters.
# bad
result = hash.map { |k, v| v + 1 }
# good
result = hash.map { |_, v| v + 1 }
Name identifiers in English.
# bad - variable name written in Bulgarian with latin characters
zaplata = 1000
# good
salary = 1000
Use snake_case
for symbols, methods and variables.
# bad
:'some symbol'
:SomeSymbol
:someSymbol
someVar = 5
def someMethod
...
end
def SomeMethod
...
end
# good
:some_symbol
def some_method
...
end
Use CamelCase
for classes and modules. (Keep acronyms like HTTP,
RFC, XML uppercase.)
# bad
class Someclass
...
end
class Some_Class
...
end
class SomeXml
...
end
# good
class SomeClass
...
end
class SomeXML
...
end
Use SCREAMING_SNAKE_CASE
for other constants.
# bad
SomeConst = 5
# good
SOME_CONST = 5
The names of predicate methods (methods that return a boolean value)
should end in a question mark.
(i.e. Array#empty?
).
The names of potentially dangerous methods (i.e. methods that
modify self
or the arguments, exit!
(doesn't run the finalizers
like exit
does), etc.) should end with an exclamation mark if
there exists a safe version of that dangerous method.
# bad - there is not matching 'safe' method
class Person
def update!
end
end
# good
class Person
def update
end
end
# good
class Person
def update!
end
def update
end
end
Define the non-bang (safe) method in terms of the bang (dangerous) one if possible.
class Array
def flatten_once!
res = []
each do |e|
[*e].each { |f| res << f }
end
replace(res)
end
def flatten_once
dup.flatten_once!
end
end
Prefer map
over collect
, find
over detect
, select
over
find_all
, reduce
over inject
and size
over length
. This is
not a hard requirement; if the use of the alias enhances
readability, it's ok to use it. The rhyming methods are inherited from
Smalltalk and are not common in other programming languages. The
reason the use of select
is encouraged over find_all
is that it
goes together nicely with reject
and its name is pretty self-explanatory.
Use flat_map
instead of map
+ flatten
.
This does not apply for arrays with a depth greater than 2, i.e.
if users.first.songs == ['a', ['b','c']]
, then use map + flatten
rather than flat_map
.
flat_map
flattens the array by 1, whereas flatten
flattens it all the way.
# bad
all_songs = users.map(&:songs).flatten.uniq
# good
all_songs = users.flat_map(&:songs).uniq
Avoid superfluous comments.
# bad
counter += 1 # increments counter by one
Keep existing comments up-to-date. An outdated comment is worse than no comment at all.
Avoid writing comments to explain bad code. Refactor the code to make it self-explanatory.
Annotations should usually be written on the line immediately above the relevant code.
The annotation keyword is followed by a colon and a space, then a note describing the problem.
Use TomDoc for method documentation. An example:
# Public: Duplicate some text an arbitrary number of times.
#
# text - The String to be duplicated.
# count - The Integer number of times to duplicate the text.
#
# Examples
#
# multiplex('Tom', 4)
# # => 'TomTomTomTom'
#
# Returns the duplicated String.
def multiplex(text, count)
text * count
end
In cases where the problem is so obvious that any documentation would be redundant, annotations may be left at the end of the offending line with no note. This usage should be the exception and not the rule.
def bar
sleep 100 # OPTIMIZE
end
Use TODO
to note missing features or functionality that should be
added at a later date.
Use FIXME
to note broken code that needs to be fixed.
Use OPTIMIZE
to note slow or inefficient code that may cause
performance problems.
Use HACK
to note code smells where questionable coding practices
were used and should be refactored away.
Use REVIEW
to note anything that should be looked at to confirm it
is working as intended. For example: REVIEW: Are we sure this is how the
client does X currently?
Use other custom annotation keywords if it feels appropriate, but be
sure to document them in your project's README
or similar.
Prefer modules to classes with only class methods. Classes should be used only when it makes sense to create instances out of them.
# bad
class SomeClass
def self.some_method
# body omitted
end
def self.some_other_method
end
end
# good
module SomeClass
module_function
def some_method
# body omitted
end
def some_other_method
end
end
Favor the use of module_function
over extend self
when you want
to turn a module's instance methods into class methods.
# bad
module Utilities
extend self
def parse_something(string)
# do stuff here
end
def other_utility_method(number, string)
# do some more stuff
end
end
# good
module Utilities
module_function
def parse_something(string)
# do stuff here
end
def other_utility_method(number, string)
# do some more stuff
end
end
Use the attr
family of functions to define trivial accessors or
mutators.
# bad
class Person
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
def first_name
@first_name
end
def last_name
@last_name
end
end
# good
class Person
attr_reader :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
end
Prefer duck-typing over inheritance.
# bad
class Animal
# abstract method
def speak
end
end
# extend superclass
class Duck < Animal
def speak
puts 'Quack! Quack'
end
end
# extend superclass
class Dog < Animal
def speak
puts 'Bau! Bau!'
end
end
# good
class Duck
def speak
puts 'Quack! Quack'
end
end
class Dog
def speak
puts 'Bau! Bau!'
end
end
Avoid the usage of class (@@
) variables due to their "nasty" behavior
in inheritance.
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.
Assign proper visibility levels to methods (private
, protected
)
in accordance with their intended usage. Don't go off leaving
everything public
(which is the default). After all we're coding
in Ruby now, not in Python.
Indent the public
, protected
, and private
methods as much the
method definitions they apply to. Leave one blank line above the
visibility modifier
and one blank line below in order to emphasize that it applies to all
methods below it.
class SomeClass
def public_method
# ...
end
private
def private_method
# ...
end
def another_private_method
# ...
end
end
Use def self.method
to define singleton methods. This makes the code
easier to refactor since the class name is not repeated.
class TestClass
# bad
def TestClass.some_method
# body omitted
end
# good
def self.some_other_method
# body omitted
end
end
Avoid using class << self
blocks to define singleton methods. Use
def self.method
instead.
class Hotel
# bad
class << self
def first_method
# body omitted
end
def second_method_etc
# body omitted
end
end
# good
def self.first_method
# body omitted
end
def self.second_method_etc
# body omitted
end
end
Never return from an ensure
block. If you explicitly return from a
method inside an ensure
block, the return will take precedence over
any exception being raised, and the method will return as if no
exception had been raised at all. In effect, the exception will be
silently thrown away.
def foo
begin
fail
ensure
return 'very bad idea'
end
end
Use implicit begin blocks where possible.
# bad
def foo
begin
# main logic goes here
rescue
# failure handling goes here
end
end
# good
def foo
# main logic goes here
rescue
# failure handling goes here
end
Avoid using rescue
in its modifier form.
# bad - this catches all StandardError exceptions
do_something rescue nil
Don't use exceptions for flow of control.
# 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. This will trap signals and calls to
exit
, requiring you to kill -9
the process.
# bad
begin
# calls to exit and kill signals will be caught (except kill -9)
exit
rescue Exception
puts "you didn't really want to exit, right?"
# exception handling
end
# good
begin
# a blind rescue rescues from StandardError, not Exception as many
# programmers assume.
rescue => e
# exception handling
end
# also good
begin
# an exception occurs here
rescue StandardError => e
# exception handling
end
Put more specific exceptions higher up the rescue chain, otherwise they'll never be rescued from.
# bad
begin
# some code
rescue Exception => e
# some handling
rescue StandardError => e
# some handling
end
# good
begin
# some code
rescue StandardError => e
# some handling
rescue Exception => e
# some handling
end
Release external resources obtained by your program in an ensure block.
f = File.open('testfile')
begin
# .. process
rescue
# .. handle error
ensure
f.close unless f.nil?
end
Favor the use of exceptions for the standard library over introducing new exception classes.
Prefer literal array and hash creation notation (unless you need to pass parameters to their constructors, that is).
# bad
arr = Array.new
hash = Hash.new
# good
arr = []
hash = {}
Prefer %w
to the literal array syntax when you need an array of
strings.
# bad
STATES = ['draft', 'open', 'closed']
# good
STATES = %w(draft open closed)
Use Set
instead of Array
when dealing with unique elements. Set
implements a collection of unordered values with no duplicates. This
is a hybrid of Array
's intuitive inter-operation facilities and
Hash
's fast lookup.
Prefer symbols instead of strings as hash keys.
# bad
hash = { 'one' => 1, 'two' => 2, 'three' => 3 }
# good
hash = { one: 1, two: 2, three: 3 }
Avoid the use of mutable objects as hash keys.
Never modify a collection while traversing it.
Prefer string interpolation instead of string concatenation:
# bad
email_with_name = user.name + ' <' + user.email + '>'
# good
email_with_name = "#{user.name} <#{user.email}>"
Prefer single-quoted strings when you don't need string interpolation or
special symbols such as \t
, \n
, '
, etc.
# bad
name = "Bozhidar"
# good
name = 'Bozhidar'
Avoid using String#+
when you need to construct large data chunks.
Instead, use String#<<
. Concatenation mutates the string instance in-place
and is always faster than String#+
, which creates a bunch of new string objects.
# good and also fast
html = ''
html << '<h1>Page title</h1>'
paragraphs.each do |paragraph|
html << "<p>#{paragraph}</p>"
end
Don't use regular expressions if you just need plain text search in string:
string['text']
Avoid using $1-9 as it can be hard to track what they contain. Named groups can be used instead.
# 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
(not to be
confused with \Z
which is the equivalent of /\n?\z/
).
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.
regexp = %r{
start # some text
\s # white space char
(group) # first group
(?:alt1|alt2) # some alternation
end
}x
Use %()
for single-line strings which require both interpolation
and embedded double-quotes. For multi-line strings, prefer heredocs.
# 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.
# bad
%r(\s+)
# still bad
%r(^/(.*)$)
# should be /^\/(.*)$/
# good
%r(^/blog/2011/(.*)$)
Prefer ()
as delimiters for all %
literals.
ruby -w
safe code, i.e. code should not produce any warnings.alias
when alias_method
will do.OptionParser
for parsing complex command line options and
ruby -s
for trivial command line options.The conventions and rules here are taken from the Ruby Style Guide and Github's internal Ruby style guide.