02 October 2013

A Sliver of Ruby #3: Nesting Scopes and Constant Name Resolution

I was plodding my way along The Path To Enlightenment, this morning (ruby koans), and I was rather stumped on the following:

class Animal
  LEGS = 4
  def legs_in_animal
    LEGS
  end
end

class MyAnimals
  LEGS = 2

  class Bird < Animal
    def legs_in_bird
      LEGS
    end
  end
end

class MyAnimals::Oyster < Animal
  def legs_in_oyster
    LEGS
  end
end

def test_who_wins_with_both_nested_and_inherited_constants
  assert_equal 2, MyAnimals::Bird.new.legs_in_bird
end

def test_who_wins_with_explicit_scoping_on_class_definition
  assert_equal 4, MyAnimals::Oyster.new.legs_in_oyster
end

Notice that in the first test, the lexical scope wins, but in the second test, the inheritance hierarchy wins...and I was baffled as to why...

Turns out that, constant names are resolved in the following order (according to zetetic):
  1. the enclosing scope
  2. the outer scope (if any)
  3. included modules
  4. the superclass
  5. the class Object
  6. the class Kernel
And also referenced this complete example:


The bottom-line answer to my question is: the syntax used to declare Oyster (i.e. using the qualified name as opposed to re-opening MyAnimals and nesting Oyster within) specifically means that it does not participate in the lexical scope of MyAnimals, it's merely sharing namespace.  There's no outer scope for Oyster and therefore the name resolution makes it all the way down to the 4th source.

It's the difference between this:

class MyAnimals::Oyster < Animal
  def legs_in_oyster
    LEGS
  end
end

and this:

class MyAnimals
  class Oyster < Animal
    def legs_in_oyster
      LEGS
    end
  end
end

No comments:

Post a Comment