Ruby Programming: 'gets' execution expired Timeout.timeout error - ruby

I have constructed a very simple Ruby program whilst exploring the Timeoutmodule, using this site as a guide.
print "\nEnter Input:"
require 'timeout'
Timeout::timeout(5) do
input = gets.chomp
end
print "\nEXITING\n"
When I run the script, it prompts the user for input, however after 5 seconds, instead of simply printing "EXITING" and promptly terminating as it logically should, it instead crashes, returning the error:
'gets': execution expired (Timeout::Error)
I am wondering why this is, and how this error could be avoided whilst the script waits for user input for 5 seconds before terminating regardless.
Thanks in advance.

Please see the documentation for the Module Timeout
It clearly states that it performs an operation in a block, raising an error if it takes longer than sec seconds to complete.
To get your desired output, you can use a rescue block to rescue the Timeout::Error and then show your EXITING message:
require 'timeout'
print "\nEnter Input:"
begin
Timeout::timeout(5) do
input = gets.chomp
end
rescue Timeout::Error => e
print "\nEXITING\n"
end

Related

Escape Mechinze Error in Ruby Scraping

I am getting the following server response error while trying to scrape SERP results:
/Users/*********/.rvm/gems/ruby-2.3.0/gems/mechanize-2.7.5/lib/mechanize/http/agent.rb:323:in `fetch': 503 => Net::HTTPServiceUnavailable for http://******.*****.com/sorry/index?continue=http://www.********.com/search%3Fq%3D<term1>%2B<term2> -- unhandled response (Mechanize::ResponseCodeError)
I am trying to figure out how to escape the error / exception, so that the program will continue to run instead of automatically exiting.
Like anything in Ruby it probably boils down to rescue and recover:
loop do
begin
Mechanize.do_stuff!
# Success!
break
rescue Mechanize::ResponseCodeError
# Server-side failure, so let's try again after a quick break
sleep(10)
end
end
Note the sleep(10) is there to avoid slamming the server furiously and making it malfunction even harder.

How does `rescue $!` work?

I know that the global variable $! holds the most recent exception object, but I am having confusion with the syntax below. Can anyone help me understand the following syntax?
rescue $!
This construct prevents exception from stopping your program and bubbling up the stack trace. It also returns that exception as a value, which can be useful.
a = get_me_data rescue $!
After this line, a will hold either requested data or an exception. You can then analyze that exception and act accordingly.
def get_me_data
raise 'No data for you'
end
a = get_me_data rescue $!
puts "Execution carries on"
p a
# >> Execution carries on
# >> #<RuntimeError: No data for you>
More realistic example
lines = File.readlines(filename) rescue $!
You either get the lines or the error (if file doesn't exist, you don't have permissions, etc). In any case, execution doesn't stop.

Equivalent to Perl's END block in Ruby

Is there a Perl equivalent END block in Ruby? In Perl, if I specify an END block, the code in that block will get executed no matter where the program bails out. It is great functionality for closing open file handles. Does Ruby support similar functionality? I tried Ruby's "END{}" block but that doesnt seem to get called if I had an exit in the code due to an error.
Thanks!
Use at_exit, which will run regardless of whether an exception was raised or not:
at_exit { puts 'exited!' }
raise
prints "exited" as expected.
You should only consider this if you cannot use an ensure, as at_exit causes logic to reside far away from where the actual exit occurs.
Yes. A block may have an 'ensure' clause. Here's an example:
begin
# This will cause a divide by zero exception
puts 3 / 0
rescue Exception => e
puts "An error occurred: #{e}"
ensure
puts "I get run anyway"
end
Running this produces:
An error occurred: divided by 0
I get run anyway

Timeout issue making system call in Ruby on Windows XP

The following code
require 'timeout'
begin
timeout(20) do # Line 4
result = `hostname`
end # Line 6
rescue Timeout::Error
puts "Timeout"
exit
end
puts "Result:" + result # Line 12
throws the error
issue.rb:12:in <main>': undefined local variable or methodresult' for
main:Object (NameError)
but if I comment out the timeout element (lines 4 and 6), it works fine. I have tried using IO.popen, IO.select etc but none of this helps. I've used this timeout technique in many other areas and it worked fine.
It doesn't appear to be related to the timeout value as I have experimented with much larger and smaller values.
Am using Ruby 1.92 on Windows XP. Any help much appreciated.
p.s. My original problem was not running "hostname" but a more complex SQL Server batch job. As a bonus point, would a long running system task that exceeded the timeout be automatically killed? I have read lots of posts about the timeout library not honouring timeouts when busy running system tasks?
The result variable is being defined inside the timeout block, so it's not visible in the outer scope. You should initialize it before:
result = nil
begin
timeout(20) do # Line 4
result = `hostname`
end # Line 6
rescue Timeout::Error
...

Ruby, windows, active_record, and Control-C

What is active_record doing to the signal processes under windows (I don't see this with the same versions on the mac) that causes it to behave so strangely? For instance:
require 'rubygems'
trap("INT"){puts "interrupted"}
puts __LINE__
sleep 5
require 'active_record'
trap("INT"){puts "interrupted again"}
puts __LINE__
sleep 5
When I run the above code (ruby 1.8.6, gem 1.3.1, activerecord 2.2.2,) I can hit ^C as many times as I like during the first sleep, but the first interrupt after the require of activerecord causes the script to terminate. In the above case, the trap still executes, it only fails to allow the program to continue. Usually.
Removing the second call to trap does not have any effect upon the behaviors.
The real annoyance is that in some conditions, the trap fails to execute at all. Considering that the whole point of doing this is to get my code to clean up after itself (remove its footprint in the database so the next guy sees a sane state,) this is a real problem. For instance:
require 'rubygems'
require 'active_record'
trap("INT"){puts "interrupted"}
puts __LINE__
gets
Pressing ^C after seeing the puts will not execute the trap at all.
I only see this problem after requiring active_record. Is there a workaround? I'd be curious to know if this is a bug or if there is an explanation of some sort. As I said, I have no issue with this on the mac - repeated ^Cs result in multiple executions of the trap proc.
thanks...
Considering that the whole point of doing this is to get my code to clean up after itself (remove its footprint in the database ...
Have you considered just using a database transaction? It seems like it would be a much easier way to solve the problem.
I saw a different pattern when trying to duplicate this problem:
puts "start"
trap("INT") { puts "interrupted" }
sleep 5
puts "end"
On Ubuntu (Ruby 1.8.6) this produces
start
interrupted
interrupted
(etc)
interrupted
end
So "interrupted" prints each time Crtl-C is pressed, until the 5 seconds are up. Under Windows (also Ruby 1.8.6), this produces:
start
interrupted
end
i.e. it prints "interrupted" once and then exits.
So it appears that while handling SIGINT Ruby exits the sleep routine and continues on to the next statement. My guess (hand-waving) is that this is somehow due to Ruby using green threads instead of native threads on Windows. Any experts please chime in here.
You could emulate the Unix-y behavior by restarting sleep in the handler:
puts "start"
trap("INT") do
puts "interrupted"
sleep 5
end
sleep 5
puts "end"
Unfortunately this resets the timer each time SIGINT is trapped, so it needs some hacking:
$interval = 5
def go_to_sleep(secs)
$started = Time.now
sleep secs
end
trap("INT") do
puts "interrupted"
time_to_sleep = [0,$interval - (Time.now - $started)].max
if time_to_sleep > 0
sleep time_to_sleep
end
end
puts "one"
go_to_sleep($interval)
puts "two"
go_to_sleep($interval)
puts "three"
go_to_sleep($interval)

Resources