-
Use two spaces per indentation level (aka soft tabs). No hard tabs. [link]
# standard def some_method do_something end # non-standard - four spaces def some_method do_something end
-
Avoid
;
to separate statements and expressions. Use one expression per line. [link]# standard puts 'foobar' puts 'foo' puts 'bar' # non-standard puts 'foobar'; puts 'foo'; puts 'bar'
-
Class definitions should be laid out in a standard fashion even if they have no body. [link]
# standard class FooError < StandardError end # non-standard class FooError < StandardError; end # non-standard FooError = Class.new(StandardError)
-
Avoid putting method definitions and the body on a single line. [link]
# standard def some_method body end # non-standard def too_much; something; something_else; end # non-standard def no_braces_method; body end # non-standard def no_braces_method; body; end # non-standard def some_method() body end # non-standard def no_op; end
-
Use spaces around operators, after commas, colons and semicolons, around
{
and before}
. [link]sum = 1 + 2 a, b = 1, 2 [1, 2, 3].each { |e| puts e }
The only exception, regarding operators, is the exponent operator:
# standard e = M * c**2 # non-standard e = M * c ** 2
Treat
{
and}
consistently.# standard { :one => 1, :two => 2 } # non-standard {:one => 1, :two => 2}
As far as embedded expressions go, omit spaces.
# standard - no spaces "string#{expr}" # non-standard "string#{ expr }"
-
No spaces after
(
,[
or before]
,)
. [link]some(arg).other [1, 2, 3].size
-
No space after
!
. [link]# standard !something # non-standard ! something
-
Use empty lines between method definitions and also to break up a method into logical paragraphs. [link]
def some_method data = initialize(options) data.manipulate! data.result end def some_method result end
-
Indent
when
as deep ascase
. Space out the when clauses as separate paragraphs. [link]# standard 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 # non-standard 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
-
When assigning the result of a conditional expression to a variable, use simple indentation. [link]
# standard result = if some_cond calc_something else calc_something_else end # standard result = if some_cond calc_something else calc_something_else end # non-standard result = if some_cond calc_something else calc_something_else end
-
Avoid comma after the last parameter in a method call. [link]
# standard some_method(size, count, color) # non-standard some_method( size, count, color, ) # non-standard some_method(size, count, color, )
-
Do not use spaces around the
=
operator when assigning default values to method parameters: [link]# standard def some_method(arg1=:default, arg2=nil, arg3=[]) # do something... end # non-standard def some_method(arg1 = :default, arg2 = nil, arg3 = []) # do something... end
-
Avoid line continuation
\
. [link]# non-standard result = 1 \ - 2 long_string = 'First part of the long string' \ ' and second part of the long string'
-
Avoid multi-line method chaining styles. [link]
# non-standard one.two.three. four one.two.three .four
-
Use simple indentation for the parameters of a method call if they span more than one line. [link]
# standard def send_mail(source) Mailer.deliver( to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text ) end # standard def send_mail(source) Mailer.deliver(to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text) end # non-standard def send_mail(source) Mailer.deliver( to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text) end # non-standard def send_mail(source) Mailer.deliver(to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text) end
-
Align the elements of array literals spanning multiple lines. [link]
# standard menu_item = [ 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam' ] menu_item = [ 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam' ] # non-standard menu_item = ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam'] # non-standard menu_item = ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']
-
Add underscores to large numeric literals to improve their readability. [link]
# standard num = 1_000_000 # non-standard num = 1000000
-
Avoid trailing whitespace. [link]
-
End each file with a newline. [link]
-
Use
def
with parentheses when there are arguments. Omit the parentheses when the method doesn't accept any arguments. [link]# standard def some_method # body omitted end # standard def some_method_with_arguments(arg1, arg2) # body omitted end # non-standard def some_method() # body omitted end # non-standard def some_method_with_arguments arg1, arg2 # body omitted end
-
Avoid using
for
. Use iterators. [link]arr = [1, 2, 3] # standard arr.each { |elem| puts elem } elem # => NameError: undefined local variable or method `elem' # non-standard for elem in arr do puts elem end elem # => 3
-
Never use
then
for multi-lineif/unless
. [link]# standard if some_condition # body omitted end # non-standard if some_condition then # body omitted end
-
Always put the condition on the same line as the
if
/unless
in a multi-line conditional. [link]# standard if some_condition do_something do_something_else end # non-standard if some_condition do_something do_something_else end
-
Favor the ternary operator(
?:
) overif/then/else/end
constructs. [link]# standard result = some_condition ? something : something_else # non-standard result = if some_condition then something else something_else end
-
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. [link]# standard if some_condition nested_condition ? nested_something : nested_something_else else something_else end # non-standard some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else
-
Never use
if x; ...
. Use the ternary operator instead. [link] -
Use
if
andcase
as expressions which return a result. [link]# standard result = if condition x else y end # non-standard if condition result = x else result = y end
-
Use
!
instead ofnot
. [link]# standard x = !something # non-standard x = (not something)
-
Avoid multi-line
?:
(the ternary operator); useif/unless
instead. [link] -
Favor modifier
if/unless
usage when you have a single-line body. Another good alternative is the usage of control flowand/or
. [link]# standard do_something if some_condition # standard some_condition and do_something # non-standard if some_condition do_something end
-
Favor
unless
overif
for negative conditions (or control flow||
). [link]# standard do_something unless some_condition # standard some_condition or do_something # non-standard do_something if !some_condition # non-standard do_something if not some_condition
-
Avoid
unless
withelse
. Try rewriting these with the positive case first. [link]# standard if success? puts 'success' else puts 'failure' end # non-standard unless success? puts 'failure' else puts 'success' end
-
Don't use parentheses around the condition of an
if/unless/while/until
. [link]# standard if x > 10 # body omitted end # non-standard if (x > 10) # body omitted end
-
Omit parentheses around parameters for methods that are part of an internal DSL (e.g. Rake, Rails, RSpec), methods that have "keyword" status in Ruby (e.g.
attr_reader
,puts
) and attribute access methods. Use parentheses around the arguments of all other method invocations. [link]class Person attr_reader :name, :age # omitted end temperance = Person.new('Temperance', 30) temperance.name puts temperance.age x = Math.sin(y) array.delete(e) bowling.score.should == 0
-
Omit the outer braces around an implicit options hash. [link]
# standard user.set(:name => 'John', :age => 45, :permissions => { :read => true }) # non-standard user.set({ :name => 'John', :age => 45, :permissions => { :read => true }})
-
Omit both the outer braces and parentheses for methods that are part of an internal DSL. [link]
class Person < ActiveRecord::Base # standard validates :name, :presence => true, :length => { :within => 1..10 } # non-standard validates(:name, :presence => true, :length => { :within => 1..10 }) end
-
Omit parentheses for method calls with no arguments. [link]
# standard Kernel.exit! 2.even? fork 'test'.upcase # non-standard Kernel.exit!() 2.even?() fork() 'test'.upcase()
-
Prefer
{...}
overdo...end
for single-line blocks. Avoid using{...}
for multi-line blocks (multiline chaining is always ugly). Always usedo...end
for "control flow" and "method definitions" (e.g. in Rakefiles and certain DSLs). Avoiddo...end
when chaining. [link]names = ['Bozhidar', 'Steve', 'Sarah'] # standard names.each { |name| puts name } # non-standard names.each do |name| puts name end # standard names.select { |name| name.start_with?('S') }.map { |name| name.upcase } # non-standard names.select do |name| name.start_with?('S') end.map { |name| name.upcase }
-
Use the new lambda literal syntax for single line body blocks. Use the
lambda
method for multi-line blocks. [link]# standard l = ->(a, b) { a + b } l.call(1, 2) l = lambda do |a, b| tmp = a * 7 tmp * b / 50 end # non-standard l = ->(a, b) do tmp = a * 7 tmp * b / 50 end # non-standard l = lambda { |a, b| a + b } l.call(1, 2)
-
Prefer
proc
overProc.new
. [link]# standard p = proc { |n| puts n } # non-standard p = Proc.new { |n| puts n }
-
Prefer
proc.call()
overproc[]
orproc.()
for both lambdas and procs. [link]# standard l = ->(v) { puts v } l.call(1) # non-standard l = ->(v) { puts v } l[1] # non-standard l = ->(v) { puts v } l.(1)
-
Prefix with
_
unused block parameters and local variables. [link]# standard result = hash.map { |_, v| v + 1 } # standard result = hash.map { |_k, v| v + 1 } # non-standard result = hash.map { |k, v| v + 1 }
-
Avoid
return
where not required for flow of control. [link]# standard def some_method(some_arr) some_arr.size end # non-standard def some_method(some_arr) return some_arr.size end
-
Avoid
self
where not required. [link]# standard def ready? if last_reviewed_at > last_updated_at worker.update(content, options) self.status = :in_progress end status == :verified end # non-standard 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
-
Don't use the return value of
=
(an assignment) in conditional expressions unless the assignment is wrapped in parentheses. [link]# standard if (v = array.grep(/foo/)) do_something(v) ... end # standard v = array.grep(/foo/) if v do_something(v) ... end # non-standard if v = array.grep(/foo/) do_something(v) ... end
-
Use shorthand self assignment operators whenever applicable. [link]
# standard x += y x *= y x **= y x /= y x ||= y x &&= y # non-standard x = x + y x = x * y x = x**y x = x / y x = x || y x = x && y
-
Use
||=
to initialize variables only if they're not already initialized. [link]# standard name ||= 'Bozhidar' # non-standard name = name ? name : 'Bozhidar' # non-standard name = 'Bozhidar' unless name
-
Use
&&=
to preprocess variables that may or may not exist. [link]# standard something &&= something.downcase # standard something = something && something.downcase # standard something = something.downcase if something # non-standard if something something = something.downcase end # non-standard something = something ? nil : something.downcase
-
Avoid explicit use of the case equality operator
===
. [link]# standard something.is_a?(Array) (1..100).include?(7) some_string =~ /something/ # non-standard Array === something (1..100) === 7 /something/ === some_string
-
Never put a space between a method name and the opening parenthesis. [link]
# standard f(3 + 2) + 1 # non-standard 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)
. [link] -
Favor the use of
Array#join
overArray#*
with [link] a string argument.# standard %w(one two three).join(', ') # => 'one, two, three' # non-standard %w(one two three) * ', ' # => 'one, two, three'
-
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. [link]# standard [*paths].each { |path| do_something(path) } # non-standard paths = [paths] unless paths.is_a? Array paths.each { |path| do_something(path) } # non-standard (and a bit more readable) Array(paths).each { |path| do_something(path) }
-
Use ranges or
Comparable#between?
instead of comparison logic. [link]# standard do_something if (1000..2000).include?(x) # standard do_something if x.between?(1000, 2000) # non-standard do_something if x >= 1000 && x <= 2000
-
Favor the use of predicate methods to explicit comparisons with
==
. Numeric comparisons are OK. [link]# standard if x.even? end if x.odd? end if x.nil? end if x.zero? end if x == 0 end # non-standard if x % 2 == 0 end if x % 2 == 1 end if x == nil end
-
Don't do explicit non-
nil
checks unless you're dealing with boolean values. [link]# standard do_something if something # standard def value_set? @some_boolean.present? end # non-standard do_something if !something.nil? do_something if something != nil
-
Avoid use of nested conditionals for flow of control. [link]
Prefer a guard clause when you can assert invalid data.
# standard def compute_thing(thing) return unless thing[:foo] update_with_bar(thing[:foo]) return re_compute(thing) unless thing[:foo][:bar] partial_compute(thing) end # non-standard def compute_thing(thing) if thing[:foo] update_with_bar(thing) if thing[:foo][:bar] partial_compute(thing) else re_compute(thing) end end end
Prefer
next
in loops instead of conditional blocks.# standard [0, 1, 2, 3].each do |item| next unless item > 1 puts item end # non-standard [0, 1, 2, 3].each do |item| if item > 1 puts item end end
-
Use
snake_case
for symbols, methods and variables. [link]# standard :some_symbol def some_method ... end # non-standard :'some symbol' :SomeSymbol :someSymbol someVar = 5 def someMethod ... end def SomeMethod ... end
-
Use
CamelCase
for classes and modules. (Keep acronyms like HTTP, RFC, XML uppercase.) [link]# standard class SomeClass ... end class SomeXML ... end # non-standard class Someclass ... end class Some_Class ... end class SomeXml ... end
-
Use
snake_case
for naming files, e.g.hello_world.rb
. [link] -
Use
snake_case
for naming directories, e.g.lib/hello_world/hello_world.rb
. [link] -
Aim to have just a single class/module per source file. Name the file name as the class/module, but replacing CamelCase with snake_case. [link]
-
Use
SCREAMING_SNAKE_CASE
for other constants. [link]# standard SOME_CONST = 5 # non-standard SomeConst = 5
-
The names of predicate methods should end in a question mark. (i.e.
Array#empty?
). Methods that don't return a boolean, shouldn't end in a question mark. [link] -
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. [link]# standard class Person def update end end # standard class Person def update! end def update end end # non-standard - there is no matching 'safe' method class Person def update! end end
-
Define the non-bang (safe) method in terms of the bang (dangerous) one if possible. [link]
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
overcollect
,detect
overfind
,select
overfind_all
, andsize
overlength
.Use
reduce
overinject
if the result is a single object. Useinject
overreduce
if the result is an enumeration. [link] -
Don't use
count
as a substitute forsize
. [link]# standard some_hash.size # non-standard some_hash.count
-
Use
flat_map
instead ofmap
+flatten
. [link]# standard all_songs = users.flat_map(&:songs).uniq # non-standard all_songs = users.map(&:songs).flatten.uniq
-
Use
reverse_each
instead ofreverse.each
. [link]# standard array.reverse_each { ... } # non-standard array.reverse.each { ... }
-
Write self-documenting code and ignore the rest of this section. [link]
-
Use one space between the leading
#
character of the comment and the text of the comment. [link] -
Comments longer than a word are capitalized and use punctuation. Use one space after periods. [link]
-
No superfluous comments! [link]
# non-standard counter += 1 # Increments counter by one.
-
Keep existing comments up-to-date. An outdated comment is worse than no comment at all. [link]
-
Avoid writing comments to explain bad code. Refactor the code to make it self-explanatory. (Do or do not - there is no try. --Yoda) [link]
-
Annotations should usually be written on the line immediately above the relevant code. [link]
-
The annotation keyword is followed by a colon and a space, then a note describing the problem. [link]
-
If multiple lines are required to describe the problem, subsequent lines should not be indented. [link]
def bar # FIXME: This has crashed occasionally since v3.2.1. It may # be related to the BarBazUtil upgrade. baz(:quux) 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. [link]
def bar sleep 100 # OPTIMIZE end
-
Use
TODO
to note missing features or functionality that should be added at a later date. [link] -
Use
FIXME
to note broken code that needs to be fixed. [link] -
Use
OPTIMIZE
to note slow or inefficient code that may cause performance problems. [link] -
Use
HACK
to note code smells where questionable coding practices were used and should be refactored away. [link] -
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?
[link] -
Use other custom annotation keywords if it feels appropriate, but be sure to document them in your project's
README
or similar. [link]
-
Use a consistent structure in your class definitions. [link]
class Person # extend and include go first extend SomeModule include AnotherModule # inner classes CustomErrorKlass = Class.new(StandardError) # constants are next SOME_CONSTANT = 20 # afterwards we have attribute macros attr_reader :name # followed by other macros (if any) validates :name # public class methods are next in line class << self def some_method end end # followed by public instance methods def some_method end # protected and private methods are grouped near the end protected def some_protected_method end private def some_private_method 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. [link]
# standard module SomeClass module_function def some_method # body omitted end def some_other_method end end # non-standard class SomeClass def self.some_method # body omitted end def self.some_other_method end end
-
Declare module namespaces separately. [link]
# standard module Namespace class SomeClass # body omitted end end # non-standard class Namespace::SomeClass # body omitted end
-
Favor the use of
module_function
overextend self
when you want to turn a module's instance methods into class methods. [link]# standard module Utilities module_function def parse_something(string) # do stuff here end def other_utility_method(number, string) # do some more stuff end end # non-standard module Utilities extend self 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. [link]# non-standard 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 # standard class Person attr_reader :first_name, :last_name def initialize(first_name, last_name) @first_name = first_name @last_name = last_name end end
-
Always supply a proper
to_s
method for classes that represent domain objects. [link]class Person attr_accessor :first_name, :last_name def initialize(first_name, last_name) self.first_name = first_name self.last_name = last_name end def to_s "#{first_name} #{last_name}" end end
-
Prefer duck-typing over inheritance, assuming there are no common implementations of methods. [link]
# standard class Duck def speak puts 'Quack! Quack' end end class Dog def speak puts 'Bau! Bau!' end end # non-standard class Animal # abstract method def speak end end class Duck < Animal def speak puts 'Quack! Quack' end end class Dog < Animal def speak puts 'Bau! Bau!' end end
-
Avoid the usage of class (
@@
) variables. [link]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"
-
Assign proper visibility levels to methods (
private
,protected
) in accordance with their intended usage. [link] -
Indent the
public
,protected
, andprivate
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. [link]class SomeClass def public_method # ... end private def private_method # ... end def another_private_method # ... end end
-
Use
class << self
to define singleton methods. [link]class TestClass # standard class << self def first_method # body omitted end def second_method_etc # body omitted end end # non-standard def self.some_other_method # body omitted end # non-standard def TestClass.some_method # body omitted end end
-
Signal exceptions using the
fail
method. Useraise
only when catching an exception and re-raising it (because here you're not failing, but explicitly and purposefully raising an exception). [link]begin fail 'Oops' rescue => error raise if error.message != 'Oops' end
-
Don't specify
RuntimeError
explicitly in the two argument version offail/raise
. [link]# standard fail 'message' # non-standard fail RuntimeError, 'message'
-
Prefer supplying an exception class and a message as two separate arguments to
fail/raise
, instead of an exception instance. [link]# standard fail SomeException, 'message' # non-standard fail SomeException.new('message')
-
Never return from an
ensure
block. [link]def foo begin fail ensure return 'very bad idea' end end
-
Use implicit begin blocks where possible. [link]
# standard def foo # main logic goes here rescue # failure handling goes here end # non-standard def foo begin # main logic goes here rescue # failure handling goes here end end
-
Mitigate the proliferation of
begin
blocks by using contingency methods [link]# standard def with_io_error_handling yield rescue IOError # handle IOError end with_io_error_handling { something_that_might_fail } with_io_error_handling { something_else_that_might_fail } # non-standard begin something_that_might_fail rescue IOError # handle IOError end begin something_else_that_might_fail rescue IOError # handle IOError end
-
Don't suppress exceptions. [link]
# non-standard do_something rescue nil # non-standard begin # an exception occurs here rescue SomeError # the rescue clause does absolutely nothing end
-
Always specify an exception type in the rescue clause. [link]
# standard def foo read_file rescue Errno::ENOENT => e handle_error(e) end
-
Don't use exceptions for flow of control. [link]
# standard if d.zero? puts 'Cannot divide by 0!' else n / d end # non-standard begin n / d rescue ZeroDivisionError puts 'Cannot divide by 0!' end
-
Release external resources obtained by your program in an ensure block. [link]
f = File.open('testfile') begin # .. process rescue # .. handle error ensure f.close if f end
-
Favor the use of exceptions for the standard library over introducing new exception classes. [link]
-
Prefer literal array and hash creation notation (unless you need to pass parameters to their constructors, that is). [link]
# standard arr = [] hash = {} # non-standard arr = Array.new hash = Hash.new
-
Prefer
%w
to the literal array syntax when you need an array of words. Apply this rule only to arrays with two or more elements. [link]# standard states = %w(draft open closed) # non-standard states = ['draft', 'open', 'closed']
-
Prefer
%i
to the literal array syntax when you need an array of symbols. Apply this rule only to arrays with two or more elements. [link]# standard states = %i(draft open closed) # non-standard states = [:draft, :open, :closed]
-
Avoid comma after the last item of an
Array
orHash
literal, especially when the items are not on separate lines. [link]# standard values = [1001, 2020, 3333] # non-standard values = [1001, 2020, 3333, ]
-
When accessing the first or last element from an array, prefer
first
orlast
over[0]
or[-1]
. [link] -
Use
Set
instead ofArray
when dealing with unique elements. [link] -
Prefer symbols instead of strings as hash keys. [link]
# standard hash = { :one => 1, :two => 2, :three => 3 } # non-standard hash = { 'one' => 1, 'two' => 2, 'three' => 3 }
-
Use hash rockets instead of Ruby 1.9 symbol notation. [link]
# standard hash = { :one => 1, :two => 2, :three => 3 } # non-standard hash = { one: 1, two: 2, three: 3 }
-
Avoid the use of mutable objects as hash keys. [link]
-
Use
Hash#key?
instead ofHash#has_key?
andHash#value?
instead ofHash#has_value?
. As noted here by Matz, the longer forms are considered deprecated. [link]# standard hash.key?(:test) hash.value?(value) # non-standard hash.has_key?(:test) hash.has_value?(value)
-
Use
Hash#fetch
when dealing with hash keys that should be present. [link]heroes = { :batman => 'Bruce Wayne', :superman => 'Clark Kent' } # standard heroes.fetch(:supermann) # raises a KeyError making the problem obvious # non-standard heroes[:batman] # => "Bruce Wayne" heroes[:supermann] # => nil
-
Introduce default values for hash keys via
Hash#fetch
as opposed to using custom logic. [link]# standard batman.fetch(:is_evil, true) # => false
-
Prefer the use of the block instead of the default value in
Hash#fetch
. [link]# standard batman.fetch(:powers) { get_batman_powers } # non-standard batman.fetch(:powers, get_batman_powers)
-
Use
Hash#values_at
when you need to retrieve several values consecutively from a hash. [link]# standard email, username = data.values_at('email', 'nickname') # non-standard email = data['email'] nickname = data['nickname']
-
Rely on the fact that as of Ruby 1.9 hashes are ordered. [link]
-
Never modify a collection while traversing it. [link]
-
Prefer string interpolation instead of string concatenation: [link]
# standard email_with_name = "#{user.name} <#{user.email}>" # non-standard email_with_name = user.name + ' <' + user.email + '>'
-
Use single quotes by default [link]
# standard name = 'Bozhidar' # non-standard name = "Bozhidar"
-
Don't use regular expressions if you just need plain text search in string:
string['text']
[link] -
For simple constructions you can use regexp directly through string index. [link]
match = string[/regexp/] # get content of matched regexp first_group = string[/text(grp)/, 1] # get content of captured group string[/text (grp)/, 1] = 'replace' # string => 'text replace'
-
Use non-capturing groups when you don't use captured result of parentheses. [link]
/(?:first|second)/ # standard /(first|second)/ # non-standard
-
Don't use the cryptic Perl-legacy variables denoting last regexp group matches (
$1
,$2
, etc). UseRegexp.last_match[n]
instead. [link]/(regexp)/ =~ string ... # standard process Regexp.last_match[1] # non-standard process $1
-
Avoid using numbered groups as it can be hard to track what they contain. Named groups can be used instead. [link]
# standard /(?<meaningful_var>regexp)/ =~ string ... process meaningful_var # non-standard /(regexp)/ =~ string ... process Regexp.last_match[1]
-
Character classes have only a few special characters you should care about:
^
,-
,\
,]
, so don't escape.
or brackets in[]
. [link] -
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/
). [link]string = "some injection\nusername" string[/^username$/] # matches string[/\Ausername\z/] # doesn'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. [link]regexp = / start # some text \s # white space char (group) # first group (?:alt1|alt2) # some alternation end /x
-
For complex replacements
sub
/gsub
can be used with block or hash. [link]
-
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. [link]# standard (requires interpolation, has quotes, single line) %(<tr><td class="name">#{name}</td>) # non-standard (no interpolation needed) %(<div class="text">Some text</div>) # should be '<div class="text">Some text</div>' # non-standard (no double-quotes) %(This is #{quality} style) # should be "This is #{quality} style" # non-standard (multiple lines) %(<div>\n<span class="big">#{exclamation}</span>\n</div>) # should be a heredoc.
-
Avoid
%q
unless you have a string with both'
and"
in it. Regular string literals are more readable and should be preferred unless a lot of characters would have to be escaped in them. [link]# standard name = 'Bruce Wayne' time = "8 o'clock" question = '"What did you say?"' # non-standard name = %q(Bruce Wayne) time = %q(8 o'clock) question = %q("What did you say?")
-
Use
%r
only for regular expressions matching more than one '/' character. [link]# standard %r(^/blog/2011/(.*)$) # non-standard %r(\s+) # non-standard %r(^/(.*)$) # should be /^\/(.*)$/
-
Avoid the use of
%x
unless you're going to invoke a command with backquotes in it(which is rather unlikely). [link]# standard date = `date` echo = %x(echo `date`) # non-standard date = %x(date)
-
Avoid the use of
%s
. It seems that the community has decided:"some string"
is the preferred way to create a symbol with spaces in it. [link] -
Prefer
()
as delimiters for all%
literals, except%r
. Since braces often appear inside regular expressions in many scenarios a less common character like{
might be a better choice for a delimiter, depending on the regexp's content. [link]# standard %w(one two three) %q("Test's king!", John said.) # non-standard %w[one two three] %q{"Test's king!", John said.}
-
Code in a functional way, avoiding mutation when that makes sense. [link]
-
Avoid methods longer than 1 line of code inside the def end block. [link]
-
Avoid hashes as optional parameters. [link]
-
Avoid parameter lists longer than three parameters. [link]
-
Use module instance variables instead of global variables. [link]
# standard module Foo class << self attr_accessor :bar end end Foo.bar = 1 # non-standard $foo_bar = 1
-
Avoid
alias
whenalias_method
will do. [link] -
Use
OptionParser
for parsing complex command line options andruby -s
for trivial command line options. [link] -
Prefer
Time.now
overTime.new
when retrieving the current system time. [link] -
Do not mutate arguments unless that is the purpose of the method. [link]
-
Avoid more than three levels of block nesting. [link]