Ruby Gotchas

14 Sep 2020

NOTE: I primarily wrote this to verify my site still works.
My previous SitePoint articles are much more detailed.

1. Common Trip-ups for New Rubyists: Part 1
2. Common Trip-ups for New Rubyists: Part 2

One Space Too Many

Can't remember how many times I've hit this one.
A paren-enclosed argument list will throw an error if you put a space before it.

def add(a, b)
  a + b
end
Parens surrounding the arguments works as expected.
add(1, 2)
=>3
Removing the parens still works.
add 1, 2
=>3
But if we put a space before a paren-enclosed argument list, we get a syntax error.
add (1, 2)
syntax error, unexpected ',', expecting ')'

Loops Stuck on the First Iteration

Undefined variables in blocks are undefined each time the block is executed.

3.times do
  sum ||= 0
  sum += 1
  puts sum
end

Results in:

1
1
1

For loops work as expected.

for i in 1..3
  sum ||= 0
  sum += 1
  puts sum
end

Results in:

1
2
3

However, it's generally not considered idiomatic Ruby, so it's better to just declare all variables that will be needed outside of the closure.

Class Variables

Ruby provides two notations for class variables, @ and @@.

class Animal
  @speak_type = "grr"

  def self.speak
    puts @speak_type
  end
end

class Cat < Animal
  @speak_type = "meow"
end
Cat.speak
Animal.speak

Results in:

meow
grr

However, if we use a @@ variable...

class Animal
  @@speak_type = "grr"

  def self.speak
    puts @@speak_type
  end
end

class Cat < Animal
  @@speak_type = "meow"
end
Cat.speak
Animal.speak

We get:

meow
meow

What has happened?

The assignment of @@speak_type of Cat has overwritten the value in the parent class!

This can lead to unexpected behavior, so you should always use @ class variables.

#eql? vs #equal?

#eql? compares the type and value, while #equal? compares the object_id.

x = "cat"
=> "cat"
y = "cat"
=> "cat"
x === y
=> true
x.eql? y
=> true
x.equal? y
=> false

String Interpolation Requires Double-Quotes

This one probably gets a lot of people that come from Python.
If you attempt to use Ruby's hash-bracket string interpolation with single quotes, it won't perform interpolation.

name = "bob"
=> "bob"
'Hello, #{name}'
=> "Hello, \#{name}"
"Hello, #{name}"
=> "Hello, bob"

Another reason you will encounter this is because many developers assume single-quotes are faster to evaluate or due to following a style guide that recommends single quotes unless interpolation is needed. So another developer will add an interpolated value to a string without changing to double-quotes.

https://stackoverflow.com/questions/1836467/is-there-a-performance-gain-in-using-single-quotes-vs-double-quotes-in-ruby"

In this case, unless your team is already following the convention, I think it is appropriate to just always use double-quotes.