In my Rails app, I enable users to upload images using Carrierwave and Amazon S3. I want to implement a feature that lets users edit existing images by rotating it 90 degrees.
I'm confused about where this code would go. Does it go in the image uploader file, or the image controller? And how is it called? I believe it should look something like this:
image = Image.find(params[:id])
image_obj = MiniMagick::Image.read(image.file)
image_obj.rotate(-90)
image_obj.write(image.file)
But I haven't been able to find examples to help me. If anyone can give me a pointer in the right direction, I would really appreciate it!
Edit
Thanks to deep for their thorough response! Here is what I ended up doing:
In my view:
# image.html.erb:
<%= link_to rotate_image_path(:id => image.id), :remote => true %>
In my controller:
# image_controller.rb:
def rotate
@image = Image.find(params[:id])
@image.rotated = true
@image.save
respond_to do |format|
format.js { render :nothing => true }
end
end
In my model:
# image.rb
attr_accessible :rotated
after_save :rotate_image, if: ->(obj){obj.rotated.present? && obj.rotated?}
def rotate_image
self.image_path.recreate_versions!
end
In my uploader:
# image_uploader.rb
process :rotate_img
def rotate_img
if model.rotated.present? && model.rotated?
manipulate! do |img|
img.rotate '-90'
img
end
end
end
The only real change I made was in the uploader, where I ran into errors trying to do a condition process. I put the conditional within the rotate_img method.
Here's my solution
First define a attribute accessor in your model and on update set it to true.
In your model
#image.rb
attr_accessor :rotate
In your controller
#images_controller.rb
def update
@image = Image.find(params[:id])
@image.rotate = true
@image.save
redirect_to root_path, :notice => "Bla bla bla"
end
Carrierwave provides a recreate_versions!
method which will process and re-upload the image. In you case you can add a after_save
callback that will trigger recreate_versions!
method only if the rotate attribute is set to true.
In your model
#image.rb
after_save :rotate_image, if: ->(obj){ obj.rotate.present? and obj.rotate? }
def rotate_image
self.file.recreate_versions!
end
Now in your image uploader you can write the code to rotate a image.
#image_uploader.rb
.......
# It will replace the original image with rotated version
process :rotate_img, :if => model.rotate.present and model.rotate?
def rotate_img
manipulate! do |img|
img.rotate "90"
img #returns the manipulated image
end
end
If you don't want to replace the original image then all you have to do is to call process inside a version like
# Create different versions of your uploaded files:
version :rotated_img do
process :rotate_img
end