Rob Wilkerson
Rob Wilkerson

Reputation: 41236

Force a save! operation to fail

I have an ImagesController that kicks off a non-trivial save operation. Here's the high-level background:

I have several different physical file types to deal with in my application. Each of these belongs_to a Binary. Files can be created, of course, and this process involves uploading a physical file to a temporary location, possibly validating the physical file (for size, type or whatever) and then, assuming no issues, moving the file to a permanent location before creating database records in the appropriate tables (let's use images and binaries for the purpose of my question).

An Image belongs to a Binary and there's a BinaryObserver that observes the Image class. In that observer class, there's a before_create() method that tells the Binary class to upload the image's physical file and save a related record in the database (with name, path, URI info, etc.).

One of the things that the observer class method does is test for a callback method in the original model, Image in this case. If the method exist and returns false for any reason, then I need for the save operation to fail. I can't figure out how to do that properly.

In my controller's create() method, I have:

if @image.save!
  flash[:notice] = "Successfully created image."
  redirect_to @image
else
  flash[:notice] = "Upload failed."
  render :action => 'new'
end

I can't figure out how to trigger the else block. In my BinaryObserver class, I have:

def before_create( model )
    binary = Binary.new.upload( model.upload )

    if !model.respond_to?( 'before_file_save' ) || model.before_file_save()
        binary         = binary.store()
        model.binary_id = binary.id
        model.active    = 1

        return true
    else
        # binary.destroy()
        File.delete( File.join( Rails.root, binary.path ) )
        return false
    end
end

At the moment, Image.before_file_save() simply returns false. The physical file is getting deleted, but the save isn't failing and my controller displays an error message that its show() method can't find the image (which makes sense since the image wasn't saved and the physical file was deleted).

How can I force the save operation to fail if the observer class' before_create() method returns false?

Thanks.

Upvotes: 0

Views: 1084

Answers (2)

localshred
localshred

Reputation: 2233

It's my understanding that using the bang (!) syntax on Model methods will raise an exception if the operation fails. Obviously, the save isn't failing, but the Observer needs to let your controller know something went wrong. I would follow the same AR way of doing it, with exceptions:

begin
    @image.save!
    flash[:notice] = "Successfully created image."
    redirect_to @image
rescue YourObserverException => e
    flash[:notice] = "Upload failed."
    render :action => 'new'
end

This would mean the observer doesn't return true, but does raise YourObserverException if the upload fails (where you are returning false).

Upvotes: 1

Cody Caughlan
Cody Caughlan

Reputation: 32748

Your Observer IS still running inside an ActiveRecord Transaction that is created basically at the top. So DB wise you should still have integrity. But in order to pass that info up to the top, I would set an instance variable in the object, right before you return false. Since the controller is still operating on the same object as the Observer, this same information is available.

... in your Observer model.is_valid = false # or_something_went_wrong = true, etc. return false

Then just operate on that accordingly in your Controller.

Upvotes: 0

Related Questions