Conditional Expressions

Conditional Expressions

Conditional keywords

  • Never use then for multi-line if/unless.
    [link]
  # bad
  if some_condition then
    ...
  end

  # good
  if some_condition
    ...
  end
  • Never use do for multi-line while or
    until.[link]
  # bad
  while x > 5 do
    ...
  end

  until x > 5 do
    ...
  end

  # good
  while x > 5
    ...
  end

  until x > 5
    ...
  end
  • The and, or, and not keywords are banned. It’s
    just not worth it. Always use &&, ||, and ! instead.
    [link]

  • Modifier if/unless usage is okay when
    the body is simple, the condition is simple, and the whole thing fits on
    one line. Otherwise, avoid modifier if/unless.
    [link]

  # bad - this doesn't fit on one line
  add_trebuchet_experiments_on_page(request_opts[:trebuchet_experiments_on_page]) if request_opts[:trebuchet_experiments_on_page] && !request_opts[:trebuchet_experiments_on_page].empty?

  # okay
  if request_opts[:trebuchet_experiments_on_page] &&
       !request_opts[:trebuchet_experiments_on_page].empty?

    add_trebuchet_experiments_on_page(request_opts[:trebuchet_experiments_on_page])
  end

  # bad - 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)

  # okay
  return if reconciled?
  • Never use unless with else. Rewrite
    these with the positive case first.[link]
  # bad
  unless success?
    puts 'failure'
  else
    puts 'success'
  end

  # good
  if success?
    puts 'success'
  else
    puts 'failure'
  end
  • Avoid unless with multiple
    conditions.[link]
    # bad
    unless foo? && bar?
      ...
    end

    # okay
    if !(foo? && bar?)
      ...
    end
  • Avoid unless with comparison operators if you can use if with an opposing comparison operator.[link]
    # bad
    unless x == 10
      ...
    end

    # good
    if x != 10
      ...
    end

    # bad
    unless x < 10
      ...
    end

    # good
    if x >= 10
      ...
    end

    # ok
    unless x === 10
      ...
    end
  • Don’t use parentheses around the
    condition of an if/unless/while.
    [link]
  # bad
  if (x > 10)
    ...
  end

  # good
  if x > 10
    ...
  end

Ternary operator

  • Avoid the ternary operator (?:) except
    in cases where all expressions are extremely trivial. However, do use the
    ternary operator(?:) over if/then/else/end constructs for single line
    conditionals.[link]
  # bad
  result = if some_condition then something else something_else end

  # good
  result = some_condition ? something : something_else
  • 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]
  # bad
  some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else

  # good
  if some_condition
    nested_condition ? nested_something : nested_something_else
  else
    something_else
  end
  • Avoid multiple conditions in ternaries.
    Ternaries are best used with single conditions.
    [link]

  • Avoid multi-line ?: (the ternary
    operator), use if/then/else/end instead.
    [link]

  # bad
  some_really_long_condition_that_might_make_you_want_to_split_lines ?
    something : something_else

  # good
  if some_really_long_condition_that_might_make_you_want_to_split_lines
    something
  else
    something_else
  end

Nested conditionals

  • Avoid the use of nested conditionals for flow of control.
    ([More on this][avoid-else-return-early].) [link]

Prefer a guard clause when you can assert invalid data. A guard clause
is a conditional statement at the top of a function that returns as soon
as it can.

The general principles boil down to:

* Return immediately once you know your function cannot do anything more.
* Reduce nesting and indentation in the code by returning early. This makes

the code easier to read and requires less mental bookkeeping on the part
of the reader to keep track of else branches. * The core or most important flows should be the least indented.

## bad
def compute
  server = find_server
  if server
    client = server.client
    if client
      request = client.make_request
      if request
         process_request(request)
      end
    end
  end
end

## good
def compute
  server = find_server
  return unless server
  client = server.client
  return unless client
  request = client.make_request
  return unless request
  process_request(request)
end

Prefer next in loops instead of conditional blocks.

## bad
[0, 1, 2, 3].each do |item|
  if item > 1
    puts item
  end
end

## good
[0, 1, 2, 3].each do |item|
  next unless item > 1
  puts item
end

See also the section “Guard Clause”, p68-70 in Beck, Kent.
Implementation Patterns. Upper Saddle River: Addison-Wesley, 2008, which
has inspired some of the content above.