Query Methods in Rails and true/false
In Ruby, unlike many other languages, only false and nil evaluate to false.
However, I learned today that the ActiveRecord::Base class in Rails gives us not only attribute setters and getters, but a query method too.
And this query method has its own rules.
Where o is an instance of an ActiveRecord class, and name is a column/attribute:
>> o.name = nil => nil >> o.name? => false
>> o.name = false => false >> o.name? => false
>> o.name = '' => "" >> o.name? => false
>> o.name = 0 => 0 >> o.name? => false
>> o.name = [] => [] >> o.name? => true
>> o.name = {} => {} >> o.name? => true
>> o.name = 0.0 => 0.0 >> o.name? => true
So this has its own set of rules: false and nil still evaluate to false, but so do 0 and an empty string. However, empty arrays, empty hashes and the 0.0 float evaluate to true.
For [] and {}, you can do foo.empty?. But for 0.0, you have to do foo.zero?.
Ugh; more arbitrary stuff to memorize (or test in the console).
I wish there was some method that combined ?, empty? and zero?. Perhaps there is one that I've overlooked?
I think Why complained about the lack of the very method you are looking for. He probably implemented/named it somewhere too, if you were inclined to look.
Posted by: Ian Bicking on September 21, 2007 1:18 PM | permalinkIan:
Are *you* becoming a Ruby guy now? Wow.
I still like Python, but (sorry), it's now a close second.
Posted by: Joe Grossberg on September 21, 2007 2:36 PM | permalinkI don't do Rails (I have no interest in web apps), but I gather from this blog that these statements
puts 'hello' if o.name
puts 'hello' if o.name?
behave differently depending on the class of o.name. If so, that's pretty scary. That's a typo bug waiting to trip the client programmer.
Elizabeth:
I don't think it's the class of "o" that matters; it's the class of "name" that does.
And that is indeed a gotcha.
On the other hand, o#name? only exists if o inherits from ActiveRecord::Base (or it's specifically defined), and behaves the same for all such classes (unless some bad programmer overrides the behavior).
Posted by: Joe Grossberg on September 21, 2007 5:51 PM | permalinkEmpty hashes, empty arrays, and 0.0 eval to true?
Am I the only one that considers that to be a fatal flaw/bug?
Posted by: Bubba on September 26, 2007 3:46 AM | permalinkBubba:
If you're not talking about an object that inherits from RoR's ActiveRecord, then "0" and empty-string do too.
I agree that it sucks (i.e. a "bug" and not a "feature") but, when using Ruby, you quickly learn to use methods like nil? empty? exists? etc.
Posted by: Joe Grossberg on September 26, 2007 8:30 AM | permalinkBubba et al.,
It's normal in Ruby for everything to be considered true, except for false and nil. Rails creates confusion with ActiveRecord's query_attribute returning true/false in an inconsistent manner. It appears to be doing both a half-baked and over-zealous job of making Ruby boolean tests behave like Python's.
Seriously, if Rails wants 0 and "0" to be false, then 0.0 and "0.0" should be false as well. And if Rails wants an empty String to be false, then an empty Array, empty Hash, and anything for which object.empty? is true, should also be false. And if Rails wants "f" and "false" to be false, then "F", "FALSE", and "False" should also be false.
On a lighter note, if sally and sammy are ActiveRecord objects and sex is a database column, then
sally.sex = "f"
sammy.sex = "m"
puts "I'm ready, willing, and able" if sally.sex?
puts "I'm ready, willing, and able" if sammy.sex?
It appears that Rails struggles with gender bias and heterosexual frustration.
Elizabeth:
Maybe we're miscommunicating here.
I don't believe that the *strings* "f", "false", etc. should evaluate to false.
Only the empty string.
Posted by: Joe Grossberg on September 29, 2007 8:51 AM | permalinkI, too, Joe, am very troubled by "f" and "false" Strings evaluating to false. That's why I presented the silly scenario of sally and sammy, in which the String "f" should *not* be considered false.
Nevertheless, if the designer(s) of ActiveRecord really want the Strings "f" and "false" to be false, they should go all the way and have query_attribute do a *case-insensitive* check of "f" and "false".
Likewise, query_attribute's check for zero-ness should apply to all Numerics, not just Fixnums, and its check for empty-ness should apply to anything with an empty? method, not just Strings. That would take care of Bubba's "fatal flaw/bug."
No more comments! Either someone has violated Godwin's Law, I'm tired of the discussion or, most likely, the ten-week window has closed. You can, however, contact me through email.