Rails Cropping and Uploading to S3 using CarrierWave and MiniMagick

carrierwave, ruby-on-rails

Solution

Like a bad plane crash, there are a lot of things that I did incorrectly.

First, I had the arguments for crop in the wrong order. In my question, you'll notice I had:

img.crop('[x offset]x[y offset]+[width]+[height]')

The correct order is:

img.crop('[width]x[height]+[x offset]+[y offset]')

So that was problem one. Trying to do it in the wrong order was throwing an error (dug into the console to find it) about the geometry being provided as invalid.

Next problem: breaking a yield chain with my cropper method and returning a STRING instead of an IMAGE. Turns out that the crop method returns a string. Look at the original cropper method:

def cropper
  return unless model.cropping?
  manipulate! do |img|
    img = img.crop("#{model.crop_x}x#{model.crop_y}+#{model.crop_w}+#{model.crop_h}")
    img
  end
end

Oops! Last thing being returned there is a string. Turns out, the correct way is to just call the method on the image directly:

def cropper
  return unless model.cropping?
  manipulate! do |img|
    img.crop("#{model.crop_w}x#{model.crop_h}+#{model.crop_x}+#{model.crop_y}")
    img = yield(img) if block_given?
    img
  end
end

The final thing I was doing wrong - and this was a complete amateur hour mistake - was not making some key attributes accessible. In my infinite wisdom, I assumed that attr_accessor would make the attributes accessible. Nope. I had to modify my model to make the crop_x, crop_y, crop_w, and crop_h arguments accessible.

attr_accessor :crop_x, :crop_y, :crop_w, :crop_h, :original_width, :original_height
attr_accessible :avatar, :remove_avatar, :crop_x, :crop_y, :crop_w, :crop_h
after_update :reprocess_avatar, :if => :cropping?

A last note, not critical but handy to know. I did not need to call process! as recreate_versions! does that for me.

Hope this helps at least one person out there.

Problem

I've been at this one for a while now. Using this RailsCast I have been able to modify it to work with CarrierWave - at least in theory. I am attempting to allow the user to crop their profile photo and then upload it to S3 using CarrierWave. Here is what works so far: - User uploads an image along with the rest of their profile info - They are successfully taken to a crop page where they can crop the photo - photo is uploaded and original image dimensions are successfully captured - Clicking on "Crop" to save the image does not trigger any errors (model saves just fine) but also does not actually crop or recreate versions as far as I can tell. So, here's the code I've got. Here's the model: ``` attr_accessor :crop_x, :crop_y, :crop_w, :crop_h, :original_width, :original_height attr_accessible :avatar, :remove_avatar after_update :reprocess_avatar, :if => :cropping? mount_uploader :avatar, ProfileBaseUploader def cropping? !crop_x.blank? && !crop_y.blank? && !crop_w.blank? && !crop_h.blank? end private def reprocess_avatar avatar.process! avatar.recreate_versions! end ``` Pretty boiler plate - the crop_x, etc attributes are assigned from the crop view. I have confirmed that these are getting passed in and assigned correctly and that the reprocess_avatar method is getting called. Here is my uploader code: ``` include CarrierWave::MiniMagick include CarrierWaveDirect::Uploader storage :fog require 'guid' process :cropper process :store_best_geometry version :tiny_thumb do process :resize_to_limit => [50, 50] end version :thumb do process :resize_to_limit => [200, 200] end version :large do process :resize_to_fit => [500, 500] end def extension_white_list %w(jpg jpeg gif png) end def filename @name ||= "#{secure_token}.#{file.extension}" if original_filename.present? end def store_best_geometry manipulate! do |img| if model model.original_width = img['width'] model.original_height = img['height'] end img = yield(img) if block_given? img end end def cropper return unless model.cropping? manipulate! do |img| img = img.crop("#{model.crop_x}x#{model.crop_y}+#{model.crop_w}+#{model.crop_h}") img end end protected def secure_token var = :"@#{mounted_as}_secure_token" model.instance_variable_get(var) or model.instance_variable_set(var, Guid.new) end ``` I think that the process! and recreate_versions! methods are simply not functioning properly but I have no clue as to why. I don't have any errors that I can provide (if anybody knows how I could generate one I would gladly do so). I know that the store_best_geometry method is working great. Can't say the same thing for cropper. Any ideas?

Original source