Accessing class constants from within a module?

Any idea how I can access a class constant from within a module when that
module is include'd in the class? I.o.w. how do I make this work?

lizzy:~% cat m
module Debug
  def debug(level, msg)
    puts msg unless level > DEBUG # XXX DEBUG is wrong!
  end
end

class C1
  include Debug
  DEBUG = 1
  def foo
    debug(1, "this is foo")
  end
end

class C2
  include Debug
  DEBUG = 2
  def bar
    debug(1, "this is bar")
  end
end

C1.new.foo
C2.new.bar
lizzy:~% ruby m
m:3:in `debug': uninitialized constant Debug::DEBUG (NameError)
        from m:11:in `foo'
        from m:23
lizzy:~%

···

--
Jos Backus _/ _/_/_/ Sunnyvale, CA
                                _/ _/ _/
                               _/ _/_/_/
                          _/ _/ _/ _/
jos at catnook.com _/_/ _/_/_/ require 'std/disclaimer'

You probably don't want to do that anyway. If you change to Debug::DEBUG
in the class definitions, you'll be modifying a constant which can only
have one value at a time. You want two different values (and you can't
really tell the module to figure out which class constant is which
without a lot of headaches), so maybe just set it as a default instance
variable in your new objects?

Here is code that seems to do what you want:

# debug needs error checking in case @debug_level is nil
module Debug
  def debug(level, msg)
    puts msg unless level > @debug_level
  end
end

class C1
  include Debug
  def initialize
    @debug_level = 1
  end
  def foo
    debug(1, "this is foo")
  end
end

class C2
  include Debug
  def initialize
    @debug_level = 2
  end
  def bar
    debug(1, "this is bar")
  end
end

C1.new.foo
C2.new.bar

-Michael Libby

···

On Thu, 2004-12-02 at 13:00 +0900, Jos Backus wrote:

Any idea how I can access a class constant from within a module when that
module is include'd in the class? I.o.w. how do I make this work?

lizzy:~% cat m
module Debug
  def debug(level, msg)
    puts msg unless level > DEBUG # XXX DEBUG is wrong!
  end
end

class C1
  include Debug
  DEBUG = 1
  def foo
    debug(1, "this is foo")
  end
end

class C2
  include Debug
  DEBUG = 2
  def bar
    debug(1, "this is bar")
  end
end

C1.new.foo
C2.new.bar
lizzy:~% ruby m
m:3:in `debug': uninitialized constant Debug::DEBUG (NameError)
        from m:11:in `foo'
        from m:23
lizzy:~%

Jos Backus wrote:

Any idea how I can access a class constant from within a module when that
module is include'd in the class? I.o.w. how do I make this work?

Use dynamic lookup, rather than lexical lookup:

module Debug
  def debug(level, msg)
    puts msg unless level > DEBUG # XXX DEBUG is wrong!

       puts msg unless level > self.class::DEBUG

···

  end
end

Joel's solution was what I was looking for originally (thanks Joel!) but your
solution admittedly works well too. Thanks for that, Michael.

Another approach I tried was using a class variable but that doesn't work
either, presumably because it is likewise trying to find @@debug in Debug, not
the including class:

lizzy:~% cat n
module Debug
  def debug(level, msg)
    puts msg unless level > @@debug
  end
end

class C1
  @@debug = 1
  include Debug
  def foo
    debug(1, "this is foo")
  end
end

class C2
  @@debug = 1
  include Debug
  def bar
    debug(2, "this is bar")
  end
end

C1.new.foo
C2.new.bar
lizzy:~% ruby n
n:3:in `debug': uninitialized class variable @@debug in Debug (NameError)
        from n:11:in `foo'
        from n:23
lizzy:~%

Cheers,

···

On Thu, Dec 02, 2004 at 01:56:49PM +0900, Michael C. Libby wrote:

You probably don't want to do that anyway. If you change to Debug::DEBUG
in the class definitions, you'll be modifying a constant which can only
have one value at a time. You want two different values (and you can't
really tell the module to figure out which class constant is which
without a lot of headaches), so maybe just set it as a default instance
variable in your new objects?

--
Jos Backus _/ _/_/_/ Sunnyvale, CA
                                _/ _/ _/
                               _/ _/_/_/
                          _/ _/ _/ _/
jos at catnook.com _/_/ _/_/_/ require 'std/disclaimer'

It's things like these in Ruby that make my head spin :slight_smile: But it does make
sense: at time of invocation we want to say either C1::DEBUG or C2::DEBUG,
depending on the invoking method. `::' can be preceded by any expression
yielding a class, in this case self.class. This evaluates to either C1 or C2,
as appropriate, and voila.

Thanks guys!

···

On Thu, Dec 02, 2004 at 02:55:04PM +0900, Joel VanderWerf wrote:

Jos Backus wrote:
>Any idea how I can access a class constant from within a module when that
>module is include'd in the class? I.o.w. how do I make this work?

Use dynamic lookup, rather than lexical lookup:

>module Debug
> def debug(level, msg)
> puts msg unless level > DEBUG # XXX DEBUG is wrong!
      puts msg unless level > self.class::DEBUG
> end
>end

--
Jos Backus _/ _/_/_/ Sunnyvale, CA
                                _/ _/ _/
                               _/ _/_/_/
                          _/ _/ _/ _/
jos at catnook.com _/_/ _/_/_/ require 'std/disclaimer'

I like the self.class::CONSTANT expression better myself because it
keeps the object instances cleaner. I just didn't think it would be
thateasy and went for the first thing that might work that came to mind.

Next time I'm going to be thinking, "ok, I know Ruby has an
easier/faster/cleaner way to express this." :slight_smile:

-Michael Libby

···

On Thu, 2004-12-02 at 16:21 +0900, Jos Backus wrote:

Joel's solution was what I was looking for originally (thanks
Joel!) but your solution admittedly works well too. Thanks for
that, Michael.

> Joel's solution was what I was looking for originally (thanks
> Joel!) but your solution admittedly works well too. Thanks for
> that, Michael.

I like the self.class::CONSTANT expression better myself because it
keeps the object instances cleaner. I just didn't think it would be
thateasy and went for the first thing that might work that came to mind.

Your solution has the advantage that you can control debugging on a per-object
basis. Depending on the state of the object, while debugging, this can
sometimes be handy.

Next time I'm going to be thinking, "ok, I know Ruby has an
easier/faster/cleaner way to express this." :slight_smile:

My words exactly :slight_smile:

-Michael Libby

Cheers,

···

On Thu, Dec 02, 2004 at 08:08:11PM +0900, Michael C. Libby wrote:

On Thu, 2004-12-02 at 16:21 +0900, Jos Backus wrote:

--
Jos Backus _/ _/_/_/ Sunnyvale, CA
                                _/ _/ _/
                               _/ _/_/_/
                          _/ _/ _/ _/
jos at catnook.com _/_/ _/_/_/ require 'std/disclaimer'