Carrierwave: How to Avoid Issues with Version Inheritance
Сarrierwave is a popular image upload gem used by the Rails community to upload files to the server.
Recently I had a task to upload two types of images into the application. Under the hood,the only difference between them was the target directory on the server. Sounds pretty simple, right?
To follow DRY principle, I decided to not create different uploader classes and use inheritance structure.
I created simple ImageUploader class:
Then I created PurchasedImageUploader that completely inherits from the previous one:
Then I uploaded several files and got unexpected result:
PurchasedImage.last.file.small.url
=> "/image/9/small_file_name.png"
PurchasedImage.last.file.url
=> "/purchased_image/9/file_name.png"
Hmm…
What is going on?
After some research I found out that existing behaviour was correct by design. storage_dir
, that was defined inside the inherited class, would not be applied to versions. We should define storage_dir
inside each block. It sounds confusing to me.
“original class” means the first class in chain that inherits from the “CarrierWave::Uploader::Base”.
Let’s review that behaviour.
To provide more deep ancestor chain let’s add:
# app/uploaders/purchased_image_uploader.rb
version :small do
process resize_to_fill: [120, 120]
end
If we pick direct version we can find out ancestors chain:
PurchasedImage.last.file.versions[:small].class
#=> PurchasedImageUploader::Uploader70329100898520
PurchasedImage.last.file.versions[:small].class.ancestors
#=>[
PurchasedImageUploader::Uploader70329100898520,
ImageUploader::Uploader70329129758160,
ImageUploader,
CarrierWave::MiniMagick,
CarrierWave::Uploader::Base,
...
]
“PurchasedImageUploader::Uploader70329100898520
” — This is a class that was created specially for version :small for PurchasedImageUploader.
“ImageUploader::Uploader70329129758160
” — A class for ImageUploader :small version (that has the same name).
Each version that was defined inside of the PurchasedImageUploader
creates its own special class. That version class is inherited from the same special class that was created by the version with the same name in the parent class. And so on to ImageUploader
— the original class that inherits from CarrierWave::Uploader::Base
.
Simply, by design, each version block inherits from another version block with the same name that was defined in parent class.
About version creation you can find out here.
Solution
Or just do not use an inheritance for uploader files, you can achieve the same goal with “includes” :)
BTW: fog_public
method will have the same issue if you would use a fog storage.
Solution is not perfect, but it works.
Also, if you find another solution — please let me know.
Link to dummy app: Demo App.
Don't want to miss anything?
Subscribe and get stories like these right into your inbox.
Keep reading
Contact us
Let's explore how our expertise can help you achieve your goals! Drop us a line, and we'll get back to you shortly.