Our style guide is a fork of bbatsov's great work. We mainly stripped out all rules RuboCop analyzes and punctually made changes.
You can generate a PDF or an HTML copy of this guide using Transmuter.
- Source Code Layout
- Syntax
- Naming
- Classes
- Exceptions
- Collections
- Strings
- Regular Expressions
- Percent Literals
- Metaprogramming
- Misc
Nearly everybody is convinced that every style but their own is ugly and unreadable. Leave out the "but their own" and they're probably right...
-- Jerry Coffin (on indentation)
- Use
UTF-8
as the source file encoding.
-
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
-
Don't use
||=
to initialize boolean variables. (Consider what would happen if the current value happened to befalse
.)# bad - would set enabled to true even if it was false enabled ||= true # good enabled = true if enabled.nil?
-
Use
[*var]
orArray()
instead of explicitArray
check, when dealing with a variable you want to treat as an Array, but you're not certain it's an array.# bad paths = [paths] unless paths.is_a? Array paths.each { |path| do_something(path) } # good [*paths].each { |path| do_something(path) } # good (and a bit more readable) Array(paths).each { |path| do_something(path) }
-
Use ranges or
Comparable#between?
instead of complex comparison logic when possible.# bad do_something if x >= 1000 && x <= 2000 # good do_something if (1000..2000).include?(x) # good do_something if x.between?(1000, 2000)
The only real difficulties in programming are cache invalidation and naming things.
-- Phil Karlton
-
Use
snake_case
for symbols.# bad :'some symbol' :SomeSymbol :someSymbol # good :some_symbol
-
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 likeexit
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 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
-
Consider using
Struct.new
, which defines the trivial accessors, constructor and comparison operators for you.# 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 # better Person = Struct.new(:first_name, :last_name) do 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
-
Assign proper visibility levels to methods (
private
,protected
) in accordance with their intended usage. Don't go off leaving everythingpublic
(which is the default). After all we're coding in Ruby now, not in Python. -
Always define private class methods on the metaclass of your class. When you have the need for a private class method, there is most likely a related public class method on the same class, which is calling the private class method.
# bad class Cake def self.slice_cake slice_into_pieces(8) end def self.slice_into_pices(n) ... end private_class_method :slice_into_pieces end # good class Cake class << self def slice_cake slice_into_pieces(8) end private def slice_into_pieces(n) ... end end end
-
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
-
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 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.
-
Rely on the fact that as of Ruby 1.9 hashes are ordered.
-
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}>"
-
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.# good and also fast html = '' html << '<h1>Page title</h1>' paragraphs.each do |paragraph| html << "<p>#{paragraph}</p>" end
-
When using heredocs for multi-line strings keep in mind the fact that they preserve leading whitespace. It's a good practice to employ some margin based on which to trim the excessive whitespace.
code = <<-END.gsub(/^\s+\|/, '') |def test | some_method | other_method |end END #=> "def\n some_method\n \nother_method\nend"
Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.
-- Jamie Zawinski
-
Don't use regular expressions if you just need plain text search in string:
string['text']
-
Avoid using numbered groups as it can be hard to track what they contain. Named groups can be used instead.
# bad /(regexp)/ =~ string ... process Regexp.last_match[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
%()
(it's a shorthand for%Q
) 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>)
-
Avoid needless metaprogramming.
-
Do not mess around in core classes when writing libraries. (Do not monkey-patch them.)
-
Avoid using
method_missing
for metaprogramming because backtraces become messy, the behavior is not listed in#methods
, and misspelled method calls might silently work, e.g.nukes.launch_state = false
. Consider using delegation, proxy, ordefine_method
instead. If you must usemethod_missing
:-
Be sure to also define
respond_to_missing?
-
Only catch methods with a well-defined prefix, such as
find_by_*
-- make your code as assertive as possible. -
Call
super
at the end of your statement -
Delegate to assertive, non-magical methods:
# bad def method_missing?(meth, *args, &block) if /^find_by_(?<prop>.*)/ =~ meth # ... lots of code to do a find_by else super end end # good def method_missing?(meth, *args, &block) if /^find_by_(?<prop>.*)/ =~ meth find_by(prop, *args, &block) else super end end # best of all, though, would to define_method as each findable attribute is declared
-
- Do not mutate arguments unless that is the purpose of the method.
This work is licensed under a Creative Commons Attribution 3.0 Unported License
Most of the work is done by bbatsov.