I've had a chance to look around in StackOverflow and found this same question which I was trying to better understand from Ruby Koans (Ruby Koans: explicit scoping on a class definition part 2).
class MyAnimals
LEGS = 2
class Bird < Animal
def legs_in_bird
LEGS
end
end
end
def test_who_wins_with_both_nested_and_inherited_constants
assert_equal 2, MyAnimals::Bird.new.legs_in_bird
end
# QUESTION: Which has precedence: The constant in the lexical scope,
# or the constant from the inheritance heirarachy?
# ------------------------------------------------------------------
class MyAnimals::Oyster < Animal
def legs_in_oyster
LEGS
end
end
def test_who_wins_with_explicit_scoping_on_class_definition
assert_equal 4, MyAnimals::Oyster.new.legs_in_oyster
end
# QUESTION: Now Which has precedence: The constant in the lexical
# scope, or the constant from the inheritance heirarachy? Why is it
# different than the previous answer?
Based on the explanation in the link, it seems like the main confusion others (including myself) had was because of the class definition:
class MyAnimals::Oyster < Animal
# stuff goes in here
end
My original thought was that MyAnimals::Oyster means that the Oyster class was defined within MyAnimals. In other words, I thought the above code was analogous to the following code:
class MyAnimals
class Oyster < Animal
# stuff goes in here
end
end
To test my thought, I did the following in IRB:
class MyAnimals
LEGS = 2
class Bird < Animal
def legs_in_bird
LEGS
end
end
end
class MyAnimals::Oyster # You should notice that I'm not inheriting from Animal anymore
def legs_in_oyster
LEGS
end
end
If my reasoning is correct, then I would expect that the below code returns 2
MyAnimals::Oyster.new.legs_in_oyster # => NameError: uninitialized constant MyAnimals::Oyster::LEGS
Since this doesn't return 2, can someone explain to me why it doesn't return 2?
EDIT: I neglected to add the Animal class; here it is:
class Animal
LEGS = 4
def legs_in_animal
LEGS
end
class NestedAnimal
def legs_in_nested_animal
LEGS
end
end
end
In strictly lexically/statically scoped languages like C++ or Java, identifiers are resolved by simply checking the current scope, ascending one scope higher and repeating until the base most scope is reached. For example: if your example were C++, LEGS would be searched for first in the calling class Bird/Oyster, then Animal, then My Animal.
Ruby has a kind of dynamic scoping. Each object, even if it resides in the same 'place' can have its own scope lookup order depending on how it was defined or create at runtime. You can think of each method as having a stack of scopes to search, and how it was defined pushes new scopes onto that stack.
Because of the way Bird is defined it gets (BaseScope is not its real name, you did not provide enough code to provide this) BaseScope::MyAnimals::Bird
, BaseScope::MyAnimals::Animal
, BaseScope::MyAnimals
and BaseScope
to search through for resolving names.
While The first Oyster only gets BaseScope::MyAnimals::Oyster
, BaseScope::MyAnimals::Animal
and BaseScope
.
The Oyster without inheritance gets even less, just BaseScope::MyAnimals::Oyster
and BaseScope
.
Each use of the class keyword and inheritance in this example pushes another scope to check onto the stack of scopes for its contents to search. So using class MyAnimals::Oyster
only pushed one entry onto this stack.
For simplicity I left out the method legs_in_oyster. It is a scope that could be searched. It's trivial definition is self explanatory and including it would add much useless text to this answer.
I also left out the global scope for simplicity. I know Koans has at least one scope at or between BaseScope and the global scope.