I want to extend the functionality of ActiveRecord’s `belongs_to’ macro
method so that, in addition to performing its normal tasks and creating
the default association methods, I can have it also create a couple
extra custom association methods. I’ll post my code of how I tried
(and failed) to do this below. Anyone know if/how this can be done?
Specifically, what I’m doing is trying to have `belongs_to’ add two more
methods to an AR class if the association is polymorphic
(options[:polymorphic] == true). If the association is polymorphic, I
try to add the following two custom association methods:
CODE
def association_string
“#{association.id}:#{association.class.base_class}”
end
def association_string=(ref)
i, t = ref.split(’:’)
association = eval(t).find(i.to_i)
end
CODE
I try to override belongs_to by using alias_method_chain' so I can call the original implementation, followed by my own logic. The code is in a file in my
config/initializers’ directory so it will get picked up when
the rails env loads.
The error I get is: alias_method':NameError: undefined method
belongs_to’ for class `ActiveRecord::Base’
Indeed, if I load a script/console session and do:
ActiveRecord::Base.methods.find {|m| m =~ /belongs_to/}
I get no match for belongs_to' (though
has_and_belongs_to_many’ is
found interestingly enough).
I see that in the docs `belongs_to’ is in module
ActiveRecord::Associations::ClassMethods which, I’d assume, gets
included (or extended) into AR::Base, making the method available.
Either way, the method I need to override isn’t defined/part-of
AR::Base. Anyone know how/why/what/where or how to better accomplish the
above?
Here’s the full code listing of what I’m trying to do:
FILE: config/initializers/polymorphic_references.rb
==========CODE==========
module PolymorphicReferences
##########
Extensions to ActiveRecord
module ActiveRecord
def self.included(base)
base.send :extend, ClassMethods
base.send :include, InstanceMethods
base.send :alias_method_chain, :belongs_to, :polymorphism
end
##########
#
# ActiveRecord::Base class methods
#
module ClassMethods
#
# Load object polymorphically
#
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, “…message…” unless
i.is_a?(String) and t.is_a?(String)
i = i.to_i rescue nil
t = eval(t) rescue nil
elsif class_or_str.is_a?(String) and
record_id.is_a?(Integer)
i = record_id
t = eval(class_or_str) rescue nil
elsif class_or_str.is_a?(Class) and
record_id.is_a?(Integer)
i = record_id
t = class_or_str
else
raise ArgumentError, “…message…”
end
raise ArgumentError, “…message…” unless i
raise ArgumentError, “…message…” unless t
raise ArgumentError, “…message…” unless i > 0 and
t.respond_to?(:descends_from_active_record?) and
t != ::ActiveRecord::Base
t.base_class.find(i)
end
#
# Hook into belongs_to to add association_string methods:
#
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
##########
#
# ActiveRecord::Base instance methods
#
module InstanceMethods
#
# This returns a polymorphic “reference” string.
#
def polymorphic
return nil if new_record?
“#{id}:#{self.class.base_class}”
end
end
end
end
##########
Plug the system into rails
ActiveRecord::Base.send :include, PolymorphicReferences::ActiveRecord
========== END OF CODE ==========
Thanks in advance for any help/responses/insights/condolences.