Skip to content
Jane S. Sebastian edited this page Jul 8, 2015 · 1 revision

Ruby Style Guide

This is a work in progress Ruby Style Guide, which represents the coding conventions most recently used.

This is a much modified revision of content and structure from these three sources: Airbnb, Github and the original Ruby style guide written by Bozhidar Batsov.

Table of Contents

  1. Whitespace
    1. General
    2. Inline
    3. Newlines
  2. Comments
    1. TODOs
    2. Commented-out code
  3. Naming
  4. Methods
    1. Method definitions
    2. Method calls
    3. Method chaining
    4. Method return values
    5. Method whitespace
  5. Conditions
    1. Conditional expressions
    2. Ternary operator
    3. Case Indentation
  6. Syntax
  7. Classes
  8. Collections
  9. Strings
  10. Regular Expressions
  11. Percent Literals
  12. Line Length Techniques
  13. Be Consistent
  14. Above All Else

Whitespace

General

  • Use UTF-8 as the source file encoding.

  • Use Unix-style line endings.

  • Use soft-tabs with a two space-indent.

  • Never leave trailing whitespace.

  • Lines should be approximately 80 and no more than 100 characters long.

  • End each file with a newline.

Inline

  • Use spaces around operators; after commas, colons, and semicolons; and after { and before }.

    sum = 1 + 2
    a, b = 1, 2
    1 > 2 ? true : false; puts 'Hi'
    [1, 2, 3].each{ |x| puts x }
    { :one => 1, :two => 2 }

    Except for embedded expressions:

    "string#{expr}"
  • No spaces after (, [ or before ], ).

    some(arg).other
    [1, 2, 3].length
  • Prefer no spaces before { or after }.

    [1, 2, 3].map{ |x| x + 2 }.sort
  • No space after !.

    !something
  • No space inside range literals.

    1..3
    'a'...'z'
  • Method whitespace guidelines can be found with the Method whitespace section.

  • Conditional whitespace guidelines can be found within the Conditions section.

Newlines

  • Don't use ; to separate statements and expressions. Use one expression per line.

  • Add a new line after methods, conditionals, blocks, case statements, etc.

    if robot.is_awesome?
      destroy_tokyo
    end
    
    robot.add_trait(:human_like_intelligence)
  • No new line after a class or module definition and the final end statement.

    class RobotGodzilla
      def destroy_tokyo
        ...
      end
    
      private
    
      def send_robot_present
        ...
      end
    end

Comments

Good code is like a good joke - it needs no explanation.
-- Russ Olsen

Use comments in a reasonable fashion. Comments are very important, but the best code is self-documenting. Giving sensible names to types and variables is much better than using obscure names that you must then explain through comments.

When writing your comments, write for your audience: the next contributor who will need to understand your code. Be generous — the next one may be you!

TODOs

Use TODO comments for code that is temporary, a short-term solution, or good-enough but not perfect.

TODOs should include the string TODO in all caps, followed by the full name of the person who can best provide context about the problem referenced by the TODO, in parentheses. A colon is optional. A comment explaining what there is to do is required. The main purpose is to have a consistent TODO format that can be searched to find the person who can provide more details upon request. A TODO is not a commitment that the person referenced will fix the problem. Thus when you create a TODO, it is almost always your name that is given.

Space holder for our business rule todos

  # TODO(Santi White): Use proper namespacing for this constant.

Commented-out code

Avoid leaving commented-out code in our codebase. If it must happen, create a TODO to document why it has been commented out and how to restore the code or remove the commented-out code completely.

Naming

The only real difficulties in programming are cache invalidation and naming things.
-- Phil Karlton

  • Use snake_case for methods and variables.

  • Use CamelCase for classes and modules. (Keep acronyms like HTTP, RFC, XML uppercase.)

  • Use SCREAMING_SNAKE_CASE for other constants.

  • The names of predicate methods (methods that return a boolean value) should end in a question mark. (i.e. Array#empty?).

Methods

  • Avoid single-line methods. Although they are somewhat popular in the wild, there are a few peculiarities about their definition syntax that make their use undesirable.

    # good
    def some_method
      body
    end
    
    # avoid
    def no_braces_method; body end
    
    def no_braces_method; body; end
    
    def some_method() body end
    
    def too_much; something; something_else; end

    One exception to the rule are empty-body methods.

    def no_op; end

Method definitions

  • 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
  • Use spaces around the = operator when assigning default values to method parameters.

    def some_method(arg1 = :default, arg2 = nil, arg3 = [])
      ...
    end
  • Avoid self where not required. It is only required when calling a self write accessor.

    # good
    def ready?
      if last_reviewed_at > last_updated_at
        worker.update(content, options)
        self.status = :in_progress
      end
      status == :verified
    end
    
    # avoid
    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

Method calls

Use parentheses for a method call:

  • If the method returns a value.

    # good
    @current_user = User.find_by_id(1964192)
    
    # avoid
    @current_user = User.find_by_id 1964192
  • If the first argument to the method uses parentheses.

    # good
    put!((x + y) % len, value)
    
    # avoid
    put! (x + y) % len, value
  • Never put a space between a method name and the opening parenthesis.

    # good
    f(3 + 2) + 1
    
    # avoid
    f (3 + 2) + 1

Omit parentheses for a method call:

  • If the method accepts no arguments.

    # good
    nil?
    
    # avoid
    nil?()
  • If it is a Rails convention to omit the parentheses, such as calling the render method within a controller or view.

    # good
    render :partial => 'foo'
    
    # avoid
    render(:partial => 'foo')

In either case:

  • If a method accepts an options hash as the last argument, do not use { } during invocation.

    # good
    get '/v1/reservations', :id => 54875
    
    # avoid
    get '/v1/reservations', { :id => 54875 }

Method chaining

  • Use trailing . style for both chaining methods and scopes. It indicates the expression continues and allows for the code to be easily used within console.

    # good
    one.two.three.
      four
    
    # avoid
    one.two.three
      .four

Method return values

  • Avoid return where not required. The last line or variable of a method will automatically be returned.

    # good
    def some_method(some_arr)
      some_arr.size
    end
    
    # avoid
    def some_method(some_arr)
      return some_arr.size
    end

Method indentation

  • Indent the public, protected, and private methods as much the method definitions they apply to. Leave one blank line above and below them.

    class SomeClass
      def public_method
        # ...
      end
    
      private
    
      def private_method
        # ...
      end
    end
  • Align the parameters of a method call if they span more than one line. When aligning parameters is not appropriate due to line-length constraints, single indent for the lines after the first is also acceptable.

    def send_mail(source)
      Mailer.deliver(:to => '[email protected]',
                     :from => '[email protected]',
                     :subject => 'Important message',
                     :body => source.text)
    end
    
    def send_mail(source)
      Mailer.deliver(
        :to => '[email protected]',
        :from => '[email protected]',
        :subject => 'Important message',
        :body => source.text
      )
    end

Conditions

  • The and and or and 'not' keywords are banned. It's just not worth it. Always use && and || and ! instead.

  • Avoid the use of !!.

  • Avoid the case equality operator === which is intended for case statements and eql? as the stricter comparison semantics provided by it are rarely needed in practice.

  • Favor the use of predicate methods to explicit comparisons with ==.

  • Avoid use of nested conditionals for flow of control. Prefer a guard clause when you can assert invalid data. A guard clause is a conditional statement at the top of a function that bails out as soon as it can.

    # good
    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
    
    # avoid
    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
  • Avoid using the return value of an assignment within conditional expressions.

    # good
    v = next_value
    if v == 'hello' ...
    
    # avoid
    if v = array.grep(/foo/) ...
  • Prefer next in loops instead of conditional blocks.

    # good
    [0, 1, 2, 3].each do |item|
      next unless item > 1
      puts item
    end
    
    # avoid
    [0, 1, 2, 3].each do |item|
      if item > 1
        puts item
      end
    end
  • Use ranges or Comparable#between? instead of complex comparison logic when possible.

    # good
    do_something if x.between?(1000, 2000)
    do_something if (1000..2000).cover?(x)
    
    # avoid
    do_something if x >= 1000 && x <= 2000
    
    # avoid
    do_something if (1000..2000).include?(x)
  • Use single line if statements but only when logic is very simple and fits on one line.

    # good
    return if self.reconciled?
    
    display_trebuchet_experiments if trebuchet_meets_display_requirements?
    
    # avoid
    add_trebuchet_experiments_on_page if trebuchet_is_this_special_type_that_bears_scrutiny && !trebuchet_restricted_access?
    
    # avoid - this is complex and deserves multiple lines and a comment
    parts[i] = part.to_i(INTEGER_BASE) if !part.nil? && [0, 2, 3].include?(i)
  • Never use unless with else. Rewrite these with the positive case first.

    # good
    if success?
      puts 'success'
    else
      puts 'failure'
    end
    
    # avoid
    unless success?
      puts 'failure'
    else
      puts 'success'
    end
  • Favor unless over if for negative conditions (or control flow ||).

    # good
    do_something unless some_condition
    
    # another good option
    some_condition || do_something
    
    # avoid
    do_something if !some_condition
    
    # avoid
    do_something if not some_condition
  • Avoid unless with multiple conditions. In the uncommon event of needing them, put parentheses around these conditions.

    # okay
    if !(foo? && bar?)
      ...
    end
    
    # avoid
    unless foo? && bar?
      ...
    end

Ternary operator

  • Favor the ternary operator(?:) over if/else/end constructs for trivial expressions.

    # good
    result = some_condition ? something : something_else
    
    # avoid
    result = if some_condition then something else something_else end
  • Do not nest or have multi-line ternary operators, use if/else/end instead.

    # good
    if some_condition
      nested_condition ? nested_something : nested_something_else
    else
      something_else
    end
    
    # avoid
    some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else

Case Indentation

  • If you are reading this to find the preferred style for a case statement, this subject needs to be revisited. If it is successfully revisited and a style for the (hopefully) rarely used case statement is agreed upon, kindly add it in this space.

Syntax

  • Never use for, use iterators such as each instead, unless you know exactly why. Which could be because for does not have scope and thus variables defined within in it are available outside of it.

    arr = [1, 2, 3]
    
    # good
    arr.each{ |elem| puts elem }
    
    # avoid
    for elem in arr do
      puts elem
    end
  • Prefer {...} over do...end for single-line blocks. Avoid using {...} for multi-line blocks (multiline chaining is always ugly). Always use do...end for "control flow" and "method definitions" (e.g. in Rakefiles and certain DSLs). Avoid do...end when chaining.

    names = ["Bozhidar", "Steve", "Sarah"]
    
    # good
    names.each{ |name| puts name }
    
    # avoid
    names.each do |name|
      puts name
    end
    
    # good
    names.select{ |name| name.start_with?("S") }.map{ |name| name.upcase }
    
    # avoid
    names.select do |name|
      name.start_with?("S")
    end.map{ |name| name.upcase }

    Some will argue that multiline chaining would look okay with the use of {...}, but they should ask themselves if this code is really readable and whether the block's content can be extracted into nifty methods.

  • Use ||= freely to initialize variables.

    # set name to Bozhidar, only if it's nil or false
    name ||= 'Bozhidar'
  • Don't use ||= to initialize boolean variables. (Consider what would happen if the current value happened to be false.)

    # good
    enabled = true if enabled.nil?
    
    # avoid with extreme prejudice - would set enabled
    # to true even if it was false
    enabled ||= true
  • Avoid unused block parameters by referring to either the keys or values of a hash if both are not required.

    # good
    result = hash.values.map{ |v| v + 1 }
    
    hash.keys.each{ |k| puts k }
    
    # avoid
    result = hash.map{ |_, v| v + 1 }
    
    hash.each{ |k, _| puts k }
  • When a method block takes only one argument, and the body consists solely of reading an attribute or calling one method with no arguments, use the &: shorthand.

    # good
    bluths.map(&:occupation)
    bluths.select(&:blue_self?)
    
    # avoid
    bluths.map { |bluth| bluth.occupation }
    bluths.select { |bluth| bluth.blue_self? }

Classes

  • Avoid the usage of class (@@) variables due to their "nasty" behavior in inheritance.

    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"

    As you can see all the classes in a class hierarchy actually share one class variable. Class instance variables should usually be preferred over class variables.

  • Use def self.method to define singleton methods. This makes the methods more resistant to refactoring changes.

    class TestClass
      # good
      def self.some_other_method
        ...
      end
    
      # avoid
      def TestClass.some_method
        ...
      end
  • Avoid class << self except when necessary, e.g. single accessors and aliased attributes.

    class TestClass
      # good
      class << self
        attr_accessor :per_page
        alias_method :nwo, :find_by_name_with_owner
      end
    
      def self.first_method
        ...
      end
    
      def self.second_method_etc
        ...
      end
    
      # avoid
      class << self
        def first_method
          ...
        end
    
        def second_method_etc
          ...
        end
      end
    end

Collections

  • Use literal array and hash creation notation unless you need to pass parameters to their constructors.

    # good
    arr = []
    hash = {}
    
    # avoid
    arr = Array.new
    hash = Hash.new
  • Rely on the fact that as of Ruby 1.9 hashes are ordered.

  • Do not modify a collection while traversing it.

  • Use Set instead of Array when dealing with unique elements. Set implements a collection of unordered values with no duplicates. This is a hybrid of Array's intuitive inter-operation facilities and Hash's fast lookup.

  • Prefer %w to the literal array syntax when you need an array of strings.

    # good
    STATES = %w(draft open closed)
    
    # avoid
    STATES = ["draft", "open", "closed"]
  • Align the elements of array literals spanning multiple lines.

    # good
    menu_item = [
      'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
      'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam'
    ]
    
    # good
    menu_item =
      ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
       'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']
    
    # avoid - single indent
    menu_item = ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
      'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']
  • Use hashrocket syntax for Hash literals instead of the JSON style introduced in 1.9. This allows for more consistency.

    # good
    hash = { :one => 1, :two => 2, :three => 3 }
    
    # avoid
    hash = { one: 1, two: 2, three: 3 }
  • Use symbols instead of strings as hash keys.

    # good
    hash = { :one => 1, :two => 2, :three => 3 }
    
    # avoid
    hash = { 'one' => 1, 'two' => 2, 'three' => 3 }
  • Use multi-line hashes when it makes the code more readable

    hash = {
      :protocol => 'https',
      :only_path => false,
      :controller => :users,
      :action => :set_password,
      :redirect => @redirect_url,
      :secret => @secret
    }
  • When accessing the first or last element from an array, prefer first or last over [0] or [-1].

  • Prefer map over collect, find over detect, select over find_all, reduce over inject and size over length.

  • Don't use count as a substitute for size. For Enumerable objects other than Array it will iterate the entire collection in order to determine its size.

  • Use reverse_each instead of reverse.each. reverse_each doesn't do a new array allocation and that's a good thing.

    # good
    array.reverse_each { ... }
    
    # avoid
    array.reverse.each { ... }

Strings

  • Use single-quoted strings. Though this will require delimiter change and possibly more escape characters, it is helpful to quickly know when a string is just a string and the shift key is for suckers.

    # good
    name = 'Paddington'
    
    # avoid
    name = "Paddington"
  • Prefer string interpolation instead of string concatenation:

    # good
    email_with_name = "#{user.name} <#{user.email}>"
    
    # avoid
    email_with_name = user.name + ' <' + user.email + '>'
  • Avoid using String#+ when you need to construct large data chunks. Instead, use String#<<. Concatenation mutates the string instance in-place and is always faster than String#+, 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
  • For large blocks of text, prefer heredocs.

Regular Expressions

  • Avoid using $1-9 as it can be hard to track what they contain. Named groups can be used instead.

    # good
    /(?<meaningful_var>regexp)/ =~ string
    ...
    process meaningful_var
    
    # avoid
    /(regexp)/ =~ string
    ...
    process $1
  • 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/] # 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

Percent Literals

  • Use %w freely.

    STATES = %w(draft open closed)
    
    => ["draft", "open", "closed"]
  • Use %r only for regular expressions matching more than one '/' character.

    # good
    %r(^/blog/2011/(.*)$)
    
    # avoid
    %r(\s+)
    
    %r(^/(.*)$)
    # should be /^\/(.*)$/

Line Length Techniques

Airbnb's listed techniques for breaking complex statements into multiple lines that are all < 100 characters.

  • Liberal use of linebreaks inside unclosed ( { [.

  • Chaining methods, ending unfinished chains with a ..

  • Use heredocs for multi-line strings.

  • Breaking long logical statements with linebreaks after operators like && and ||.

  • Examples of most of these techniques can be found at Airbnb.

Be Consistent

If you're editing code, take a few minutes to look at the code around you and determine its style. If they use spaces around all their arithmetic operators, you should too. If their comments have little boxes of hash marks around them, make your comments have little boxes of hash marks around them too.

The point of having style guidelines is to have a common vocabulary of coding so people can concentrate on what you're saying rather than on how you're saying it. We present global style rules here so people know the vocabulary, but local style is also important. If code you add to a file looks drastically different from the existing code around it, it throws readers out of their rhythm when they go to read it. Avoid this.

Above all else

Follow your ¯\_(ツ)_/¯ !