Bob Aman

Multi-line Boolean Control Flow in Ruby

Over the years, I’ve frequently found myself implementing systems that follow Postel’s Law on a fairly regular basis. An extremely common scenario that comes up when building such liberal systems and parsers is a short-circuited sequence of searches.

Often, these search sequences will involve long if chains that repeatedly check for nil. However, this is both inefficient and inelegant. I’ve just (re-)discovered that Ruby has a much better way of accomodating this style of branching logic.

Take, for example, a particularly liberal feed parser that needs to return the title of a feed:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def feed_node
  @feed_node ||= (
    root.xpath(
      '/atom:feed',
      'atom' => 'http://www.w3.org/2005/Atom'
    ).first or
    root.xpath('/feed').first or
    root.xpath(
      '/rdf:RDF/rdf:channel',
      'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
    ).first or
    root.xpath('/rss/channel').first
  )
end

def title
  @title ||= (
    begin
      node = feed_node.xpath('title').first
      node.text if node
    end or
    begin
      node = feed_node.xpath(
        'dc:title',
        'dc' => 'http://purl.org/dc/elements/1.1/'
      ).first
      node.text if node
    end or
    begin
      node = feed_node.xpath(
        'rdf:title',
        'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
      ).first
      node.text if node
    end
  )
end

Admittedly, the example above would allow for completely mental mixes of RSS, Atom, and ancient RDF, and it’s perhaps a bit contrived, but hopefully it illustrates how much cleaner you can make this kind of logic with some well-placed control-flow or’s. Because both or and and have extremely low operator precedence, there’s little risk of accidentally doing something unintended, and the use of begin; end allows for longer blocks of short-circuited logic. Additionally, instead of begin; end, you can also use method calls when chunks of logic start to get too long or unwieldy.

Of course, neither or nor and are anything new, but I rarely see them used across multiple lines, and it turns out that they’re really useful in longer expressions.

Semantic Versioning With Bundler

When you’re setting up dependencies in your Gemfile, you generally want to give gem authors the benefit of the doubt. Assume that they will adhere to semantic versioning unless you have explicit knowledge that this is not the case for a particular gem.

Many developers make a habit of specifying their dependencies far too pessimistically, which tends to result in dependency conflicts, especially in larger projects. Instead of "~> x.y.z", consider using "~> x.y", ">= x.y.z". This compound requirement should be your instinctive default way of specifying a version constraint.