Problem trying to get a constant with correct scope

A

Alexandre Mutel

I'm trying to get a constant inside a class (but i have to do it outside
the declaration)

module A
class B
end
class C
end
end

How to get constant B from inside C but executed from outside first
declaration?
After module A is declared, trying :
failed and const_get is not working too.

But if i do something like this:
module A
class B
end
class C
$test_binding = binding
end
end

puts eval("B",$test_binding)

it's working... Is using the binding is the only way to get correct
scope and resolve constant correctly from inside class C?
 
P

pharrington

I'm trying to get a constant inside a class (but i have to do it outside
the declaration)

module A
  class B
  end
  class C
  end
end

How to get constant B from inside C but executed from outside first
declaration?
After module A is declared, trying :


failed and const_get is not working too.
Your goal is confusing. module A contains the "B" constant, not class
C. How does "A::B" not satisfy your needs? What exactly are you trying
to do?
 
A

Alexandre Mutel

pharrington said:
Your goal is confusing. module A contains the "B" constant, not class
C. How does "A::B" not satisfy your needs? What exactly are you trying
to do?

Sorry for the shortcut in my explanation. In fact, my test case is a bit
more dynamic. I'm developing a DSL that evaluate blocks that can contain
a line like this:
A_CONSTANT some,args,...etc.

So i'm trying to get the constant dynamically and not statically :

I have something like :
class ConstSearcher
def self.method_missing(sym,*args)
puts const_get(sym)
end
end

module A
class B
end
class C < ConstSearcher
puts B # << static constant lookup is working
end
end

But performing a dynamic lookup is not : A::C.instance_eval("B 11111")
:
NameError: (irb):3:in `const_get': uninitialized constant A::C::B
from (irb):13
from (irb):3:in `method_missing'
from (eval):1
from :0

i tried several methods (eval with binding...etc.) without any success.
Does i have to loop through parent modules and perform a const_get on
each module ? (generating a huge performance penalty).
 
A

Alexandre Mutel

Currently, i'm using something like

class Module
def dyn_const_get(sym)
list = name.split('::').inject([Object]) {|hierarchy,name| hierarchy
<< hierarchy.last.const_get(name)}
result = nil
while !list.empty? && result.nil?
begin
lookup_in = list.pop
result = lookup_in.const_get sym
rescue
end
end
result
end
end

And performing : A::C.dyn_const_get:)B) returns
A::B

But that's a very bad option in terms of performance... I'm wondering if
there are any other options?
 
P

pharrington

Currently, i'm using something like

class Module
  def dyn_const_get(sym)
    list = name.split('::').inject([Object]) {|hierarchy,name| hierarchy
<< hierarchy.last.const_get(name)}
    result = nil
    while !list.empty? && result.nil?
      begin
        lookup_in = list.pop
        result = lookup_in.const_get sym
      rescue
      end
    end
    result
  end
end

And performing : A::C.dyn_const_get:)B) returns
A::B

But that's a very bad option in terms of performance... I'm wondering if
there are any other options?

As far as I know the only way to do this is to traverse the
inheritance hierarchy :\
 
R

Rob Biedenharn

Currently, i'm using something like

class Module
def dyn_const_get(sym)
list = name.split('::').inject([Object]) {|hierarchy,name|
hierarchy
<< hierarchy.last.const_get(name)}
result = nil
while !list.empty? && result.nil?
begin
lookup_in = list.pop
result = lookup_in.const_get sym
rescue
end
end
result
end
end

And performing : A::C.dyn_const_get:)B) returns
A::B

But that's a very bad option in terms of performance... I'm
wondering if
there are any other options?


Would your DSL rather do something with const_missing instead of
method_missing? (see Module#const_missing) I suppose that you could
set A::C::B = A::B

-Rob


Rob Biedenharn http://agileconsultingllc.com
(e-mail address removed)
 
A

Alexandre Mutel

pharrington said:
As far as I know the only way to do this is to traverse the
inheritance hierarchy :\

Ok, so that's a bad news...
Would your DSL rather do something with const_missing instead of
method_missing? (see Module#const_missing) I suppose that you could
set A::C::B = A::B
-Rob

Nope, because the block I'm trying to evaluate is "A_CONSTANT
some,parameters", so i expect to parse the parameters as well for this
constant. Moreover, the const_missing doesn't solve the issue as i still
have to lookup through parent's modules to get the constant dynamically
as it is performed statically...
 
P

pharrington

Ok, so that's a bad news...


Nope, because the block I'm trying to evaluate is "A_CONSTANT
some,parameters", so i expect to parse the parameters as well for this
constant. Moreover, the const_missing doesn't solve the issue as i still
have to lookup through parent's modules to get the constant dynamically
as it is performed statically...

If constant lookup is causing a significant impact on your project's
performance, mayhaps a good idea would be to create a tree of commonly
used constants (whatever that means for your app) for fast lookup?
 
A

Alexandre Mutel

Just a general remark about this problem. Shouldn't Ruby have a builtin
mechanism to resolve dynamically and efficiently constants handling
lexical scopes correctly?

Seems that in 1.9, we have this features:
irb(main):001:0> class A
irb(main):002:1> BAR = 1
irb(main):003:1> class B
irb(main):004:2> def self.foo(&b)
irb(main):005:3> instance_eval(&b)
irb(main):006:3> end
irb(main):007:2> end
irb(main):008:1> end
=> nil
irb(main):009:0> A::B.foo do
irb(main):010:1* puts BAR
irb(main):011:1> end
1
=> nil
irb(main):012:0>

BUT now, try to do it with const_get, and we got a NameError:
irb(main):012:0> A::B.foo do
irb(main):013:1* puts const_get:)BAR)
irb(main):014:1> end
NameError: uninitialized constant A::B::BAR
from (irb):13:in `const_get'
from (irb):13:in `block in irb_binding'
from (irb):5:in `instance_eval'
from (irb):5:in `foo'
from (irb):12
from E:/Code/Ruby/bin/irb:12:in `<main>'

This is quite frustrating when writing a DSL language to not being able
to resolve constants dynamically (without getting a costly module
hierarchy and checking the constant in each module...)

Should'nt this constant lookup be consistent between static and
const_get?
 
W

Walton Hoops

-----Original Message-----
From: (e-mail address removed) [mailto:[email protected]]
Just a general remark about this problem. Shouldn't Ruby have a builtin
mechanism to resolve dynamically and efficiently constants handling
lexical scopes correctly?

Seems that in 1.9, we have this features:
irb(main):001:0> class A
irb(main):002:1> BAR = 1
irb(main):003:1> class B
irb(main):004:2> def self.foo(&b)
irb(main):005:3> instance_eval(&b)
irb(main):006:3> end
irb(main):007:2> end
irb(main):008:1> end
=> nil
irb(main):009:0> A::B.foo do
irb(main):010:1* puts BAR
irb(main):011:1> end
1
=> nil
irb(main):012:0>

BUT now, try to do it with const_get, and we got a NameError:
irb(main):012:0> A::B.foo do
irb(main):013:1* puts const_get:)BAR)
irb(main):014:1> end
NameError: uninitialized constant A::B::BAR
from (irb):13:in `const_get'
from (irb):13:in `block in irb_binding'
from (irb):5:in `instance_eval'
from (irb):5:in `foo'
from (irb):12
from E:/Code/Ruby/bin/irb:12:in `<main>'

This is quite frustrating when writing a DSL language to not being able
to resolve constants dynamically (without getting a costly module
hierarchy and checking the constant in each module...)

irb(main):001:0> class A
irb(main):002:1> BAR=1
irb(main):003:1> class B
irb(main):004:2> def self.foo(&b)
irb(main):005:3> instance_eval(&b)
irb(main):006:3> end
irb(main):007:2> end
irb(main):008:1> end
=> nil
irb(main):012:0> A::B.foo do
irb(main):013:1* puts eval('BAR')
irb(main):014:1> end
1
=> nil

Done. A more experienced Rubist may be able to do it without eval, but
that works.
Should'nt this constant lookup be consistent between static and
const_get?

No, that's not what const_get is for. It's operating on a class
not a scope. That's why it's an instance method. The way you
calling it is literally equivalent too:

A::B.const_get:)BAR)

In other words, you are asking for _B's_ constants. Since B,
nor any of its ancestors have the constant BAR, an error occurs.

Maybe this demonstrates more clearly what I mean:

irb(main):001:0> class A
irb(main):002:1> BAR=1
irb(main):003:1> def self.BAR
irb(main):004:2> BAR
irb(main):005:2> end
irb(main):006:1> class B
irb(main):007:2> end
irb(main):008:1> end
=> nil
irb(main):009:0> A.BAR
=> 1
irb(main):011:0> A::B.BAR
NoMethodError: undefined method `BAR' for A::B:Class
from (irb):11
from C:/Ruby19/bin/irb:12:in `<main>'
irb(main):012:0> A.const_get :BAR
=> 1
irb(main):013:0> A::B.const_get :BAR
NameError: uninitialized constant A::B::BAR
from (irb):13:in `const_get'
from (irb):13
from C:/Ruby19/bin/irb:12:in `<main>'
irb(main):014:0>

Notice, const_get works the same as the accessor method
for BAR.
 
A

Alexandre Mutel

Walton said:
irb(main):012:0> A::B.foo do
irb(main):013:1* puts eval('BAR')
irb(main):014:1> end
1
=> nil

Done. A more experienced Rubist may be able to do it without eval, but
that works.

Oh, thanks, at least the eval is working on constants with 1.9, this is
good to know.
No, that's not what const_get is for. It's operating on a class
not a scope. That's why it's an instance method. The way you
calling it is literally equivalent too:

A::B.const_get:)BAR)

In other words, you are asking for _B's_ constants. Since B,
nor any of its ancestors have the constant BAR, an error occurs.

I understand the current const_get behavior, but still, it would be nice
to have something equivalent to fully resolving a constant in a faster
way without going through the eval way.

I was thinking about a const_get with an aditionnal parameter like
def const_get(symbol, check_module_hierarchy=false) {}, still living
this method in the module.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,822
Latest member
israfaceZa

Latest Threads

Top