Ruby
Background
Much of the Ruby section of this style guide comes from the semi-official Ruby Style Guide. Not all styles suggested in that guide are applicable, though, and this page should take priority.
Remember to be consistent and ensure readable code!
Style
- Use soft tabs with a two space indent.
- ALWAYS remove trailing white space.
- End your file with an empty line.
- Your editor should be set to use unix-style line endings.
- In Sublime Text, specify a setting:
"default_line_ending": "unix"
- Prefer lines under 80 characters. Keep all lines under 100.
- In Sublime Text, rulers are a good way of keeping track. Specify a setting:
"rulers": [80, 100]
- Do not use block comments.
# bad
=begin
comment line
another comment line
=end
# good
# comment line
# another comment line
- Use spaces after operators, commas, semicolons, and curly braces.
2 + 2 = 4
first, second = 1, 2
{ first: value, second: value }
- Do not use spaces after brackets, parentheses, or
!
.
[1, 2, 3].length
method(argument).upcase
!space_after_bang
- Avoid single line methods (including empty body methods).
# good
def my_good_method
body
end
def my_good_empty_body_method
end
# bad
def bad_method; end
- When using
case
statements, indentwhen
as deep ascase
.
case tracking_number
when begins_with('1Z') then 'UPS'
when begins_with('5000') then 'USPS'
else 'Unknown'
end
- When assigning a conditional result to a variable, preserve alignment:
# good
shipping_method = if shipping_speed == 'Express'
'UPS Next Day Air'
else
'UPS Ground'
end
# good; especially at conserving line length
email_message =
if products_ordered? && in_production?
'your items should be completed soon'
else
'your items will be ready in approximately 2 weeks'
end
- Use empty lines between methods and to split methods into logical paragraphs.
def first_method
data = initialize(options)
data.manipulate!
data
end
def second_method
result
end
- When continuing a chained method invocation on another line keep the
.
on the second line.
# good
one.two.three
.four
# bad
one.two.three.
four
- Align the parameters of a method call if they span more than one line.
# starting point (line is too long)
def send_mail(source)
Mailer.deliver(to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text)
end
# good
def send_mail(source)
Mailer.deliver(to: 'bob@example.com',
from: 'us@example.com',
subject: 'Important message',
body: source.text)
end
# good (normal indent)
def send_mail(source)
Mailer.deliver(
to: 'bob@example.com',
from: 'us@example.com',
subject: 'Important message',
body: source.text
)
end
# okay-ish (aligned params and values, hard to read with long params)
def send_mail(source)
Mailer.deliver(to: 'bob@example.com',
from: 'us@example.com',
subject: 'Important message',
body: source.text)
end
Syntax
- Use
def
with parentheses when there are arguments. Omit the parentheses when the method doesn’t accept any arguments.
def some_method
...
end
def some_method_with_arguments(arg1, arg2)
...
end
- 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.
arr = [1, 2, 3]
# bad
for elem in arr do
puts elem
end
# good
arr.each { |elem| puts elem }
- Never use
then
for multi-lineif/unless
.
# bad
if some_condition then
...
end
# good
if some_condition
...
end
- AVOID the ternary operator EXCEPT in cases where all expressions are trivial.
Favor the operator, however, 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
- Avoid multi-line ternary operator. Favor
if/unless
construct. - Favor
if/unless
usage when you have a single-line body.
# bad
if some_condition
do_something
end
# good
do_something if some_condition
- The
and
andor
keywords should be avoided. Use&&
and||
instead. - Never use
unless
withelse
. Rewrite with the positive case first. - Do not use parentheses around
if/unless/while
conditionals. - Prefer
{...}
overdo..end
for single line blocks. Avoid using{...}
for multi-line blocks. Do not usedo..end
while chaining. Avoid multi-line chaining. -
Avoid
return
where not required. Take advantage of implicit return. - 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
- Using the return value of = (an assignment) is ok. Avoid using the return value in the second part of a conditional:
# bad
if (v = array.grep(/foo/)) ...
# bad
if v = array.grep(/foo/) && v.length > 1 ...
# good
if v = array.grep(/foo/) ...
# good
v = array.grep(/foo/)
if v && v.length > 1 ...
# also good - has correct precedence.
if (v = next_value) == "hello" ...
- Use
||=
freely to initialize variables. - Never use
||=
to set boolean values. (Think about what happens when the first value is falsey?) - Never put a space between a method and it’s opening parentheses.
# bad
f (3 + 2) + 1
# good
f(3 + 2) + 1
- Following the rule above, if the first argument begins with an open parentheses, always use parentheses in the method invocation.
f((3 + 2) + 1)
-
Use the new lambda literal syntax (
->
) for single line body blocks. Use thelambda
method for multi-line blocks. - Prefer
==
overeql?
. The stricter comparison is usually not needed.- (We use
eql?
sporadically, but should work to remove them.)
- (We use
- Use guard clauses when you can assert invalid data. Bail out of the function as soon as possible.
def my_method(argument)
return unless argument
do_something_with(argument)
end
- Use
_
for unused block parameters. - Use
is_a?
orkind_of?
instead of===
to check types.===
is not an equality operator (read the first response here).- If you find yourself checking types, you should ask yourself if that’s even the right approach to solve the problem.
Naming
- Use
snake_case
for symbols, methods, and variables. - Use
CamelCase
for classes and modules. - Use
SCREAMING_SNAKE_CASE
for constants. - Methods that return a boolean value should end in a question mark.
def complete?
art_approved && quantities_submitted
end
- Potentially dangerous methods (ones that modify
self
or arguments) should end in an exclamation mark. Bang methods should only exist if there’s a safe version available.
Classes/Modules
-
Prefer modules to classes with only class methods. Classes should be used only when it makes sense to create instances out of them.
-
Use
def self.method
to define singletons. -
Indent the
public
,protected
, andprivate
methods as much the method definitions they apply to. Leave one blank line above them.
class SomeClass
def public_method
...
end
private
def private_method
...
end
end
Exceptions
- Do not use exceptions for flow control.
- Avoid rescuing the
Exception
class. Be specific. - When rescuing exceptions, notify Airbrake using
Airbrake.notify
.
Collections
- Prefer
%w
over literal array syntax when needing an array of strings. - Prefer symbols over strings as hash keys.
- Prefer Ruby 1.9 hash syntax, where possible. When keys are NOT symbols, use hash-rocket syntax.
Strings
- Prefer string interpolation over concatenation.
- Do not pad string interpolation code.
- Prefer double-quoted strings, unless your string contains a quotation mark or escape characters that you want to suppress.
# bad
name = 'Kotis'
# good
name = "Kotis"
comment = 'Daniel said, "Go Kotis!"'
Regular Expressions
- 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
.
string = "some injection\nusername"
string[/^username$/] # matches
string[/\Ausername\z/] # doesn't match
Keyword Arguments
- Prefer keyword arguments over “options-hash-as-arguments” style.
- Prefer keyword arguments when a method’s arguments are non-obvious when called:
# instead of this
def submit_order(user, employee = false)
...
end
submit_order(user, true) # what does true mean?
# do this
def submit_order(user, employee: false)
...
end
submit_order(user, employee: true)
Testing
Use RSpec, capybara-webkit, FactoryGirl, and fuubar rspec formatter in your applications.
- Prefer lazy-loading objects with objects that can be shared between tests.
let(:ups_params) do
{ origin_zip: "98103", weight: "5", shipping_method: "UPS Ground" }
end
let(:estore) { FactoryGirl.create(:estore) }
- Break tests into contexts, and use whitespace to separate
it
,let
, andbefore
statements. Whitespace should also be used to break spec files into readable sections.- Use your own discretion. Just make it readable!
Miscellaneous
- Default Sublime Text Settings:
"default_line_ending": "unix",
"rulers":
[
80,
100
],
"tab_size": 2,
"translate_tabs_to_spaces": true,
"trim_trailing_white_space_on_save": true,
"ensure_newline_at_eof_on_save": true