Reputation: 6628
I have a simple module that defines a constant and makes it private:
module Foo
Bar = "Bar"
private_constant :Bar
end
I can include it in a class like this, and it works as expected:
class User
include Foo
def self.test
Bar
end
end
puts User.test
# => Bar
begin
User::Bar
rescue => exception
puts "#{exception} as expected"
# => private constant Foo::Bar referenced as expected
end
(let's call it "typical class" definition)
Then I tried the Class.new
approach, but this failed miserably:
X = Class.new do
include Foo
def self.test
Bar # Line 28 pointed in the stack trace
end
end
begin
X::Bar
rescue => exception
puts "#{exception}"
# => private constant Foo::Bar
end
puts X.test
# test.rb:28:in `test': uninitialized constant Bar (NameError)
# from test.rb:28:in `<main>'
Why? I always though class Something
and Something = Class.new
are equivalent. What's the actual difference?
Then I had a strike of inspiration, and recalled there's alternative way to define class methods, which actually worked:
X = Class.new do
class << self
include Foo
def test
Bar
end
end
end
begin
X::Bar
rescue => exception
puts "#{exception}"
# => uninitialized constant X::Bar
end
puts X.test
# Bar
Again - why this one work, and why the exception is now different: private constant Foo::Bar
vs uninitialized constant X::Bar
?
It seems like those 3 ways of initializing classes differ in a nuanced way.
What is exactly going on in here?
Upvotes: 3
Views: 58
Reputation: 22325
This is one of the biggest gotchas in Ruby: constant definition scope is partially syntactic, that is, it depends on how the code around it is structured.
module Foo
Bar = "Bar"
end
Bar
is inside a module
definition, so it is defined in that module.
class << self
include Foo
end
Bar
gets included inside a class
definition, so it is defined in that class.
Class.new do
include Foo
end
There is no enclosing class
or module
(this is a normal method call with a block), so the constant is defined at top level.
As for your third error, I believe that is because the constant got defined in the singleton class (that's what class << self
is) versus the class itself. They are two separate class objects.
Upvotes: 3