Finding all by Polymorphic Type in Rails?

Harish Shetty's solution will not work for namespaced model files which are not stored directly in Rails.root/app/models but in a subdirectory. Although it correctly globs files in subdirectories, it then fails to include the subdir when turning the file name into a constant. The reason for this is, that the namespacing subdir is removed by this line:

klass = File.basename(file, ".rb").camelize.constantize rescue nil

Here is what I did to retain the namespacing subdir:

file.sub!(File.join(Rails.root, "app", "models"), '')
file.sub!('.rb', '')
klass = file.classify.constantize rescue nil

Here's the full modified solution:

  def self.all_polymorphic_types(name)
    @poly_hash ||= {}.tap do |hash|
      Dir.glob(File.join(Rails.root, "app", "models", "**", "*.rb")).each do |file|
        file.sub!(File.join(Rails.root, "app", "models"), '')
        file.sub!('.rb', '')
        klass = file.classify.constantize rescue nil
        next unless 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

Now, the method will turn /models/test/tensile.rb correctly into Test::Tensile before reflecting on its associations.

Just a minor improvement, all credit still goes to Harish!


You can also try this way.cause above solution doesn't work for me cause i had some mongo's model.

def get_has_many_associations_for_model(associations_name, polymorphic=nil)

  associations_name = associations_name.to_s.parameterize.underscore.pluralize.to_sym
  active_models = ActiveRecord::Base.descendants
  get_model = []
  active_models.each do |model|

    has_many_associations =model.reflect_on_all_associations(:has_many).select{|a|a.name==associations_name }
    has_many_associations = has_many_associations.select{ |a| a.options[:as] == polymorphic.to_s.to_sym} if polymorphic.present?
    get_model << model if has_many_associations.present?

  end
  get_model.map{|a| a.to_s}
end

Anb call it like

get_has_many_associations_for_model("assignments", "assignable")

Here Second parameters is optional for if you want polymorphic records than pass it otherwise leave it as blank.

It will Return Array of Model name as String.


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 unless 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).map(&:to_s)
# returns ['Project', 'Event', 'Group']