Source Code Layout

  • Use UTF-8 as the source file encoding. [link]

  • Use two spaces per indentation level (a.k.a. soft tabs). No hard tabs. [link]

    # bad: four spaces
    def some_method
        do_something
    end
    
    # good
    def some_method
      do_something
    end
    
  • Use Unix-style line endings. (BSD/Solaris/Linux/OS X users are covered by default, but Windows users have to be extra careful.) [link]

    • If you're using Git you might want to add the following configuration setting to protect your project from Windows line endings creeping in:

      $ git config --global core.autocrlf true
      
  • Don't use ; to separate statements and expressions. As a corollary, use one expression per line. [link]

    # bad
    puts "foobar"; # superfluous semicolon
    
    puts "foo"; puts "bar" # two expressions on the same line
    
    # good
    puts "foobar"
    
    puts "foo"
    puts "bar"
    
    puts "foo", "bar" # this applies to puts in particular
    
  • Prefer a single-line format for class definitions with no body. [link]

    # bad
    class FooError < StandardError
    end
    
    # passable
    class FooError < StandardError; end
    
    # good
    FooError = Class.new(StandardError)
    
  • Use spaces around operators, after commas, colons and semicolons, and around { and before } when used in blocks. Whitespace might be (mostly) irrelevant to the Ruby interpreter, but its proper use is the key to writing easily readable code. [link]

    sum = 1 + 2
    a, b = 1, 2
    [1, 2, 3].each { |e| puts e }
    class FooError < StandardError; end
    

    The only exception, regarding operators, is the exponent operator:

    # bad
    e = M * c ** 2
    
    # good
    e = M * c**2
    
  • No spaces after (, [, or { when used for hash literals or interpolation. No spaces before ], ), or } when used for hash literals or interpolation. [link]

    # bad
    some( arg ).other
    [ 1, 2, 3 ].size
    
    # good
    some(arg).other
    [1, 2, 3].size
    
  • No space after !. [link]

    # bad
    ! something
    
    # good
    !something
    
  • No space inside range literals. [link]

      # bad
      1 .. 3
      "a" ... "z"
    
      # good
      1..3
      "a"..."z"
    
  • Indent when as deep as case. This is the style established in both "The Ruby Programming Language" and "Programming Ruby". [link]

    # bad
    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
    
    # good
    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, move the conditional to a new line and indent one level. [link]

    # bad:  pretty convoluted
    kind = case year
    when 1850..1889 then "Blues"
    when 1890..1909 then "Ragtime"
    when 1910..1929 then "New Orleans Jazz"
    when 1930..1939 then "Swing"
    when 1940..1950 then "Bebop"
    else "Jazz"
    end
    
    result = if some_cond
      calc_something
    else
      calc_something_else
    end
    
    # bad:  random indentation and all lines change with the variable name
    kind = case year
           when 1850..1889 then "Blues"
           when 1890..1909 then "Ragtime"
           when 1910..1929 then "New Orleans Jazz"
           when 1930..1939 then "Swing"
           when 1940..1950 then "Bebop"
           else "Jazz"
           end
    
    result = if some_cond
               calc_something
             else
               calc_something_else
             end
    
    # good (and a bit more width efficient)
    kind =
      case year
      when 1850..1889 then "Blues"
      when 1890..1909 then "Ragtime"
      when 1910..1929 then "New Orleans Jazz"
      when 1930..1939 then "Swing"
      when 1940..1950 then "Bebop"
      else "Jazz"
      end
    
    result =
      if some_cond
        calc_something
      else
        calc_something_else
      end
    
  • Use empty lines between method definitions and also to break up methods into logical paragraphs internally. [link]

    def some_method
      data = initialize(options)
    
      data.manipulate!
    
      data.result
    end
    
    def some_method
      result
    end
    
  • Avoid a comma after the last parameter in a method call when the parameters are on the same line. [link]

    # bad
    some_method(size, count, color, )
    
    # good
    some_method(size, count, color)
    
  • Use spaces around the = operator when assigning default values to method parameters: [link]

    # bad
    def some_method(arg1=:default, arg2=nil, arg3=[])
      # do something...
    end
    
    # good
    def some_method(arg1 = :default, arg2 = nil, arg3 = [])
      # do something...
    end
    

    While several Ruby books suggest the first style, the second is much more prominent in practice (and arguably a bit more readable).

  • Avoid using line continuation (\). [link]

    # bad
    result = 1 - \
             2
    
    result = 1 \
             - 2
    
    # passable
    long_string = "First part of the long string" \
                  " and second part of the long string"
    
    # good
    long_string = "First part of the long string" +
                  " and second part of the long string"
    
  • When continuing a chained method invocation on another line, include the . on the first line to indicate that the expression continues. [link]

    # bad:  makes the code harder to play with in a REPL
    one.two.three
      .four
    
    # passable:  irb friendly
    one.two.three.
      four
    
    # good
    one.
      two.
      three.
      four
    
  • Align the parameters of a method call if they span more than one line. When aligning parameters, single indent parameter lines. [link]

    # bad:  line is too long
    def send_mail(source)
      Mailer.deliver(to: "[email protected]", from: "[email protected]", subject: "Important message", body: source.text)
    end
    
    # bad:  double indent
    def send_mail(source)
      Mailer.deliver(
          to: "[email protected]",
          from: "[email protected]",
          subject: "Important message",
          body: source.text)
    end
    
    # bad:  random indention that can easily cause all lines to change
    def send_mail(source)
      Mailer.deliver(to: "[email protected]",
                     from: "[email protected]",
                     subject: "Important message",
                     body: source.text)
    end
    
    # good:  normal indent
    def send_mail(source)
      Mailer.deliver(
        to: "[email protected]",
        from: "[email protected]",
        subject: "Important message",
        body: source.text
      )
    end
    
  • Align the elements of array literals spanning multiple lines. [link]

    # bad:  single indent
    menu_item = ["Spam", "Spam", "Spam", "Spam", "Spam", "Spam", "Spam", "Spam",
      "Baked beans", "Spam", "Spam", "Spam", "Spam", "Spam"]
    
    # passable
    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"
    ]
    
  • Add underscores to large numeric literals to improve their readability. [link]

    # bad:  how many 0s are there?
    num = 1000000
    
    # good:  much easier to parse for the human brain
    num = 1_000_000
    
  • Use RDoc and its conventions for API documentation. Don't put an empty line between the comment block and the def. [link]

  • Limit lines to 80 characters. [link]

  • Avoid trailing whitespace. [link]

  • End each file with a newline. [link]

  • Don't use block comments. They cannot be preceded by whitespace and are not as easy to spot as regular comments. [link]

    # bad
    =begin
    comment line
    another comment line
    =end
    
    # good
    # comment line
    # another comment line