Close
Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

LABS
Perplexing constant scope behavior

Why can’t I refer to constants nested inside a class within a module eval?

irb(main):001:0> class A
irb(main):002:1>    class B
irb(main):003:2>    end
irb(main):004:1> end
=> nil
irb(main):005:0> A.module_eval { puts B }
NameError: uninitialized constant B
           from (irb):5
           from (irb):5:in `module_eval'
           from (irb):5
           from :0

This is frustrating. Can anyone explain why it has to be this way? Perhaps there’s a good reason I’m not considering.

Comments
  1. josh says:

    Doing a module_eval of a block apparently leaves the constants bound in the block’s context, not in the eval context. Try adding a top-level constant B = “surprise!” and do it again. But if you change the block to a string, it will bind B to A::B. Isn’t Ruby fun sometimes?

  2. Nathan Sobo says:

    Well it’s really sad this is the case. I’m working on a solution to my gripes in the previous post that takes

    describe "Something worth describing" do...

    and turns it into a sanitized/camelized Test::Unit style class with the contents of the block class evaluated inside of it. But if there are constant references in the block, they are bound globally, which kills test isolation. Look here:

    describe "Something worth describing" do
       class RarelyAView
       end
    end
    

    It would be really nice if this defined SomethingWorthDescribing::RarelyAView so that I could use this constant freely within the test and not worry about other tests’ usage of Foo.

    The Frankenstein welding of Rspec and Test::Unit is going well though. For now I can at least fall back on replacing the describe sugar with SomethingWorthDescribingTest < Screw::Unit::TestCase and still use other sugary Rspec syntax within it.

  3. Jacob Radford says:

    You could do something like this:

    A.module_eval { puts self::B }

  4. Nathan,
    I remembered you when I read this post about the same problem where Ola Bini give you a real answer:

    it’s an artifact of the way Ruby’s parsing works. Basically, a “real” constant reference will have it’s cref set during parse time – meaning it will be resolved using real lexical scope. The difference here is that when you list all constants in a scope or use const_get, you’re really using the dynamic scope

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *