# Polymorphism and belongs\_to: trying to override belongs\_to

**URL:** <https://www.ruby-forum.com/t/polymorphism-and-belongs-to-trying-to-override-belongs-to/168358>\
**Category:** Rails\
**Created:** [May 19, 2009, 11:17pm UTC](https://www.ruby-forum.com/t/polymorphism-and-belongs-to-trying-to-override-belongs-to/168358 "2009-05-19T23:17:20Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![kendall](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/k/9dc877/32.png) [@kendall](https://www.ruby-forum.com/u/kendall)\
**Post date:** [May 19, 2009, 11:17pm UTC](https://www.ruby-forum.com/t/polymorphism-and-belongs-to-trying-to-override-belongs-to/168358/1 "2009-05-19T23:17:20Z")

</div>

Here is my story:

I have two models that represent people or groups that can “own”  
things. One is Person, the other is Group. Then, I have stuff that can  
be owned by either a Person or a Group, like a Post for instance:

class Post  
belongs\_to :owner, :polymorphic =\> true  
end

class Person  
has\_many :posts, :as =\> :owner  
end

class Group  
has\_many :posts, :as =\> :owner  
end

For my own reasons, I have a form where I create/edit a Post,  
including selecting the owner (yes, it may seem odd to have to  
“select” an owner, but that is the way it is). I first tried using the  
‘select’ form helper, attaching it to the owner attribute:

…\<%= f.select :owner, @owners.map { |o| [o.displayname, o.id] }  
%\>…

I soon realized my obvious mistake, I need both the id AND type for  
the owner. So, I created a read/write custom attribute for Post:

def owner\_string  
“#{owner.id}:#{owner.class.base\_class}” # base\_class just in case  
owner using STI  
end  
def owner\_string=(ref)  
i, t = ref.split(’:’)  
owner = eval(t).find(i.to\_i)  
end

Then I updated my view code to use my pseudo-attribute: owner\_string  
instead…

…\<%= f.select :owner\_string, @owners.map { |o| [o.displayname, “#  
{o.id}:#{o.class.base\_class}”] } %\>…

This seems to work all right. However, I’ve got a lot of models that  
can be “owned” just like the Post class. I’ve also got polymorphism  
going on in a few other places.

So, to avoid repetition, my next step was to write some code, throw it  
in config/initializers, and use it to extend ActiveRecord::Base. I’ll  
include the code at the bottom and explain the problem here. My code  
adds an instance method to AR::Base called “polymorphic” that returns,  
for that instance a string holding the instances id and type (the base  
AR type). Also, there is a class AR::Base method also named  
“polymorphic” that takes a string containing an id/type as previously  
shown above (“id:type”) and loads the instance if it exists.

Finally I _tried_ to “override” (not sure if this is the correct ruby  
term for this) the belongs\_to macro method so that, when called (in my  
model definitions) it will first perform the default behavior, then it  
will detect if the belongs\_to call is for a polymorphic association,  
and if so, also create pseudo attribute methods such as “owner\_string”  
and “owner\_string=(val)” (replacing “owner” w/the association name, of  
course) so that I can immediately have these “attributes” as I  
demonstrated above.

That last part, “overriding” belongs\_to is what I can’t make work. The  
following code works with the exception of the alias\_method\_chain  
call. There doesn’t appear to be a method named belongs\_to in  
AR::Base.

So, anyone know how things really work here? How, perhaps, I can get  
this to work? I also welcome general commentary/insights as to how I  
can better deal with any of the above issues.

I’m very grateful, in advance, to anyone who actually reads this whole  
thing and responds.

## WARNING: Long code listing (excuse the verbose param-checking used for debugging) ---------- file: config/initializers/polymorphic\_references.rb

module PolymorphicReferences  
module ActiveRecord  
def self.included(base)  
base.send :extend, ClassMethods  
base.send :include, InstanceMethods  
base.send :alias\_method\_chain, :belongs\_to, :polymorphism # This  
breaks everything!  
end  
module ClassMethods  
def polymorphic(class\_or\_str, record\_id = nil)  
i = t = nil  
if class\_or\_str.is\_a?(String) and record\_id.nil?  
i, t = class\_or\_str.split(’:’) rescue [nil, nil]  
raise ArgumentError, “Invalid polymorphic object reference  
string (bad format): #{class\_or\_str}” unless i.is\_a?(String) and  
t.is\_a?(String)  
i = i.to\_i rescue nil  
t = eval(t) rescue nil  
elsif class\_or\_string.is\_a?(String) and record\_id.is\_a?  
(Integer)  
i = record\_id  
t = eval(class\_or\_string) rescue nil  
elsif class\_or\_string.is\_a?(Class) and record\_id.is\_a?  
(Integer)  
i = record\_id  
t = class\_or\_string  
else  
raise ArgumentError, “Invalid parameter types: (#  
{class\_or\_str.class}, #{record\_id.class})”  
end  
raise ArgumentError, “Invalid value for object id in  
polymorphic reference string: #{class\_or\_str}” unless i  
raise ArgumentError, “Invalid polymorphic type name: #  
{class\_or\_string}” unless t  
raise ArgumentError, “Invalid object reference (bad values):  
[type=#{t}] [id=#{i}]” unless i \> 0 and t.respond\_to?  
(:descends\_from\_active\_record?) and t != ::ActiveRecord::Base  
t.base\_class.find(i)  
end  
def belongs\_to\_with\_polymorphism(association\_id, options = {})  
add\_poly = options[:polymorphic]  
belongs\_to\_without\_polymorphism(association\_id, options)  
if add\_poly  
string\_reader = “#{association\_id}\_string”  
string\_writer = “#{association\_id}\_string=”  
object\_reader = association\_id.dup  
object\_writer = “#{association\_id}=”  
define\_method(string\_reader) do  
ref = self.send(object\_reader)  
ref.polymorphic rescue nil  
end  
define\_method(string\_writer) do |reference|  
obj = self.class.polymorphic(reference) if reference  
self.send(object\_writer, obj)  
end  
end  
end  
end  
module InstanceMethods  
def polymorphic  
return nil if new\_record?  
“#{id}:#{self.class.base\_class}”  
end  
end  
end  
end  
ActiveRecord::Base.send :include, PolymorphicReferences::ActiveRecord
