5
\$\begingroup\$

I really like the ruby ? : syntax for if/else statements.

However, I'm not sure how to do the following with a nested statement:

if information.is_empty?
  if leader? || possessor?
    link_to('update ' + contextual('your') + ' credentials!', edit_user_path) 
  else
    user.name + ' hasn\'t entered this info!'
  end
else
  information        
end

Should I bother? Does the ? : syntax (for want of a term!) actually make code pretty unreadable and non-self-documenting?

If you think I should use the ? : syntax, how would I tackle this?

\$\endgroup\$

2 Answers 2

11
\$\begingroup\$
  1. The ?: is called a ternary operator. It's not specific to Ruby; a lot of languages have it (in fact, I believe I linked you to that wikipedia page in a comment very recently).

  2. Go easy on the newlines; there's a lot of whitespace there (update: question was edited; now it's gone)

  3. Use string interpolation

  4. We've talked about that is_ prefix on ? methods already :)

To answer your question: Ternaries have their use, but like everything else, they can also be abused.

In practice, there's no difference between a ternary and and a good ol' if...else, so you can use either - but that argument goes both ways. Don't use ternaries just because you can.

A good case for a ternary (in my opinion), would be something like this (just as an example):

puts some_boolean_value ? "yes" : "no"

or something like

"You have #{messages.count} new #{messages.one? ? 'message' : 'messages'}"

There's no logic going on in either the if or the else branch, and both are very short, so it's short and sweet to use a ternary.

In Ruby you can also spell the first one out like:

puts if some_boolean_value
  "yes"
else
  "no"
end

It's neat that Ruby treats if...else as an expression, but here it seems like a waste of space for something so simple.

In your case: Your code does have more branching logic, so I'd definitely avoid ternaries, and keep it as-is. Personally, I'd never nest ternary expressions inside each other, just for readability reasons.

Alternatively, you can return early (assuming this code is in a method):

return information unless information.is_empty?

if leader? || possessor?
  link_to("update #{contextual "your"} credentials", edit_user_path)
else
  "#{user.name} hasn't entered this info!"
end

If you wrote it all in ternaries, it'd be pretty ridiculous:

information.is_empty? ? leader? || professor? ? link_to("update #{contextual "your"} credentials", edit_user_path) : "#{user.name} hasn't entered this info!" : information # what?

The middle road would be to use a ternary for the innermost nested branch only

if information.is_empty?
  leader? || professor? ? link_to("update #{contextual "your"} credentials", edit_user_path) : "#{user.name} hasn't entered this info!"
else
  information
end

But, as you can see, that innermost nested branch is also the one with the most going on, so it's still less readable than a normal if...else

\$\endgroup\$
2
  • \$\begingroup\$ I think you did something strange in some of the examples; syntax highlighting looks wrong (although I don't know ruby) \$\endgroup\$
    – Izkata
    Commented Apr 17, 2014 at 20:12
  • \$\begingroup\$ @Izkata Should be fine. The highlighting is just stumbling over the string-inside-a-string in the string interpolations. It's basically "text #{ruby code with a string in it} more text" and the syntax highlighter doesn't understand that \$\endgroup\$
    – Flambino
    Commented Apr 17, 2014 at 20:26
3
\$\begingroup\$

I think that the key concern should be clarity. Specifically, I recommend eliminating the nesting, which would make it clear that one of three branches will be taken no matter what.

In addition…

  • Standard Ruby style uses two spaces of indentation.
  • is_empty? is a redundant name. The question mark alone is enough to convey the fact that it is a predicate.
  • Use less clumsy string quoting.
if !information.empty?
  information
elsif leader? || possessor?
  link_to("update #{contextual('your')} credentials!", edit_user_path) 
else
  "#{user.name} hasn't entered this info!"
end

You could write that using a ternary expression, but I would advise against it on readability grounds.

\$\endgroup\$
1
  • \$\begingroup\$ I see the leader? || professor? thing as orthogonal to whether there's any information. For a litmus test: You can't swap the branches around in your code and get the same result (not saying that's a great test, but just as an example) \$\endgroup\$
    – Flambino
    Commented Apr 17, 2014 at 17:21

Not the answer you're looking for? Browse other questions tagged or ask your own question.