15

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!

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
yoda_alex
  • 310
  • 1
  • 3
  • 10

4 Answers4

21

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.

mculp
  • 2,637
  • 1
  • 25
  • 34
  • Well, how then to do what OP thinks it's doing? Is there something wrong with `Class.new` that could be improved? – DomQ Oct 06 '22 at 21:43
  • https://stackoverflow.com/a/34529095/435004 sheds some light. This appears to be a matter of lexical scoping; `@@foo` appears to designate the `foo` class variable of the class currently being defined using the `class` keyword; (at least in a recent Ruby) it has no meaning at the top level (outside any such `class`). – DomQ Oct 06 '22 at 22:04
8

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
WarHog
  • 8,622
  • 2
  • 29
  • 23
  • That gives me an error unfortunately. I've updated the original question with the details. – yoda_alex Nov 04 '11 at 21:30
  • Hmm - only Ruby 1.9.2 may give you a such warning about 'class variable access from toplevel'. But `NameError` with `uninitialized class variable` message may only come from Ruby 1.8 because method `class_variable_set` is private in this version. Maybe it's stupid question :) but are you sure that you're running both version in the same ruby version? If I use 1.8 with your initial snippet, I don't have any warning message and if I use 1.9 with my snippet it seems to be ok too... – WarHog Nov 04 '11 at 21:48
  • 1
    Thanks WarHog. I just tried your code on 1.9.2p290 and it worked with no errors. But when I run it with 1.9.3p0 I get the error I describe above. I'll take a look at the what's new for 1.9.3 and see if anything might explain the difference in behavior. – yoda_alex Nov 04 '11 at 22:49
  • It might be issue 3080 (http://redmine.ruby-lang.org/issues/3080) that's causing my problem on 1.9.3. Seems like its a regression. – yoda_alex Nov 04 '11 at 23:25
  • Another possible solution is to use a class method to access the class variable as also described here http://stackoverflow.com/a/21444098/1145454 – Luc Wollants Jan 29 '14 at 22:34
2

if you want only to suppress this warnings you can use

$VERBOSE = nil

in top of you code

Daniel Gohlke
  • 91
  • 1
  • 4
2

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
Emma
  • 6,112
  • 2
  • 18
  • 11
  • Thanks for the reply. Unfortunately when running with 1.9.3, the first version causes the error I saw, and the second gives the warnings I had! I'm starting to suspect this is a change in behaviour / regression in ruby 1.9.3. – yoda_alex Nov 04 '11 at 23:24