Forum: Ruby on Rails Polymorphism and belongs_to: trying to override belongs_to

Announcement (2017-05-07): is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see and for other Rails- und Ruby-related community platforms.
Kendall (Guest)
on 2009-05-20 01:17
(Received via mailing list)
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

class Person
  has_many :posts, :as => :owner

class Group
  has_many :posts, :as => :owner

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:

...<%= :owner, { |o| [o.displayname,] }

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.class.base_class}" # base_class just in case
owner using STI
  def owner_string=(ref)
    i, t = ref.split(':')
    owner = eval(t).find(i.to_i)

Then I updated my view code to use my pseudo-attribute: owner_string

...<%= :owner_string, { |o| [o.displayname, "#
{}:#{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

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
---------- 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!
    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
          i = i.to_i  rescue nil
          t = eval(t) rescue nil
        elsif class_or_string.is_a?(String) and record_id.is_a?
          i = record_id
          t = eval(class_or_string) rescue nil
        elsif class_or_string.is_a?(Class) and record_id.is_a?
          i = record_id
          t = class_or_string
          raise ArgumentError, "Invalid parameter types: (#
{class_or_str.class}, #{record_id.class})"
        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
      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
          define_method(string_writer) do |reference|
            obj = self.class.polymorphic(reference) if reference
            self.send(object_writer, obj)
    module InstanceMethods
      def polymorphic
        return nil if new_record?
ActiveRecord::Base.send :include, PolymorphicReferences::ActiveRecord
This topic is locked and can not be replied to.