Finding all by Polymorphic Type in Rails?
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)
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']
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.
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!
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
You should be able to just use the associated collection:
model.assignments.all
精彩评论