AssociationTypeMismatch (<superclass> expected, got <subclass>)

Perhaps this is just
Sti + namespace - Rails - Ruby-Forum
coming back to bite me – I suspect I’m missing a trivial declaration.

==== Error message (note that NOAA is an STI subclass of WeatherStation)

ActiveRecord::AssociationTypeMismatch (WeatherStation(#2169635200)
expected, got NOAA(#2185634180)):

==== Source of the error (station is, in fact, an NOAA object)

PremiseWeatherStation.create(:premise => self, :weather_station =>
station)

==== Models

class Premise < ActiveRecord::Base
has_many :premise_weather_stations, :dependent => :destroy
has_many :weather_stations, :through => :premise_weather_stations

end

class PremiseWeatherStation < ActiveRecord::Base
belongs_to :premise
belongs_to :weather_station
end

WeatherStation is the ‘parent’ of STI models

class WeatherStation < ActiveRecord::Base
has_many :premise_weather_stations, :dependent => :destroy
has_many :premises, :through => :premise_weather_stations

end

NOAA is an STI subclass of WeatherStation

class NOAA < WeatherStation

end

==== Tables:

create_table “premise_weather_stations”, :force => true do |t|
t.integer “premise_id”
t.integer “weather_station_id”

end

create_table “premises”, :force => true do |t|

end

create_table “weather_stations”, :force => true do |t|
t.string “callsign”
t.string “type” # for STI support

end

====

I don’t think I’m doing anything “un-RAILs-ish”. Any idea what I’m
missing?

  • ff

UPDATE: Stranger and stranger. I do NOT get this error running in the
console. I do NOT get this error running the test suite. I ONLY get
this error under ‘rails server’. I tracked the Rails source to where
the AssociationTypeMismatch is raised in association_proxy.rb:

    def raise_on_type_mismatch(record)
      unless record.is_a?(@reflection.klass) || 

record.is_a?(@reflection.class_name.constantize)
message =
“#{@reflection.class_name}(##{@reflection.klass.object_id}) expected,
got #{record.class}(##{record.class.object_id})”
raise ActiveRecord::AssociationTypeMismatch, message
end
end

and inserted a Rails.logger.debug() statement.

When I’m running in the test suite, I see:

class=NOAA, record.is_a?(WeatherStation) => true,
record.is_a?(WeatherStation) => true

When I’m running in under the server, the same run prints:

class=NOAA, record.is_a?(WeatherStation) => false,
record.is_a?(WeatherStation) => false

Is there ANYTHING about the server environment (in development mode)
that would account for is_a? behaving differently???

On Fri, Apr 15, 2011 at 10:53 PM, Fearless F.
[email protected]wrote:

PremiseWeatherStation.create(:premise => self, :weather_station =>
station)

I’ve been unable to reproduce your error in the rails server or in the
console. However, what I do see is that “self” is a reserved word. The
variable name of the object you want to pass in should not be called
“self”.
When I used your statement as is I received a different TypeMismatch
error;
it was for Premise because self was passing in PremiseWeatherStation. My
only suggestion right now is to change the variable name being passed
into
:premise => to something else and try your statement again.

B.

On Sat, Apr 16, 2011 at 3:05 AM, Fearless F. [email protected]
wrote:

Thank you for digging around. I should have shown more context –
station)
What’s really odd is that I monkey patched the raise_on_type_mismatch()
method to print out the record and the @reflection. Regardless of where
I run them (console, test, or server), they print out as the same.

HOWEVER, record.is_a?(@reflection.klass) returns true in the console or
test suite, false in the server. I’m really mystified.

I don’t know why your monkey patch is showing one thing in the console
and
something else in the server. However I suspect that the reason is the
same
as why you are getting the type mismatch in the first place. I believe
it
has to do with how the find_stations_near method is working. Since I
don’t
know how you wrote that method I had to assume that what you were doing
was
getting a list of WeatherStation objects. If I just grab all the
WeatherStation objects in my DB, regardless of if they are
WeatherStation or
NOAA type and run them through your map and PremiseWeatherStation.create
statements they work fine. Both server and console. I ran it again but
this
time told it to return all NOAA objects instead of WeatherStation. It
worked
without issue. What is the code for the method find_stations_near?

B.

Bryan C. wrote in post #993205:

What is the code for the method find_stations_near?

First, here’s the code for find_local_weather_stations(). The create
method raises the error:

def find_local_weather_stations(limit = 10)
premise_weather_stations.delete_all
stations = WeatherStation.find_stations_near(self)
premise_weather_stations = stations[0, limit].map do |station|
PremiseWeatherStation.create(:premise => self, :weather_station =>
station)
end
end

The find_stations_near(self) method calls an external web site to get
the callsigns, creates an in-memory “candidate” with that callsign, and
then saves the candidate if not already in the database, or updates the
existing one if it is. Both paths use the save!() method, so we can be
assured that the weather_station really is in the database.
find_stations_near() ultimately returns:

def load_stations(stations)
  stations.map {|station| load_station(station) }
end

def load_station(candidate)
  if (incumbent =

WeatherStation.find_by_callsign(candidate.callsign))
incumbent.resolve_location
incumbent.save!
incumbent
else
candidate.resolve_location
candidate.save!
candidate
end
end

I don’t see anything odd in that code.

Hi Bryan:

Bryan C. wrote in post #993150:

PremiseWeatherStation.create(:premise => self, :weather_station =>
station)
I’ve been unable to reproduce your error in the rails server or in the
console. However, what I do see is that “self” is a reserved word. The
variable name of the object you want to pass in should not be called
“self”.

Thank you for digging around. I should have shown more context –
‘self’ is in fact what I intended:

class Premise < ActiveRecord::Base
has_many :premise_weather_stations, :dependent => :destroy
has_many :weather_stations, :through => :premise_weather_stations

def find_local_weather_stations
stations = WeatherStation.find_stations_near(self)
premise_weather_stations = stations.map do |station|
PremiseWeatherStation.create(:premise => self, :weather_station =>
station)
end
end

end

which is to say, find_local_weather_stations is an instance method which
associates this Premise (‘self’) with a list of WeatherStation
(‘station’) objects, creating a PremiseWeatherStation object to form
each association.

What’s really odd is that I monkey patched the raise_on_type_mismatch()
method to print out the record and the @reflection. Regardless of where
I run them (console, test, or server), they print out as the same.

HOWEVER, record.is_a?(@reflection.klass) returns true in the console or
test suite, false in the server. I’m really mystified.

Okay, I have found a fix, or at least a workaround. If a Rails core
team member is reading this, I’d appreciate knowing if this warrants a
lighthouse ticket.

If I add a .reload (to the AR that is already in the db):

def load_station(candidate)
if (incumbent =
WeatherStation.find_by_callsign(candidate.callsign))
incumbent.resolve_location
incumbent.save!
incumbent.reload # RELOAD ADDED HERE
else
candidate.resolve_location
candidate.save!
candidate
end
end

… it no longer raises an AssociationTypeMismatch in this code (called
some lines later):

PremiseWeatherStation.create(:premise => self, :weather_station =>
station)

Mind you, this ONLY happens when run as a server and not in the console.
(See the OP for the schema and models). I’m going back to work on other
stuff, but if a Rails developer wants more information on this, I’d be
happy to provide it.