I watched Railscast 253 and decided to use Carrierwave for my file uploading needs. I know I must be doing something very dumb, but I am neither successful in overriding the store_dir method nor the filename method in my uploader. The following is my code which nearly identical to Ryan Bates' code in the Railscast.
class DocumentUploader < CarrierWave::Uploader::Base
# some stuff here
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"documents/#{model.class.to_s.underscore}/#{model.id}"
end
# more stuff here
def filename
"#{active_customer.last_name}_#{active_customer.first_name}_#{active_system.desc}.pdf" if original_filename
end
end
The uploaded files are instead being stored in public/uploads/tmp with some default file name. What could I be doing wrong here? Is there an important step in the Carrierwave set up that maybe Ryan Bates did not include in the Railscast?
Related
I've introduced a new version on my Carrierwave Uploader. When I create a new Event it creates both versions correctly. But when I update it, only the file I attached gets uploaded, but versions do not get recreated.
I am using CarrierWave 1.2.2, and looking at the changelog, it doesn't seem to have been a bug that got fixed in the newer versions
class CoverUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
if Rails.env.development? || Rails.env.test?
storage :file
elsif Rails.env.production?
storage :fog
end
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
if ENV['HEROKU_APP_NAME'].to_s.include?('-pr-')
"review_apps/#{model.class.to_s.underscore}/#{model.id}"
else
"#{Rails.env}/#{model.class.to_s.underscore}/#{model.id}"
end
end
# Provide a default URL as a default if there hasn't been a file uploaded:
def default_url(*args)
ActionController::Base.helpers.asset_path('test.jpg')
end
# Create different versions of your uploaded files:
version :optimised do
process convert: 'webp'
process :set_content_type_to_webp
def full_filename(_for_file = model.cover.file)
"cover_#{model.id}.webp"
end
def exists?
file&.exists?
end
end
def extension_blacklist
%w(webp)
end
private
# Required to actually force Amazon S3 to treat it like an image
def set_content_type_to_webp
file.instance_variable_set(:#content_type, 'image/webp')
end
end
#ogelacinyc was partly correct when he found the bug in full_filename. I went back to test normal functionality with creating another version, with a simple dimension change. I could then see that update would recreate the versions by itself, just like I expected.
That made me think that maybe there is something wrong with my version :optimised block. So after commenting one by one, I found that full_filename was the culprit. It could have been model.cover.file failing silently, but I think it was model.id, as can be seen in the description for filename method in Carrierwave
So instead, I grab the filename directly, extract extension and substitute it with webp:
def full_filename(for_file = model.file_name.file)
extension = File.extname(for_file)
"cover_#{for_file.sub(extension, '.webp')}"
end
Which works without problems!
You need to add an after_save callback to Event and then call recreate_versions! on your mounted uploader.
Assuming you have an Event model with the following, this would solve your problem.
class Event < ApplicationRecord
mount_uploader :cover_image, CoverUploader
after_save :recreate_versions!
delegate :recreate_versions!, to: :cover_image, allow_nil: true
end
See CarrierWave's README also.
How would I use populate rails fixtures(yaml) with carrierwave uploads?
The documentation doesn't seem to cover this and the carrierwave wiki does not either.
I have tried
and I have verified that the above ruby code generates a valid file object.
I doubt fixtures are going anywhere as they are used exclusively in the Rails codebase itself. Plus i use them, so that is all that matters.
I also found this article that helped me, and also you.
Boiled down in test_helper.rb
class ActiveSupport::TestCase
# Add more helper methods to be used by all tests here...
CarrierWave.root = Rails.root.join('test/fixtures/files') #path is up to you
def after_teardown
super
CarrierWave.clean_cached_files!(0)
end
end
class CarrierWave::Mount::Mounter
def store!
# Not storing uploads in the tests
end
end
I then put in my uploader:
def store_dir
if Rails.env.test?
"images"
else
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
I'm sure i can get rid of the need for that store_dir ternary like statement, but for now it works. I've got another way if this doesn't do work for you, but this is cleaner.
I am currently using carrierwave, and i am wondering if the following his possible. If so how!. Thanks in advance
I have a user and wants to upload an avatar then the following folder would be created
public/image/avatar/customer.id/image01/small
public/image/avatar/customer.id/image01/normal
public/image/avatar/customer.id/image01/big
public/image/avatar/customer.id/image02/small
public/image/avatar/customer.id/image02/normal
public/image/avatar/customer.id/image02/big
Basically, I do not want to overide the previous image has i want to keep them, but create a folder for the newest picture to have it there. Also want the customer id has a path.
Thanks.
PS: if its possible, please provide a tutorial or somesort, if not possible would paperclip allow it? Thanks i can't seem to find anything about it.
uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
include CarrierWave::RMagick
# include CarrierWave::MiniMagick
# Choose what kind of storage to use for this uploader:
storage :file
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"public/image/avatar/#{current_user.id}"
end
version :small do
end
version :normal do
end
version :big do
end
end
Your image uploader should look something like that.
I've followed the instructions on how to define a test-specific store directory for carrierwave uploads, which suggests opening the CarrierWave::Uploader::Base class and redefining store_dir and cache_dir like so:
if defined?(CarrierWave)
CarrierWave::Uploader::Base.descendants.each do |klass|
next if klass.anonymous?
klass.class_eval do
def cache_dir
"#{Rails.root}/spec/support/uploads/tmp"
end
def store_dir
"#{Rails.root}/spec/support/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
end
end
This works like a charm, except when I run rspec with spork, then it fails to modify the class and uploads are stored in the default location. Does anyone have any clue why this might be happening?
Someone else working on our project solved this problem by adding a line with just AvatarUploader ahead of the CarrierWave::Uploader::Base.descendants.each line, like this:
if defined?(CarrierWave)
AvatarUploader # load AvatarUploader class
CarrierWave::Uploader::Base.descendants.each do |klass|
#...
Not entirely sure why this works, but it does.
Little addition to accepted answer, for anyone coming here:
if uploader classes are not loaded before the call to
CarrierWave::Uploader::Base.descendants, it will return empty array, so either specify each uploader like in accepted answer or you could do something like this to require all uploaders from let's say rails uploaders folder
Dir["#{Rails.root}/app/uploaders/*.rb"].each {|file| require file}
CarrierWave::Uploader::Base.descendants.each do |klass|
#...
I'm moving from attachment_fu to carrierwave, since attachment_fu is broken with Rails 3, and would like to be able to use the attachment_fu image files that I already have, instead of carrierwave making new ones and moving them somewhere else.
My images are partitioned by id, so if the user id is 61 and they upload a file called "foo.png", then their old attachment_fu image would be at "public/images/users/0000/0061/foo.png"
In my users model, I mount the carrierwave image uploader with:
attr_accessible :user_avatar
mount_uploader :user_avatar, UserAvatarUploader
In my UserAvatarUploader, I have this:
def store_dir
File.join Rails.root, "public/images/users", ("%08d" % model.id).scan(/\d{4}/).join("/")
end
so when I set :user_avatar, I should get "public/images/users/0000/0061/foo.png"
but when I try to set the :user_avatar in the User model to the old image
user.user_avatar = "#{Rails.root}/public/images/users/0000/0061/foo.png"
it comes back as something like "/uploads/tmp/20110916-1244-15398-7724/foo.png"
It seems that store_dir is not being overwritten properly. What do I need to do to make this work right?
Carrierwave default base location is the public folder
try to change it by using
CarrierWave.configure do |config|
config.root = Rails.root
end