title: Cropping using Paperclip, ImageMagick, Jcrop and S3 storage: Why won't 'image.reprocess!' reprocess?
body: <p>I'm (loosely) following <a href="http://railscasts.com/episodes/182-cropping-images" rel="noreferrer">RailsCasts tutorial #182</a>, which uses Paperclip, ImageMagick, and Jcrop to allow custom cropping of uploaded images.</p>
  
  <p>As I'm using Amazon S3 for file storage, I've had to rejigger parts of the tutorial to suit. Everything seems to be working perfectly <em>except</em> for the fact that the cropped version of my image isn't being reprocessed (or the result of that reprocess is not re-uploaded to S3)--so after the cropping process, I'm left with the same image I originally uploaded (this is true for all image sizes I store for each image).</p>
  
  <p>Here is my Feature (as in Feature Image) model:</p>
  
  <pre><code>class Feature &lt; ActiveRecord::Base
    require "#{Rails.root}/lib/paperclip_processors/cropper.rb"
  
    attr_accessible    :image_file_name, :image
    attr_accessor      :crop_x, :crop_y, :crop_w, :crop_h
    after_update       :reprocess_image, :if =&gt; :cropping?
  
    if Rails.env == "production"
    S3_CREDENTIALS = { :access_key_id     =&gt; '&lt;REDACTED&gt;',
                       :secret_access_key =&gt; '&lt;REDACTED&gt;',
                       :bucket            =&gt; "&lt;REDACTED&gt;"}
    else
    S3_CREDENTIALS = { :access_key_id     =&gt; '&lt;REDACTED&gt;',
                       :secret_access_key =&gt; '&lt;REDACTED&gt;',
                       :bucket            =&gt; "&lt;REDACTED&gt;"}
    end
  
    has_attached_file :image,
                      :styles          =&gt; { :small =&gt; "240x135&gt;", :croppable =&gt; "960x960&gt;", :display =&gt; "960x540&gt;" },
                      :processors      =&gt; [:cropper],
                      :storage         =&gt; :s3,
                      :s3_credentials  =&gt; S3_CREDENTIALS,
                      :path            =&gt; "features/:id/:style.:extension"
  
    validates_attachment_content_type :image, :content_type =&gt; ['image/jpeg', 'image/gif', 'image/png',
                                                                'image/pjpeg', 'image/x-png'], 
                                              :message =&gt; 'must be a JPEG, GIF or PNG image'
  
    def cropping?
      !crop_x.blank? &amp;&amp; !crop_y.blank? &amp;&amp; !crop_w.blank? &amp;&amp; !crop_h.blank?
    end
  
    def image_geometry(style = :original)
      @geometry ||= {}
      path = (image.options[:storage]==:s3) ? image.url(style) : image.path(style)
      @geometry[style] ||= Paperclip::Geometry.from_file(path)
    end
  
    private
  
    def reprocess_image
      image.reprocess!
    end
  end
  </code></pre>
  
  <p>Here is my 'cropper.rb' (Paperclip processor):</p>
  
  <pre><code>module Paperclip
    class Cropper &lt; Thumbnail
      def transformation_command
        if crop_command
          crop_command + super.sub(/ -crop \S+/, '')
        else
          super
        end
      end
  
      def crop_command
        target = @attachment.instance
        if target.cropping?
          " -crop '#{target.crop_w}x#{target.crop_h}+#{target.crop_x}+#{target.crop_y}'"
        end
      end
    end
  end
  </code></pre>
  
  <p>The relevant actions of my FeaturesController:</p>
  
  <pre><code>class FeaturesController &lt; ApplicationController
  
    def new
      @feature = Feature.new
    end
  
    def create
      @feature = Feature.new(params[:feature])
      if @feature.save
        if params[:feature][:image].blank?
          flash[:notice] = "New feature added!"
          redirect_to @feature
        else
          render :crop
        end
      else
        @title = "Add a New Feature"
        render :new
      end
    end
  
    def edit
      @feature = Feature.find(params[:id])
      @title = "Edit #{@feature.headline}"
    end
  
    def update
      @feature = Feature.find(params[:id])
      if @feature.update_attributes(params[:feature])
        if params[:feature][:image].blank?
          flash[:notice] = "Feature updated!"
          redirect_to @feature
        else
          render :crop
        end
      else
        @title = "Edit Feature"
        render :edit
      end
    end
  end
  </code></pre>
  
  <p>And the relevant lines of my 'crop.html.erb' view:</p>
  
  <pre><code>&lt;% content_for :javascript_includes do %&gt;
      &lt;%= javascript_include_tag 'jquery.Jcrop.min' %&gt;
      &lt;script type="text/javascript" charset="utf-8"&gt;
      $(function() {
          $('#cropbox').Jcrop({
              onChange: update_crop,
              onSelect: update_crop,
              setSelect: [0, 0, 960, 540],
              aspectRatio: 960/540
          });
      });
  
      function update_crop(coords) {
          var ratio = &lt;%= @feature.image_geometry(:original).width %&gt; / &lt;%= @feature.image_geometry(:croppable).width %&gt;;
          $("#crop_x").val(Math.round(coords.x * ratio));
          $("#crop_y").val(Math.round(coords.y * ratio));
          $("#crop_w").val(Math.round(coords.w * ratio));
          $("#crop_h").val(Math.round(coords.h * ratio));
      };
      &lt;/script&gt;
  &lt;% end %&gt;
  &lt;% content_for :style_includes do %&gt;
      &lt;%= stylesheet_link_tag 'jquery.Jcrop', :media =&gt; 'screen' %&gt;
  &lt;% end %&gt;
  
  &lt;%= image_tag @feature.image.url(:croppable), :id =&gt; "cropbox" %&gt;
  
  &lt;% form_for @feature do |f| %&gt;
      &lt;% for attribute in [:crop_x, :crop_y, :crop_w, :crop_h] %&gt;
          &lt;%= f.hidden_field attribute, :id =&gt; attribute %&gt;
      &lt;% end %&gt;
      &lt;p&gt;&lt;%= f.submit "Crop" %&gt;&lt;/p&gt;
  &lt;% end %&gt;
  </code></pre>
  
  <p>The problem is not that there's an error with the custom crop (offset, crop area, etc.), it's that there is <strong><em>no crop happening</em></strong> when I click 'crop'--I'm just left with the images I got from the original upload/process.  It doesn't appear that 'image.reprocess!' is happening at all (or the results of the reprocess aren't being saved to S3).</p>
  
  <p>Why might that be, and what can I do about it?</p>