retailevolved
retailevolved

Reputation: 485

Rails Cropping and Uploading to S3 using CarrierWave and MiniMagick

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:

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?

Upvotes: 2

Views: 2168

Answers (1)

retailevolved
retailevolved

Reputation: 485

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.

Upvotes: 9

Related Questions