Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby Dynamic Classes. How to fix "warning: class variable access from toplevel"

I'm trying to write a program that dynamically defines ruby classes based on configuration read from a file. I know I can use Class.new to do this. Here's an example program:

x = [1,2,3]

Test = Class.new do
  @@mylist = x

  def foo
    puts @@mylist
  end
end

Test.new.foo

When I run this I get the following output (running with ruby 1.9.3p0):

c:/utils/test.rb:4: warning: class variable access from toplevel
c:/utils/test.rb:7: warning: class variable access from toplevel
1
2
3

Does anyone know what causes these warnings and how I can get rid of them?

I've tried replacing the line tjhat does

@@mylist = x

with this

class_variable_set(:@@mylist, x)

But when I do that I get this error instead:

c:/utils/test.rb:7: warning: class variable access from toplevel
c:/utils/test.rb:7:in `foo': uninitialized class variable @@mylist in Object (NameError)
        from c:/utils/test.rb:11:in `'

Thanks in advance!

like image 659
yoda_alex Avatar asked Nov 04 '11 20:11

yoda_alex


3 Answers

This isn't doing what you think it's doing. Since you're not creating a class with the class keyword, your class variable is being set on Object, not Test. The implications of this are pretty huge, which is why Ruby is warning you. Class variables are shared between ancestors, and objects usually inherit from Object.

like image 59
mculp Avatar answered Nov 03 '22 12:11

mculp


Just to remove this warning, you should use class_variable_set method:

x = [1,2,3]

Test = Class.new do
  class_variable_set(:@@mylist, x)

  def foo
    puts @@mylist
  end
end
like image 8
WarHog Avatar answered Nov 03 '22 10:11

WarHog


Rather than defining your "mylist" class variable on the class when declaring the class, you can declare class level variables on it later on as below. Two different methods are shown. The former only works in 1.9, the latter works in both versions, but is less idiomatic.

x = [1,2,3]

Test = Class.new do
  def foo
    puts @@mylist
  end
end

# ruby 1.9.2
Test.class_variable_set(:@@mylist, x)   

# ruby 1.8.7
Test.class_eval {
  @@mylist = x
}

Test.new.foo
like image 2
Emma Avatar answered Nov 03 '22 10:11

Emma