Question

Formerly: Running a model method on a paperclip attachment after create or update (paperclip callbacks don't seem to work)

Edit (later that day) I figured out my problem. The processor apparently works with the file that is updated, but doesn't save any files until after processing. I changed my Zip::ZipFile to open 'file' rather than 'attachment.path' since the attachment path doesn't actually hold anything yet. This fixed the first problem. Now I'm having other problems that I'll need to track down. But the answer below is mostly correct.

Edit (1/31/2011):

So I have taken the advice to create a processor for my attachment which will perform all the necessary actions. So far, it looks like it should work; the processor starts and does all the initialization stuff, apparently. However, when I get the point where I want to access the zip file that gets uploaded, I get an error saying that the file cannot be found. The code for my processor is below:

class Extractor < Processor
    attr_accessor :resolution, :whiny
    def initialize(file, options = {}, attachment = nil)
      super
      @file = file
      @whiny = options[:whiny].nil? ? true : options[:whiny]
      @basename = File.basename(@file.path, File.extname(@file.path))
      @attachment = attachment
      @instance = attachment.instance
    end
    def make
      # do your conversions here, you've got @file, @attachment and @basename to work with
      export_path = attachment.path.gsub('.zip', '_content')

      Zip::ZipFile.open(attachment.path) { |zip_file|
        zip_file.each { |image|
          image_path = File.join(export_path, image.name)
          FileUtils.mkdir_p(File.dirname(image_path))
          unless File.exist?(image_path)
            zip_file.extract(image, image_path)
            # ..stuff that it does..
          end
        }
      }
      # clean up source files, but leave the zip
      FileUtils.remove_dir(export_path)


      # you return a file handle which is the processed result
      dst = File.open result_file_path
    end
end

And here is the contents of the error that I get:

Zip::ZipError in GalleriesController#create

File /home/joshua/railscamp/moments_on_three/public/assets/archives/delrosario.zip not found

Rails.root: /home/joshua/railscamp/moments_on_three
Application Trace | Framework Trace | Full Trace

config/initializers/extractor.rb:16:in `make'
app/controllers/galleries_controller.rb:32:in `new'
app/controllers/galleries_controller.rb:32:in `create'

Request

Parameters:

{"utf8"=>"✓",
 "authenticity_token"=>"0s4L4MrlqjDTMjzjgkUdvUxeHklZNOIShDhT6fgOICY=",
 "gallery"=>{"name"=>"DelRosario",
 "public"=>"0",
 "owner_id"=>"1",
 "shoot_date(1i)"=>"2011",
 "shoot_date(2i)"=>"1",
 "shoot_date(3i)"=>"31",
 "category_id"=>"1",
 "archive"=>#<ActionDispatch::Http::UploadedFile:0x00000004148d78 @original_filename="delrosario.zip",
 @content_type="application/zip",
 @headers="Content-Disposition: form-data; name=\"gallery[archive]\"; filename=\"delrosario.zip\"\r\nContent-Type: application/zip\r\n",
 @tempfile=#<File:/tmp/RackMultipart20110131-9745-14u347v>>},
 "commit"=>"Create Gallery"}

From what I can tell it's looking for the file in the right place, but the file doesn't seem to be uploaded yet to access it. As far as I'm aware, Paperclip is smart enough to know and wait for the attachment to upload before it tries to process it. Can anyone spot what I'm doing wrong here?

Thanks a lot.

Old stuff:

I'm developing a photo gallery app using Rails 3 and Paperclip. The Admin is able to create a gallery and upload a zip file containing a bunch of images.

What I want to happen:

  1. Enter gallery info and zip file to upload into the form.
  2. Hit 'Create Gallery' button.
  3. Form posts, gallery saves, and zip file gets uploaded.
  4. After zip file is uploaded, run the method :extract_photos (btw, this code works). 4.a. At the end of this method, zip file is destroyed.
  5. Admin is redirected to gallery page with all the photos in it (where gallery has_many photos).

I've tried to make this work several different ways.

Before, I created a controller method which would allow the Admin to click a link which ran the :extract_photos method. This worked on my computer, but for some reason the server had trouble routing this on the client's computer. So it's a no go. Plus I thought it was an ugly way of doing it.

Recently, I tried using callback methods. after_save didn't work because it apparently interrupts the form POST and the file doesn't get uploaded and the :extract_photos method can't find the file.

I checked out callback methods on the Paperclip github page, and it talks about the callbacks:

Before and after the Post Processing step, Paperclip calls back to the model with a few callbacks, allowing the model to change or cancel the processing step. The callbacks are "before_post_process" and "after_post_process" (which are called before and after the processing of each attachment), and the attachment-specific "beforepost_process" and "afterpost_process". The callbacks are intended to be as close to normal ActiveRecord callbacks as possible, so if you return false (specifically - returning nil is not the same) in a before filter, the post processing step will halt. Returning false in an after filter will not halt anything, but you can access the model and the attachment if necessary.

I've tried using before_post_process and after_post_process, but it can't find the file to run the process, so the file obviously isn't getting uploaded by the time those methods are getting called (which I think is strange). Additionally, when I try beforepost_process and afterpost_process, I get a NoMethodError.

So how do I call a method on an attachment when it is created or updated, but after the file is uploaded and in the right place?

UPDATE

I tried the code below, moving my extraction method code into the make method of the processor. I've gotten farther than I have did before with trying to write a processor, but it's still a no-go. The process throws an exception as soon as I try and open the uploaded file for processing, saying the file doesn't exist. The naming scheme is correct and everything, but still nothing is getting uploaded before the process is getting triggered. Does anyone have any idea why this is happening?

Was it helpful?

Solution

You can write your own processor to accomplish this.

in your model when declaring the paperclip stuff add a custom processor

  has_attached_file :my_attachment, {
    :styles => {:original => {:processors => [:my_processor]}}
  }.merge(PAPERCLIP_SETTINGS)

then write your own processor and put it config/initializers:

module Paperclip
  class MyProcessor < Processor
    attr_accessor :resolution, :whiny
    def initialize(file, options = {}, attachment = nil)
      super
      @file = file
      @whiny = options[:whiny].nil? ? true : options[:whiny]
      @basename = File.basename(@file.path, File.extname(@file.path))
      @attachment = attachment
    end
    def make
      # do your conversions here, you've got @file, @attachment and @basename to work with

      # you return a file handle which is the processed result
      dst = File.open result_file_path
    end
  end
end

I am using a custom processor to things similar to what you are doing with lots of processing and converting of the file in the middle and it seems to work well.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top