Forum: Ruby The subtleties of const_missing

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
nseckar (Guest)
on 2005-11-16 06:39
(Received via mailing list)
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 S.
ara.t.howard (Guest)
on 2005-11-16 06:57
(Received via mailing list)
On Wed, 16 Nov 2005, Nicholas S. wrote:

> NameError: uninitialized constant A::B
> class MyClass
> returned.
> 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
nseckar (Guest)
on 2005-11-16 07:03
(Received via mailing list)
On 15-Nov-05, at 11:57 PM, Ara.T.Howard wrote:
> 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.
drbrain (Guest)
on 2005-11-16 07:30
(Received via mailing list)
On Nov 15, 2005, at 8:36 PM, Nicholas S. wrote:

> 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.
nobuyoshi.nakada (Guest)
on 2005-11-16 07:37
(Received via mailing list)
Hi,

At Wed, 16 Nov 2005 13:36:46 +0900,
Nicholas S. wrote in [ruby-talk:165996]:
>
> 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>
bob.news (Guest)
on 2005-11-16 12:38
(Received via mailing list)
Nicholas S. wrote:
> 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
ara.t.howard (Guest)
on 2005-11-16 15:48
(Received via mailing list)
On Wed, 16 Nov 2005, Nicholas S. wrote:

> 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
twifkak (Guest)
on 2005-11-16 16:44
(Received via mailing list)
Hi,

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

Devin
This topic is locked and can not be replied to.