1

I recall reading somewhere that the following two pieces of code are identical in Ruby.

# Number 1
class A
  # body
end

# Number 2
A = Class.new
  # body
end

However I noticed that when I try to set a class variable from a class-level method, the two syntaxes produce different results.

With the first syntax, the variable is set on the class itself as expected.

irb(main):001:0> class A
irb(main):002:1>   def self.foo
irb(main):003:2>     @@a = :banana
irb(main):004:2>   end
irb(main):005:1> end
=> :foo
irb(main):006:0> A.class_variables
=> []
irb(main):007:0> A.foo
=> :banana
irb(main):008:0> A.class_variables
=> [:@@a]
irb(main):009:0> A.class_variable_get :@@a
=> :banana

irb(main):010:0> Class.class_variable_get :@@a
NameError: uninitialized class variable @@a in Class
    from (irb):10:in `class_variable_get'
    from (irb):10
    from /usr/local/bin/irb:11:in `<main>'
irb(main):011:0>

With the second, the variable gets set on Class itself!

irb(main):015:0> K = Class.new do
irb(main):016:1*   def self.foo
irb(main):017:2>     @@k = :banana
irb(main):018:2>   end
irb(main):019:1> end
=> K
irb(main):020:0> K.class_variables
=> []
irb(main):021:0> K.foo
(irb):17: warning: class variable access from toplevel
=> :banana
irb(main):022:0> K.class_variables
=> [:@@k]
irb(main):023:0> K.class_variable_get :@@k
=> :banana
irb(main):024:0> Class.class_variable_get :@@k
=> :banana

Why does this happen?

In what I am working on, I have to generate a class dynamically, so I have no choice but to use the second syntax. As such, how do I ensure that the variable @@k gets set on the new Class object being defined, and not on Class itself?

missingfaktor
  • 90,905
  • 62
  • 285
  • 365

1 Answers1

4

The two pieces are not identical, there are differences in lexical scoping of the declarations.

In the first definition, the more common class declaration, definitions are lexically scoped to class A, so the class variable is set on A. Only A and classes that inherit from A will have that class variable. Since Class does not inherit from A, it doesn't get the class variable.

In the second definition, the dynamic class assignment, the definitions are lexically scoped to top level object, which is object. So a class variable will be set on Object, the class of the top level object.

Now, this has huge implications. Every object inherits from Object and every class is an instance of the class Class, you're defining a class variable on each and every class in your object space. Hence, A gets the class variable, and Class gets it as well. Try defining a new class F, and it will have it as well. This is why Ruby screams the warning:

class variable access from toplevel

This is one of reasons why class variables are typically avoided.

There are multiple ways to solve this. My favourite is, using the attr_accessor on the class instance:

K = Class.new do
  class << self
    attr_accessor :fruit
  end

  self.fruit = :banana
end

K.fruit
# => :banana

# The value isn't shared between inherited classes,
# but the variable is:

class L < K
end

L.fruit
# => nil

L.fruit = :mango
# => :mango

K.fruit
# => :banana

Edit:

Keep in mind, that these variables are still not thread-safe, and are shared by all threads. You will need thread-local variables for ensuring thread-safety.

Swanand
  • 12,317
  • 7
  • 45
  • 62