Correct use of remove_const after/within instance_eval

I’m tinkering with an application that has multiple ruby shells against
which code can be executed (with instance_eval), and I’m trying to
“undefine” a class that has been previously defined in one of the
shells. I understand that normally you would invoke the “remove_const”
method via a “send”, but I’m getting a “not defined” error. I think
perhaps Object is not the appropriate destination for the “send” when
the class was defined within an “instance_eval”, but I’m stumped on
figuring this out further.

Below is a reduced set of code that reproduces the problem. Thanks in
advance for any assistance.

class Shell; end
s = Shell.new()

s.instance_eval( <<EOS )
class Test
def a; puts “method a”; end
end

o = Test.new()
o.a()
EOS

s.instance_eval( <<EOS )
puts Test.superclass # Object, so class Test does exist at this point
Object.send( :remove_const, :Test ) # throws error: constant
Object::Test not defined (NameError)
EOS

John Cawley III wrote in post #1008833:

puts Test.superclass # Object, so class Test does exist at this point

Change this to “puts Test” and you’ll see what the problem is:

method a
#Class:0x7f05b018c600::Test

You have defined the constant Test within a module namespace which is an
anonymous Class - probably the singleton class of ‘s’ (which is an
instance of class Shell). Note: the namespace which you create a
constant within is completely orthogonal to the concept of ‘superclass’,
which is the class where you inherit instance methods from.

So here’s a fix making minimal changes to your code:

class Shell; end

Shell.class_eval( <<EOS )
class Test
def a; puts “method a”; end
end

o = Test.new()
o.a()
EOS

Shell.class_eval( <<EOS )
puts Test
send( :remove_const, :Test ) # or: Shell.send(…)
EOS

However, I think it would be tidier if you (a) use a module rather than
a class, since you never instantiate Shell; and (b) create the constant
and remove the constant both outside of the eval. Then it becomes:

module Shell; end

Shell::Test = Class.new # create constant
Shell::Test.class_eval( <<EOS )
def a; puts “method a”; end
EOS

o = Shell::Test.new
o.a

Shell.send(:remove_const, :Test) # remove constant

I think this makes it clearer: the class_eval is saying “‘def’ should
define methods within this class”, and the Shell::Test is saying which
namespace you want to create the Test constant within. In the earlier
code, class_eval was doing both.

Another option is to use the block form of Class.new:

Shell::Test = Class.new do
eval( <<EOS )
def a; puts “method a”; end
EOS
end

HTH,

Brian.

John Cawley III wrote in post #1008833:

I’m tinkering with an application that has multiple ruby shells against
which code can be executed (with instance_eval), and I’m trying to
“undefine” a class that has been previously defined in one of the
shells. I understand that normally you would invoke the “remove_const”
method via a “send”, but I’m getting a “not defined” error. I think
perhaps Object is not the appropriate destination for the “send” when
the class was defined within an “instance_eval”, but I’m stumped on
figuring this out further.

Below is a reduced set of code that reproduces the problem. Thanks in
advance for any assistance.

class Shell; end
s = Shell.new()

s.instance_eval

instance_eval() sets self equal to the receiver, but it also changes the
“current class” to the receiver’s singleton class. Knowing the
‘current’ class is important for at least one reason: def’s attach
themselves to the current class.

class_eval() sets self equal to the receiver, and it also changes the
current class to the receiver (and therefore class_eval can only be
called on a class or module)

A class is a module, and a module is a namespace. Therefore, when
instance_eval() changes the current class to s’s singleton class, the
current namespace becomes the name of s’s singleton class–but s’s
singleton class is an anonymous class with no name. If you define a
new class in that anonymous namespace, then you are going to have
a hard time referring to it from outside that namespace.

7stud – wrote in post #1008858:

If you define a
new class in that anonymous namespace, then you are going to have
a hard time referring to it from outside that namespace.

Of course, it is doable, but it requires a trick:

class <<s;self;end.class_eval {
remove_const( :Test )
}

(There was discussion of adding a ‘singleton_class’ method to 1.9, I’m
not sure if it was added or not)

I suspect the OP probably wanted to create the constant under Shell, not
under an anonymous module. But he did want each instance of the Shell
class needs its own independent Test constant, then this is the way to
do it.

Although if he does want instances of Shell, then I’d say an instance
variable (@test) would be a much cleaner way to do it.

John Cawley III wrote in post #1008928:

Thank you both much. Since there may be several Shell instances at any
time, each with different ruby code having been run against it via
instance_evals, one Shell instance might have a class defined, while
another might not. Thus the last trick, to get the anonymous class, I
think will be particularly helpful.

Cool. If you need to refer to classes by a constant , then this
will do the trick. But if you don’t need to refer to your class by name,
you can just make an anonymous class and store it in an instance
variable.

class Shell
attr_reader :klass
def defclass(str)
@klass = Class.new { eval(str) }
end
def makeobject(*args)
@klass.new(*args)
end
end

s1 = Shell.new
s1.defclass <<EOS
def a; puts “hello”; end
EOS
obj = s1.makeobject
obj.a

So this way you’ll either have an anonymous class; the other way you’ll
have a named class within an anonymous namespace, i.e.
::Test. It’s your call as to which makes more sense in your
application.

Thank you both much. Since there may be several Shell instances at any
time, each with different ruby code having been run against it via
instance_evals, one Shell instance might have a class defined, while
another might not. Thus the last trick, to get the anonymous class, I
think will be particularly helpful.

To delve further into anonymous and singleton classes, both
http://www.klankboomklang.com/2007/09/21/the-singleton-class/ and
http://www.klankboomklang.com/2007/10/05/the-metaclass/ look promising.
Would either of you have any other sites/books/articles you would
recommend for me to follow up with further study?

Again, thank you both for your assistance.