Your Ruby Gotcha of the Day
Liquid error: undefined method `login' for nil:NilClass : March 8th, 2006
In #caboose, technomancy found this particularly odd gotcha in Ruby:
class RubyGotcha
attr_accessor :arguments
def initialize
self.arguments = [1,2,3]
end
def test_bad
if false == true
# This makes arguments into a local,
# even though it is never executed.
arguments = [4,5,6]
end
puts "#{arguments}" # prints nothing
end
def test_control
puts "#{arguments}" # prints "123"
end
end
Why is there a difference? Florian Gross (aka flgr @ #ruby-lang) explains that this is because Ruby decides if a keyword is a local at parse time, not at runtime.
This is a very subtle gotcha. Code that is never actually executed can affect the output of your code. Keep it in mind. This is especially likely to be confusing in ActiveRecord, since it makes heavy use of method_missing, making the correct behavior somewhat hard to guess.
8 Responses to “Your Ruby Gotcha of the Day”
Leave a Reply
Remember: escape your underscores \_ and indent code at least 4 spaces or incur the wrath of smartypants.
March 8th, 2006 at 11:47 AM This is why I'm partial to the explicit reference to self instead of the implict. I guess it's my Smalltalk upbringing, but I like to see self.message spelled out for me.
March 9th, 2006 at 01:37 AM Josh, You aren't the only one.
March 9th, 2006 at 02:11 AM See, I thought it was just related to method_missing all along. It turns out it's rather more... sinister.
March 9th, 2006 at 03:28 AM Yeah, I've been bitten by a similar issue recently myself. I was working on a domain specific language, and I wanted the syntax to be:
graph = FunctionGrapher.graph { title = "Sort Functions" ... }And I kept wondering why the title= method wasn't being called until I thought it out and realized that it was defining a variable. I've settled with for now, rather than which isn't as elegant to me. I'm not 100% satisfied with it though.March 9th, 2006 at 04:59 AM Peter: Why not use the hash style? title => "Sort functions",
March 9th, 2006 at 05:16 AM This is the bit that's discussed in the Pickaxe (2nd edition) pp. 329-330 in the section "Variable/Method Ambiguity" (pp. 212-3 in the 1st edition). Seems like there's real potential for "action at a distance" with this -- if you open a class or module remotely, depending on where in the inclusion chain your code resides you could turn some name in a later opener of that module into a variable or a function. Then again, might be handy in an obfuscated ruby contest. That true==false idiom is particularly cute looking for such purposes. Hmmm... sounds like a branching mechanism for a really obfuscated automaton. Maybe even could implement a Turing machine with module_eval writing "tape bits" as the presence or absence of "foo = 'bar' if true==false" assignments. Weird.
March 9th, 2006 at 07:40 AM rickbradley: I was trying to decide between "if true == false" and "if 2 + 2 == 5". For now the law of non-contradiction beats Orwell.
March 12th, 2006 at 06:58 PM Kyle: You know, I hadn't thought of that. The only thing I don't like about that syntax is that you have to put commas after each statement, which breaks the magic a bit. Unless of course there's a way to define a => method, but that doesn't seem to be the case. Thanks for the idea