I’ve touched on this gotcha briefly in the past when discussing the Wat video, but I thought a few examples of when single-line conditionals can bite you would be fun.

In Ruby, we can write a conditional containing a single expression that normally takes up three lines:

unless condition

on a single line to save space:

something unless condition

And that is all well and good - these two are pretty much the same. But they’re not identical in practice. There are a few weird things about to come up.

Our first example will be using defined? to conditionally print a variable

if defined?(var1)
  puts var1 # never runs

var1 # NameError: undefined local variable

So that works just as we’d expect. The conditional never runs because defined?(var1) returns nil. After the conditional, access the undefined var1 gives us a NameError because (appropriately) its not defined. Let’s modify that a little bit and put an assignment inside of the conditional.

if defined?(var2)
  var2 = 5 # never runs

var2 # nil
defined?(var2) # "local-variable"

So that might look a bit odd. We never ran the conditional, so var2 never gets set - that makes sense. But after the block, var2 doesn’t throw a NameError when accessed anymore. This is because the Ruby parser makes room and defines var2 when it sees it on the lefthand side of an expression (even though its inside of a conditional that doesn’t run).

Let’s write the same on one line though:

var3 = 5 if defined?(var3)

var3 # 5

Even more interesting - an undefined variable written this way will become defined and assigned when run. The first thing that happens is that the parser comes along and defines var3 which it sees on the lefthand side of a conditional. Then defined? runs, which this time evaluates to "local-variable", causing the conditional to pass, and 5 to be assigned to var3. In cases like this, the single-line conditional will produce an entirely different result than block conditionals.