views:

316

answers:

3

Is there a way to find all Polymorphic models of a specific polymorphic type in Rails? So if I have Group, Event, and Project all with a declaration like:

has_many :assignments, :as => :assignable

Can I do something like:

Assignable.all

...or

BuiltInRailsPolymorphicHelper.all("assignable")

That would be nice.

Edit:

... such that Assignable.all returns [Event, Group, Product] (array of classes)

A: 

You should be able to just use the associated collection:

model.assignments.all
Toby Hede
This will return all the `assignments` for a model instance. I think he wants to return all the `assignments` for a type.
KandadaBoggu
But can't you just say Event.assignments.all?
Toby Hede
Rails doesn't add static association helpers. So `Event.assignments` will throw `NoMethodError`.
KandadaBoggu
Ah, you're right, of course.
Toby Hede
+2  A: 

There is no direct method for this. I wrote this monkey patch for ActiveRecord::Base. This will work for any class.

class ActiveRecord::Base

  def self.all_polymorphic_types(name)
    @poly_hash ||= {}.tap do |hash|
      Dir.glob(File.join(RAILS_ROOT, "app", "models", "**", "*.rb")).each do |file|
        klass = (File.basename(file, ".rb").camelize.constantize rescue nil)
        next if klass.nil? or !klass.ancestors.include?(ActiveRecord::Base)
        klass.reflect_on_all_associations(:has_many).select{|r| r.options[:as] }.each do |reflection|
          (hash[reflection.options[:as]] ||= []) << klass
        end
      end
    end
    @poly_hash[name.to_sym]
  end

end

Now you can do the following:

Assignable.all_polymorphic_types(:assignable)
# returns [Project, Event, Group]
KandadaBoggu
A: 

I created a polymorphic model class with a method 'all' to test this.

class Profile
  # Return all profile instances
  # For class return use 'ret << i' instead of 'ret << i.all'
  def self.all
    ret = []
    subclasses_of(ActiveRecord::Base).each do |i|
      unless i.reflect_on_all_associations.select{|j| j.options[:as] == :profile}.empty?
        ret << i
      end
    end
    ret.flatten
  end

  def self.all_associated
    User.all.map{|u| u.profile }.flatten
  end
end

Here is my app setup:

User < ActiveRecord::Base
  belongs_to :profile, :polymorphic => true
end

Student < ActiveRecord::Base
  has_one :user, :as => :profile
end
nanda
To get this work in development enviroment, you've to require the models files.
nanda
This will override the ActiveRecord static method `all` [ which is not a good :-)].
KandadaBoggu
This is a special class, doesn't descend from ActiveRecord.
nanda