Reputation: 3553
I'm using CarrierWave on my Rails 3 sample app. I want to validate the remote location upload so I don't get the standard error exception when a user submits an invalid URL either blank or not an image:
CarrierWave::DownloadError in ImageController#create
trying to download a file which is not served over HTTP
This is my model:
class Painting < ActiveRecord::Base
attr_accessible :gallery_id, :name, :image, :remote_image_url
belongs_to :gallery
mount_uploader :image, ImageUploader
validates :name, :presence => true,
:length => { :minimum => 5, :maximum => 100 }
validates :image, :presence => true
end
This is my controller:
class PaintingsController < ApplicationController
def new
@painting = Painting.new(:gallery_id => params[:gallery_id])
end
def create
@painting = Painting.new(params[:painting])
if @painting.save
flash[:notice] = "Successfully created painting."
redirect_to @painting.gallery
else
render :action => 'new'
end
end
def edit
@painting = Painting.find(params[:id])
end
def update
@painting = Painting.find(params[:id])
if @painting.update_attributes(params[:painting])
flash[:notice] = "Successfully updated painting."
redirect_to @painting.gallery
else
render :action => 'edit'
end
end
def destroy
@painting = Painting.find(params[:id])
@painting.destroy
flash[:notice] = "Successfully destroyed painting."
redirect_to @painting.gallery
end
end
I'm not really sure how to tackle this problem so any insight would be great.
Upvotes: 5
Views: 5812
Reputation: 86
The solution on the CarrierWave wiki is not working for me. As Peter Hulst mentioned, CarrierWave loads the file prior to validation. I found a way around this by capturing the exception when it gets thrown, and adding it back as a validation error later. For some reason when an exception is thrown, all the other record's attributes become nil, so they also have to be captured and re-added before validation. This code all goes in your model.
This still needs a little rework to use error messages from config, rather than hardcoded.
attr_accessor :additional_error_message, :original_attributes
def initialize(*args)
self.original_attributes = args[0]
begin
super
rescue CarrierWave::IntegrityError # bad file type
self.additional_error_message = 'must be a PNG, JPEG, or GIF file' # depends on your whitelist
rescue OpenURI::HTTPError # 404
self.additional_error_message = 'could not be found'
rescue RuntimeError # redirection
self.additional_error_message = 'could not be loaded'
rescue CarrierWave::DownloadError
self.additional_error_message = 'could not be loaded'
rescue
self.additional_error_message = 'could not be loaded'
end
end
before_validation do |image|
if additional_error_message.present?
errors.add(remote_image_url, additional_error_message)
self.name = original_attributes[:name] # replace this with re-adding all of your original attributes other than the remote_image_url
end
end
# the image will have an "is blank" error, this removes that
after_validation do |image|
errors.delete(:image) if additional_error_message.present?
end
Upvotes: 0
Reputation: 648
This is very annoying issue. I did the rescue_from
in my application_controller.rb
for now and just flash messages stating the issue. It's about the best I could come up with. I am not a fan of clogging up the controller and having to use that duplicate code if you have multiple models that need those validations.
rescue_from CarrierWave::DownloadError, :with => :carrierwave_download_error
rescue_from CarrierWave::IntegrityError, :with => :carrierwave_integrity_error
def carrierwave_download_error
flash[:error] = "There was an error trying to download that remote file for upload. Please try again or download to your computer first."
redirect_to :back
end
def carrierwave_integrity_error
flash[:error] = "There was an error with that remote file for upload. It seems it's not a valid file."
redirect_to :back
end
Upvotes: 1
Reputation: 22643
The solution to this problem has been added to the CarrierWave Wiki on Github.
Edit:
I'm trying to implement the proposed solution now but I can't get it working. I'm using AR on Rails 3.1.3.
Implementing the code the way it is on the wiki results in the validation actually happening fine. When I try to upload gibberish I get a nice validation message. The problem is that normal uploads are prevented also.
Upvotes: 0
Reputation: 431
I ran into this same problem. Unfortunately it looks like this is a design flaw of CarrierWave... it does not allow for proper validation of a remote url. CarrierWave will attempt to download the resource right away when the attribute is set and will throw an exception if the url is invalid, can't be accessed, or if the resource doesn't have the expected type. DownloadError or IntegrityErrors are always thrown before any validation occurs.
Therefore I couldn't find a good workaround that uses other validators. My solution ended up looking like this:
valid = false
begin
par = params[:image].except(:remote_upload_url)
@image = Image.new(par)
# this may fail:
@image.remote_upload_url = params[:image][:remote_upload_url]
valid = true
rescue CarrierWave::DownloadError
@image.errors.add(:remote_upload_url, "This url doesn't appear to be valid")
rescue CarrierWave::IntegrityError
@image.errors.add(:remote_upload_url, "This url does not appear to point to a valid image")
end
# validate and save if no exceptions were thrown above
if valid && @image.save
redirect_to(images_configure_path)
else
render :action => 'new'
end
basically, I wrap the constructor in a rescue block and initially set all parameters except for the remote url. When I set that, an exception may occur which I handle by manually setting an error in the model. Note that no other validations are performed in this scenario. It's a hack but worked for me.
I hope that this can be addressed in a future release, by delaying download of the resource until the model validation stage or after.
Upvotes: 8