Skip to content

Instantly share code, notes, and snippets.

@tyre
Created November 12, 2012 21:17
Show Gist options
  • Save tyre/4061955 to your computer and use it in GitHub Desktop.
Save tyre/4061955 to your computer and use it in GitHub Desktop.

What exactly does ::SomeModule do? I first expected that it was a method like any other, getting called in the context it was in, so I went into irb to confirm.

1.8.7 :001 > class A; class B; end; end
 => nil 
1.8.7 :002 > A.send(:::, :B)
SyntaxError: compile error
(irb):2: syntax error, unexpected tSYMBEG, expecting tCONSTANT
A.send(:::, :B)
          ^
	from (irb):2

Ruby naturally had no idea what I meant by :::, but I was trying to turn "::" into a symbol to send to my class A.

1.8.7 :003 > A.::(B)
SyntaxError: compile error
(irb):3: syntax error, unexpected tCOLON2
A.::(B)
    ^
	from (irb):3

So it seems :: isn't a method as I expected. Let's see if it accesses the current scope. So if I call ::B inside A, it should access the class B.

1.8.7 :004 > A.class_eval do
1.8.7 :005 >     puts ::B
1.8.7 :006?>   end
NameError: uninitialized constant B
	from (irb):5
	from (irb):4:in `class_eval'
	from (irb):4

Maybe I need to instance_eval? (Note: there was no reason behind this guess or why this should work)

1.8.7 :007 > A.instance_eval do
1.8.7 :008 >     puts ::B
1.8.7 :009?>   end
NameError: uninitialized constant B
	from (irb):8
	from (irb):7:in `instance_eval'
	from (irb):7

Nope. executing ::B in the context of A doesn't find it in that context.

1.8.7 :010 > A.instance_eval do
1.8.7 :011 >     puts A::B
1.8.7 :012?>   end
A::B
 => nil

Now I thought, well maybe it works like '..' in bash, which is a pointer to the directory one level up. So I reopened A::B to add a bubbles class method, and added a top-level B class with the same .bubbles method.

1.8.7 :013 > class A
1.8.7 :014?>   class B
1.8.7 :015?>     def self.bubbles
1.8.7 :016?>       puts 'bubbling away'
1.8.7 :017?>       end
1.8.7 :018?>     end
1.8.7 :019?>   end
 => nil 
1.8.7 :020 > class B
1.8.7 :021?>   def self.bubbles
1.8.7 :022?>     puts 'frat frat frat frat'
1.8.7 :023?>     end
1.8.7 :024?>   end
 => nil
1.8.7 :025 > A.class_eval do
1.8.7 :026 >     ::B.bubbles
1.8.7 :027?>   end
frat frat frat frat
 => nil

So I was right, It does go up one level! But It could also just be going all the way up to the top-level namespace (since in this instance, being 1 level deep, 1 level up and all the way up are the same.)

We need to go deeper!

1.8.7 :031 > class A; class C; class B; def self.bubbles; puts 'in too deep!';end;end;end;end
 => nil 
1.8.7 :032 > A::C.class_eval do
1.8.7 :033 >     ::B.bubbles
1.8.7 :034?>   end
frat frat frat frat
 => nil

This executed B.bubbles on the top-level class B, showing that :: bumps you all the way up the top-level context.

so it looks like it is useful when you are buried deep inside of a namespace, and know what the direct namespace from the top level is. So if you are in A::C::B and want to reference A::B, you can say ::A::B and always know you have the right one. Or if you had C::A::B and A::B and were inside C, calling A::B would give you C::A::B when you really wanted A::B, so you can use ::A::B.

@therealadam
Copy link

Yep! But also, for legacy reasons (scary music), it also calls class methods. So, Process.pid is the same and preferred to Process::pid.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment