Execute method on exception raised without rescuing - ruby-on-rails

I'm using Sidekiq, and I'd like to execute a method when an exception is raised in a background job, without rescuing the error.
Currently, I have:
begin
# job
rescue => SomeError
# run method
end
However, since it rescues the error, it does not go in the "failed" section of Sidekiq.
I'd like to execute code when an exception is raised, but without rescuing it. How can I do this ?

Just re-raise and you are all set.
begin
# job
rescue => SomeError
# run method
raise
end

Related

Ruby on Rails Exception usage

Upon finding a failed validation, invalid? will return false and exit.
If all validations pass, invalid? will return true and the code will continue.
Does the rescue code only run if all validations pass?
If so, what raised errors will it catch?
Lastly why is there no Begin?
def save
return false if invalid? # invalid? triggers validations
true
rescue ActiveRecord::StatementInvalid => e
# Handle exception that caused the transaction to fail
# e.message and e.cause.message can be helpful
errors.add(:base, e.message)
false
end
Does the rescue code only run if all validations pass?
Blockquote
No, it will run if the call of invalid? throws an exception of type StatementInvalid
what raised errors will it catch?
Blockquote
the call of invalid? here is what raises the error
why is there no Begin?
in ruby, you can remove begin if you rescue from any exception that is raised from methods body so
def method
begin
#some code
rescue
#handle
end
end
equal to
def method
some code
rescue
# handle
end
but the second syntax shorter and cleaner
Note: it doesn't same right to me to rescue from ActiveRecord::StatementInvalid
inside an override to save

How to know why a transaction was rolled back?

I have an Account model. It has usual validations and after_save callbacks. There is a requirement, and account objects are to be created following some additional validation strategy. I have the following snippet, which works fine:
def special_account_creation
Account.transaction do
a = Account.create(params)
resp = update_third_party(a) # Throws exception if unable to update
validate_amount(a, resp) # Throws exception if `a` is invalid
# rescue Rollback ??? <--- How to do this
# msg = <transaction_error_details>
# Rails.logger.info("Special Account not created due to: #{msg}")
end
end
a account is destroyed if anything goes wrong while calling a third party API or during validation.
I want to know how I can log the error to know why the transaction was rolled back.
ActiveRecord transaction catches any exception being raised inside and rolls back the transaction. After that, if the exception is ActiveRecord::Rollback, it is supressed otherwise any other exception is raised above the call stack.
Ref: https://api.rubyonrails.org/classes/ActiveRecord/Rollback.html
So you could do something like this:
Account.transaction do
begin
a = Account.create(params)
resp = update_third_party(a) # Throws exception if unable to update
validate_amount(a, resp) # Throws exception if `a` is invalid
rescue StandardError => e # <- assuming all errors need to be logged
Rails.logger.info("Special Account not created due to: #{msg}")
raise e # <-- Don't forget to raise the error!
end
end
I suggest raise the error from the rescue block - this will rollback the transaction and if the error is something other than ActiveRecord::Rollback, it will be raised outside the transaction block. These errors you can handle in rescue_from block in base controller.
Also prefer catching StandardError and not Exception: https://robots.thoughtbot.com/rescue-standarderror-not-exception
I am assuming we want to catch all possible errors in that block, hence the use of StandardError, otherwise use any specific errors that need to be intercepted.
ActiveRecord does not raise Rollback on failed update and/or failed validation.
You should rescue what was raised and then there are two opportunities: either raise Rollback to just rollback a transaction and continue normal execution flow, or re-raise what was raised to indeed break the normal flow.
def special_account_creation
Account.transaction do
a = Account.create(params)
begin
resp = update_third_party(a) # Throws exception if unable to update
validate_amount(a, resp) # Throws exception if `a` is invalid
rescue => e # might be more specific
Rails.logger.info("Transaction failed with a message #{e.message}")
raise ActiveRecord::Rollback, e.message # just to rollback
# raise e # for re-raising the exception
end
end
end

Non-fatal rescue in Rails

I'm trying to run a command that might fail sometimes. When it fails, it throws an exception.
What I'd like it to do is just log the error quietly and continue executing the next line below it, rather than aborting and going into the 'rescue' block. How should I approach this?
My current code is as follows:
rescue_from 'Gibbon::MailChimpError' do |exception|
logger.error("MAILCHIMP: #{exception}")
end
When I call the Mailchimp API, sometimes there is an error, and this disrupts the flow of my application. I just want it to carry on executing as if nothing has happened, and just note there was an error in the log.
How about something like this:
def rescuing(&block)
begin
yield
rescue NameError => e
puts "(Just rescued: #{e.inspect})"
end
end
rescuing do
puts "This is dangerous"
raise NameError
end
puts "... but I'm still alive"
Obviously, you'd have to replace NameError with the exception you want to be protected against.

How to rescue the error exception raised by the `constantize` method?

I am using Ruby on Rails 3.2.2 and I would like to properly rescue the following process flow by raising a "custom" error message:
def rescue_method
# sample_string.class
# => String
# sample_string.inspect
# => "ARubyConstantThatDoesNotExist"
begin
build_constant(sample_string)
rescue
raise("My custom error message: #{build_constant(sample_string)} doesn't exist.")
end
end
def build_constant(sample_string)
"AModule::#{sample_string}".constantize
end
Note: I feel "forced" to use the constantize method also in the raised "custom" message in order to DRY code...
When the rescue_method is executed it seems that the raise("My custom error message") code is never executed and I get the following error:
uninitialized constant AModule::ARubyConstantThatDoesNotExist
How to properly display the raised "custom" message (since a further error exception is raised in the subsequent raised "custom" message)? What do you advice about?
The problem is that your build_constant method is doing two things:
Building the class name.
Turning the name into a class using constantize.
One of those things wants to use the other when an exception is raised. A simple solution is to pull those separate tasks apart:
def build_class_name(sample_string)
"AModule::#{sample_string}"
end
def rescue_method
name = build_class_name(...)
name.constantize
rescue NameError
raise("My custom error message: #{name} doesn't exist.")
end
You should also be more specific about the exception you're looking for so I added that for free.
If you don't want to rely on catching any exception, you could use safe_constantize (https://apidock.com/rails/ActiveSupport/Inflector/safe_constantize).
Same purpose as constantize but will return nil instead, whenever a Module does not exist.
'UnknownModule::Foo::Bar'.safe_constantize # => nil
begin
"AModule::#{sample_string}".constantize
rescue SyntaxError, NameError => err
raise("My custom error message")
end

task does not get 'failed' when using rescue

I have a task like this...
def self.perform(parsed_json)
do_hard_work
use_faye_to_push_status
rescue => exception
use_faye_to_push_error
end
but, when I use the 'rescue', the task doesn't enter on the failed task list. Is there a way to, even using rescue, set the task as failed?
Rescuing from the error will stop it from proceeding any further up the call stack. With that said, you can simply raise again inside the rescue block in order to propagate it upwards:
def self.perform(parsed_json)
do_hard_work
use_faye_to_push_status
rescue => exception
use_faye_to_push_error
raise
end

Resources