I have a complicated case where I'm developing a Rails 3 Engine and I only intermittently get the error in the title. Here's the stacktrace:
ActiveRecord::ConfigurationError - Association named 'whatever' was not found; perhaps you misspelled it?:
activerecord (3.2.18) lib/active_record/associations/preloader.rb:150:in `block in records_by_reflection'
activerecord (3.2.18) lib/active_record/associations/preloader.rb:146:in `records_by_reflection'
activerecord (3.2.18) lib/active_record/associations/preloader.rb:139:in `grouped_records'
activerecord (3.2.18) lib/active_record/associations/preloader.rb:130:in `preload_one'
activerecord (3.2.18) lib/active_record/associations/preloader.rb:109:in `preload'
activerecord (3.2.18) lib/active_record/associations/preloader.rb:98:in `block in run'
activerecord (3.2.18) lib/active_record/associations/preloader.rb:98:in `run'
activerecord (3.2.18) lib/active_record/relation.rb:181:in `block in exec_queries'
activerecord (3.2.18) lib/active_record/relation.rb:180:in `exec_queries'
activerecord (3.2.18) lib/active_record/relation.rb:160:in `block in to_a'
activerecord (3.2.18) lib/active_record/explain.rb:41:in `logging_query_plan'
activerecord (3.2.18) lib/active_record/relation.rb:159:in `to_a'
activerecord (3.2.18) lib/active_record/relation/delegation.rb:39:in `+'
/Users/me/src/appointment_engine/app/controllers/appointment_engine/appointments_controller.rb:42:in `block (3 levels) in index'
/Users/me/src/appointment_engine/app/controllers/appointment_engine/appointments_controller.rb:41:in `block (2 levels) in index'
actionpack (3.2.18) lib/action_controller/metal/mime_responds.rb:196:in `respond_to'
/Users/me/src/appointment_engine/app/controllers/appointment_engine/appointments_controller.rb:12:in `index'
To summarize: There's a model named Appointment
in the engine which is polymorphically associated with has_many :through
to the host app's User
model (this is a requirement because we also associate to another model). Here's the has_many
declaration in Appointment
class Appointment < ActiveRecord::Base
has_many :scheduleables,
through: :appointments_scheduleables,
source_type: KAE.scheduleable_class.name
has_many :schedulers,
through: :appointments_schedulers,
source_type: KAE.scheduler_class.name
end
Here I ran into my first problem; I need to set the :source_type
on has_many :through
polymorphic associations (It doesn't work without it) and for that I need to know the class of the associated model but, when my engine's Appointment
model loads it does so before the host app's User
model loads and therefore my engine's module KAE
hasn't received the value for KAE.scheduleable_class
yet.
Here's how KAE
receives that value:
# in host app
class User < ActiveRecord::Base
acts_as_scheduler
end
I wrote acts_as_scheduler
as an AR mixin, it will declare the has_many :through
association to Appointment
.
My first attempt to fix this: I put the Appointment
's has_many
declaration in a hook inside a railtie:
ActiveSupport.on_load :after_initialize do
KAE::Appointment.class_eval do
has_many :scheduleables,
through: :appointments_scheduleables,
source_type: KAE.scheduleable_class.name
has_many :schedulers,
through: :appointments_schedulers,
source_type: KAE.scheduler_class.name
end
end
Ok now that works, I wait till the host app loads completely and now I have the values for KAE.scheduleable_class
and KAE.scheduler_class
, great.
Except I get the error in the title intermittently!
I can boot up fine, use the app for a while (10-30 mins) and then out of nowhere boom! I've tried it under rails server
, thin
, and unicorn
; all the same so it must be an app/framework level bug. I look in the active record preloader class to where the top of the stacktrace points to:
# lib/active_record/associations/preloader.rb:150
def records_by_reflection(association)
records.group_by do |record|
reflection = record.class.reflections[association]
unless reflection
raise ActiveRecord::ConfigurationError, "Association named '#{association}' was not found; " \
"perhaps you misspelled it?"
end
reflection
end
end
How can a model forget some of it's associations?
So now I'm doing this directly in the Appointment
model and it seems to be working so far but it's really ugly:
class Appointment < ActiveRecord::Base
if KAE.scheduler_class && KAE.scheduleable_class
has_many :scheduleables,
through: :appointments_scheduleables,
source_type: KAE.scheduleable_class.name
has_many :schedulers,
through: :appointments_schedulers,
source_type: KAE.scheduler_class.name
else
binding.pry # TODO
ActiveSupport.on_load :after_initialize do
KAE::Appointment.class_eval do
has_many :scheduleables,
through: :appointments_scheduleables,
source_type: KAE.scheduleable_class.name
has_many :schedulers,
through: :appointments_schedulers,
source_type: KAE.scheduler_class.name
end
end
end
end
Anybody know of a better way of declaring has_many :through
polymorphic associations in a Rails 3 Engine?
I've been looking at open source projects like acts_as_taggable_on_steroids
and paper_trail
to see how they do their associations but they don't have polymorphic has_many :through
.
Anybody know any projects that have this type of association?