34

This is a continuation this original SO question: Using "::" instead of "module ..." for Ruby namespacing

In the original SO question, here is the scenario presented which I'm still having trouble understanding:

FOO = 123

module Foo
  FOO = 555
end

module Foo
  class Bar
    def baz
      puts FOO
    end
  end
end

class Foo::Bar
  def glorf
    puts FOO
  end
end

puts Foo::Bar.new.baz    # -> 555
puts Foo::Bar.new.glorf  # -> 123

Can someone provide some explanation behind why the first call is returning 555 and why the second call is returning 123?

Community
  • 1
  • 1
wmock
  • 5,382
  • 4
  • 40
  • 62
  • 1
    Willson, which answer below do you think is worthy of the bounty? Thanks – rainkinz Mar 07 '13 at 21:37
  • Hint: add "puts Module.nesting" after the two puts in your code. See also this: http://coderrr.wordpress.com/2008/03/11/constant-name-resolution-in-ruby/ –  Mar 08 '13 at 15:22

4 Answers4

36

You can think of each appearance of module Something, class Something or def something as a “gateway” into a new scope. When Ruby is searching for the definition of a name that has been referenced it first looks in the current scope (the method, class or module), and if it isn’t found there it will go back through each containing “gateway” and search the scope there.

In your example the method baz is defined as

module Foo
  class Bar
    def baz
      puts FOO
    end
  end
end

So when trying to determine the value of FOO, first the class Bar is checked, and since Bar doesn’t contain a FOO the search moves up through the “class Bar gateway” into the Foo module which is the containing scope. Foo does contain a constant FOO (555) so this is the result you see.

The method glorf is defined as:

class Foo::Bar
  def glorf
    puts FOO
  end
end

Here the “gateway” is class Foo::Bar, so when FOO isn’t found inside Bar the “gateway” passes through the Foo module and straight into the top level, where there is another FOO (123) which is what is displayed.

Note how using class Foo::Bar creates a single “gateway”, skipping over the scope of Foo, but module Foo; class Bar ... opens two separate “gateways”

matt
  • 78,533
  • 8
  • 163
  • 197
  • 4
    Btw. the gateway term. In the Ruby sources there seems to be what one could call a "scope stack". So each time you type `class` or `module` a new scope is pushed onto this stack. When Ruby then searches for variables or constants it consults this stack from bottom to top, ending in the top-level `main` if it didn't find the variable on the way up. In the case of `class Foo::Bar` it really should push TWO scopes onto the stack (both `Foo` and `Bar`), but it only pushes one, hence we get the "problem". – Casper Mar 04 '13 at 11:32
  • How does this differ from the original answer? – rainkinz Mar 04 '13 at 15:30
  • 1
    @Casper that makes sense. I read about the “gateway” idea somewhere (can’t remember where unfortunately) as a way of thinking about what’s going on but I haven’t looked at the implementation. I guess one explanation for this behaviour is it allows you to open up a nested class (to monkey patch it) without needing to worry about the enclosing scope interfering. – matt Mar 08 '13 at 15:41
6

wow, great question. The best answer I can come up with is in this case you're using the module to define a namespace.

Check this out:

FOO = 123

module Foo
  FOO = 555
end

module Foo
  class Bar
    def baz
      puts FOO
    end

    def glorf3
      puts ::FOO
    end
  end
end

class Foo::Bar
  def glorf2
    puts Foo::FOO
  end

  def glorf
    puts FOO
  end
end

puts Foo::Bar.new.baz    # -> 555
puts Foo::Bar.new.glorf  # -> 123
puts Foo::Bar.new.glorf2  # -> 555
puts Foo::Bar.new.glorf3  # -> 123

So my thought is that when you define:

module Foo
  FOO = 555
end

you are creating FOO in the namespace of Foo. So when you use it here:

module Foo
  class Bar
    def baz
      puts FOO
    end
  end
end

you are in the Foo namespace. However, when you refer to it in:

class Foo::Bar
  def glorf
    puts FOO
  end
end

FOO is coming from the default namespace (as illustrated by ::FOO).

Stuart M
  • 11,458
  • 6
  • 45
  • 59
rainkinz
  • 10,082
  • 5
  • 45
  • 73
  • 1
    thanks for the examples! this makes sense except for one point: when you define the class Foo::Bar, aren't you namespacing it to Foo? Doesn't the "Foo::" part of "Foo::Bar" imply that you're namespacing that class? – wmock Feb 27 '13 at 19:34
  • Foo::Bar.new.glorf returning 123 is difficult for me to understand when Foo::Bar.new.baz returns 555. – wmock Feb 27 '13 at 19:36
  • 1
    I would have thought that too, but it looks like, what's happening is you're namespacing Bar (under Foo) explicitly by Foo::Bar and everything else in that context is still coming from the default namespace. – rainkinz Feb 27 '13 at 19:37
  • 1
    Interesting, so it seems like when you're explicitly namespacing the class, the constants will resolve to the default namespace unless you specify otherwise (i.e. Foo::FOO in glorf2) and when you're implicitly namespacing the class, the constants will resolve to namespace unless you specify otherwise (i.e. ::FOO in glorf3). – wmock Feb 27 '13 at 19:45
  • 2
    Right, that's what I think. Hopefully someone else will comment. This is really interesting... – rainkinz Feb 27 '13 at 20:23
0

the first call:

puts Foo::Bar.new.baz    # -> 555

prints the result of invoking method baz of an instance of class Foo::Bar

notice that Foo::Bar#baz definition is actually a closure on FOO. Following ruby's scope rules:

  1. FOO is searched for in Foo::Bar (the class, not the instance) scope, it is not found,
  2. FOO is searched for in the enclosing scope Foo (because we are within the module definition) and it is found there (555)

the second call:

puts Foo::Bar.new.glorf  # -> 123

prints the result of invoking method glorf of an instance of class Foo::Bar

notice that Foo::Bar#glorf definition this time is also a closure on FOO, but if we follow ruby's scope rules you'll notice that the value closed upon this time is ::FOO (top level scope FOO) in the following way:

  1. FOO is searched for in Foo::Bar (the class, not the instance) namespace, it is not found
  2. FOO is searched in the enclosing scope ('top level') and it is found there (123)
Francisco Meza
  • 875
  • 6
  • 8
0

glorf is a method of class Foo, in => [Foo, Module, Object, Kernel, BasicObject]

within that scope (i.e. in default/main module), FOO is assigned 123

module Foo is defined as

module Foo
  FOO = 555
  class Bar
    def baz
      puts FOO
    end
  end
end

where method baz belongs to class Bar in module Foo => [Bar, Foo, Object, Kernel, BasicObject]

and in that scope FOO was assigned 555

aug2uag
  • 3,379
  • 3
  • 32
  • 53
  • the 'main' mentioned above is an artifact of irb, actually `FOO=123` is a top-level method that goes into Object class. – aug2uag Mar 09 '13 at 01:06