This is a work in progress, send pulls, create issues, argue with me etc.
This style guide makes no effort to improve readability which is to a large extent subjective and hard to measure.
The Objective Ruby Style Guide aims to provide guidance on Ruby coding style that is based on objective fact and real benefits over more subjective reasons such as "I've always done it that way" or "I prefer the way that looks".
The guide will not apply to declarative DSL code.
If there are two alternatives for an expression, like quotes for example, pick one for an objective reason and use that one all the time.
Style that results in code that can be added to or modified by simply adding lines rather than changing existing ones will be preferred.
In Ruby everything is an object and almost everything is a method. This gives great power to its users. This style guide will favour use of Ruby's methods over its more mysterious keywords which often bypass calling methods you perhaps thought should be called.
This has the side effect of increased use of blocks which are an awesome Ruby feature.
Always use double quotes.
Always use %{"a string that is maybe a quote"}
.
Always use { }
, never use do; end
- Text editors are MUCH better at understanding braces
- Chaining and functional operations are clearer
- Imperative operations like
#each
do not suffer
Always capture blocks and invoke with #call
.
- Capture blocks with &block at the end of the method signature
- Invoke them with
#call
passing yielded values as an argument - Test for presence with
if block
rather thanblock_given?
This provides a consisent signal that a block argument is used over inspecting
the method implementation for the yield
keyword.
Invokation with #call
and presence test with if block
enforce that blocks
are always captured.
# Objectively inferior
def do_something_and_yield
do_something
yield if block_given?
end
# Objectively superior
def do_something_and_yield(&block)
do_something
block.call if block
end
Always define collections across multiple lines, giving each element its own
line. Elements must not share lines with their containing tokens [{(
.
Always add a trailing comma to the last element.
# Objectively inferior
[ "one", "two", "three" ]
# Objectively superior
[
"one",
"two",
"three",
]
Never use the #[]
accessor method, Always use #fetch
and #store
.
hash = { kitteh: "o hai" }
# Objectively inferior
doge = hash[:doge]
doge.so_fetching
# Some time later somewhere else in the code
# => undefined method `so_fetching' for nil:NilClass (NoMethodError)
# Objectively superior
hash.fetch(:doge).so_fetching
# Immediately raises
# `fetch': key not found: :doge (KeyError)
Never use the def
keyword. Always use #define_method
.
# Objectively inferior
def a_method(*args)
nil # LOL
end
# Objectively superior
define_method(:a_method) { |*args|
nil # LOL
}
- One way to define methods, statically and dynamincally
- Uses a method rather than a keyword
- Methods are defined with blocks which means every method is a lexically scoped closure
Always use paretheses when defining or calling a method which has arguments.
Never use paretheses when the method has no arguments.
Never use the class
keyword, Always use Class.new
# Objectively inferior
class Thing
# ...
end
# Objectively superior
Thing = Class.new {
# ...
}
- When anonymous classes need to be defined your syntax is the same
- More Ruby (better language transparency / less VM Magic)
- Forces you think about classes as objects
Crazy programmers like to reopen classes.
When the urge is too much, keep to the no class
keyword rule, instead use
#class_eval
.
# Objectively inferior
class Thing
# I re-opened u but I can't even tell LOL
end
# Objectively superior
Thing.class_eval {
# I re-opened u for sure ROFL
}
- Definition and re-opening cannot be confused
- Does not use any keywords
Never use class constants, instead define instance methods that return a new copy of your constant literals each time.
# Objectively inferior
class Thing
VALUES = { one: "one" }.freeze
end
# Objectively superior
Thing = Class.new {
define_method(:one) {
{
one: "one",
}
}
}
- Even when frozen, inner values of the constant can be mutated
- Anything at a static or class level should be avoided in an OO system
Similarly to classes, use Module.new
for definition and Module.module_eval
to re-open.
Module and class constuctors yield themselves into the block, using this yielded
value is better than using self
as self
isn't lexically scoped.
MyThings = Module.new { |things|
things::Thing = Class.new { |klass|
# class body
}
}
Prefer object composition, avoid the super
keyword.
The recommended syntax for inheritance is
Thing = Class.new(SuperThing) {
# ... specializations
}
Never inherit from a Struct
. Struct.new
actually returns a new anonymous
class instance in the same way Class.new
does.
# Objectively inferior
class ValueObject < Struct.new(:one, :two, :three)
VALUES = { one: "one" }.freeze
end
# Objectively superior
ValueObject = Struct.new(:one, :two, :three) {
define_method(:some_behaviour) {
# ooooh behave
}
}