Is it a bug(about constant reference on dynamic class)

When I use a dynamic class, It can not reference constants.
See follow code and remark.
And is there any way to work around it?

#!/usr/bin/env ruby

class T
def hello
puts self.class.constants.join " " # right, AAA
puts AAA # right, 1
end
end

T::AAA = 1

t = T.new
t.hello

c = Class.new do
def hello
puts self.class.constants.join " " # right, AAA
puts AAA # error: uninitialized constant AAA (NameError)
end
end

c::AAA = 123
inst = c.new
inst.hello

jin chizhong [email protected] wrote:

end
puts self.class.constants.join " " # right, AAA

ENOTABUG :slight_smile:

(for in-depth, read “Metaprogramming Ruby”)

When you do t = T.new, you are getting something different than when you
do inst = c.new:

irb(main):039:0> class T;def hello;puts self.class.constants.join "
";puts T_0; end; end
=> nil
irb(main):040:0> t=T.new
=> #<T:0x9f1bb6c>
irb(main):041:0> t.hello

NameError: uninitialized constant T::T_0
from (irb):39:in hello' from (irb):41 from /home/tamara/.rvm/rubies/ruby-1.9.3-p392/bin/irb:13:in
irb(main):042:0> T::T_0 = “This is now set”
=> “This is now set”
irb(main):043:0> t.hello
T_0
This is now set
=> nil

and now using the Class object:

irb(main):002:0> c = Class.new{def hello;p self.class.constants; p
self.class.const_get(“C_0”);end}
=> #Class:0x89ef2ac
irb(main):003:0> c0 = c.new
=> #<#Class:0x89ef2ac:0x89ed3e4>
irb(main):004:0> c0.hello
[]
NameError: uninitialized constant #Class:0x89ef2ac::C_0
from (irb):2:in const_get' from (irb):2:in hello’
from (irb):4
from /home/tamara/.rvm/rubies/ruby-1.9.3-p392/bin/irb:13:in `’
irb(main):005:0> c0.class.const_set(“C_0”,“this is now set”)
=> “this is now set”
irb(main):006:0> c0.hello
[:C_0]
“this is now set”
=> “this is now set”
irb(main):007:0> c0.class.const_set(“C_1”,“this is another class
constant”)
=> “this is another class constant”
irb(main):008:0> c0.hello
[:C_0, :C_1]
“this is now set”
=> “this is now set”
irb(main):009:0> c0.class.const_get(“C_1”)
=> “this is another class constant”
irb(main):010:0>

The main thing to note is the extra level of indirection in the second
case:

irb(main):040:0> t=T.new
=> #<T:0x9f1bb6c>

Notice in this case the named type T, vs here:

irb(main):003:0> c0 = c.new
=> #<#Class:0x89ef2ac:0x89ed3e4>

the indirection, thus the main difference in ruby’s syntactic ability to
deal with things.

Doing this:

T::T_0 = ‘sommat’

is under the hood doing the same thing above:

irb(main):025:0> class T;def hello;p self.class.constants; p T_0;end;end
=> nil
irb(main):026:0> T.new.hello
[]
NameError: uninitialized constant T::T_0
from (irb):25:in hello' from (irb):26 from /home/tamara/.rvm/rubies/ruby-1.9.3-p392/bin/irb:13:in
irb(main):027:0> T.const_set(“T_0”,“set via const_set”)
=> “set via const_set”
irb(main):028:0> T.new.hello
[:T_0]
“set via const_set”
=> “set via const_set”
irb(main):029:0> T::T_0 = “set via T::”
(irb):29: warning: already initialized constant T_0
=> “set via T::”
irb(main):030:0> T.new.hello
[:T_0]
“set via T::”
=> “set via T::”

Thanks for your reply.
But I still can not understand why dynamic class can only use const_get
instand of :: .
Perhaps I need to read some ruby source.
Thank you very much.

Hans M. [email protected] wrote:

tamara is wrong :: works with anoymous classes too:

It happens a lot :slight_smile:

c = Class.new {
def hello
p self.class.constants
p self.class::C_0
end
}

c::C_0 = “set with ::” #=> “set with ::”

Oh, crikey!! Of course that works. Thanks, Hans. :slight_smile:

c0 = c.new #=> #<#Class:0x00000002d032f0:0x00000002d08228>

c0.hello
#[:C_0]
#=> “set with ::”

I don’t know why I was thinking the constant had to be set from the
instance. Now I look at it, of course that makes no sense.

(still nottabug)

tamara is wrong :: works with anoymous classes too:

c = Class.new {
def hello
p self.class.constants
p self.class::C_0
end
}

c::C_0 = “set with ::” #=> “set with ::”

c0 = c.new #=> #<#Class:0x00000002d032f0:0x00000002d08228>

c0.hello
#[:C_0]
#=> “set with ::”

On Jun 1, 2013, at 5:35 PM, jin chizhong [email protected] wrote:

When I use a dynamic class, It can not reference constants.
See follow code and remark.

The reason is that your two examples have different lexical scopes and
constants are resolved lexically.

class T
def hello
puts self.class.constants.join " " # right, AAA
puts AAA # right, 1
end
end

Here, AAA is lexically within T so ruby searches T::AAA and ::AAA before
giving up.

T::AAA = 1
t = T.new
t.hello

You can see this by doing:

class Demo; def get_T; T; end; end
T = ‘top’
Demo.new.get_T # ‘top’
Demo::T = ‘demo’ # now there is ::T and Demo::T
Demo.new.get_T # ‘demo’, because Demo::T is searched before ::T

c = Class.new do
def hello
puts self.class.constants.join " " # right, AAA
puts AAA # error: uninitialized constant AAA (NameError)
end
end

Here AAA is not lexically in a class so ruby only searches ::AAA
before giving up, which is what you see below when you call inst.hello

c::AAA = 123
inst = c.new
inst.hello

If you continue from this point:

puts c::AAA # 123
AAA = 456 # set top level AAA
puts c:AAA # still 123
c.new.hello # 456 because AAA in hello now finds AAA at the top level
scope

I think the key to understanding this is to realize that constant lookup
path is established when the code is parsed and not when the code is
executed.

You asked if there is a way around this and there sort of is. If you
want to force the lookup to start in a particular module then say it
explicitly:

c = Class.new do
def hello
puts self.class.constants.join " " # right, AAA
puts self.class::AAA # forces search to start in c
end
end

Just to be a bit more complete, ruby also searches the superclasses of
any class in the lexical scope:

class A
def lookup_X
X # X is resolved as A::X, ::X
end
end

class B < A
def lookup_X_from_B
X # X is resolved as B::X, A::X, ::X
end
end

A.new.lookup_X # A::X, ::X
B.new.lookup_X # still A::X, ::X even though called on instance
of B
B.new.lookup_X_from_B # B::X, A::X, ::X

Now try setting X = 1; A::X = 2, and B::X = 3 and see what the lookup
methods return.

Gary W.

Don’t know how much you know about resolution of constants.

If you knew what is “nesting”, the key is that nesting only changes with
the class and module keywords, and in code executed as a string passed
to
the *_eval family.

In particular blocks do not change the nesting. Dynamic class definition
or
class_eval BLOCK are no exceptions. Blocks don’t change the nesting.