The difference between "class A, class B" and "class A :: B"

What is the difference between:

class A class B end end 

and

 class A end class A::B end 

Update: these two approaches are not quite the same.

In the second approach, B does not have access to the constants defined in A

In addition, as Matei Moreira correctly said, in the second approach, A must be defined before A::B

What other differences exist?

+6
source share
3 answers

In Ruby, modules and classes are instances of the Module and Class classes, respectively. They get their names from the constant to which they are assigned. When you write:

 class A::B # ... end 

You effectively write:

 A::B ||= Class.new do # ... end 

This is the actual syntax for the account assignment syntax and assumes that the constant A was correctly initialized and that it refers to Module or Class .

For example, consider how classes are usually defined:

 class A # ... end 

What really happens:

 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 what happens is in order:

  • A new instance of Class assigned to the constant A Object , if it has not already been initialized.
  • A new instance of Class assigned to the constant B A , if it has not already been initialized.

This ensures that all outer classes exist before trying to define any inner classes.

There is also a scope change that allows you to access constants A directly. For comparison:

 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 main Ruby team calls the "current class" cref . Unfortunately, the author does not specify, but, as he notes, it is separate from the context of self .


As explained here , cref is a linked list that represents the nesting of modules at some point in time.

The current cref used to search for constant and class variables and for def , undef and alias .


As others have argued, these are different ways of expressing the same thing.

There is, however, a subtle difference. When you write class A::B , you assume that class A already defined. If this is not the case, you will get a NameError and B will not be defined at all.

Writing correctly nested modules:

 class A class B end end 

Provides class A before attempting to define B

+8
source

Two different ways of saying the same thing. The fact is that class B is an internal or nested class and is accessible only through interface A.

 > 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" 

against

 > 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" > 
+3
source

They are the same. These are different ways of writing the same thing. The first is a naive way of writing it, but it is often difficult to track nesting when the class / module becomes large. Using the second method, you can avoid investing in appearance.

+1
source

Source: https://habr.com/ru/post/910915/


All Articles