Alex Rivera | Logout

AssociationTypeMismatch for the same model

Asked 2013-04-09T18:26:17.537
12

Summary/the error

I'm getting this error at different places in my application:

ActiveRecord::AssociationTypeMismatch in Settings::CompaniesController#show

Company(#70257861502120) expected, got Company(#70257861787700)

activerecord (3.2.11) lib/active_record/associations/association.rb:204:in `raise_on_type_mismatch'
activerecord (3.2.11) lib/active_record/associations/belongs_to_association.rb:6:in `replace'
activerecord (3.2.11) lib/active_record/associations/singular_association.rb:17:in `writer'
activerecord (3.2.11) lib/active_record/associations/builder/association.rb:51:in `block in define_writers'
activerecord (3.2.11) lib/active_record/attribute_assignment.rb:85:in `block in assign_attributes'
activerecord (3.2.11) lib/active_record/attribute_assignment.rb:78:in `each'
activerecord (3.2.11) lib/active_record/attribute_assignment.rb:78:in `assign_attributes'
activerecord (3.2.11) lib/active_record/base.rb:497:in `initialize'
app/controllers/settings/companies_controller.rb:4:in `new'
app/controllers/settings/companies_controller.rb:4:in `show'

Controller

The controller looks like this, but the problem can occur at any point where a Company model is used to save or update another model:

class Settings::CompaniesController < SettingsController
  def show
    @company = current_user.company
    @classification = Classification.new(company: @company)
  end

  def update
  end
end

Facts/observations

Some facts and observations:

  • The problem occurs randomly, but usually after the development server has been running for a while.
  • The problem does not occur in production.
  • The problem occurs even when I have made no changes at all to the Company model.
  • The problem is solved by restarting the server.

Theories

As far as I understand this is due to dynamic loading of classes.

Som

Edit
Report

1 Answer

33

The problem is almost assuredly because you are serializing copies of these classes into either a cache or the session, then later reconstituting them. This causes problems because classes get undefined and redefined on each request in development mode, so if you have a marshalled copy of an old definition of a class, and then manage to unmarshal it before the Rails class unloading, you're going to have two different classes with the same name.

The exception is being raised from here: https://github.com/rails/rails/blob/3-2-stable/activerecord/lib/active_record/associations/association.rb#L204-212

You can see here that it's doing something very simple - it's testing that the object passed in is_a? instance of the class passed to the association. Undefining and redefining a class means that if you have an old copy of a class, and compare it to the new version of the class, it's not going to pass muster. Consider this example:

class Foo; end
f = Foo.new

Object.send :remove_const, :Foo
class Foo; end

puts f.is_a? Foo
# => false

What's happening here is that when we undefine and redefine Foo, it actually creates a new object (remember, classes are instances of Class!). Even though we know that f is a Foo, f.is_a? Foo fails because f.class is different from Foo. is_a? checks that the given object's class either matches the passed class, or that it is a subclass of the passed class - neither is the case here. They share the same name, but they are different classes. This is the core of what's happening in your associations.

At some point, your Classification association expects a certain version of Company, and you are assignin

answered 2013-07-16T03:51:12.277

Your Answer