How do you get the parent of a polymorphic object before its been saved?
I have a standard polymorphic relationship and I need to know who its parent is before I save it.
Class Pict开发者_运维技巧ure < AR::Base
belongs_to :attachable, :polymorphic => true
end
Class Person < AR::Base
has_many :pictures, :as => :attachable
end
Class Vehicle < AR::Base
has_many :pictures, :as => :attachable
end
I'm uploading pictures via Paperclip and I build a processor that needs to do different things to different pictures (ie. the Person pictures should have Polaroid look & the vehicle pictures should have an overlay). My problem is that before the picture is saved I don't know if it is associated with a Person or a Vehicle.
I tried putting a "marker" in Person & Vehicle so that I could tell them appart, but when I'm in the Paperclip processor the only thing I see is the Picture class. :( My next thought is to climb up the stack to try and get the parent caller but that seems quite smelly to me. How would you do it?
You should be able to get it from the polymorphic association.
Class Picture < AR::Base
belongs_to :attachable, :polymorphic => true
before_create :apply_filter
private
def apply_filter
case attachable
when Person
#apply Person filter
when Vehicle
#apply Vehicle filter
end
end
end
Or, you can just ask it the association type so it dosen't have to build and compare the objects, but rather just do string comparison.
Class Picture < AR::Base
belongs_to :attachable, :polymorphic => true
before_create :apply_filter
private
def apply_filter
case attachable_type
when "Person"
#apply Person filter
when "Vehicle"
#apply Vehicle filter
end
end
end
I "solved" this problem and wanted to post it here so that it may help someone else. My solution was to create a "parent" method on the Picture class that climbed up the stack and found something that looked like it's parent.
WARNING: This is crappy code and should probably not be used under any circumstance. It worked for me, but I can't guarantee that it wont cause bodily harm sometime down the road.
caller.select {|i| i =~ /controller.rb/}.first.match(/\/.+_controller.rb/).to_s.split("/").last.split("_").first.classify.constantize
What this code does is walk up the caller
tree looking for a ancestor named *_controller.rb
. If it finds one (and it should) then it parses out the name into a class which should be the parent class of the calling code. whew
BTW: I dropped Paperclip and started using CarrierWave. It does this sort of thing much more easily and I was able to get it working in half the time. Yea CarrierWave!
I created an interpolation to solve it. My paperclip model is Asset and Project is the parent model. There are other models like Articles which are under project model and can have attachments.
Paperclip.interpolates :attachable_project_id do |attachment, style|
attachable = Asset.find(attachment.instance.id).attachable
if attachable.is_a?(Project)
project_id = attachable.id
else
project_id = attachable.project.id
end
return project_id
end
Paperclip.interpolates :attachable_class do |attachment, style|
Asset.find(attachment.instance.id).attachable.class
end
And used it in the model like:
has_attached_file :data,
:path => "private/files/:attachable_project_id/:attachable_class/:id/:style/:basename.:extension",
:url => "/projects/:attachable_project_id/:attachable_class/:id/:style",
:styles => { :small => "150x150>" }
精彩评论