There are two features of Ruby that are often frowned upon by experienced
or keywords, and the
unless ... else construct.
I want to argue that both are OK and, when used correctly, need not be considered a code smell.
or are not voodoo
Every programmer learns at some point that
or keywords are not
|| operators. Unfortunately, they usually learn it the
hard way, usually by trying to use them before assignment:
# danger: will not work as expected show_help = args.empty? or args == '--help'
This code seemingly does what it was supposed to, but has a subtle bug: it does
not respect the
Now the newbie programmer got burned, and asks “If
synonyms for operators, what are they?” Seasoned programmers then, as an answer,
mumble something about “precedence”, and they add “You must never use them
again.” From GitHub’s Ruby style guide:
orkeywords are banned. It’s just not worth it.
A hard rule like this is not very satisfying advice. (And yes, GitHub, when you open-sourced your style guides, you turned them from internal documents to advice for the community.)
The real answer is those keywords perform the same function as the operators, but they have different precedence–therefore they have different use-cases.
In fact, let’s demystify them right away:
||multiply, divide, & modulo|
||plus & minus|
They’re right there in the bottom. If you are required to remember that
multiplication happens before addition in
a + b * f, why not be aware that
assignment has higher precedence than
Avdi Grimm argues that those keywords, originating from Perl, were intended to be control flow operators. I fully subscribe to this way of thinking, and often use this and similar patterns in flow constructs:
if name = params[:full_name] and !name.empty? # do something with name end
Know your language well, and expect of others to know it, too. If beginner
programmers in your group stumble on this, help them out like you would help
with any other Ruby concept that isn’t obvious (e.g. in
class << obj syntax,
<< operator is neither shift nor append). It’s not such a big hurdle.
From Programming Perl:
The moral of the story is that you must still learn precedence (or use parentheses) no matter which variety of logical operator you use.
The case of
unless ... else
GitHub’s style guide:
in 10 years of Ruby I’ve never seen [an instance where
unless ... elseis fine].
Never, ever, ever use an else clause with an unless statement.
[…] as with anything that gives you a little power, it can be abused.
Jamis from 37signals offers an intentionally convoluted example to prove their point:
unless !person.present? && !company.present? puts "do you even know what you're doing?" else puts "and now we're really confused" end
I’m sure Jamis is able to deliberately design horrible code that can make any feature of Ruby look like an “abuse of power”.
But as with
unless ... else isn’t some highly sophisticated,
magical voodoo construct that requires extreme concentration to wrap your head
around, and is best avoided to keep code clarity. It’s just
if ... else
unless ... else when it fits, and I’ve got two simple criteria to
decide if that’s the case:
- The condition reads better in English under
- I expect the
unlesscode block to run more frequently than the
# some perfectly valid code, in my book unless response.redirect? # process response.body (the more common case) else # follow response['location'] end
However, Konstantin Haase raises a valid argument:
unless ... elsewould make sense, then
elsunlesswould make sense, too.
It’s true that
elsif is only valid in
if constructs, and there is no
unless. However, I don’t miss it, as I can’t imagine how it
would ever read well in English.