Deleting a class

Hello,

I’m try to find a way to completely delete a class. (What I would
eventually like is a way to load an entire GUI by default, but if the user
requests a new one, I’d like to unload the old one and load up the new one.)

So, in this code I create class Foo, but I can’t figure out how to delete
it. Am I doing the GC wrong, or is there some reference to Foo I haven’t
yet deleted? Or both?

def getAllClasses
classes = []
ObjectSpace.each_object (Class) do |klass|
classes << klass.to_s
end
classes
end

def getAllConstants
Module.constants
end

originalClasses = getAllClasses
originalConstants = getAllConstants

class Foo
end

Object.module_eval { remove_const ‘Foo’ }

GC.start

puts 'New classes: ‘+(getAllClasses - originalClasses ).join(’, ')
puts 'New constants: ‘+(getAllConstants - originalConstants).join(’, ')

Thanks for any help you can offer,

Chris

Here’s something weird:

If instead of using the code:

class Foo
end

…I instead use…

eval ‘class Foo; end’

…then the rest of the code does delete the class!

I just went through all of that in my mail “all the pretty evals…”; I was
asking if these are the same. Obviously they aren’t the same.

So what’s the difference? And what about the other evals: do they work
like ‘eval’ does? What about ‘require’ and ‘load’? (Those last two were
rhetorical questions; I can figure it out on my own. I just want to know
what the difference is and why it works this way.)

Chris

···

----- Original Message -----
From: “Chris Pine” nemo@hellotree.com
Newsgroups: comp.lang.ruby
Sent: Wednesday, December 11, 2002 10:20 AM
Subject: deleting a class

Hello,

I’m try to find a way to completely delete a class. (What I would
eventually like is a way to load an entire GUI by default, but if the user
requests a new one, I’d like to unload the old one and load up the new one.)

So, in this code I create class Foo, but I can’t figure out how to delete
it. Am I doing the GC wrong, or is there some reference to Foo I haven’t
yet deleted? Or both?

def getAllClasses
classes = []
ObjectSpace.each_object (Class) do |klass|
classes << klass.to_s
end
classes
end

def getAllConstants
Module.constants
end

originalClasses = getAllClasses
originalConstants = getAllConstants

class Foo
end

Object.module_eval { remove_const ‘Foo’ }

GC.start

puts 'New classes: ‘+(getAllClasses - originalClasses ).join(’, ')
puts 'New constants: ‘+(getAllConstants - originalConstants).join(’, ')

Thanks for any help you can offer,

Chris

...then the rest of the code *does* delete the class!

Well, you are just seeing that the GC is conservative.

Imagine that something write

   class A
   end

   a = A.new
   Object.module_eval { remove_const 'A' }

at this step, ruby can't remove completely the class because this class is
referenced by the object `a'

pigeon% cat b.rb
#!/usr/bin/ruby
class A
   def a
      puts "a"
   end
end

a = A.new

Object.module_eval { remove_const 'A' }
class A
   def b
      puts "b"
   end
end

b = A.new
a.a
b.b
b.a
pigeon%

pigeon% b.rb
a
b
./b.rb:20: undefined method `a' for #<A:0x401ad338> (NameError)
pigeon%

This is something similar in your case, because it exist a *temporary*
variable which still make reference to the class

I just went through all of that in my mail "all the pretty evals..."; I was
asking if these are the same. Obviously they aren't the same.

Well apparently there is another problem :slight_smile:

Just try this

pigeon% ruby -e 'class A; end; A.module_eval "12"'
pigeon%

pigeon% ruby -e '.module_eval "12"'
-e:1: undefined method `module_eval' for :Array (NameError)
pigeon%

#module_eval is a method defined in Module, and only object which inherit
  from Module can access this method.

Now

pigeon% ruby -e 'class A; end; A.instance_eval "12"'
pigeon%

pigeon% ruby -e '.instance_eval "12"'
pigeon%

#instance_eval is defined in Kernel and *all* objects can use this method.

You have 2 methods with differents names and defined in 2 differents
classes.

Now

pigeon% ruby -e 'class A; end; A.eval "12"'
-e:1: private method `eval' called for A:Class (NameError)
pigeon%

pigeon% ruby -e '.eval "12"'
-e:1: private method `eval' called for :Array (NameError)
pigeon%

#eval is a module function of Kernel

Now to see the difference

pigeon% cat b.rb
#!/usr/bin/ruby
class A
   def A.a
      puts "A::a"
   end

   def a
      puts "A#a"
   end
end

def a
   puts "Object#a"
end

x = A.new

eval 'p self; a'

x.instance_eval 'p self; a'

class A
   eval 'p self; a'
end

A.instance_eval 'p self; a'
pigeon%

pigeon% b.rb
main
Object#a
#<A:0x401ad1e4>
A#a
A
A
pigeon%

Guy Decoux

···

A::a
A::a

I don’t understand. Are you saying there’s a temporary reference to the
class? I was asking why that would be the case when I define a class one
way, but not another?

This code does NOT remove the class Foo:

class Foo; end
Object.module_eval { remove_const ‘Foo’ }
GC.start

But this DOES remove it:

eval 'class Foo; end’
Object.module_eval { remove_const ‘Foo’ }
GC.start

To sum up all the ways of defining a class I can think of, the following
ways of defining a class WILL allow the class to be removed (at least by
removing the constant and starting the GC):

–> eval ‘class Foo; end’
–> Foo = Class.new
–> load ‘foo.rb’
–> require ‘foo.rb’

(foo.rb is simply: class Foo; end)

And the following WILL NOT allow the class to be removed:

–> class Foo; end
–> eval ‘Foo = Class.new’ # How weird is that??

So I just wanted to know why. If you are saying there’s a temporary
reference, why is it avoided by using ‘eval’ (or sometimes caused by using
’eval’)? Is there a way I can get rid of this temporary reference?

Chris

I don't understand. Are you saying there's a temporary reference to the
class?

Yes,

This code does NOT remove the class Foo:

  class Foo; end
  Object.module_eval { remove_const 'Foo' }
  GC.start

In this case, some where on the stack, it exist an object which reference
Foo (this can be a node, or anything else)

But this DOES remove it:

  eval 'class Foo; end'
  Object.module_eval { remove_const 'Foo' }
  GC.start

Your code is just different, and in this case no object reference Foo

So I just wanted to know why. If you are saying there's a temporary
reference, why is it avoided by using 'eval' (or sometimes *caused* by using
'eval')? Is there a way I can get rid of this temporary reference?

no : this is why the GC is called "conservative". It can mark object, that
normally don't exist but it's not important. When you write

   class Foo; end
   Object.module_eval { remove_const 'Foo' }

Now it's not possible to access the class Foo.

Guy Decoux

When you write

class Foo; end
Object.module_eval { remove_const ‘Foo’ }

Now it’s not possible to access the class Foo.

···

----- Original Message -----

Sure it is:

def getAllClasses
classes = []
ObjectSpace.each_object (Module) do |klass|
classes << klass.inspect
end
classes
end

originalClasses = getAllClasses

class Foo; end

Object.module_eval { remove_const ‘Foo’ }

GC.start

deletedClasses = []
ObjectSpace.each_object (Module) do |klass|
deletedClasses << klass if(!originalClasses.include?(klass.inspect))
end

foo = deletedClasses[0].new

p foo # --> #Foo:0x2774c30

Note that if you change

class Foo; end

to

eval ‘class Foo; end’

you’ll get a NoMethodError, since the class is deleted. This proves that my
code isn’t holding a reference to the class.

Chris

  class Foo; end

pigeon% cat b.rb
#!/usr/bin/ruby
def getAllClasses
   classes =
   ObjectSpace.each_object (Module) do |klass|
      classes << klass.inspect
   end
   classes
end

originalClasses = getAllClasses
class Object
   class Foo; end
end

Object.module_eval { remove_const 'Foo' }

GC.start

deletedClasses =
ObjectSpace.each_object (Module) do |klass|
   deletedClasses << klass if(!originalClasses.include?(klass.inspect))
end

foo = deletedClasses[0].new
p foo
pigeon%

pigeon% b.rb
./b.rb:24: undefined method `new' for nil (NameError)
pigeon%

you'll get a NoMethodError, since the class is deleted. This proves that my
code isn't holding a reference to the class.

This prove nothing.

Guy Decoux

Are you saying I do have a reference to Foo in my code?! There might be a
reference to Foo somewhere, but it ain’t in my code.

You said I couldn’t get the object back. I could. But this is all
tangential to my original question: “How are these class definitions
different?”

Your answer seems to be: “They are different internally to Ruby.”

Well, I knew that. :slight_smile: If there’s no principle behind why these are
different, fine. I was just wondering if there was something there which I
didn’t understand (and if there was any way I could actually delete a class
no matter how it was created, which you weren’t able to do, either).

I have come to expect everything to make sense, to be intentional in Ruby;
but I think I am getting to the point where I am noticing implementation
specifics, rather than language specifics, which are not so easily
explained.

Anyway, thank you for your responses,

Chris