This guide is an adaptation of ClearCove’s guide and Bozhidar Batsov’s guide.
We are using RuboCop to police our standards as you develop.
Global
1
$ gem install rubocop
your project Gemfile
1
2
3
4
group :development, :test do
gem 'rubocop'
gem 'rubocop-rspec'
end
Config Files for RuboCop
Add the file rails/.rubocop-rails.yml to the project root.
Update your .rubocop.yml
to look this.
1
2
3
4
require: rubocop-rspec
inherit_from:
- .rubocop-rails.yml
LF
(Unix style).Indent only at the beginning of a line.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Bad
{
first_key: "value",
second_key: "value",
third_key: "value",
thousandth_key: "value"
}
# Good
{
first_key: "value",
second_key: "value",
third_key: "value",
thousandth_key: "value"
}
Use spaces around operators, after commas, colons and semicolons, around {
and before }
.
1
2
3
4
5
6
7
8
9
1 + 3
array = [1, 2, 3, 4]
hash = { :a => 1, :b => 2, :c => 3 }
"#{ interpolated_strings }"
lambda { |args| also_for_blocks }
No spaces after (
, [
or before ]
, )
.
1
2
some(arg).other
[1, 2, 3].length
No spaces after !
.
1
!array.include?(element)
Indent when
as deep as case
.
1
2
3
4
5
6
7
8
case int
when 1
puts "one"
when 2
puts "two"
else
puts ":("
end
Use empty lines between def
s for readability.
1
2
3
4
5
6
7
def method_one
puts "Hello, World"
end
def method_two
puts "Hello again, World"
end
Put two spaces before statement modifiers such as postfixed if
, unless
, while
, until
, or rescue
.
1
2
"true" if true
"true" unless false
Leave an empty line after a class
and before its end
.
1
2
3
4
5
6
7
class Person
def method
# ...
end
end
Don’t use a ;
to separate statements or expressions. An exception to this would be the definition of a class with no body.
1
class SomeError < StandardError; end
However, for such a case, the alternative syntax is preferable as it avoids the use of a semi-colon.
1
SomeError = Class.new(StandardError)
Add underscores to large numerical literals to improve their readability.
1
2
3
4
5
# Bad
number = 1000000
# Good
number = 1_000_000
Use YARD and it’s conventions for code documentation.
1
2
3
4
5
6
7
8
# Checks if a {Foo} is the same as a {Bar}.
#
# @param foo [Foo] a foo
# @param bar [Bar] a bar
# @return [Boolean] true if foo is equal to bar
def foobar(foo, bar)
true if Foo == Bar
end
snake_case
for methods and variables.CamelCase
for classes and modules (keep acronyms like HTTP, or XML uppercase).SCREAMING_SNAKE_CASE
for constants.Avoid prefixing methods that return a boolean with verbs such as is_
or has_
.
1
2
is_admin = true
has_permissions = true
Suffix methods with an ?
if they return a Boolean
.
1
2
3
def exists?
true unless false
end
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).
self
, the non-dangerous version can perform the operation on a dup
of the self
.k
for the key part of a Hash
v
for the value part of a Hash
e
for the elements of an Enumerable
f
for files and names of filess
for a stringi
, j
, k
… for indexesv
for any valueinject
and the block is short, use the argument names |m, e|
(memo, each).Start expensive method names with compute_
1
2
3
4
5
def compute_the_meaning_of_life
# 1. Write some code
# 2. ???
# 3. Profit!
end
Use _
for unused parameters.
1
hash.map { |_, v| v ^ 2 }
Use parenthesis around method arguments.
1
2
3
def some_method(with, arguments)
# ...
end
When a method accepts multiple arguments, or has optional arguments, consider using keyword arguments.
1
2
3
def some_method(foo: nil, bar: nil)
# ...
end
If the method in question is high-use and has the potential to change its arguments down the road, keyword arguments can be preferable due to the ease of maintenance, as potentially not every call of the method has to be updated with the new arguments.
each
to iterate over Enumerable
objects.then
in a multi-line if
.Only use the ternary operator if the expressions are trivial.
1
completed? ? "Accepted" : "Denied"
and
or or
keywords. Stick to &&
and ||
(unless you really know why you should use them).Never use unless
with an else
. Re-write it with the positive case first.
1
2
3
4
5
6
7
8
9
10
11
12
13
# Bad
unless success?
"failure"
else
"success"
end
# Good
if success?
"success"
else
"failure"
end
Don’t use parenthesis around the condition in an if
, unless
, or while
unless you are assigning a value.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Bad
if (x > y)
"okay"
end
# Good
if x > y
"okay"
end
# Acceptable
if (x = y)
"okay"
end
do
…end
for multi-line blocks. Use {
…}
for single-line blocks.return
unless it’s required.Don’t use for
unless it’s intentional. for
doesn’t introduce a new scope, and variables defined within a for
loop are available outside the loop.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
array = [1, 2, 3]
# Bad
for number in array do
puts number
end
# Note that number is accessible outside of the for loop
number # => 3
# Good
array.each { |number| puts number }
# number is not accessible outside each's block
number # => NameError: undefined local variable or method `number'
@@class_variables
unless you know what you’re doing.self.method_name
to define class methods.Don’t indent private
, protected
, or public
.
1
2
3
4
5
6
7
8
9
10
11
12
13
class Person
def some_method
# ...
end
private
def method_name
# ...
end
end
Use self
when referring to internal attributes.
1
2
3
4
5
6
7
8
9
class Person
attr_accesible :first_name, :last_name
def full_name
"#{ self.first_name } #{ self.last_name }"
end
end
Obey the Law of Demeter
A method m
of an object O
may only invoke methods of the following kinds of objects:
O
itselfm
’s parametersm
O
’s direct component objectsO
, in the scope of m
Use delegation where it makes sense in order to obey the Law of Demeter in your code.
StandardError
, instead, rescue specific exceptions.OutOfDiskSpaceError = Class.new(StandardError)
). Exception class names should be suffixed with Error
to go along with the convention.Use string interpolation, not concatenation. (Review #formatting or more information on spacing.)
1
2
3
4
5
# Bad
"Something " + some_var + " wrong?"
# Good
"Something #{ some_var } wrong?"
When constructing a large string, use <<
instead of +
.
1
2
3
4
5
6
7
8
9
# Bad
html = "<p>"
html = html + some_variable
html = html + "</p>"
# Good
html = "<p>"
html << some_variable
html << "</p>"
Object#to_s
on interpolated objects, as it’s invoked automatically."
for defining string literals.
"
, use single-quotes '
to define it.()
for delimiters of %
literals. In the case of regular expressions, {}
to avoid conflicts, since ()
commonly appear.Use the newest syntax style for defing hashes.
1
2
3
4
5
# Bad :'(
{ :key => "value" }
# Good :|
{ key: "value" }
Use hash rockets where it makes sense (i.e. "foo" => "bar"
).
Use the new stabby lambda for single-line body blocks.
1
l = ->(a, b) { puts [a, b] }
Use the lambda
method for multi-line body blocks.
1
2
3
4
5
l = lambda do |a, b|
if a == b
puts a
end
end
Omit parameter paranthesis when defining a stabby lambda with no parameters.
1
l = -> { puts "foo" }
proc
over Proc.new
.proc.call()
for both lambdas and procs.Use Array()
over [*var]
.
1
2
3
4
5
6
7
foo = "bar"
# Bad
foos = [*foo]
# Good
foos = Array(foo)