Rescue PG::UndefinedTable instead of ActiveRecord::StatementInvalid - ruby-on-rails

If I try to, for example, drop a table that doesn't exist, I will get the following error:
"#<ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: relation \"aiurea\" does not exist\n
I can rescue from it using ActiveRecord::StatementInvalid, but it's too generic for me; I would like to only rescue when the underlying error is PG::UndefinedTable. How can I do this?
P.S.: I saw error.cause to lead to the underlying error, but I'm not sure whether this is "public" interface and it is an unobtrusive way to get to it.

2018 Update:
ex.original_exception has been deprecated in favor of ex.cause. This worked for me:
rescue ActiveRecord::StatementInvalid => ex
if ex.cause.is_a?(PG::UndefinedTable)
# do something
else
raise ex
end
end

ActiveRecord::StatementInvalid is a special Error type that encapsulates other errors in it. You can access the original one with .original_exception:
rescue ActiveRecord::StatementInvalid => ex
ex.original_exception # this will return the `PG::UndefinedTable` class, which you can further inspect.
end
Better way to do is:
rescue ActiveRecord::StatementInvalid => ex
if ex.original_exception.is_a?(PG::UndefinedTable)
# do something
else
raise ex
end
end

Something like this should work
rescue ActiveRecord::StatementInvalid => ex
if ex.message =~ /PG::UndefinedTable/
// Do stuff here
else
raise ex
end
end

Related

Exception not falling in models

I wrote a function in ruby on rails model like below
def sorted_exp
begin
exp.order("currently_active = true DESC, TO_DATE(to_date, 'MM-DD-YYYY') DESC")
rescue
exp.order("currently_active = true DESC")
end
end
but there are few entries in to_date column due to which exception falls like 'september 2018'. When I tried to handle exception in model, it failed, and does not go in the rescue section. I don't know why it does not catch the error in model here, and why it does not return a query in the rescue section.
The exception raised is the following:
PG::InvalidDatetimeFormat: ERROR: invalid value "se" for "MM"
In the sorted_exp method, the output of the query is not being used. Rails actually executes the call to the DB when the value of the call is being used. In this case, the value of this is probably being used in some other function and the error is being raised from there, pointing to this line: exp.order("currently_active = true DESC, TO_DATE(to_date, 'MM-DD-YYYY') DESC")
I'm not sure of your exact use case, but the only way of catching the exception in this block would be to use values that the query is supposed to return, like counting the number of objects returned(Again, it depends on your use case).
For example, the following query raises an error inspite of being in a begin..rescue block:
begin
User.order("TO_DATE(users.created_at, 'MONTH-YYYY') DESC")
rescue
puts "In rescue block"
end
This raises the following error:
ActiveRecord::StatementInvalid: PG::UndefinedFunction: ERROR: function to_date(timestamp without time zone, unknown) does not exist
However, when the output of this query is used in the begin block itself, the exception gets caught. For example:
begin
sorted_users = User.order("TO_DATE(users.created_at, 'MONTH-YYYY') DESC")
count = sorted_users.count
rescue
puts "In rescue block"
end
The output for this is:
In rescue block
This is because the query was actually executed in the begin block itself, and hence, got caught by our rescue block.

Handle exception in ruby on rails

I called a method #txt.watch inside model from worker and Inside watch() there is an array of parameters(parameters = self.parameters). Each parameter have unique reference id.
I want to rescue each exception error for each parameter from inside worker.
class TextWorker
def perform(id)
#txt = WriteTxt.find(id)
begin
#txt.watch
total_complete_watch = if #txt.job_type == 'movie'
#txt.total_count
else
#txt.tracks.where(status:'complete').size
end
#txt.completed = total_completed_games
#txt.complete = (total_complete_games == #txt.total_count)
#txt.completed_at = Time.zone.now if #txt.complete
#txt.zipper if #txt.complete
#txt.save
FileUtils.rm_rf #txt.base_dir if #txt.complete
rescue StandardError => e
#How to find errors for each reference_id here
raise e
end
end
end
Is there any way to do. Thanks u very much.
I assume self.parameters are in your Model class instance. In that case, do as follows and you can reference them.
begin
#txt.watch
rescue StandardError
p #parameters # => self.parameters in the Model context
raise
end
Note:
As a rule of thumb, it is recommended to limit the scope of rescue as narrow as possible. Do not include statements which should not raise Exceptions in your main clause (such as, #txt.save and FileUtils.rm_rf in your case). Also, it is far better to limit the class of an exception; for example, rescue Encoding::CompatibilityError instead of EncodingError, or EncodingError instaed of StandardError, and so on. Or, an even better way is to define your own Exception class and raise it deliberately.

Ruby rescue multiple specific errors

In Rails i am trying to validate dates that are being imported through an excel document. it wont go through ActiveRecord so i cant use the Timeliness gem that i have in the system that i use to verify other dates.
So i wrote my own gem that verifies the format of a date, but there are some dates that get through that are not valid like 31/04/2013, if the date is in an incorrect format then it will raise a RuntimeError which i rescue and supply a error message. but in ruby:
Date.new(2013,4,31)
ยป ArgumentError: invalid date
So i would like to rescue either of them. i am just afraid that some ArgumentError will appear and it wont be this exact one. so i would like it to rescue only ArgumentError: invalid date, is this possible?
This is the excel date checker i wrote
def as_date
return nil if self.blank?
begin
date = DateDojo::DateSensei.date_format_validation(self)
if date.class == Date
return date
else
return false
end
rescue RuntimeError
:invalid_date_format_to_make_validations_cry_and_die_sad_face
rescue ArgumentError
:dates_that_wouldnt_exist_even_in_the_correct_format
end
end
You can target a specific error message like so:
begin
...
rescue ArgumentError => e
if e.message =~ /invalid date/
# Do something
else
puts e.message
end
end

Begin and Rescue block exception handling

I have little experience in rails exception handling. I have this snippet
def update
#game = Game.find(params[:id])
begin
params[:game][:tier] = eval(params[:game][:tier])
rescue
#game.errors.add(:tier, "Please make sure the correct format for tier, example [100, 1000, 10000]")
end
#.... more code
end
In case params[:game][:tier] = "[100,200]" everything is perfect.
In case of error case of ruby syntax like params[:game][:tier] = "[100,200] abc" it catch the error however the application just crush.
How can I handle exception with 'eval()' such that it won't crush the app? Why begin and rescue does not work in this case? Appreciate any help for ruby enlightenment thanks :)
What if params[:game][:tier] was "[100,200]; system('rm -rf /')"?
Since the incoming data is expected to be an array, I would not use eval but JSON.parse instead:
> JSON.parse("[100,200]")
=> [100, 200]
> JSON.parse("[100,200] abc")
JSON::ParserError: 746: unexpected token at 'abc'...
Then rescue from only a JSON::ParserError exception
rescue JSON::ParserError => e
This will also solve the rescue not catching the exception problem you're having.
duplicate of this
however you should rescue in this way
def update
#game = Game.find(params[:id])
begin
params[:game][:tier] = eval(params[:game][:tier])
rescue Exception => e
#game.errors.add(:tier, "Please make sure the correct format for tier, example [100, 1000, 10000]")
end
#.... more code
end
in order to make it work

rescuing from Mysql2::Error

I have a simple question. I have a join table which has an index that ensure that (col 1, col 2) is unique.
I am adding to that table using mysql2 gem and am trying to catch the Mysql2::Error if the attempt results in a duplicate key error. While I am getting the duplicate key error, my rescue body is not being executed.
begin
self.foo << bar
rescue Mysql2::Error
logger.debug("#{$!}")
end
I am receiving the following error upon executing self.foo << bar
Mysql2::Error: Duplicate entry '35455-6628' for key 'index_foos_bars_on_foo_id_and_bar_id': INSERT INTO foos_bars (foo_id, bar_id) VALUES (35455, 6628)
BUT my rescue statement is not being hit! The exception is not be successfully rescued from. What am I doing wrong? If I remove Mysql2::Error and rescue for everything, then it works. But that is bad practice - I just want to rescue from Mysql2::Error which in the event of a duplicate entry.
Thanks,
Mysql2::Error is wrapped in another exception class now. Change your code to:
begin
self.foo << bar
rescue Exception => e # only for debug purposes, don't rescue Exception in real code
logger.debug "#{e.class}"
end
...and you'll see the real exception class that you need to rescue.
Edit: It seems in this case it turned out to be ActiveRecord::RecordNotUnique

Resources