Forum: Ruby removing a constant definition from an environment

97e3d8ba68b082414cc3438d15c9d6e2?d=identicon&s=25 robert.evans (Guest)
on 2005-11-14 22:29
(Received via mailing list)
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
4b174722d1b1a4bbd9672e1ab50c30a9?d=identicon&s=25 leavengood (Guest)
on 2005-11-14 22:47
(Received via mailing list)
On 11/14/05, Robert Evans <robert.evans@acm.org> 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
97e3d8ba68b082414cc3438d15c9d6e2?d=identicon&s=25 robert.evans (Guest)
on 2005-11-14 22:59
(Received via mailing list)
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 method `remove_const' for main:Object
         from (irb):3
irb(main):004:0> ??


Any other ideas?

Thanks,
Bob Evans
Dd76a12d66f843de5c5f8782668e7127?d=identicon&s=25 mfp (Guest)
on 2005-11-14 23:17
(Received via mailing list)
On Tue, Nov 15, 2005 at 06:56:59AM +0900, Robert Evans 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)
Dd76a12d66f843de5c5f8782668e7127?d=identicon&s=25 mfp (Guest)
on 2005-11-14 23:20
(Received via mailing list)
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)
573b9499030e1ccb867ef80f0ff1ac49?d=identicon&s=25 jgbailey (Guest)
on 2005-11-14 23:23
(Received via mailing list)
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.
4b174722d1b1a4bbd9672e1ab50c30a9?d=identicon&s=25 leavengood (Guest)
on 2005-11-14 23:26
(Received via mailing list)
On 11/14/05, Robert Evans <robert.evans@acm.org> 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 method `remove_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
97e3d8ba68b082414cc3438d15c9d6e2?d=identicon&s=25 robert.evans (Guest)
on 2005-11-15 00:08
(Received via mailing list)
Thanks for all responses. I think I can work something out based on
these. I'll post my answer.

Bob
D9179cdd918879d0510dfc56411e4772?d=identicon&s=25 discordantus (Guest)
on 2005-11-15 02:48
(Received via mailing list)
On 11/14/05, Mauricio Fernández <mfp@acm.org> 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
97e3d8ba68b082414cc3438d15c9d6e2?d=identicon&s=25 robert.evans (Guest)
on 2005-11-15 06:18
(Received via mailing list)
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 Evans
Dd76a12d66f843de5c5f8782668e7127?d=identicon&s=25 mfp (Guest)
on 2005-11-15 10:22
(Received via mailing list)
On Tue, Nov 15, 2005 at 10:44:48AM +0900, Mark Hubbart wrote:
> On 11/14/05, Mauricio Fernández <mfp@acm.org> 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
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.