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?