Skip to content

A community-driven Ruby coding style guide (Habanero version)

Notifications You must be signed in to change notification settings

habanerohq/ruby-style-guide

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 

Repository files navigation

The Ruby Style Guide

Source Code Layout

  • 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 as case. 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]

Syntax

  • 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-line if/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(?:) over if/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 and case 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 of not. [link]

    # standard
    x = !something
    
    # non-standard
    x = (not something)
  • Avoid multi-line ?: (the ternary operator); use if/unless instead. [link]

  • Favor modifier if/unless usage when you have a single-line body. Another good alternative is the usage of control flow and/or. [link]

    # standard
    do_something if some_condition
    
    # standard
    some_condition and do_something
    
    # non-standard
    if some_condition
      do_something
    end
  • Favor unless over if 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 with else. 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 {...} 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. [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 over Proc.new. [link]

    # standard
    p = proc { |n| puts n }
    
    # non-standard
    p = Proc.new { |n| puts n }
  • Prefer proc.call() over proc[] or proc.() 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 over Array#* 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] or Array() instead of explicit Array 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

Naming

  • 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 like exit 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 over collect, detect over find, select over find_all, and size over length.

    Use reduce over inject if the result is a single object. Use inject over reduce if the result is an enumeration. [link]

  • Don't use count as a substitute for size. [link]

    # standard
    some_hash.size
    
    # non-standard
    some_hash.count
  • Use flat_map instead of map + flatten. [link]

    # standard
    all_songs = users.flat_map(&:songs).uniq
    
    # non-standard
    all_songs = users.map(&:songs).flatten.uniq
  • Use reverse_each instead of reverse.each. [link]

    # standard
    array.reverse_each { ... }
    
    # non-standard
    array.reverse.each { ... }

Comments

  • 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]

Comment Annotations

  • 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]

Classes & Modules

  • 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 over extend 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, and private 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

Exceptions

  • Signal exceptions using the fail method. Use raise 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 of fail/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]

Collections

  • 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 or Hash 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 or last over [0] or [-1]. [link]

  • Use Set instead of Array 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 of Hash#has_key? and Hash#value? instead of Hash#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]

Strings

  • 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"

Regular Expressions

  • 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). Use Regexp.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]

Percent Literals

  • 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.}

Misc (and still important)

  • 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 when alias_method will do. [link]

  • Use OptionParser for parsing complex command line options and ruby -s for trivial command line options. [link]

  • Prefer Time.now over Time.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]

About

A community-driven Ruby coding style guide (Habanero version)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published