The subtleties of const_missing

N

Nicholas Seckar

Hello all,

I've run into an issue regarding the use of const_missing. This issue
revolves around the fact that these two cases are supposed to behave
differently from each other:

irb(main):001:0> B = 10
irb(main):002:0> module A; B; end
=> 10
irb(main):003:0> A::B
NameError: uninitialized constant A::B
from (irb):3

However it seems impossible to detect which case we are in within the
const_missing method. A more detailed explanation follows:

Suppose we are lazy and avoid typing "require 'my_module.rb'" by
defining a const_missing that performs that require when MyModule is
first referenced. Now, suppose we are writing my_class, and wish to
include MyModule. So, we put this code in my_class.rb:

class MyClass
include MyModule
...
end

Our const_missing handler is called, and it loads 'my_module.rb',
finds the now present MyModule constant, and returns it. Everything
is good.

However, let's say we're slightly misguided and do MyClass::MyModule.
Since this is not defined, our const_missing handler is once again
called. 'my_module.rb' is thus loaded again, (usually to no effect,)
and MyModule returned.

This is a pretty clear violation of the semantics of ::. Indeed, Ruby
will produce a warning regarding our misbehaved const_missing. That
said, there does not seem to be a way to behave correctly; from
inside the const_missing handler there is no way to tell which case
the constant is missing in.

One way to 'fix' this is to have our const_missing look in it's
parent modules for the missing constant. If we find it there, we know
this is a case of A::B. However this only works if the missing
constant has been defined in one of the parents. Thus, we can still
load ::B using A::B, but afterwards A::B will fail.

So, sorry for rambling on. The real question is: Can const_missing
detect if we are in a "A::B" case rather than "module A; B; end" ?

Thanks for your time and any replies,
Regards,
Nicholas Seckar
 
A

Ara.T.Howard

Hello all,

I've run into an issue regarding the use of const_missing. This issue
revolves around the fact that these two cases are supposed to behave
differently from each other:

irb(main):001:0> B = 10
irb(main):002:0> module A; B; end
=> 10
irb(main):003:0> A::B
NameError: uninitialized constant A::B
from (irb):3

However it seems impossible to detect which case we are in within the
const_missing method. A more detailed explanation follows:

Suppose we are lazy and avoid typing "require 'my_module.rb'" by defining a
const_missing that performs that require when MyModule is first referenced.
Now, suppose we are writing my_class, and wish to include MyModule. So, we
put this code in my_class.rb:

class MyClass
include MyModule
...
end

Our const_missing handler is called, and it loads 'my_module.rb', finds the
now present MyModule constant, and returns it. Everything is good.

However, let's say we're slightly misguided and do MyClass::MyModule. Since
this is not defined, our const_missing handler is once again called.
'my_module.rb' is thus loaded again, (usually to no effect,) and MyModule
returned.

This is a pretty clear violation of the semantics of ::. Indeed, Ruby will
produce a warning regarding our misbehaved const_missing. That said, there
does not seem to be a way to behave correctly; from inside the const_missing
handler there is no way to tell which case the constant is missing in.

One way to 'fix' this is to have our const_missing look in it's parent
modules for the missing constant. If we find it there, we know this is a
case of A::B. However this only works if the missing constant has been
defined in one of the parents. Thus, we can still load ::B using A::B, but
afterwards A::B will fail.

So, sorry for rambling on. The real question is: Can const_missing detect if
we are in a "A::B" case rather than "module A; B; end" ?

but why do you need to distinguish?

harp:~ > cat a.rb
class Module
def const_missing c
puts "const_missing..."
const_set c, 42
end
end

module A
p B
end

p A::B

harp:~ > ruby a.rb
const_missing...
42
42

the 'const_missing' hook will continue to get call only so long as it remain
undefined - if your handler merely loads a file but does not actually define
any const then of course it will continue to fire. what, exactly, does your
handler do?

kind regards.

-a
--
===============================================================================
| ara [dot] t [dot] howard [at] gmail [dot] com
| all happiness comes from the desire for others to be happy. all misery
| comes from the desire for oneself to be happy.
| -- bodhicaryavatara
===============================================================================
 
N

Nicholas Seckar

but why do you need to distinguish?

[snip]

the 'const_missing' hook will continue to get call only so long as
it remain
undefined - if your handler merely loads a file but does not
actually define
any const then of course it will continue to fire. what, exactly,
does your
handler do?

The loaded file is expected to define the constant; We assume b.rb
contains class or module B (or defines it in any other way). If the
constant isn't defined by the loaded file then a NameError is raised
as if normal.
 
E

Eric Hodel

Hello all,

I've run into an issue regarding the use of const_missing. This
issue revolves around the fact that these two cases are supposed to
behave differently from each other:

irb(main):001:0> B = 10
irb(main):002:0> module A; B; end
=> 10
irb(main):003:0> A::B
NameError: uninitialized constant A::B
from (irb):3

However it seems impossible to detect which case we are in within
the const_missing method. A more detailed explanation follows:

Suppose we are lazy and avoid typing "require 'my_module.rb'" by
defining a const_missing that performs that require when MyModule
is first referenced. Now, suppose we are writing my_class, and wish
to include MyModule. So, we put this code in my_class.rb:

I have found this type of laziness often leads to errors that are
difficult to discover because somewhere else a MyModule was defined
leaving the file I really want to use unrequired.

require 'my_module' is really not that hard to type.
 
N

nobuyoshi nakada

Hi,

At Wed, 16 Nov 2005 13:36:46 +0900,
Nicholas Seckar wrote in [ruby-talk:165996]:
I've run into an issue regarding the use of const_missing. This issue
revolves around the fact that these two cases are supposed to behave
differently from each other:

irb(main):001:0> B = 10
irb(main):002:0> module A; B; end
=> 10
irb(main):003:0> A::B
NameError: uninitialized constant A::B
from (irb):3

However it seems impossible to detect which case we are in within the
const_missing method. A more detailed explanation follows:

What about autoload?

$ echo 'B = 10' > b.rb

$ irb
irb(main):001:0> autoload :B, "b"
=> nil
irb(main):002:0> module A;end
=> nil
irb(main):003:0> A::B
NameError: uninitialized constant A::B
from (irb):3
from :0
irb(main):004:0> module A;B;end
=> 10
irb(main):005:0> A::B
NameError: uninitialized constant A::B
from (irb):5
from :0
irb(main):006:0>
 
R

Robert Klemme

Nicholas said:
Hello all,

I've run into an issue regarding the use of const_missing. This issue
revolves around the fact that these two cases are supposed to behave
differently from each other:

irb(main):001:0> B = 10
irb(main):002:0> module A; B; end
=> 10
irb(main):003:0> A::B
NameError: uninitialized constant A::B
from (irb):3

However it seems impossible to detect which case we are in within the
const_missing method. A more detailed explanation follows:

I'm not sure what you're up to. You define B in a completely different
scope than you use for lookup. Maybe you can give a more real life
example of what you are actually trying to do.

Kind regards

robert
 
A

Ara.T.Howard

The loaded file is expected to define the constant; We assume b.rb contains
class or module B (or defines it in any other way). If the constant isn't
defined by the loaded file then a NameError is raised as if normal.

i understand that. but in your original message you said
However, let's say we're slightly misguided and do MyClass::MyModule. Since
this is not defined, our const_missing handler is once again called.
'my_module.rb' is thus loaded again, (usually to no effect,) and MyModule
returned.

This is a pretty clear violation of the semantics of ::. Indeed, Ruby will
produce a warning regarding our misbehaved const_missing. That said, there
does not seem to be a way to behave correctly; from inside the const_missing
handler there is no way to tell which case the constant is missing in.

here you will see that wether we reference B from inside M, or as M::B the
handler is called exactly once.

harp:~ > cat a.rb
class Module
def const_missing c
puts "const_missing called once..."
const_set c, 42
end
end

module A
p B
end

p A::B


harp:~ > ruby a.rb
const_missing called once...
42
42

here is another example which loads a file

harp:~ > cat a.rb
class Module
def const_missing c
puts "const_missing called once..."
autoload = {
:B => 'b.rb'
}
autoload[c] ? require(autoload[c]) : super
const_set(c, const_get(c))
end
end

module A
B
end

A::B

module A; p B; end
p A::B

harp:~ > ruby a.rb
const_missing called once...
42
42

so what __exactly__ are you trying to do in your handler that does not work? i
understand you problem but, as the second example shows, it can easily be
solved without distinguishing the two syntax cases because, afaikt, ruby
handles them precisely the same.

hth.

-a
--
===============================================================================
| ara [dot] t [dot] howard [at] gmail [dot] com
| all happiness comes from the desire for others to be happy. all misery
| comes from the desire for oneself to be happy.
| -- bodhicaryavatara
===============================================================================
 
D

Devin Mullins

Hi,

Rails does this very thing. You should check out their const_missing and
see if it suffers from the self-same problem.

Devin
 

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,969
Messages
2,570,161
Members
46,710
Latest member
bernietqt

Latest Threads

Top