AR reflections being set to nil

I'm trying to track down a bug with my application in edge Rails and discovered that somewhere after the first time I access the associations for my AR class, the reflections for the class are reset to nil! This, of course, wreaks havoc with my ability to use the class' associations, specifically some :includes I'm trying to process in a find().

This doesn't occur with gem rails, and I'm trying to figure out where it might be happening. Its running in development mode, so it is reloading classes, but this is within a single request, so no reload is happening. Watching the AR::Reflection::Classmethods#create_reflection and #reflection methods suggests that it is happening somewhere else, but I can't seem to find anywhere else in the sources where it might clear this. Can anyone help me track down this bug?

Could you show us a failing test case?

Thanks, jeremy

Unfortunately, it's in the middle of a complex app and I haven't been able to reduce it.

Actually, just trying to pull tracing data out (to show how it gets cleared), I stumbled onto what is most probably the solution. It tuns out that I was mistaken, this is between requests in development mode, so the class at issue *should* be reloaded, but isn't. It seems to have been cleared, but is still hanging around and the class reload has not taken place. I discovered this by putting a breakpoint into the class definition itself, which was triggered for the first request, but not the second. Running with cache_classes = true seems to work just fine...

Are there any known bugs with cache_classes in edge? How has this changed since 1.1.6? The bug seems to be that my AR class is cleared of associations (seemingly in preparation for reload), but it does not reload on reference and instead tries to use the existing "empty" class definition.

Reflections refer to the class object which is reloaded. I’d need to know a little more about why you have kept a reflection around between reloading classes it refers to.

jeremy

I had the same problem with dynamically created classes. I posted my findings here before, but didnt' get much of a response in return. That was probably a month or two back.

The methods from belongs_to, etc existed and worked, but using the associations in :include statements came up with nothing.

I never figured out why this was occuring though because it was getting into details of the framework that I didn't understand.

Anyway, my problems basically came down to the class I was using being a dynamic one (not specified in a rb file). Not sure if this helps, but it sounds like the same problem.

I think you misunderstood, so let me rephrase. I've got an AR-derived class (ActorTag) with a set of associations. The first controller action completes perfectly, but on the second I get an error in my AR-derived class in which an association necessary for find(..., :include => ...) is not found. It turns out that it is using the same class object in both requests (although it should have been reloaded, given that cache_classes=false) except that by the time it hits the second one the reflections attribute has been reset to nil. None of this happens if cache_classes has been set to true.

Now, I have verified that this is the same class by examining the object_id's of the ActorTag class object. I have also verified the lack of reload by placing a breakpoint in my class definition code (which gets triggered exactly once). Placing 'assert { not base.reflections.empty? }' in ActiveRecord::Associations::ClassMethods::JoinDependency#initialize produces a break and I can engage in this particular dialogue:

irb(#<ActiveRecord::Associations::ClassMethods::JoinDependency:0x3448438>):001:0> base => ActorTag irb(#<ActiveRecord::Associations::ClassMethods::JoinDependency:0x3448438>):002:0> ActorTag => ActorTag irb(#<ActiveRecord::Associations::ClassMethods::JoinDependency:0x3448438>):003:0> ActorTag.object_id => 28185842 irb(#<ActiveRecord::Associations::ClassMethods::JoinDependency:0x3448438>):004:0> base.object_id => 28406902 irb(#<ActiveRecord::Associations::ClassMethods::JoinDependency:0x3448438>):005:0> ActorTag.reflections => {:actor=>#<ActiveRecord::Reflection::AssociationReflection:0x35c2840 @primary_key_name="actor_id", @through_reflection=false, @name=:actor, @class_name="Actor", @active_record=ActorTag, @options={}, @macro=:belongs_to>, :bookmarks=>#<ActiveRecord::Reflection::AssociationReflection:0x35b48d0 @primary_key_name="actor_tag_id", @name=:bookmarks, @active_record=ActorTag, @options={:through=>:url_tags}, @macro=:has_many>, :notes=>#<ActiveRecord::Reflection::AssociationReflection:0x35ae05c @primary_key_name="actor_tag_id", @name=:notes, @active_record=ActorTag, @options={:through=>:url_tags}, @macro=:has_many>, :url_tags=>#<ActiveRecord::Reflection::AssociationReflection:0x35bd278 @primary_key_name="actor_tag_id", @name=:url_tags, @active_record=ActorTag, @options={:dependent=>:destroy, :include=>[:tag, :link]}, @macro=:has_many>, :tag=>#<ActiveRecord::Reflection::AssociationReflection:0x35c0e78 @primary_key_name="tag_id", @through_reflection=false, @name=:tag, @class_name="Tag", @active_record=ActorTag, @options={}, @macro=:belongs_to>, :links=>#<ActiveRecord::Reflection::AssociationReflection:0x35b8b9c @primary_key_name="actor_tag_id", @name=:links, @active_record=ActorTag, @options={:through=>:url_tags}, @macro=:has_many>} irb(#<ActiveRecord::Associations::ClassMethods::JoinDependency:0x3448438>):006:0> base.reflections => {}

So... The base passed in to JoinDependency#initialize is an ActorTag class with reflections cleared to nil. When I access the ActorTag class *by name*, I get a different object with a proper reflections array. Am I wrong to think that something has screwed up in signalling that the class should be automatically reloaded on reference? It wasn't reloaded when it should have been, and I'm now seeing a class with the right name, but messed-up contents.

I have now done Dependencies.log_activity=true in my execution environment and seen:

Dependencies: removing constant ActorTag

and no subsequent reload. If I use the class name in the breakpoint dialogue, it does reload the class. Now, the one other datapoint is that I have had one model class reload since the constant was removed, and the ActorTag class is being accessed via one of it's associations. The @klass object in that reflection is the source of the bad AR-derived class! I now have to track down where it is being initialized, because that clearly is somehow bypassing the name-lookup (which would trigger reload).

OK. Final comment, and strange interdepency discovered. It seems that I had a stray "require" in one of my model files. After tracing Dependencies and watching create_reflections executing as a few files were reloaded, I discovered that the association I was tracking was actually derived from an inherited class, which wasn't being reloaded when the derived class was reloaded. As a result I was seeing stale classes in the reflections inherited from the parent class. Why wasn't the parent class reloaded? Check out what I had in my derived class definition:

Is your model in question use any external libraries that Rails cannot find through const_missing?