Skip to content

Instantly share code, notes, and snippets.

@kbparagua
Last active April 18, 2017 06:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kbparagua/a70449db5803273159603a3ca332b1d9 to your computer and use it in GitHub Desktop.
Save kbparagua/a70449db5803273159603a3ca332b1d9 to your computer and use it in GitHub Desktop.

Ruby Style Guide

This style guide is based on this community style guide.

Most of the rules listed will have a brief explanation to understand the reasoning behind each. If none is provided, maybe it's too obvious, or assume that its main purpose is to improve readability.

All new projects must adhere to this style guide. Existing and ongoing projects should stick with their current conventions or they can adapt this if no convention is being followed yet.

General

  1. Limit lines to 120 characters only. [link]

  2. Indent using soft indents (2 whitespace) instead of real tab.[link]

  3. Avoid trailing whitespace.[link]

    • Configure your text editor to automatically trim trailing whitespaces.
  4. End each file with newline.[link]

    • Configure your text editor to automatically put empty line for each file.

Operators

  1. Whitespace around binary operators.[link]
# bad
x>1
str='Hello World'
1+300

# good
x > 1
str = 'Hello World'
1 + 300
  1. No whitespace after unary operators.[link]
# bad
! is_correct

# good
!is_correct
  1. Whitespace after comma.[link]
# bad
person.update :name,'Robert'

# good
person.update :name, 'Robert'

Assignment

  1. Long right-hand expression should start on the next line.[link]
# bad
# less readable.
result = my_super_long_variable_name.
  long_public_method_name_something argument
  
# ok
# more readable but effortful indentation.
result = my_super_long_variable_name.
          long_public_method_name_something argument
          
# good
# more readable but unnecessary new line.
result =
  my_super_long_variable_name.
    long_public_method_name_something argument
   

# better
# more readable.
result =
  my_super_long_variable_name.long_public_method_name_something argument
  1. if-else, case-when, loop, and other multi-line block statements should start on the next line.[link]
# bad
# less readable, assignment is not instantly visible.
output = if some_condition
  # do something
else
  # do another thing
end

# good
# more readable, assignment is instantly visible but not width efficient.
output = if some_condition
            # do something
         else
            # do another thing
         end
         
# better
# more readable and width efficient.
output =
  if some_condition
    # do something
  else
    # do another thing
  end
  1. Content of large hash value should start on the next line, 1 pair per line.[link]
# bad
# less readable.
my_big_hash = {:a => 'Ace of Spades', :b => 'Two of Spades', :c => 'Three of Spades',
  :d => 'Four of Spades', :e => 'Five of Spades', :g => 'Joker'
}

# ok
# key-value pairs are much clearer.
my_big_hash = {:a => 'Ace of Spades',
  :b => 'Two of Spades',
  :c => 'Three of Spades',
  :d => 'Four of Spades',
  :e => 'Five of Spades',
  :g => 'Joker'
}

# ok
# much readable but indentation is a little bit off and not width efficient.
my_big_hash = {:a => 'Ace of Spades',
                :b => 'Two of Spades',
                :c => 'Three of Spades',
                :d => 'Four of Spades',
                :e => 'Five of Spades',
                :g => 'Joker'
              }

# good
# much readable and uniform indentation, but not width efficient.
my_big_hash = 
  {
    :a => 'Ace of Spades',
    :b => 'Two of Spades',
    :c => 'Three of Spades',
    :d => 'Four of Spades',
    :e => 'Five of Spades',
    :g => 'Joker'
  }

# better
# much readable and width efficient.
my_big_hash = {
  :a => 'Ace of Spades',
  :b => 'Two of Spades',
  :c => 'Three of Spades',
  :d => 'Four of Spades',
  :e => 'Five of Spades',
  :g => 'Joker'
}
  1. Content of large array value should start on the next line, with each of the succeeding line aligned.[link]
# bad
# less readable.
huge_array = [ace, king, queen, jack, ten, nine, eight, seven, six, 
  five, four, three, two
]

# ok
# readable but effortful indentation.
huge_array = [ace, king, queen, jack, ten, nine, eight, seven, six, 
              five, four, three, two]
              
# ok
# more readable but not width efficient.
huge_array = 
  [
    ace, king, queen, jack, ten, nine, eight, seven, six, five, four,
    three, two
  ]

# good
# more readable but too much line usage.
huge_array = [
  ace,
  king,
  queen,
  jack,
  ten,
  nine,
  eight,
  seven,
  six,
  five,
  four,
  three,
  two
]
            
# better
# more readable and width efficient.
huge_array = [
  ace, king, queen, jack, ten, nine, eight, seven, six, five, four,
  three, two
]

Hash

  1. Use hash rocket (fat arrow) => instead of colon :.[link]
# bad
# confusing especially for symbol values.
{foo: 'value', bar: 'value 1'}
{foo: :value, bar: :value1}

# good
# much readable.
{:foo => 'value', :bar => 'value 1'}
{:foo => :value, :bar => :value1}
  1. No whitespace around hash body. Curly braces {} with spaces inside will only be used for blocks, in order to make a visual difference between blocks and hashes.[link]
# bad
{ :foo => 'value', :bar => 'value 1' }

# good
{:foo => 'value', :bar => 'value 1'}
  1. 1 key-value pair per line for large hash.[link]
# bad
{:a => 'aAa', :b => 'B', :c => 'CCC', :d => 'Dee', :e => 'elephant',
  :f => 'FFfff', :g => 'GEE'}
  
# good
# but the indentation is a little bit confusing.
{:a => 'aAa',
  :b => 'B',
  :c => 'CCC',
  :d => 'Dee',
  :e => 'elephant',
  :f => 'FFfff', 
  :g => 'GEE'}
  
# better
{
  :a => 'aAa',
  :b => 'B',
  :c => 'CCC',
  :d => 'Dee',
  :e => 'elephant',
  :f => 'FFfff',
  :g => 'GEE'
}

Array

  1. No whitespace around array body.[link]
# bad
[ 1, 2, 3 ]

# good
[1, 2, 3]
  1. Align values for large arrays.[link]
# bad
[red, blue, green, white, black, yellow, pink, 
  orange, gray]

# good
# more readable but not line efficient.
[
  red,
  blue,
  green,
  white,
  black,
  yellow,
  pink, 
  orange,
  gray
]

# better
# much readable and line efficient.
[
  red, blue, green, white, black, yellow, pink,
  orange, gray
]

Numbers

  1. Use _ to promote readability of numbers.[link]
# bad
# how many zeroes?!
number = 1000000

# good
number = 1_000_000

Strings

  1. Use single quotes (') by default.[link]
# bad
# needs to check the whole string content if there is an interpolation.
"The quick brown fox jumps over the lazy dog"

# good
# can quickly recognize that there is no interpolation.
'The quick brown fox jumps over the lazy dog'
  1. Use double quotes "" if interpolation is needed.[link]

  2. Use double quotes "" if you have single quote in your string.[link]

  3. Use << instead of + when concatenating strings.[link]

# bad
# slower
str = 'Hello'
str += ' World'

# good
# faster
str = 'Hello'
str << ' World'

Check this reference.

  1. Use join when concatenating with a uniform delimiter.[link]
folder = 'lib'
subfolder = 'foo'
file = 'baz.rb'

# bad
# less readable and hard to change the delimiter.
filename = "#{folder}/#{subfolder}/#{file}"

# good
# more readable and easy to change the delimiter.
filename = [folder, subfolder, file].join '/'
  1. Use \ to break long strings instead of +.[link]
# bad
# slower
'Bacon chicken turkey' +
' hotdog eggs tuna spam coffee' +
' mustard ketchup mayo'

# good
# faster
'Bacon chicken turkey' \
' hotdog eggs tuna spam coffee' \
' mustard ketchup mayo'

Check this reference.

Blocks

  1. Whitespace around body of proc, lambda, and block.[link]
# bad
# format is too tight.
Proc.new {puts 'hello'}
list.each {|item| item.delete}

# good
# good spacing and much readable.
Proc.new { puts 'hello' }
list.each { |item| item.delete }
  1. Whitespace before block.[link]
# bad
list.each{ |item| item.save }

# good
list.each { |item| item.save }
  1. Use do and end for multi-line blocks.[link]
# bad
list.each { |item|
  item.name = 'new name'
  item.save
}

# good
list.each do |item|
  item.name = 'new name'
  item.save
end
  1. Do not use 2 or more consecutive line-break inside blocks.[link]
# bad
# unnecessary logical separation.
list.each do |item|
  statement_1
  
  
  statement_2
end

# good
# enough logical separation.
list.each do |item|
  statement_1
  
  statement_2
end
  1. Do not chain multi-line blocks.[link]
# bad
list.map do |item|
  # do something here
  # evaluate something
end.each do |item|
  # do something here
  # random stuff
end

# good
result = 
  list.map do |item|
    # do something here
    # evaluate something
  end
  
result.each do |item|
  # do something here
  # random stuff
end

Methods

a. Definition

  1. Method definition should not use parentheses.[link]
# bad
def foo(arg1, arg2, arg3)
  # do something
end

# good
def foo arg1, arg2, arg3
  # do something
end
  1. 1 Line break to separate method definitions.[link]
# bad
# unnecessary or too much logical separation. 
def foo
  statement_1
  statement_2
  
  statement_3
end



def bar
  statement_a
  statement_b
  
  statement_c
end

# good
# enough logical separation.
def foo
  statement_1
  statement_2
  
  statement_3
end

def bar
  statement_a
  statement_b
  
  statement_c
end
  1. Do not use 2 or more consecutive line-break inside methods.[link]
# bad
# too much logical separation.
def method
  statement_1
  
  
  statement_2
end

# good
# enough logical separation.
def method
  statement_1
  
  statement_2
end
  1. Avoid using more than 3 arguments.[link]
# bad
# hard to memorize argument order.
def update_something color, size, material, brand
  # update
end

# good
# no need to memorize argument order, just the argument name.
def update_something properties = {:color => nil, :size => nil, :material => nil, :brand => nil}
  # update
end

# good
# arguments are encapsulated on an object.
def update_something properties = PropertyList.new
  # update
end

# good
# easier to memorize argument order.
def plot x, y, z
  # plot point
end

b. Execution

  1. No space around ().[link]
# bad
foo( arg1 ).bar( arg2 ).baz

# good
foo(arg1).bar(arg2).baz
  1. Avoid passing method call as argument.[link]
# bad
object.explode universe.answer(human.get(:life))

# good
life = human.get :life
answer = universe.answer life
object.explode answer
  1. Single-line method call should not use parentheses.[link]
# bad
foo(arg1, arg2, arg3)

# good
foo arg1, arg2, arg3
  1. Large hash as argument.[link]
# bad
object.update(:prop1 => 'Value 01',
              :prop2 => 'Value 02',
              :prop3 => 'Value 03',
              :prop4 => 'Value 04')

# good
object.update(
  :prop1 => 'Value 01',
  :prop2 => 'Value 02',
  :prop3 => 'Value 03',
  :prop4 => 'Value 04'
)
  1. Large array as argument.[link]
# bad
object.add ['jack of diamonds', 'two of spades', 'ten of clubs',
  'seven of diamonds', 'king of hearts', 'ace of hearts', 'nine of spades',
  'four of diamonds', 'joker', 'three of clubs', 'joker']

# good
object.add ['jack of diamonds', 'two of spades', 'ten of clubs',
            'seven of diamonds', 'king of hearts', 'ace of hearts',
            'nine of spades', 'four of diamonds', 'joker', 'three of clubs', 
            'joker']

# better
object.add [
  'jack of diamonds', 'two of spades', 'ten of clubs', 'seven of diamonds',
  'king of hearts', 'ace of hearts', 'nine of spades', 'four of diamonds',
  'joker', 'three of clubs', 'joker'
]
  1. As much as possible, write 1 argument per line for long argument list.[link]
# bad
do_this(
  'value', 'another value', [
    item_1, item_2, item_3, item_4
    ...
    item_n
  ],
  {
    :a => 1,
    :b => 2,
    ...
    :z => 26
  },
  1000, 2000, object
)


# good
do_this(
  'value',
  'another value',
  [
    item_1, item_2, item_3, item_4
    ...
    item_n
  ],
  {
    :a => 1,
    :b => 2,
    ...
    :z => 26
  },
  1000,
  2000,
  object
)
  1. As much as possible, use 1 method call per line for long method chains.[link]
# bad
object.foo.
  bar(:key => 'Bar!').baz(:a, :b).
  fizz(:x => 1000, :y => 2000, :z => 3000)
  
# good
object.
  foo.
  bar(:key => 'Bar!').
  baz(:a, :b).
  fizz(:x => 1000, :y => 2000, :z => 3000)
  1. Use trailing . for long method chains.[link]
# bad
object
  .foo
  .bar
  .baz

# good
object.
  foo.
  bar.
  baz

Conditions

  1. No parentheses around conditions.[link]
if expression > 100
  1. Do not use and and or on conditions.[link]

  2. Don't use unless with else. Convert to if-else statement.[link]

  3. Use early return if possible.[link]

# bad
def foo
  if must_be_true
    # do something
  end
end

# good
def foo
  return unless must_be_true
  
  # do something
end
  1. Use ternary operator ?: for simple statements instead of if-else.[link]
some_condition ? do_something : do_something_else(:arg => 100)
  1. For long ternary operator ?: statement, write 1 option per line.[link]
some_condition ?
  do_something('hello').map(&:to_f) :
  do_something_else(:arg1 => 100, :arg2 => 200)
  1. 1 expression per line for long condition.[link]
# bad
if something || another ||
  maybe_true || sometimes_false
  
# good
if something ||
  another ||
  maybe_true ||
  sometimes_false
  1. For long condition, leave 1 line break before the body.[link]
# bad
if something ||
  another ||
  maybe_true ||
  sometimes_false
  # body starts here
end

# good
if something ||
  another ||
  maybe_true ||
  sometimes_false
  
  # body starts here
end
  1. Favor if/else modifier for simple single-line body.[link]
# bad
if something_is_true
  puts something
end
  
# good
puts something if something_is_true
  1. Do not use if/else modifier for multi-line blocks.[link]
# bad
list.each do |item|
  #
  # multi-line body
  #
end if test_condition

# good
if test_condition
  list.each do |item|
    #
    # multi-line body
    #
  end
end
  1. Favor unless over if for negative conditions.[link]
# bad
dance if !tired

# good
dance unless tired
  1. Don't use unless for negative expressions.[link]
# bad
do_something unless list.empty?
do_another unless text.blank?

# good
do_something if list.any?
do_another if text.present?
  1. Align when with case.[link]
case something
when 1
  puts 1
when 2
  puts 2
end
  1. Multiple expressions alignment.
# option 1
try_this_one ||
or_this_one ||
or_maybe_this_one ||
okay_this_one

    
# option 2
try_this_one ||
  or_this_one ||
  or_maybe_this_one ||
  okay_this_one
  
# option 3
try_this_one ||
  or_this_one ||
    or_maybe_this_one ||
      okay_this_one
    

TODO: Discuss this.

Comments

  1. Only use multi-line comments for removing a block of code temporarily.[link]
  2. Do not use end-of-line comments.[link]
# bad
do_something # comment about the process

# good
# comment about the process
do_something
  1. Use TODO: to note task that needs to be done on a specific statement or block of code in the future.[link]
# TODO: refactor this
def complex_method
  # do something unexplainable here
end
  1. Use SEE: <insert link> if you need to add a reference to a specific statement or block of code.[link]
# SEE: http://en.wikipedia.org/wiki/Earth_radius#Mean_radius
EARTH_MIN_RADIUS_KM = 6371;
EARTH_MIN_RADIUS_MI = 3958;

Class and Module

  1. Line break around class and module contents.[link]
module MyModule

  def my_method
    # do something
  end

end
  1. Use 2 line breaks to create logical separations inside class and module.[link]
class User

  include Authenticator
  include PasswordManager


  attr_accessor :name, :password, :email

end
  1. Use 3 line breaks to create a logical separation between group of methods inside module/class.[link]
# ok
# related methods are grouped but there is no visible logical separation.
class User
  
  def fook
    # foo related method
  end
  
  
  def fooz
    # foo related method
  end


  def bark
    # bar related method
  end
  
  
  def barz
    # bar related method
  end

end
  1. 5 line breaks between class/module definitions.[link]

  2. Always use self when calling public and protected properties of a class.[link]

# bad
class Person
  
  def save
    something = 'xxx'
    collection = []
    
    collection.each do |it|
      result = it.evaluate_this something
      nonsense = result * 100
    end
    
    # is name a local variable or an instance method/variable?
    raise 'error' if name.blank?
  end
  
end

# good
class Person
  
  def save
    something = 'xxx'
    collection = []
    
    collection.each do |it|
      result = it.evaluate_this something
      nonsense = result * 100
    end
    
    # it is clear that name is an instance method/variable.
    raise 'error' if self.name.blank?
  end
  
end
  1. Class statements order.[link]
require 'uri'

class Person

  # include and extend directives
  extend Ability
  include Model
  
  
  # constants
  MY_CONSTANT = 100
  
  
  # attribute directives
  attr_accessor :name, :age
  attr_reader :nationality
  
  
  #
  #
  # Other macros here
  #
  #
  

  # class methods
  def self.class_method
    # do something
  end
  
  
  
  # public methods
  def public_method
    # do something
  end
  
  
  
  # protected methods
  protected
  
  def protected_utility_method
    # do something
  end
  
  
  
  # private methods
  private

  def private_utility_method
    # do something
  end
  
end

Visibility Modifier

  1. Use protected for utility methods that use instance variables/methods.[link]
class User

  protected
  
  def format_name
    self.name = self.last_name + ', ' + self.first_name
  end
  
  
  def default_instance_var
    @my_variable = 'Default Value'
  end
  
end
  1. Use private for utility methods that don't use instance variables/methods.[link]
class User
  
  private
  
  def day_only date
    date.strftime '%A'
  end
  
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment