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

···

--
Posted via http://www.ruby-forum.com/.

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 `<main>'
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 `<main>'
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 `<main>'
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::"

···

jin chizhong <lists@ruby-forum.com> wrote:

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

--
Posted via http://www.ruby-forum.com/.

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 ::"

···

--
Posted via http://www.ruby-forum.com/.

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 Wright

···

On Jun 1, 2013, at 5:35 PM, jin chizhong <lists@ruby-forum.com> wrote:

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.

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.

···

--
Posted via http://www.ruby-forum.com/.

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)

···

Hans Mackowiak <lists@ruby-forum.com> wrote: