Accessing class constants from within a module?

J

Jos Backus

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'
 
M

Michael C. Libby

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:~%

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
 
J

Joel VanderWerf

Jos said:
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
 
J

Jos Backus

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?

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,
--
Jos Backus _/ _/_/_/ Sunnyvale, CA
_/ _/ _/
_/ _/_/_/
_/ _/ _/ _/
jos at catnook.com _/_/ _/_/_/ require 'std/disclaimer'
 
J

Jos Backus

Use dynamic lookup, rather than lexical lookup:

puts msg unless level > self.class::DEBUG

It's things like these in Ruby that make my head spin :) 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!

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

Michael C. Libby

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.

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

-Michael Libby
 
J

Jos Backus

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." :)

My words exactly :)
-Michael Libby

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

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

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top