I see. But it does feel like code smell to me. And, you can always do
something like :
region = [params[:state], params[:country], 'US'].detect(&:present?)
Including all the items in an Array and picking the first present one
circumvents any opportunity to use logic short-circuiting to avoid
unnecessary, potentially expensive calculation, not to mention unnecessary
allocation of container objects and the overhead of symbol-to-proc.
Yes, that's too much overhead, Here's the benchmark:
state=nil; country='Canada';
Benchmark.bm(10) do |bm|
bm.report("nonblank?") { n.times { region = state.nonblank? || country.nonblank? || 'US' } }
bm.report("detect ") { n.times { region = [state.nonblank?, country, 'US'].select &:present? } }
end
user system total real
nonblank? 0.210000 0.000000 0.210000 ( 0.212206)
detect 2.320000 0.070000 2.390000 ( 2.487010)
Over 10X slower! The above example doesn't emphasize short-circuiting
either. You could easily find examples that were 100X slower because
of complex expressions.
I definitely wouldn't write it that way. If I had to live without
nonblank?, this is what I'd write:
region = (params[:state] unless params[:state].blank?) ||
(params[:country] unless params[:country].blank?) ||
'US'
but you can see that's not DRY since every argument is mentioned
twice. That's what led us to add nonblank?. It's concise, DRY, and
performs well.
My biggest concern with nonblank? is that idiomatic Ruby is to return a
boolean from question mark methods.
Generally that's true, but Ruby has nonzero? which behaves exactly
like nonblank?: you get a value back that is _equivalent_ to boolean
but has other uses too. Another Ruby example is =~ which returns nil
or an index, although that doesn't end in ?.
I like Pratik's solution, and would like to see a scenario where the short-circuit logic is important
I just grep'd our company's code base (65K LOC) and found 190 usages
of nonblank?, not including the definition and tests. About 70% are
using it as a boolean (we haven't used present? since that was
introduced in Rails 2.2 and we were on 2.1 until the end of the
summer) but 30% are the pattern discussed here. For example, from our
EmailAddress class:
@friendly_name = tmail.name.nonblank? || friendly_name
I also found several cases where the mapping to nil was explicitly
important (because we wanted it to the be the same as "not present")
as in
vid = cookies[:vid].nonblank?
Of the 70% that treat nonblank?'s result as a boolean, I didn't find a
single case that put !! in front to force the result to a proper
boolean. Likely that's because when you really want a true boolean, !
blank? reads best.
-Colin