Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between "class A; class B" and "class A::B"

Tags:

ruby

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?

like image 206
nickh Avatar asked Mar 16 '12 14:03

nickh


3 Answers

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:

  1. A new Class instance is asssigned to the A constant of Object, unless it was already initialized.
  2. A new 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 for def, undef and alias.


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.

like image 88
Matheus Moreira Avatar answered Sep 20 '22 10:09

Matheus Moreira


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.s­ay
=> "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.s­ay
=> "In B"
>  
like image 32
TCopple Avatar answered Sep 20 '22 10:09

TCopple


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.

like image 43
sawa Avatar answered Sep 22 '22 10:09

sawa