Any tutorial and book will teach you there are two sets of similar operators in Ruby: &&/|| vs and/or (and also &/| for bit operations, but that’s not the case today). But typical tutorial will not provide further explanation why we need both of those pairs.

Some of tutorials and blog posts about this matter even go as far as explicit recommendations never use and/or, because && and || is enough for all reasonable cases, and because and/or provide only confusion. They would typically warn you about and’s lower precedence than =, but nobody will tell you how this can and should be useful.

Unfortunately, even respectable (and forced by Rubocop) Ruby Style Guide explicitly bans those operators and embraces usage of && and || even for control flow. (Faithfully, this is one of Rubocop checks I turn off immediately after adding Rubocop to every new project of mine.)

But and and or are you friends, which allow to write very clean and DRY and readable code: exactly the thing we love Ruby for.

They are control flow operators.


File.exists? or raise "No config file!"
document.ready? and return

This idiom is one of (numerous) good parts of Perl heritage in Ruby, it is very terse and readable and demonstrate intentions well.

Somebody may argue && and || can be used for both examples? Or, to heck with it, why we should do control flow this way, we have our if and unless, even modyfying ones?..

So, let’s look at some examples.

Case 1. Task: receive some data from method; if data received, process it further, if not—return (or fail) immediately. For example, complex Nokogiri document parsing:

def extract_actors(movie_doc)
  list ='ul#actorsList')
  # now, if no list in doc, we should return, otherwise do lots of stuff

What options do we have?

# option 1
list ='ul#actorsList')
return unless list # extra independent statement

# option 2
if (list ='ul#actorsList'))
  # ^ extra parentheses for distinguishing from ==

# option 3..20
# ...your choice!

# using control flow operator:
list ='ul#actorsList') or return

Which is cleaner declaration of intent?.. For me—definitely the last one. And you can’t replace it with ||, exactly because of operator precedence.

Case 2. List of checks: before some complex processing you want to check your arguments and maybe state, fail if something is not OK, and only then do the processing.

I do it like this:

File.exists?(config) or raise "Configuration file #{config} not found"
x.is_a?(Numeric) && y.is_a?(Numeric) or
  raise "Only numeric values expected"
halted? || waits_for_halt? and raise "Process already halted"

# to your normal processing code...

The same code with modyfying if/unless is not that clear:

raise "Configuration file #{config} not found" unless File.exists?(config)
raise "Only numeric values expected" unless x.is_a?(Numeric) && y.is_a?(Numeric)
raise "Process already halted" if halted? || waits_for_halt?

Code with control flow operators reads like IMPORTANT_CONDITION or raise ...something.... Code with modifying statements reads like raise THIS LONG VALUEABLE STRING unless .... something.

And again, you will not want to use && and || here because of operator precedence (and some complications with parentheses, also).

I like this (a bit abstruse) definition:

Logical operators (&& and ||) operate on values; control flow operators (and and or) operate on side effects.

PS: Of course, this is not first blog post with such kind of rant/explanation, there are plenty of them. Just wanted to make [another] clear point about that.