Can't get stacktrace with `eval` when there is an error in the expression - ruby

I'm using eval on large blocks of code with this rescue block:
rescue => e
logger.error e.message
e.backtrace.each { |line| logger.error line }
end
The rescue block does not provide line number, etc. for the exception. I just get this:
undefined method `+' for nil:NilClass
which is not very helpful when there is a lot of code to evaluate. I tried various things such as:
eval(exp, binding, __FILE__, __LINE__)
but they do not provide any additional information on the line number of where the error is.

May be this can help. This answer is sort of based on details from this article. Assuming that you have expression to be evaluated in exp variable, I adjust the line number value passed as 3rd parameter to String#module_eval.
begin
exp = <<EOL
a = nil
a + 10
EOL
String.module_eval(exp,__FILE__, __LINE__ - 1 - exp.split("\n").size )
rescue Exception => e
puts e
puts e.backtrace
end
The output of above program:
undefined method `+' for nil:NilClass
E:/hello.rb:4:in `<main>'
E:/hello.rb:6:in `module_eval'
E:/hello.rb:6:in `<main>'
[Finished in 0.2s]

Related

undefined method `text' for nil:NilClass (NoMethodError) for and action that was excuted

I have the following code:
def find_status(arg)
10.times do
table = table_element(:css => 'css.path')
break if table.visible?
end
table = table_element(:css => 'css.path')
if table.visible?
table.each do |row|
STDOUT.puts row[1].text
match = /^#{arg}\n(String \S+) at .+/.match(row[1].text)
return match[1] if match
end
end
return "status unknown"
end
Now the problem is that I'm getting the following error:
undefined method `text' for nil:NilClass (NoMethodError)
The weird part is that it prints exactly what I wanted it to print and points out that the error is on the "STDOUT" row.
So to sum it up it's executing the command but says row is a nil value.
Help would be appreciated.
If I understand this correctly, just test for nil first, then use the row text if a row exists.
STDOUT.puts row[1] ? row[1].text : 'nil'

Catching a lot of errors and putting all the errors being caught into a constant

Is there a way, within a rescue clause, to put all the errors into an array and call them from there, if the error is in the array?
For example:
FATAL_ERRORS = %w(Mechanize::ResponseCodeError RestClient::ServiceUnavailable OpenSSL::SSL::SSLError RestClient::BadGateway)
begin
# Do some cool stuff
rescue FATAL_ERRORS => e
puts "Exiting #{e}"
What I've tried:
I've tried grabbing the error from the current thread:
FATAL_ERRORS = Thread.current[:errors] ||= %w(Mechanize::ResponseCodeError RestClient::ServiceUnavailable OpenSSL::SSL::SSLError RestClient::BadGateway)
begin
# Do some cool stuff
rescue FATAL_ERRORS => e
puts "Exiting #{e}"
Also I've tried the splat operator:
FATAL_ERRORS = %w(Mechanize::ResponseCodeError RestClient::ServiceUnavailable OpenSSL::SSL::SSLError RestClient::BadGateway)
begin
# Do some cool stuff
rescue *FATAL_ERRORS => e
puts "Exiting #{e}"
Both the splat and the thread produce the following exception:
rescue in <main>': class or module required for rescue clause (TypeError)
How can I successfully rescue multiple errors without putting them all on the rescue line and making it look bad?
splat does work. Problem is in the way you are making FATAL_ERRORS constant. With %w notation, it converts value as string:
%w(Mechanize::ResponseCodeError)
=> ["Mechanize::ResponseCodeError"] # Note the string value instead of class constant.
Try
FATAL_ERRORS = [Mechanize::ResponseCodeError, RestClient::ServiceUnavailable, OpenSSL::SSL::SSLError, RestClient::BadGateway]

Error handling for Ruby Kernel method not working

I have found myself in need to execute a string. Current method is to use the Kernel#eval() method. Everything is working fine, but my error handling isn't working. For example, a missing closing quotation mark will completely kill and exit the program.
Here's an excerpt. Any idea why I can't catch the error?
def process(str)
print "\n"
eval(str)
rescue => e
puts e
end
>> process('"')
console.rb:90:in `eval': (eval):1: unterminated string meets end of file (SyntaxError)
from console.rb:90:in `process'
from console.rb:81:in `bouncer'
from console.rb:14:in `block in prompt'
from console.rb:11:in `loop'
from console.rb:11:in `prompt'
from console.rb:97:in `<main>'
According to the documentation:
A rescue clause without an explicit Exception class will rescue all StandardErrors (and only those).
SyntaxError is not a StandardError. To catch it, you have to be explicit, e.g.:
def process(str)
print "\n"
eval(str)
rescue Exception => e
puts e
end
process('"')
Output:
(eval):1: unterminated string meets end of file

Variables not recognized within Rescue in Ruby

I have the following code:
rescue Timeout::Error, StandardError => e
puts "Caught exception: #{e.message}".red
log.puts("#{e.backtrace}")
email_ids_all.each do |email_delete|
call= "/api/v2/emails/#{email_delete}/"
......
Before this rescue piece I have defined log and email_ids_all. However, neither of these are recognized within the ruby script. If i do this:
rescue Timeout::Error, StandardError => e
File.open(rescuelogfile, 'w') do |log| #setup log to write response codes.
puts "Caught exception: #{e.message}".red
log.puts("#{e.backtrace}")
email_ids_all.each do |email_delete|
call= "/api/v2/emails/#{email_delete}/"
....
log works fine, which makes sense. It would take a lot of writing to redefine the email_ids_all array and other variables contained inside my rescue block.
Is there anyway to allow variables to be recognized inside the rescue? Basically my code is laid out like this:
begin
#some code
rescue
#above code
end
I am using ruby 1.9.3.
EDIT----
log starts right after my begin statement :
begin
File.open(logfile, 'w') do |log| #setup log to write response codes.
log.puts works throughout the entire code except when an error is thrown, and then it runs the rescue script where log is not available.
The same goes for email_ids_all. There is an API call that generates about 10,000 emails and each of them is added to the array email_ids_all. The script is receiving an error about halfway through generating these emails, and so I need the rescue script to delete all email ids in the email_ids_all array. But for whatever reason, I get the following error:
FS_Test_Env.rb:762:in `block in <main>': undefined local variable or method `email_ids_all' for main:Object (NameError)
from FS_Test_Env.rb:759:in `open'
from FS_Test_Env.rb:759:in `rescue in <main>'
from FS_Test_Env.rb:7:in `<main>'
Any thoughts?
The way you put it, it should work, for example:
irb(main):001:0> begin
irb(main):002:1* x = 1
irb(main):003:1> x / 0
irb(main):004:1> rescue Exception => e
irb(main):005:1> p x
irb(main):006:1> end
1
=> 1
So it looks like the exception is thrown before your variables are defined.
The scope of the block parameter log is limited to that block. This is the whole point of the open with block.
Maybe you want to do:
begin
log = File.open('logfile', 'w')
...
rescue
...
ensure
log.close
end
Note that this does not cover errors when opening the logfile.
Regarding email_ids_all, I guess (!) you have the exception in a statement like:
email_ids_all = ... a long and complex calculation which raises an exception
If yes, the problem is that the assignment happens only after the whole right-hand side is calculated. The var email_ids_all is not yet created when the exception happens.
In order to access the elements created before the exception, you have to keep track of them, e.g.
begin
email_ids = []
10000.times do
email_ids << ... # create element eventually raising an exception
end
rescue
... # treat the already created elements
end

How to suppress and not to print the backtrace of the exception on the terminal in ruby using Thor?

Following is my method that might raise the exception.
Its a method of the CLI too that I am building.
Whenever the exception occurs, I want to catch that and just print my custom message on the terminal.
# variation 1
def self.validate(yaml_path)
begin
....
....
rescue
puts "Error"
end
end
# variation 2
def self.validate(yaml_path)
begin
....
....
rescue Exceptino => e
puts "Error: #{e.message}"
end
end
But the backtrace gets printed on the terminal.
How to avoid the backtrace to get printed?
± ../../bin/cf site create
ruby-1.8.7-p352
Error during processing: syntax error on line 52, col 10: ` - label: Price'
/Users/millisami/.rvm/rubies/ruby-1.8.7-p352/lib/ruby/1.8/yaml.rb:133:in `load': syntax error on line 52, col 10: ` - label: Price' (ArgumentError)
.... backtrace .....
.............
The answer was to rescue it on the executable file at bin/<exe>.
Thanks for suggesting
begin
Cf::CLI.start
rescue Psych::SyntaxError
$stderr.puts "\n\tError during processing: #{$!.message}\n\n"
end
The following code doesn't output the backtrace.
class CLS
def hi
begin
raise "X"
rescue
puts $!.message
end
end
end
CLS.new.hi
Have you checked to see if there is another point in the stack where another method is rescuing the exception, outputting the stack trace and then re-raising the exception?
The reason you're not rescuing the exception is because Psych::SyntaxError is not descended from StandardError, so a simple rescue won't catch it. You need to specify a descendant of Psych::SyntaxError:
>> require 'psych'
=> true
>> begin; raise Psych::SyntaxError; rescue; puts "GOT IT"; end
# Psych::SyntaxError: Psych::SyntaxError
# from (irb):8
# from /Users/donovan/.rvm/rubies/ruby-1.9.2-p180/bin/irb:16:in `<main>'
>> Psych::SyntaxError.ancestors
=> [Psych::SyntaxError, SyntaxError, ScriptError, Exception, Object, PP::ObjectMixin, Kernel, BasicObject]
>> begin; raise Psych::SyntaxError; rescue Exception; puts "GOT IT"; end
GOT IT
Notice that in my example rescue Exception does catch it. You should generally be as specific as you can when rescuing unless you really need to rescue all Exceptions. Be aware that suppressing backtraces is good when the exception is something you expect, but if you don't expect it in general it makes debugging much harder.

Resources