Polymorphic belongs_to with :class_name => abstract class

SUMMARY: How do I assign objects, of different subclasses of an abstract
class, to a belongs_to relation?

I had the following class hierarchy:

Location < ActiveRecord::Base
Address < Location
Building < Location

and a class Person with these relationships:
has_one :address, :dependent => :destroy
belongs_to :building

Location is backed by table “locations,” and Person by “People.”

I had to refactor to insert an abstract class between Location and
Building:

Location < ActiveRecord::Base
Address < Location
SharedLocation < Location
Building < SharedLocation
DeletedLocation < SharedLocation

I now want Person#building to refer to either a Building or a
DeletedLocation. Refactoring the foreign key and accessor would be a
major pain.

I changed the relation for Person#building to:
belongs_to :building, :class_name => ‘SharedLocation’,
:foreign_key => ‘building_id’

(:foreign_key because I got a deprecation warning on specifying
:class_name without one.)

I also moved
has_many :people, :dependent => :nullify
from Building to SharedLocation. In the course of flailing about for a
solution, I added “:foreign_key => ‘building_id’”, but I don’t know what
I’m doing.

I ran my unit tests for setting the relation, and am now seeing
failures/errors, all of which seem to boil down to this: Attempting
person = Building.new(…)
gets an AssociationTypeMismatch, “SharedLocation expected, got
Building.”

How do I make belongs_to relations polymorphic?

Rails 2.3.2
ruby 1.8.7 (2008-08-11 patchlevel 72) [universal-darwin10.0]

Fritz A. wrote:

SUMMARY: How do I assign objects, of different subclasses of an abstract
class, to a belongs_to relation?

I had the following class hierarchy:

Location < ActiveRecord::Base
Address < Location
Building < Location

and a class Person with these relationships:
has_one :address, :dependent => :destroy
belongs_to :building

Location is backed by table “locations,” and Person by “People.”

I had to refactor to insert an abstract class between Location and
Building:

Location < ActiveRecord::Base
Address < Location
SharedLocation < Location
Building < SharedLocation
DeletedLocation < SharedLocation

I now want Person#building to refer to either a Building or a
DeletedLocation. Refactoring the foreign key and accessor would be a
major pain.

I changed the relation for Person#building to:
belongs_to :building, :class_name => ‘SharedLocation’,
:foreign_key => ‘building_id’

(:foreign_key because I got a deprecation warning on specifying
:class_name without one.)

I also moved
has_many :people, :dependent => :nullify
from Building to SharedLocation. In the course of flailing about for a
solution, I added “:foreign_key => ‘building_id’”, but I don’t know what
I’m doing.

I ran my unit tests for setting the relation, and am now seeing
failures/errors, all of which seem to boil down to this: Attempting
person = Building.new(…)
gets an AssociationTypeMismatch, “SharedLocation expected, got
Building.”

How do I make belongs_to relations polymorphic?

Rails 2.3.2
ruby 1.8.7 (2008-08-11 patchlevel 72) [universal-darwin10.0]

First off, I disagree with your use of polymorphism in this problem
domain. You could keep location a top level class along with a status
field which could simply hold the values ‘building’, or ‘deleted’.
Choose sub classing for changes and/or additions to functionality vs
changes and/or additions to state.

Anyways, disregarding the above… take a look at this excellent podcast
by Bates that explains it as well as any…

hth

ilan

Ilan B. wrote:

First off, I disagree with your use of polymorphism in this problem
domain. You could keep location a top level class along with a status
field which could simply hold the values ‘building’, or ‘deleted’.
Choose sub classing for changes and/or additions to functionality vs
changes and/or additions to state.

In this case, trying to distinguish Building from DeletedLocation would
have required a lot of branches and conditionals on that one flag, and I
follow the rule-of-thumb that if the program has to change behavior
based on a non-displayed state variable, you have to consider
refactoring the classes.

Anyways, disregarding the above… take a look at this excellent podcast
by Bates that explains it as well as any…
http://railscasts.com/episodes/154-polymorphic-association

This did help, thank you.

What misled me was that I’d hoped setting an ActiveRecord relation could
“just work” in the way other object references do. Obviously, on
reflection, the engine has to be assured that the DB plumbing is in
place.

So I added a building_type column to Person, changed Person’s belongs_to
line to
belongs_to :building, :polymorphic => true
and SharedLocation’s has_many line to
has_many :people, :dependent => :nullify, :as => :building

Thanks again.

— F