defined? is a syntax-construct (not a method) which tries to tell
you if its "parameter" is defined. The rules for this are rather
interesting.
class String
def print
p self
self
end
end
Okay, String#print is just a helper method to prove my point.
defined?("hello".print) # => "method"
defined?("hello".print.strip) # => prints out "hello", then returns "method"
defined?("hello".print.stripp) # => prints out "hello", then returns nil
Okay, so defined? may actually call methods for you to figure out if
it's "defined" or not. What if we try something more complex:
defined?(if true then "hello".print.strip end) # => "expression"
Wait, what? This does not call the print method. In fact, this check
is done entirely at parse-time. "defined?(if true then … end)" will
always return "expression". In fact, anything more complex than local
variables, method calls or constant names will just return "expression".
Well, except for "defined?" though; defined? can't check itself - it's a
syntax error.
Another intresting "feature": If any exception is raised, the whole
expression is considered undefined:
class String; def print; raise; end; end
defined?("hello".print.strip) # => nil
I wouldn’t call that a strange feature, just a surprising consequence of an un-strange feature, namely that inheritance involves expressions and that classes are first-class entities.
Folks from certain other languages would be just as surprised to find out that you can alias classes by assigning them, return them from functions, and so on.
# Define two class variables in two different classes:
class MyClass
@@base = 1
end
class OtherClass
@@base = 2
end
# Define it in the superclass too:
class Object
@@base = 3
end
class MyClass
# Magic! The subclass-variable has now changed!
p @@base # => 3
end
class OtherClass
# In both classes in fact:
p @@base # => 3
# And finally, let's reset this:
@@base = 4
end
class Object
# Whoops, this changes the superclass too:
p @@base # => 4
end
Explanation: Two class variables with the same name can't exists in the
same class hierachy. Therefore, class variable lookup (and setter) will
always traverse to the upmost-class where the variable is defined.
(Hope you don't mind that I wrote these in two different posts.)