Removing a constant definition from an environment


#1

Hi,

I am running unit tests that test the assignment of a constant value
in a “config” file that is actually ruby code, e.g.

VARIABLE=“somevalue”

On subsequent test methods in my unit test, I get a complaint that it
is already assigned.

setup
load const
end

def test_1
do something
end

def test_2
do something else
end

test 2 gets a warning that const has already been defined.

I expected the unit test to clean the environment, in lieu of that,
how can I undefined const?

Thanks,
Bob


#2

On 11/14/05, Robert E. removed_email_address@domain.invalid wrote:

I expected the unit test to clean the environment, in lieu of that,
how can I undefined const?

---------------------------------------------------- Module#remove_const
remove_const(sym) => obj

 Removes the definition of the given constant, returning that
 constant's value. Predefined classes and singleton objects (such as
 _true_) cannot be removed.

You pass it a symbol: remove_const(:VARIABLE)

Ryan


#3

Hi Ryan,

Thanks for your help.

I tried that, and it didn’t work. Here is my irb transcript trying
that. Is there another way to remove a constant? I thought I had seen
one in the Pickaxe book, but maybe I was thinking of the module method.

irb
irb(main):001:0> FOO = “my symbol value”
=> “my symbol value”
irb(main):002:0> remove_const(FOO)
NoMethodError: undefined method remove_const' for main:Object from (irb):2 irb(main):003:0> remove_const(:FOO) NoMethodError: undefined methodremove_const’ for main:Object
from (irb):3
irb(main):004:0> ??

Any other ideas?

Thanks,
Bob E.


#4

On Tue, Nov 15, 2005 at 06:56:59AM +0900, Robert E. wrote:

=> “my symbol value”
irb(main):002:0> remove_const(FOO)
NoMethodError: undefined method `remove_const’ for main:Object
from (irb):2
irb(main):003:0> remove_const(:FOO)

Object.remove_const(:FOO)


#5

On Tue, Nov 15, 2005 at 07:16:06AM +0900, Mauricio Fernández wrote:

irb(main):001:0> FOO = “my symbol value”
=> “my symbol value”
irb(main):002:0> remove_const(FOO)
NoMethodError: undefined method `remove_const’ for main:Object
from (irb):2
irb(main):003:0> remove_const(:FOO)

Sorry, I meant
Object.send(:remove_const, :FOO)


#6

Ryan,

This is a tricky problem. First, you can’t call remove_const directly
as it’s a private method on the Module class. In the simple case, that
means you have to call it from within the class or module from which
you want to remove a constant. Further, that call must be made from
within a class (as opposed to instance) method. Here is a simple
program which shows a class which can define and undefine a constant.
If you run it with ruby you should see the output in the comments

class Foo
def self.define_constant
class_eval “CONST_VAL = 1”
end

def self.undef_constant
remove_const :CONST_VAL
end
end

puts “CONST_VAL defined?: #{Foo.const_defined?(:CONST_VAL)}”
Foo.define_constant
puts “CONST_VAL defined now?: #{Foo.const_defined?(:CONST_VAL)}”
Foo.undef_constant
puts “CONST_VAL still defined?: #{Foo.const_defined?(:CONST_VAL)}”

output:

#CONST_VAL defined?: false
#CONST_VAL defined now?: true
#CONST_VAL still defined?: false

There are trickier ways to do this with the “send” method but this be
enough should get you started.


#7

Thanks for all responses. I think I can work something out based on
these. I’ll post my answer.

Bob


#8

On 11/14/05, Robert E. removed_email_address@domain.invalid wrote:

=> “my symbol value”
irb(main):002:0> remove_const(FOO)
NoMethodError: undefined method remove_const' for main:Object from (irb):2 irb(main):003:0> remove_const(:FOO) NoMethodError: undefined methodremove_const’ for main:Object
from (irb):3
irb(main):004:0> ??

Any other ideas?

This is one of those odd methods that is hard to call. It is actually
a private method in Module, which means you must call it on a module
or class, and because it is private you must use send if you want to
call it from outside the class or module:

irb(main):001:0> FOO = “something”
=> “something”
irb(main):002:0> Object.const_defined?(:FOO)
=> true
irb(main):003:0> Object.send(:remove_const, :FOO)
=> “something”
irb(main):004:0> FOO
NameError: uninitialized constant FOO
from (irb):4

But it is better to do something like this:

irb(main):005:0> class Test
irb(main):006:1> FOO = “something”
irb(main):007:1> end
=> “something”
irb(main):008:0> class Test
irb(main):009:1> FOO = “something else”
irb(main):010:1> end
(irb):9: warning: already initialized constant FOO
=> “something else”
irb(main):011:0> class Test
irb(main):012:1> remove_const(:FOO)
irb(main):013:1> FOO = “yet something else”
irb(main):014:1> end
=> “yet something else”

In other words you should call remove_const from inside the class
definition before re-creating it.

Ryan


#9

On 11/14/05, Mauricio Fernández removed_email_address@domain.invalid wrote:

irb
irb(main):001:0> FOO = “my symbol value”
=> “my symbol value”
irb(main):002:0> remove_const(FOO)
NoMethodError: undefined method `remove_const’ for main:Object
from (irb):2
irb(main):003:0> remove_const(:FOO)

Sorry, I meant
Object.send(:remove_const, :FOO)

I may be wrong, but I think using #send to bypass private method
encapsulation is not future-proof. Instead, you might use

Object.instance_eval{ remove_const :FOO }

This is almost guaranteed to work in the future.

cheers,
Mark


#10

Hey Everybody,

Thanks for your help. I went for Mark’s approach of using
Object.instance_eval(:remove_const, :FOO).

I want to make sure I understand what is going on, so let me know if
I get the reason this works wrong.

This works because: Object includes Kernel which is a Module, so it
gets all the methods on Module, which includes remove_const. Is that
correct?

Thanks,
Bob E.


#11

On Tue, Nov 15, 2005 at 10:44:48AM +0900, Mark H. wrote:

On 11/14/05, Mauricio Fernández removed_email_address@domain.invalid wrote:

Sorry, I meant
Object.send(:remove_const, :FOO)

I may be wrong, but I think using #send to bypass private method
encapsulation is not future-proof.

Right, http://eigenclass.org/hiki.rb?Changes+in+Ruby+1.9#l11