Rails - Not reaching rescue block inside a do-while loop - ruby-on-rails

I have a while do loop in rails as
session['SPEAKERS'].map do |speaker|
begin
Component::AgendaSpeaker.create_with(
name: [speaker['FIRST_NAME'], speaker['LAST_NAME']].join(' '),
job_title: speaker['TITLE'],
remote_file_url: speaker['PHOTO_URL'],
description: speaker['EXPERTISE']
).find_or_create_by(
component_id: component_id(:agenda_speakers),
weg_id: speaker['SPEAKER_ID']
)
rescue
p "Rescue reached"
ensure
p "Ensure reached"
end
end
For each execution of the loop, I try to create and save a speaker and sometimes there raises an exception in the active_record (due to non-availability of the image file or 403 forbidden exception etc).
I would want to catch that in my rescue block and process it further. However, the exception is caught and rescued elsewhere (within the active_record) which throws an exception in my console directly, and hence the code won't reach my 'rescue' block at all. However, my code reaches 'ensure' block.
How do I change my code to reach my 'rescue' block?

You're looking for find_or_create_by!
https://apidock.com/rails/ActiveRecord/Relation/find_or_create_by%21
Lots of active record methods will fail simply by returning false (save, update, create etc..), but will throw an error when an exclamation mark gets added at the end

Related

Tarantool blocks for operations, if error occurs inside box.begin()...box.commit() block

If error occurs inside box.begin()...box.commit() block, Tarantool box blocks for operations untill you manually execute box.commit()
For example, I provide incorrect data without one of fields to my function:
replace = function(space, data)
local id = data[1][1]
box.begin()
my_module.delete(space, id)
local response = my_module.insert(space, data)
box.commit()
return response
end
Inside of my_module.insert() it will encount an error, so execution of replace() will be aborted before execution of box.commit().
Later, if i call replace() again with another data, it won`t be able to execute box.begin() because of previously open transaction.
pcall(my_module.insert()) wasn`t able to catch an error.
There's a handy function for it - box.atomic
I suggest the following implementation:
replace = function(space, data)
return box.atomic(function()
local id = data[1][1]
my_module.delete(space, id)
return my_module.insert(space, data)
end)
end
pcall(my_module.insert()) wasn't able to catch an error.
pcall catches all errors. The error isn't caught probably because it occurs earlier on the delete operation.
By the way, Tarantool has inline operations for replace:
https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space_index/#box-space-replace

How can my block/yield pass a changing variable?

I'm writing the following module to capture SIGTERM that gets occasionally sent to my Delayed Job workers, and sets a variable called term_now that lets my job gracefully terminate itself before it's complete.
The following code in my module works perfect if I put it inline in my job, but I need it for several jobs and when I put it in a module it doesn't work.
I assume it's not working because it only passes term_now one time (when it's false), and even when it returns true it doesn't pass it again, therefore it never stops the job.
module StopJobGracefully
def self.execute(&block)
begin
term_now = false
old_term_handler = trap('TERM') do
term_now = true
old_term_handler.call
end
yield(term_now)
ensure
trap('TERM', old_term_handler)
end
end
end
Here's the working inline code how it's normally used (this is the code I'm trying to convert to a module):
class SMSRentDueSoonJob
def perform
begin
term_now = false
old_term_handler = trap('TERM') do
term_now = true
old_term_handler.call
end
User.find_in_batches(batch_size: 1000) do
if term_now
raise 'Gracefully terminating job early...'
end
# do lots of complicated work here
end
ensure
trap('TERM', old_term_handler)
end
end
end
you basically answered it yourself. in the example code you provided, term_now will only become true when the trap snapped before yield is called.
what you need to do is provide a mechanism that periodically fetches the information, so that you can check within the runs of ie find_in_batches.
so instead of yielding the result, your module should have a term_now method that might return an instance variable #term_now.

Ada - Task termination error - "Statement expected"

I have the following task
task body auto is
begin
Put_Line( licencepalte.all & " set off.");
delay traveltime.all;
Put_Line( licencepalte.all & " arrived.");
loop
select
indicator.passthrough;
terminate; -- error for this line: 'statement expected'
or
delay 0.2;
Put_Line( licencepalte.all & " is waiting.");
end select;
end loop;
end auto;
where we represent an indicator light and some cars (auto ~ automobiles) with tasks and protecteds. My main issue is, that I don't know, how to terminate, in case the indicator accepts the entry of the auto. You can see what I'm currently trying to do, and it pops up an error (see inline). How do I stop the task once the entry gets accepted? Why does this terminate not work? Thank you!
terminate is not an "action" that you perform. That is, you can't use a terminate statement anywhere you choose in the task body to terminate the task. The way to terminate a task is for the execution to reach the end that ends the body; in your case, exit to exit the loop works, as in Jacob's answer.
The purpose of or terminate is tell the program that a task is eligible for termination (I don't know if there's a better technical term for this). Suppose your task looks like:
task body Task_Type_1 is
begin
loop
select
accept Entry_1(...parameters...) do
-- something
end Entry_1;
or
accept Entry_2(...parameters...) do
-- something
end Entry_2;
end select;
end loop;
end Task_Type_1;
If the "something" code of the accept statements never exits the loop, the task will never terminate. This means that other constructs enclosing the task can never terminate. For example:
procedure Proc is
T1 : Task_Type_1;
begin
-- do some stuff
-- now we're at the end, and we have to wait for T1 to complete
end Proc;
The procedure creates a task of type Task_Type_1 and starts it. Then the body of the procedure is executed. When end Proc; is reached, the procedure doesn't terminate immediately, because it has to wait until the task finishes its job. But the way the task is written, the task will never complete. Therefore Proc will never return, and the program will probably deadlock.
or terminate is how to say that the task could terminate:
task body Task_Type_1 is
begin
loop
select
accept Entry_1(...parameters...) do
-- something
end Entry_1;
or
accept Entry_2(...parameters...) do
-- something
end Entry_2;
or
terminate;
end select;
end loop;
end Task_Type_1;
In this small example, where we have a procedure that just creates this one task, or terminate means: if this task reaches a point where it's blocked in the select because there aren't any entry calls waiting, and if Proc has reached the end of its code, then we terminate the task. The task body exits, any finalization that needs to be done is done, and then Proc can complete.
or terminate can be used only in a "selective accept". If you say select Some_Other_Task.Entry_2(...); so that it blocks until the other task's entry is available, you can't use or terminate in that kind of select.
In a more complex case, a procedure could create two or more tasks. When that procedure reaches its end statement, it won't return until (roughly speaking) all the tasks it creates are completed or all of the tasks that haven't completed are blocked on select statements that have or terminate clauses. If the latter happens, then all of those tasks complete and then the procedure can return.
The rule about "terminate alternatives" is in RM 9.3(6). It speaks in terms of depending on a master; in the example I showed above, Proc is the master.
If I understand your question correctly, an exit would do nicely instead of terminate.

How to unit test a method for timeout exception in ruby

I want to test a method which makes a call to a service, if the call to that service times out I am displaying a negative feedback to user. How to unittest the timeout use case ??
My method looks like:
def method
x = callservice()
if x[:value]
display_positve_feedback("positive")
else
display_negative_feedback("negative")
end
rescue Timeout::Error => e
display_negative_feedback("Timeout, please wait for 5 mins and check again")
end
end
I have mocked callservice but how to I make that service Timeout to check the timeout use case???
You don't use a return code. If you add it, you could test for the method result.
Example:
def method
begin
x = callservice()
if x[:value]
display_positve_feedback("positive")
return true
else
display_negative_feedback("negative")
return false
end
rescue Timeout::Error => e
display_negative_feedback("Timeout, please wait for 5 mins and check again")
return nil
end
raise "This should never happen"
end
Now you can test on true, falseor nil.
Suppressing exceptions is usually not a good practice that I would recommend to anyone.
When you catch (rescue) the timeout exception, re-raise it (or your own application-specific one) in the rescue code and have the caller (your test) check that an exception is thrown for the timeout.
You could use assert_raise or expect raise_error, for example, depending on what testing framework you use.
Using an exception, the calling code only needs to know about a potential exception rather than having to check for specific return values, which makes the code simpler and more clear.
Stub the callservice method to raise a Timeout::Error. How you'll do that depends on what testing framework you're using. For example, in RSpec it might look something like this:
my_obj.stub(:callservice) { raise Timeout::Error }
expect(my_obj).to receive(:display_negative_feedback)
.with("Timeout, please wait for 5 mins and check again")
my_obj.method

A Lua iterator that fails silently?

I have a very simple problem with a simple iterator.
Let's say that I design a function, files(), that iterates over all the files in a folder:
for file in files("/path/to/folder") do
print(file)
end
Now, this seems perfect, but there's a problem here: What if the the folder doesn't exist, or we don't have read permission to it?
How would we indicate such error?
One solution would be to have files() return nil, "no read permission" in this case. We'd then be able to wrap the call to files() inside assert():
for file in assert(files("/path/to/folder")) do
print(file)
end
This seemingly solves the problem. But this forces our users to always use assert(). What if the user doesn't care about errors? For this kind of users we'd want our files() to behave as if the folder is empty. But Lua --in case files() indicates error-- would try to call the returned nil and this will result in an error ("attempt to call a nil value").
So,
How can we design an iterator, files(), that would cater to both users that care about errors and users that don't?
If it's not possible, what alternative would you suggest?
First: Instead of returning nil + error message consider raising an error in the files function (using error). This way you can't forget the assert call, and you won't get the confusing "attempt to call a nil value" error.
You could pass an extra boolean parameter to files when you don't want to raise errors -- you should return an empty function (function() end) instead of calling error in this case.
A more general approach is the following:
-- an iterator that immediately stops a for loop
local function dummy_iter() end
-- catch errors and skip for loop in that case
function iterpcall( g, ... )
local ok, f, st, var = pcall( g, ... )
if ok then
return f, st, var
else
return dummy_iter
end
end
for file in iterpcall( files, "/path/to/folder" ) do
print( file )
for line in iterpcall( io.lines, file ) do -- works for other iterators as well
print( line )
end
end
The implementation of iterpcall above only handles errors raised in the iterator generator (files or io.lines), not in the iterator function (f) itself. You would have to wrap f in a closure with a pcall to do that.
There also question what you whant to do if you get error in the middle of iteration (e.g. access deny for subfolder with recurcive iteration). In this case assert does not help.
In this case I create 2 variant of iterators (inner and outer).
-- raise error
for file in files(...) do ... end
-- return error
files(...,function(file) ... end)
Or just create 2 different iterators.

Resources