What's the difference between:
class A
class B
end
end
and
class A
end
class A::B
end
Update: These 2 approaches are not exactly the same.
In the second approach, B
doesn't have access to constants defined in A
.
Also, as Matheus Moreira correctly stated, in the second approach, A
must be defined before A::B
can be defined.
What other differences are there?
In Ruby, modules and classes are instances of the Module
and Class
classes, respectively. They derive their names from the constant they are assigned to. When you write:
class A::B
# ...
end
You are effectively writing:
A::B ||= Class.new do
# ...
end
Which is valid constant assignment syntax, and assumes that the A
constant has been properly initialized and that it refers to a Module
or a Class
.
For example, consider how classes are usually defined:
class A
# ...
end
What is effectively happening is this:
Object::A ||= Class.new do
# ...
end
Now, when you write:
class A
class B
# ...
end
end
What actually happens looks like this:
(Object::A ||= Class.new).class_eval do
(A::B ||= Class.new).class_eval do
# ...
end
end
Here's what is happening, in order:
Class
instance is asssigned to the A
constant of Object
, unless it was already initialized.Class
instance is asssigned to the B
constant of A
, unless it was already initialized.This ensures the existence of all outer classes before attempting to define any inner classes.
There is also a change in scope, which allows you to directly access A
's constants. Compare:
class A
MESSAGE = "I'm here!"
end
# Scope of Object
class A::B
# Scope of B
puts MESSAGE # NameError: uninitialized constant A::B::MESSAGE
end
# Scope of Object
class A
# Scope of A
class B
# Scope of B
puts MESSAGE # I'm here!
end
end
According to this blog post, the Ruby core team calls the "current class" the cref
. Unfortunately, the author does not elaborate, but as he notes, it is separate from the context of self
.
As explained here, the cref
is a linked list that represents the nesting of modules at some point in time.
The current
cref
is used for constant and class variable lookup and fordef
,undef
andalias
.
As the others have stated, they are different ways of expressing the same thing.
There is, however, a subtle difference. When you write class A::B
, you assume that the A
class has already been defined. If it has not, you will get a NameError
and B
will not be defined at all.
Writing properly nested modules:
class A
class B
end
end
Ensures the A
class exists before attempting to define B
.
Two different ways to say the same thing. That thing is that class B is an inner or nested class and can only be accessed through the the A interface.
> class A
.. def say
.... "In A"
....end
..
.. class B
.... def say
...... "In B"
......end
....end
..end
=> nil
> A.new.say
=> "In A"
> B.new.say
=> #<NameError: uninitialized constant B>
> A::B.new.say
=> "In B"
versus
> class A
.. def say
.... "In A"
....end
..end
=> nil
> class A::B
.. def say
.... "In B"
....end
..end
=> nil
> A.new.say
=> "In A"
> B.new.say
=> #<NameError: uninitialized constant B>
> A::B.new.say
=> "In B"
>
They are the same. They are different ways of writing the same thing. The first one is the naive way of writing it, but often, it gets hard to keep track of the nesting once the class/module gets large. Using the second way, you can avoid nesting in the appearance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With