views:

39

answers:

2

Short: I have an foreign key attribute and want to know what is the class (or reference table) of that foreign key field.

Context:

given 2 tables: users(id, [other fields]) and issues(id, user_id, assigned_to, [other fields])

Here is my active record of Issue (irrelevant parts are extracted)

class User < ActiveRecord::Base
  ...
end
class Issue < ActiveRecord::Base
  belongs_to :user
  belongs_to :assigned_user, :foreign_key => 'assigned_to', :class_name => 'User'
  ...
end

I want to make a user readable change logging. e.g. when changing assigned user I want to get a message like this: Assigned to is changed from Otto to Zoltan. ActiveRecord has the function changes which is a good starting point but it give me only reference ID-s. To translate into names I need to read user by id.

For association :user it is quite easy because I have to follow conventions only. But how to fetch the same info for assigned_to attribute (I want to make a general solution)? Is it possible to figure out whether we have association for the given attribute? Can we extract the class of that association?

+1  A: 

First, you can use reflect_on_association to get the metadata for the association you want. Then, from its result (which is a MacroReflection descendant) you can find out the class:

reflection = Issue.reflect_on_association(:assigned_user)
reflection.class # => User
reflection.class_name # => 'User'

Refer to docs here and there.

neutrino
A: 

Thank you. Here is the final solution for the specific problem (for learning):

  def textalize_changes
    if changed?
      r = ""
      changes.keys.each do |k|
        r << "#{k.humanize} changed "
        r << "from `#{translate(k,changes[k][0])}` " 
        r << "to `#{translate(k,changes[k][1])}`"
        r << "<br/>"
      end
      r
    end
  end

  def translate(attr_name, attr_value)
    ass = self.class.reflect_on_all_associations(:belongs_to).reject{|a| attr_name != a.primary_key_name}
    if 1 == ass.length and !attr_value.blank?
      obj = ass[0].klass.find(attr_value)
      obj.send(:name)
    else
      attr_value
    end
  end
takacsot