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

Rick DeNatale rick.denatale at gmail.com
Thu Aug 5 08:51:03 EDT 2010


On Thu, Aug 5, 2010 at 5:16 AM, Bruno Cardoso <lists at ruby-forum.com> wrote:
> 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.

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: http://github.com/rubyredrick
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale


More information about the rspec-users mailing list