Ruby gotchas - trinary operator and a function call, or why white spaces matter
17 Jun 2016I was refactoring code recently when I found this particular glitch in Ruby (excuse me - a feature 😉). The case was simple I wanted to clean some tests and remove fixture path - it was usually the same so I decided to skip it, I didn't want to change all the code base so I decided to still allow it.
So the first-comes-to-my-mind solution was to use a trinary operator to check if the path has or has not the fixture prefix and act accordingly. That was my first attempt:
Fail:
real_path = fixture_path.start_with? ('spec/fixtures/') ? fixture_path : "spec/fixtures/#{fixture_path}"
The space matched RubyMine's formatting rules that we use. To my surprise that didn't work! Why? It looks like a perfectly normal code. Isn't it?
In this case real_path will end up being a boolean false
Well guess what did work?
This works!
real_path = fixture_path.start_with?('spec/fixtures/') ? fixture_path : "spec/fixtures/#{fixture_path}"
But this is realy fragile, so I wanted to find a more robust solution, and here it is:
real_path = (fixture_path.start_with? ('spec/fixtures/')) ? fixture_path : "spec/fixtures/#{fixture_path}"
This way I enforced the function call.
Initially I though it doesn't make sense. But it actually does!
I think one of the points of Ruby is to be a great language for creating domain specific languages and in this context this is what you actually want. Remember you can drop brackets.
connect_to_server use_https ? "https://#{server}" : "http://#{server}"
In case like this you actually want the trinary operator to be evaluated first, then the method to be executed. I can imagine also:
connect_to_server ((use_https || force_https) && can_use_https) ? "https://#{server}" : "http://#{server}"
Just as an example.
There are even more gotchas in the language that I need to be aware so here's some reminder: