[RSpec] Dynamic reloading with Object.send(:remove_const)

Hi,

I have a function that adds dynamic validations to my models. I use a
combination of “:remove_const” and “load” methods to reload the code
everytime I want to re-apply my dynamic validations. It seams RSpec has
some issues with this.

I posted here a simpler example code that shows what I mean:

http://pastie.org/private/ruvgxgipxfge6yzuudbq

Is it possible to test this with RSpec?

Regards

Rick Denatale wrote:

Your pastie says

this should have made “Account” go back to its original state but

it’s not working.

this works outside of RSpec.

but I don’t understand in what context. In general this won’t work in
Ruby.

Removing the constant only removes the constant from the internal hash
which binds the name to the object value. It doesn’t change the class
object (pointed to by the klass field) of existing instances.

Here’s a little experiment. The defineFoo method should be the moral
equivalent of the load, and the existence of the m2 method is an
analogue to whether the class has the validation callback.

def defineFoo
eval(“class Foo;def m1;end;end”)
end

defineFoo

foo = Foo.new

foo.methods - Object.instance_methods # => [“m1”]

Foo.class_eval(“def m2;end”)
foo.methods - Object.instance_methods # => [“m1”, “m2”]

Object.send(:remove_const, ‘Foo’)
defineFoo

foo2 = Foo.new

foo2.methods - Object.instance_methods # => [“m1”]
foo.methods - Object.instance_methods # => [“m1”, “m2”]

foo.class == foo2.class # => false

Eureka!

You are absolutely right. I completly forgot that I need to instantiate
the object again to have the updated definition of the class. Old
instances remain the same even after the class definition is changed.

Thanks Rick.

On Wed, Aug 4, 2010 at 12:41 PM, Bruno C. [email protected]
wrote:

Is it possible to test this with RSpec?

Your pastie says

this should have made “Account” go back to its original state but

it’s not working.

this works outside of RSpec.

but I don’t understand in what context. In general this won’t work in
Ruby.

Removing the constant only removes the constant from the internal hash
which binds the name to the object value. It doesn’t change the class
object (pointed to by the klass field) of existing instances.

Here’s a little experiment. The defineFoo method should be the moral
equivalent of the load, and the existence of the m2 method is an
analogue to whether the class has the validation callback.

def defineFoo
eval(“class Foo;def m1;end;end”)
end

defineFoo

foo = Foo.new

foo.methods - Object.instance_methods # => [“m1”]

Foo.class_eval(“def m2;end”)
foo.methods - Object.instance_methods # => [“m1”, “m2”]

Object.send(:remove_const, ‘Foo’)
defineFoo

foo2 = Foo.new

foo2.methods - Object.instance_methods # => [“m1”]
foo.methods - Object.instance_methods # => [“m1”, “m2”]

foo.class == foo2.class # => false

Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Github: rubyredrick (Rick DeNatale) · GitHub
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

On Thu, Aug 5, 2010 at 5:16 AM, Bruno C. [email protected]
wrote:

which binds the name to the object value. It doesn’t change the class
defineFoo

the object again to have the updated definition of the class. Old
instances remain the same even after the class definition is changed.

Thanks Rick.

That’s all well and good, but I have to step back and say that I
detect a code smell called “stupid ruby tricks” here.

This type of dynamic class alteration can lead to all kinds of trouble
trying to understand, and debug the code. Debugging Rails callbacks
is tough enough even without such unorthodox code. If you pursue this
technique I predict several frustrating debugging sessions in your
future.

Why not just use the AR API and start with something like

class Account < ActiveRecord::Base
class << self
attribute_accessor :validating_description
end

validates_numericality_of :description :if => lambda { |account|
Account.validating_description}

end

Then just use

Account.validating_description = true

to turn it off and

Account.validating_description = false

to turn it off?

Even then, I’m not sure I understand the use case for turning this off
at the class label, usually such conditional validations are done on
the basis of instance state, which is why the proc associated with the
:if and :unless options takes the instance as an argument, of course
the proc is free to ignore that as I’ve done here.

But it’s your use case, and other than poking you to think about
whether you want to do it on a class or instance basis, I’ll defer to
your superior domain knowledge.


Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Github: rubyredrick (Rick DeNatale) · GitHub
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale