One of boons of using Ruby is its expressiveness and readability. A small detail that struck me when I started writing Ruby code was the ability to use words like and and or as keywords in conditional expressions (in lieu of && and ||). A nice touch that allows code to read more like English.

However, it turns out that the English forms of these conditional operators are not quite equivalent to their symbol counterparts. Consider the following exercise on the console:

>> username = 'frog'
>> password = nil
>> login_valid = !username.blank? && !password.blank?
>> puts login_valid
false

Login is valid if both username and password are not blank. Since password is nil, the value of login_valid is false as we would expect. Now let's try the same code, replacing && with and to represent the AND conditional operator.

>> username = 'frog'
>> password = nil
>> login_valid = !username.blank? and !password.blank?
>> puts login_valid
true

Whoa!? The login_valid variable now evaluates to true. What happened? Well, and is not simply an alias for &&, it's a different operator with a different level of precedence. Programming Ruby by Dave Thomas contains a table of operators in order of precedence and notes that "the word forms of these operators (and, or, and not) have a lower precedence than the corresponding symbol forms (&&, ||, and !)." More specifically, the word forms are lower in precedence than assignment operators (= in the example above), while the symbol forms are higher in precedence.

So in the second example, the variable login_valid is assigned the value of !username.blank? before the and condition !password.blank? is evaluated. Crazy, and a little scary - I'd expect both forms to work the same way, or at least both to have precedence over assignment operators. The solution in this case is either to use && as in the first example or wrap the full condition in parentheses to specify precedence explicitly:

>> username = 'frog'
>> password = nil
>> login_valid = (!username.blank? and !password.blank?)
>> puts login_valid
false