Work with Rescue in Rails

work with rescue in Rails

All code after the end of the rescue block is interpreted only if there are no returns in the rescue block. So you can call return at the end of your rescue block.

def index
begin
@user = User.find(params[:id])
rescue
flash[:notice] = "ERROR"
redirect_to(:action => 'index')
return
end
flash[:notice] = "OK"
redirect_to(:action => 'index')
end

or

def index
@user = User.find(params[:id])
# after is interpret only if no exception before
flash[:notice] = "OK"
redirect_to(:action => 'index')
rescue
flash[:notice] = "ERROR"
redirect_to(:action => 'index')
end

But in your case the better is to use rescue_from or rescue_in_public

like

class UserController < ApplicationController
def rescue_in_public(exception)
flash[:notice] = "ERROR"
redirect_to(:action => 'index')
end

def index
@user = User.find(params[:id])
flash[:notice] = "OK"
redirect_to(:action => 'index')
end
end

But the using of rescue_in_public is not really good advice

Where to write begin rescue in Ruby (on Rails)

I would argue that when using rescue blocks it is a common pattern to follow two guidelines:

  1. Put the rescue block only around the lowest number of lines possible. Usually, this should only be one line. That certainly depends on your code but in general, exceptions are raised by specific methods. The rescue block should only be around that block if possible. That makes debugging and refactoring much easier.
  2. When adding a rescue block then name those exceptions that you expect to happen precisely. Otherwise you might catch errors that you didn't expect (syntax errors or method calls on nil). What again makes debugging much harder.

In your example, I agree with the PR reviewer but I would add the specific exceptions that might occur and that I want to handle. for example like this:

def get_list
http, uri_path, headers = set_request("whatever_api_url")

begin
http.get(uri_path, headers)
rescue Net::HTTPNotFound => e
# handle page not found
rescue Net::HTTPForbidden => e
# handle invalid credentials
rescue Net::HTTPServerError, Net::HTTPServerException
# handle server error
end
end

Of cause, how to handle exceptions and if that is even possible depends on your application. And therefore there is no hard rule but only there guidelines.

Rails 6 proper rescue from error and fetch error message

Why do you need the private method? Assume success unless you rescue, then assume error right?

def call
response = api.check_applicant_status(id: applicant_id, options: options)
Success(response)
rescue Errors::BadRequestError => e
Failure(e.message)
end

Rails | Rescue exception in model and use the error in controller

You can add error to object when callback is not successful:

def post_to_twitter
begin
user.twitter.update(tweet)
rescue Twitter::Error => error
# error will be appear in `@tweet.errors`
errors.add(:base, "Please fix the error #{error.message}")
false
end
end

Then, do whatever you need in controller when @tweet.save returns false (and it will return false since the callback was not successful):

def create
@tweet = Tweet.new(tweet_params)
@tweet.user_id = current_user.id
if @tweet.save
redirect_to new_tweet_path, notice: "Your tweet has been successfully posted"
else
# render 'new'
redirect_to root_path, notice: @tweet.errors.full_messages.join(',')
end
end

How to rescue model transaction and show the user an error?

def exchange_status_with(address)
ActiveRecord::Base.transaction do
self.save!
address.save!
end
rescue ActiveRecord::RecordInvalid => exception
# do something with exception here
end

FYI, an exception looks like:

#<ActiveRecord::RecordInvalid: Validation failed: Email can't be blank>

And:

exception.message
# => "Validation failed: Email can't be blank"

Side note, you can change self.save! to save!


Alternate solution if you want to keep your active model errors:

class MyCustomErrorClass < StandardError; end

def exchange_status_with(address)
ActiveRecord::Base.transaction do
raise MyCustomErrorClass unless self.save
raise MyCustomErrorClass unless address.save
end
rescue MyCustomErrorClass
# here you have to check self.errors OR address.errors
end

Begin, Rescue and Ensure in Ruby?

Yes, ensure ensures that the code is always evaluated. That's why it's called ensure. So, it is equivalent to Java's and C#'s finally.

The general flow of begin/rescue/else/ensure/end looks like this:

begin
# something which might raise an exception
rescue SomeExceptionClass => some_variable
# code that deals with some exception
rescue SomeOtherException => some_other_variable
# code that deals with some other exception
else
# code that runs only if *no* exception was raised
ensure
# ensure that this code always runs, no matter what
# does not change the final value of the block
end

You can leave out rescue, ensure or else. You can also leave out the variables in which case you won't be able to inspect the exception in your exception handling code. (Well, you can always use the global exception variable to access the last exception that was raised, but that's a little bit hacky.) And you can leave out the exception class, in which case all exceptions that inherit from StandardError will be caught. (Please note that this does not mean that all exceptions are caught, because there are exceptions which are instances of Exception but not StandardError. Mostly very severe exceptions that compromise the integrity of the program such as SystemStackError, NoMemoryError, SecurityError, NotImplementedError, LoadError, SyntaxError, ScriptError, Interrupt, SignalException or SystemExit.)

Some blocks form implicit exception blocks. For example, method definitions are implicitly also exception blocks, so instead of writing

def foo
begin
# ...
rescue
# ...
end
end

you write just

def foo
# ...
rescue
# ...
end

or

def foo
# ...
ensure
# ...
end

The same applies to class definitions and module definitions.

However, in the specific case you are asking about, there is actually a much better idiom. In general, when you work with some resource which you need to clean up at the end, you do that by passing a block to a method which does all the cleanup for you. It's similar to a using block in C#, except that Ruby is actually powerful enough that you don't have to wait for the high priests of Microsoft to come down from the mountain and graciously change their compiler for you. In Ruby, you can just implement it yourself:

# This is what you want to do:
File.open('myFile.txt', 'w') do |file|
file.puts content
end

# And this is how you might implement it:
def File.open(filename, mode='r', perm=nil, opt=nil)
yield filehandle = new(filename, mode, perm, opt)
ensure
filehandle&.close
end

And what do you know: this is already available in the core library as File.open. But it is a general pattern that you can use in your own code as well, for implementing any kind of resource cleanup (à la using in C#) or transactions or whatever else you might think of.

The only case where this doesn't work, if acquiring and releasing the resource are distributed over different parts of the program. But if it is localized, as in your example, then you can easily use these resource blocks.


BTW: in modern C#, using is actually superfluous, because you can implement Ruby-style resource blocks yourself:

class File
{
static T open<T>(string filename, string mode, Func<File, T> block)
{
var handle = new File(filename, mode);
try
{
return block(handle);
}
finally
{
handle.Dispose();
}
}
}

// Usage:

File.open("myFile.txt", "w", (file) =>
{
file.WriteLine(contents);
});

How do I rescue an exception from inside an Enumerator?

I think I've figured out what's going on here. When you write code in an Enumerator, the block isn't actually executed within the Enumerator. Therefore, if I add a rescue within the Enumerator, it doesn't matter.

This is because the |y| in Enumerator is actually a yielder object which does the yielding (more on that in the Enumerator documentation or the Enumerator::Yielder documentation.

You have to rescue things beforehand.

Rails: rescue class inheritance

Those rescue blocks are executed in order. You should put more specific ones first. Move that StandardError one as last.

Capture key inside rescue block in Rails

begin
GroupProduct.insert_all(arr, unique_by: %i[ group_id product_id ])
rescue ActiveRecord::InvalidForeignKey => e
match_on_product_it = /\APG::ForeignKeyViolation:.+\(product_id\)=\((\d+)\)/.match e.message
arr = arr.reject {|r| r[:product_id] == match_on_product_it[1].to_i}
retry
end


Related Topics



Leave a reply



Submit