How to edit existing image using Carrierwave and MiniMagick? - ruby-on-rails

Say I have:
Class Image < ApplicationRecord
require 'carrierwave'
mount_uploader :specialup, SpecialupUploader
...methods...
def crop
require 'mini_magick'
...some code...
end
...methods...
end
Carrierwave uses Minimagick too and modifies its commands in some way that causes bugs in all other places that are not connected with carrierwave.
If you're intrested in specifics (and to help others to find this question in future): Carrierwave adds option -specialup(uploader's name) to all the minimagick commands (which base on mogrify). So Mogrify rolls out error messages like:
`mogrify: unrecognized option '-specialup' # error/mogrify` and so on
I tried to reload minimagick with require 'mini_magick' in the method's body but there's no result. Also tried load 'mini_magick' - the same.
I can't figure out how to make it right!

Related

How to upload while resizing the original image itself in Shrine

I use Shrine in a Ruby on Rails application to create the process of resizing and uploading images to storage.
My current code is:
image_uploader.rb
require "image_processing/mini_magick"
class ImageUploader < Shrine
plugin :derivatives
Attacher.derivatives_processor do |original|
magick = ImageProcessing::MiniMagick.source(original)
{
resized: magick.resize_to_limit!(120, 120)
}
end
end
user.rb
class User < ApplicationRecord
include ImageUploader::Attachment(:image)
before_save :image_resize
def image_resize
self.image_derivatives!
end
end
I implemented it while reading the official documentation, but this is not desirable in two ways.
Requires trigger in model code. Can it be completed with only image_uploader.rb?
Access to images generated with this code requires a "resized" prefix(e.g. #user.image(:resized).url), and the original image will also remain in storage. I want to process the original image itself.
Is there a way to upload while solving these two issues?
You can add the following patch, which will trigger derivatives creation as part of promoting cached file to permanent storage:
# put this in your initializer
class Shrine::Attacher
def promote(*)
create_derivatives
super
end
end
You can just override the model method that retrieves the attached file to return the resized version. You can use the included plugin to do this for all models using this uploader:
class ImageUploader < Shrine
# ...
plugin :included do |name|
define_method(name) { super(:resized) }
end
end
As for the second question: It will still keep the original file in the storage, but just return the resized version instead by default. It's generally better to do this in a view decorator instead.
You always want to keep the original file in the storage, because you never know when you'll need to reprocess it. It can happen that you find your current resizing logic not to be ideal for certain filetypes and sizes, in which case you'll need to regenerate the resized version for previous attachments. And you wouldn't be able to do that if you didn't have the original file anymore.

undefined method `version' for main:Object carrierwave

I have carrierwave's uploader working without applying any changes to uploaded images. Though when I comment out the version block in the generated uploader file I get an "undefined method `version' for main:Object" message.
version is a built in method, and based on what googled, and even a railscast video on it I should be able to just uncomment it and roll.
# Create different versions of your uploaded files:
version :thumb do
process :resize_to_limit => [313, 344]
end
What could I have going on in my codebase that would be causing an issue with carrierwave's version method?
Error implicitly says that your version method not in scope of carrierwave uploader.
Your uploader should look like this.
class Uploader < CarrierWave::Uploader::Base
version 'anything' do
....
end
end
But as per error your uploader code is some thing like this:
class Uploader < CarrierWave::Uploader::Base
...
end
version 'anything' do
....
end
As your version method is not present inside uploader. Ruby considering it for main object when try to execute.
Few more thing also you need to make sure present in your code. You must have rmagick gem in your gemfile. You have imagemagick installed on your development machine.
include CarrierWave::RMagick uncommented in your uploader.

Rails convert Paperclip directory structure to Carrierwave

I was using the Paperclip gem: https://github.com/thoughtbot/paperclip
I'm now using Carrierwave: https://github.com/carrierwaveuploader/carrierwave
My production website is currently using Paperclip. I'm going to be updating the production website to use Carrierwave.
The folder structure for uploads in Paperclip differs from Carrierwave.
I'm also using Amazon S3 to store uploads.
I'm wondering if there's a way to convert my production files uploaded with Paperclip to Carrierwave.
For example, with Paperclip in production I currently have something like the following for resumes:
bucket_name/model_name/resume/000/000/model_id/original/test.pdf
With Carrierwave it should be:
bucket_name/uploads/model_name/resume/model_id/original/test.pdf
Right now it seems I have to make this conversion manually. I was wondering if there's a better approach.
Please advise.
The CarrierWave::Compatibility::Paperclip module already provides this functionality. Just do the following in your uploader:
class MyUploader < CarrierWave::Uploader::Base
include CarrierWave::Compatibility::Paperclip
# The :id_partition symbol will trigger a proc in the Paperclip compatibility module that will build out the properly partition directory structure
def store_dir
"#{model.class.to_s.underscore}/#{mounted_as.to_s}/:id_partition"
end
end
Have you tried changing the store_dir options define in carrierwave uploader to look exactly like that of paperclip
def store_dir
"#{model.class.to_s.underscore}/resume/#{id_partitioning}/original/"
end
def id_partitioning
("%09d" % model.id).scan(/.{3}/).join("/")
end
Note : I just done remember how the paperclip does the id_partitioning (how much '0' it pad to the left based on object id )
but based upon your format 000/000/model_id look to me like 9 character Please confirm
Hope this help

How to stub carrierwave in Rspec?

I want to stub carrierwave to prevent it from fetch images on the web during my tests. How would I stub things to achieve this?
My crawler parses a remote web page, and saves one image url into the model. Carrierwave will fetch that image automatically during the save operation. It works well.
However I have a test about the parsing of pages, and every-time it will download the file, which slows down the testing.
UPDATE:
I mount the uploader as the following (in the pre-existing paperclip column)
mount_uploader :image, TopicImageUploader, :mount_on => :image_file_name
I tried to stub the following, but neither worked:
Topic.any_instance.stub(:store_image!)
Topic.any_instance.stub(:store_image_file_name!)
Topic.any_instance.stub(:store_image_remote_url!)
TopicImageUploader.any_instance.stub(:download!)
This is what I'm using in my spec_helper:
class CarrierWave::Mount::Mounter
def store!
end
end
This completely blocks all real file uploads (note that I'm using this with carrier wave 0.5.8, which is newest version at the time of writing, if you're using much older version, it might differ). If you want to control tests which stub uploads, you could use:
CarrierWave::Mount::Mounter.any_instance.stub(:store!)
I reduced my test-suite time from 25 seconds to just 2 seconds with a simple config in the CarrierWave initializer:
# config/initializers/carrier_wave.rb
CarrierWave.configure do |config|
config.enable_processing = false if Rails.env.test?
end
This config skips the image manipulation (resizing, cropping, ...) of ImageMagick, MiniMagick ect.
allow_any_instance_of(CarrierWave::Uploader::Base).to receive(:store!).and_return nil

How to create unit test for Paperclip::Geometry?

I'm writing unit test for a image uploader that uses paperclip.
It's being bumpy and I was slowly moving thought the hurdles till I got stuck in the paperclip::geometry class
Here is my code below
require 'test_helper'
require File.join(File.dirname(__FILE__),"../../config/initializers","paperclip")
class PhotoTest < ActiveSupport::TestCase
#include ActionController::TestProcess
should_belong_to(:product)
should_have_attached_file :data
setup do
#I had to do this way because the include right below the class line was not working
image = Photo.create(:data => ActionController::TestUploadedFile.new(ActionController::TestCase.fixture_path + "base-production-pack.png",'image/png'))
#geo = Paperclip::Geometry.from_file(image)
end
end
the paperclip::geometry is giving me the error:
test: Photo should have a paperclip attachment named #data. (PhotoTest):
Paperclip::NotIdentifiedByImageMagickError: #<Photo:0x1054aa6b8> is not recognized by the 'identify' command.
paperclip (2.3.6) lib/paperclip/geometry.rb:24:in `from_file'
/test/unit/photo_test.rb:13
I have a initializer file "paperclip.rb" that points to the identify on my local machine
Thanks in advance
Paperclip::Geometry.from_file expects a path, so you should do:
Paperclip::Geometry.from_file(image.data.path)
Sidenote: It's good that you're testing, however there's little point testing paperclip's geometry method because paperclip's test suite covers that. Your tests should cover what your code is expected to do (eg confirming thumbnails are resized as expected).

Resources