Paperclip :style depending on model (has_many polymorphic images)

后端 未结 10 947
醉梦人生
醉梦人生 2021-02-03 11:33

I have set up my models to use a polymorphic Image model. This is working fine, however I am wondering if it is possible to change the :styles setting for each model. Found some

相关标签:
10条回答
  • 2021-02-03 12:12

    I am really late to the party here, but I wanted to clarify something about accessing model data for anyone else that happens on to this thread. I just faced this issue while using Paperclip to apply watermarks based on data from the model, and got it working after a lot of investigation.

    You said:

    After reading a lot on the Paperclip forum I don't believe it's possible to access the instance before it has been saved. You can only see the Paperclip stuff and that's it.

    In fact, you can see the model data if it's been set in the object before your attachment is assigned!

    Your paperclip processors and whatnot are invoked when the attachment in your model is assigned. If you rely on mass assignment (or not, actually), as soon as the attachment is assigned a value, paperclip does its thing.

    Here's how I solved the problem:

    In my model with the attachment (Photo), I made all the attributes EXCEPT the attachment attr_accessible, thereby keeping the attachment from being assigned during mass assignment.

    class Photo < ActiveRecord::Base
      attr_accessible :attribution, :latitude, :longitude, :activity_id, :seq_no, :approved, :caption
    
      has_attached_file :picture, ...
      ...
    end
    

    In my controller's create method (for example) I pulled out the picture from the params, and then created the object. (It's probably not necessary to remove the picture from params, since the attr_accessible statement should prevent picture from being assgined, but it doesn't hurt). Then I assign the picture attribute, after all the other attributes of the photo object have been setup.

    def create
      picture = params[:photo].delete(:picture)
      @photo = Photo.new(params[:photo])
      @photo.picture = picture
    
      @photo.save
      ...
    end
    

    In my case, one of the styles for picture calls for applying a watermark, which is the text string held in the attribution attribute. Before I made these code changes, the attribution string was never applied, and in the watermark code, attachment.instance.attribution was always nil. The changes summarized here made the entire model available inside the paperclip processors. The key is to assign your attachment attribute last.

    Hope this helps someone.

    0 讨论(0)
  • 2021-02-03 12:12

    the :styles property takes a Proc as argument, so you can do all kinds of fancy stuff :)

    class Image < AR::Base
      has_attached_file :file, :styles => Proc.new { |a| a.instance.file_styles }
    
      def file_styles; { :thumb => "150x150>", :normal => "492x600>" } end
    end
    
    class Didum < Image
      def file_styles; { :thumb => "50x50>", :normal => "492x600>" } end
    end
    

    Note - the above code should work, but honestly I have no setup to verify it, but looks like the Paperclip::Attachment#styles does call if it responds to it, see http://rdoc.info/github/thoughtbot/paperclip/master/Paperclip/Attachment:styles

    UPDATE the object passed into the Proc is not the instance, but the Paperclip::Attachment, but the instance is accessible through instance on the attachment

    PS: And I've seen this in some other places, but can't remember where...

    0 讨论(0)
  • 2021-02-03 12:16

    I found the workaround to pickup styles on create. The key is to implement before_post_process and after_save hooks.

    class Image < ActiveRecord::Base
    
      DEFAULT_STYLES = {
        medium: "300x300>", thumb: "100x100>"
      }
    
      has_attached_file :file, styles: ->(file){ file.instance.styles }
    
    
      validates_attachment_content_type :file, :content_type => /\Aimage\/.*\Z/
    
      # Workaround to pickup styles from imageable model
      # paperclip starts processing before all attributes are in the model
      # so we start processing after saving
    
      before_post_process ->{
        !@file_reprocessed.nil?
      }
    
      after_save ->{
        if !@file_reprocessed && (file_updated_at_changed? || imageable_type_changed?)
          @file_reprocessed = true
          file.reprocess!
        end
      }
    
    
      belongs_to :imageable, polymorphic: true
    
      def styles
        if imageable_class.respond_to?(:image_styles)
          imageable_class.image_styles
        end || DEFAULT_STYLES
      end
    
      def imageable_class
        imageable_type.constantize if imageable_type.present?
      end
    
    
    end
    

    So you have to define image_styles class method in imageable_class In my case it was

    class Property < ActiveRecord::Base
    
      def self.image_styles
        {
          large: "570x380#",
          thumb: "50x70#",
          medium: "300x200#"
        }
      end
    
    end
    
    0 讨论(0)
  • 2021-02-03 12:17

    I've had a similar question when dealing with dynamic behavioral changes on my models. Playing around with irb, I found out that this works:

    module Foo
       attr_accessor :bar
    end
    class Bar
       extends Foo
    end
    bar.bar = 'test' # 'test'
    bar.bar # 'test'
    # also works for instances of Bar!
    

    So i would create an attribute called image_style that could be changed to whatever module you want to add, by using this code on Image initialization:

      def after_initialize
        if self.image_style?
           extend Object.const_get(image_style)
        else
           extend DefaultImageStyle
        end
      end
    

    I just wonder if this works with paperclip since the after_initialize method may be called after paperclip does it's magic.. Worth a try though!

    0 讨论(0)
  • 2021-02-03 12:17

    Looking at the Paperclip source for Attachment it looks like the styles hash can take an object that responds to call so you may be able to do something like:

    class Image < ActiveRecord::Base
      belongs_to :imageable, :polymorphic => true
      has_attached_file :file, :styles => lambda {|attachment| attachment.instance.imageable_type.constantize.image_styles }
    end
    

    Then in any model that has many images:

    class Art < ActiveRecord::Base
      has_many :images, :as => :imageable
    
      def self.image_styles
        { :thumb => "150x150>", :normal => "492x600>" }
      end
    end
    

    Edit: Looks like someone else beat me to it :P.

    0 讨论(0)
  • 2021-02-03 12:19

    I liked MarkGranoff's answer but I came up with a much simpler implementation that does the trick for me and seems more maintainable.

    #create new and instantiate the fields you need ( or all except the attachment )
    @photo = Photo.new(:attribution => params[:photo][:attribution],
                       :latitude => params[:photo][:latitude ])
    
    #then just assign all params as normal
    @photo = Photo.assign_attributes(params[:node])
    

    The second statement assigns the attachment so the processor is fired, and since you've already assigned :attribution and :latitude, their values will be available in the processor via the attachment.instance method.

    Thanks everyone here for the insight!

    0 讨论(0)
提交回复
热议问题