Exception not falling in models - ruby-on-rails

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.

Related

Escaping ensure in console/rake/rails

Given something like this
def infinite
puts Time.now
rescue => err
puts err.message
ensure
infinite
end
When you run this in console/rake and hit ctrl-c - nothing happens. How do you escape this with CTRL-C?
Use catch instead which is an alternative control flow.
catch executes its block. If throw is not called, the block executes normally, and catch returns the value of the last expression evaluated.
Ruby searches up its stack for a catch block whose tag has the same object_id as the throw (symbols are almost always used as the have the same object_id). When found, the block stops executing and returns val (or nil if no second argument was given to throw).
def infinate
catch(:done) do
begin
infinite
rescue SystemExit, Interrupt => e
puts e.message
throw :done
end
end
end
Using ensure with a condition like that is semantically wrong as the whole point of ensure is to run code that always should be run.
Using rescue to create an infinite loop via recursion seem overly complicated and could cause a SystemStackError later on.
Why not use an actual loop:
def infinite
loop do
begin
puts Time.now
rescue => err
puts err.message
end
end
end
With the above, Ctrl-C works just fine, because rescue without an explicit exception class will only handle StandardErrors.
I'm not sure if this is the proper solution but this worked for me:
def infinite
puts Time.now
rescue SystemExit, Interrupt
#skip_ensure = true
puts 'SystemExist/Interrupt'
rescue => err
puts err.message
ensure
infinite unless #skip_ensure
end

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.

Why doesn't calling next within a rescue block within a transaction within a loop work?

I have a loop like this:
# Iterate a list of items
req_wf_list.each do |req_wf|
# Begin a transaction
ReqWf.transaction do # ReqWf is an ActiveRecord model class
# Do some things
# ...
# 1. I want to be able to continue processing with the
# next iteration of the loop if there is an error here
# 2. I also want to rollback the transaction associated with
# this particular iteration if I encounter an error
begin
# Do something that might return an error
rescue
# Do some error processing
puts "Caught such and such error"
# Don't complete transaction (rollback),
# don't "do some more things",
# proceed to next item in req_wf_list
next
end
# Do some more things
# Shouldn't make it here if there is an error but I do indeed make it here
# ...
# End transaction
end
# End loop
end
Now, I would expect that calling "next" within the rescue block would cause the transaction associated with that particular iteration of the loop to rollback and for execution to resume at the top of the next iteration of the loop. Instead, execution appears to resume at the "Do some more things" line. It is as if the "next" statement is completely ignored. What am I missing?
Most likely that in this case next applies to transaction so you are in a nested loop situation.
This is an example of what can be done to solve the issue
req_wf_list.each do |req_wf|
catch :go_here do #:missingyear acts as a label
ReqWf.transaction do
throw :go_here unless something #break out of two loops
end
end #You end up here if :go_here is thrown
end
But in general, it is not a good practice to use next. You should be able to put a global begin .. rescue and have all the conditions inside of it, so that nothing else gets executed once you catch an error.
Update
I did some a small test and the behavior is as you expect it.
loop = [1,2,3]
loop.each do |value|
puts "value => #{value}"
ActiveRecord::Base.transaction do
puts "Start transaction"
begin
raise
rescue
puts "ActiveRecord::StatementInvalid"
next
end
puts "Should not get here!"
end
end
The output is the following:
value => 1
Start transaction
ActiveRecord::StatementInvalid
value => 2
Start transaction
ActiveRecord::StatementInvalid
value => 3
Start transaction
ActiveRecord::StatementInvalid
Is it possible that you had another error in your code before the next was being called ?
In any case, using the next statement is not the best option as I said before.

RSpec retry throw exception and then return value

I have a retry block
def my_method
app_instances = []
attempts = 0
begin
app_instances = fetch_and_rescan_app_instances(page_n, policy_id, policy_cpath)
rescue Exception
attempts += 1
retry unless attempts > 2
raise Exception
end
page_n += 1
end
where fetch_and_rescan_app_instances access the network so can throw an exception.
I want to write an rspec test that it throws an exception first time and doesn't throw an exception second time it gets called, so I can test if the second time it doesn't throw an exception, the my_method won't throw an exeption.
I know i can do stub(:fetch_and_rescan_app_instances).and_return(1,3) and first time it returns 1 and second time 3, but I don't know how to do throw an exception first time and return something second time.
You can calculate the return value in a block:
describe "my_method" do
before do
my_instance = ...
#times_called = 0
my_instance.stub(:fetch_and_rescan_app_instances).and_return do
#times_called += 1
raise Exception if #times_called == 1
end
end
it "raises exception first time method is called" do
my_instance.my_method().should raise_exception
end
it "does not raise an exception the second time method is called" do
begin
my_instance.my_method()
rescue Exception
end
my_instance.my_method().should_not raise_exception
end
end
Note that you should really not be rescuing from Exception, use something more specific. See: Why is it a bad style to `rescue Exception => e` in Ruby?
What you do is constrain the times the message should be received (receive counts), i.e. in your case you can
instance.stub(:fetch_and_rescan_app_instances).once.and_raise(RuntimeError, 'fail')
instance.stub(:fetch_and_rescan_app_instances).once.and_return('some return value')
Calling instance.fetch_and_rescan_app_instances first time will raise RuntimeError, and second time will return 'some return value'.
PS. Calling more than that will result in an error, you might consider using different receive count specification https://www.relishapp.com/rspec/rspec-mocks/docs/message-expectations/receive-counts
This has changed a little in RSpec3.x. It seems the best approach is to pass a block to the receive that defines this type of behaviour.
The following is from the docs suggesting how to create this type of transit failure:
(This errors every other time it is called... But is easy to adapt.)
RSpec.describe "An HTTP API client" do
it "can simulate transient network failures" do
client = double("MyHTTPClient")
call_count = 0
allow(client).to receive(:fetch_data) do
call_count += 1
call_count.odd? ? raise("timeout") : { :count => 15 }
end
expect { client.fetch_data }.to raise_error("timeout")
expect(client.fetch_data).to eq(:count => 15)
expect { client.fetch_data }.to raise_error("timeout")
expect(client.fetch_data).to eq(:count => 15)
end
end

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