Constructing an Exception with a backtrace - ruby

Is there really no constructor for Exception in ruby that takes a backtrace? I want to create an exception with backtrace information, but I don't see a way to do that. Right now I'm doing this:
exception = create_error ArgumentError, "There is an error here", caller
with the create_error method defined like this, i.e. construct the exception then set the backtrace in 2 steps:
def create_error(error_class, msg, bt)
e = error_class.new(msg)
e.set_backtrace(bt)
e
end
Note, I don't want to 'raise' in this case. Am I missing something?

Related

How do I prevent two exception stacks when catching and re-raising Ruby exceptions?

In a larger project, I am catching specific exceptions, adding some details to the message and then re-raising the same exception. Here is the basic idea of what I'm doing.
#!/usr/bin/env ruby
begin
puts 12 / 0
rescue ZeroDivisionError => e
raise e.exception("new message #{e.message}")
end
When I execute temp.rb from the command line, two exceptions are printed:
% ./temp.rb
./temp.rb:4:in `/': new message divided by 0 (ZeroDivisionError)
from ./temp.rb:4:in `<main>'
./temp.rb:4:in `/': divided by 0 (ZeroDivisionError)
from ./temp.rb:4:in `<main>'
I'm assuming that there is a stack or a list of exceptions somewhere and the exit process of Ruby prints out the entire list or stack of exceptions. I can see great benefits to this. But in my case, I'd like to get rid of the original exception from the list or stack. Is there a way to do this?
The "second" (original) exception is the cause of the rescued exception which is references by your new exception as it is created with Exception#exception.
Starting with Ruby 3.1, irb prints the details of the cause in addition to the actual exception to aid in debugging. Previous Ruby versions have ignored the cause here.
Thus, you can use other means to handle your exceptions rather than using Ruby's default handler of printing the details of an otherwise unhandled exception, e.g. by adding an explicit exception handler. Alternatively, you can also explicitly set the cause as you are creating your new exception:
begin
puts 12 / 0
rescue ZeroDivisionError => e
raise e.class, "new message #{e.message}", cause: nil
end
May be something like this to extend Exception with custom method
Exception.class_eval do
def add_message(msg)
mod =
Module.new do
define_method :to_s do
"#{super()} #{msg}"
end
end
extend mod
end
end
And then
begin
puts 12 / 0
rescue ZeroDivisionError => e
raise e.add_message("(even don't try it)")
end

Testing for exceptions being raised

I'm new to ruby. Trying to write a test that passes when an exception is raised, for example:
def network_data_unavailable
assert_raise StandardError, NetworkSim.sim(totalUse, 3, "five")
end
Those inputs will cause a StandardError to be raised but my test still fails. Any help on what I'm missing here?
First of all, I think the method you're looking for is assert_raises, not assert_raise. Then you need to call it correctly by giving it a block:
#assert_raises(*exp) ⇒ Object
Fails unless the block raises one of exp. Returns the exception matched so you can check the message, attributes, etc.
[...]
assert_raises(CustomError) { method_with_custom_error }
You want to say:
assert_raises StandardError do
NetworkSim.sim(totalUse, 3, "five")
end
so that assert_raises can call the block after it has set up the exception handling. They way you're calling it, NetworkSim.sim will be called while building the argument list to assert_raises and the exception will be raised before assert_raises can do anything to catch it.

Catch exception in ruby

I have a function in ruby
def mark_completed
var_a = self.a
self.check_something
end
The other function is
def check_something(query = nil)
raise 'Some_exception' if condition_a_satisfy?
return true
end
See , the function check_something is raising exception , What I want is : - "To catch the exception and return false somehow"
How can i do it?
Note : - I cannot change my check_something function.
Rescue RuntimeError in #mark_completed
When you call Kernel#raise with a string, you're actually raising the default exception, which is RuntimeError. You should catch that explicitly to avoid catching other exceptions that descend from StandardError unintentionally, or courting disaster by rescuing Exception which is almost never a good idea. For example:
def mark_completed
self.check_something
rescue RuntimeError
false
end
This will solve the question you asked in a controlled way, but deliberately side-steps the question of whether raising exceptions is the right thing for the code to do in the first place, or whether other refactorings are possible.
This does involve changing the method, but this could still be useful to know.
Hi, I think you’re o looking for a try and catch statement in ruby. This will run Code if there is no error or exception, and if there is it will run specified Code.
For an example:
def check_something(query = nil)
begin
# to execute with no exceptions
rescue Exception
# to execute when you hit an exception
ensure
# to execute always
end

How to raise multiple exceptions in ruby

In ruby you can resque, multiple excetions like this:
begin
...
rescue Exception1, Exception2
...
rescue Exception1
...
rescue Exception2
...
end
But I do not know how to raise multiple exceptions:
1] pry(main)> ? raise
From: eval.c (C Method):
Owner: Kernel
Visibility: private
Signature: raise(*arg1)
Number of lines: 13
With no arguments, raises the exception in $! or raises
a RuntimeError if $! is nil.
With a single String argument, raises a
RuntimeError with the string as a message. Otherwise,
the first parameter should be the name of an Exception
class (or an object that returns an Exception object when sent
an exception message). The optional second parameter sets the
message associated with the exception, and the third parameter is an
array of callback information. Exceptions are caught by the
rescue clause of begin...end blocks.
raise "Failed to create socket"
raise ArgumentError, "No parameters", caller
Or I cannot figure this in the raise doc
The purpouse of this is that I have an API call, this call tries to create an object in the API.
Then the APi could return all the problems in the object, from Activerecord Validators, So I can get thinks like that such as:
422 "The Item is not even","The Item needs too be bigger than 100"
422 "The Item is not even"
200 OK "Item created"
500 "I'm a tee pot
The idea is to capture this and raise exceptions like this
Begin
API CALL
rescue ItemnotEven,ItemnotBigger
do something
retry if
rescue ItemnotEven
retry if
rescue Connection error
Log cannot connect
end
Exceptions shouldn't be used for validations. Basically you shouldn't traverse the stack for validations in general.
What you fundamentally are doing is:
X is top level and can handle everything. X calls Y. Y calls Z. Z performs validations and does something after that, raising an exception if validation failed.
What you should be doing is:
X calls Y. Y calls V and X. V performs validations and returns result based on if the thing was valid. Y doesn't get to call X if V said the thing was invalid. Y propagates the invalidness or the successful result to X. X does what it would have done with if/else on the validity, rather than rescue.
But lets say you really want to do it. You should use throw/catch instead:
def validate_date(date)
errors = []
errors << 'Improper format' unless date.match?(/^\d{2}-\d{2}-\d{4}$/)
errors << 'Invalid day' unless date.match?(/^[0-3]\d/)
errors << 'Invalid month' unless date.match?(/-[12]\d-/)
errors << 'Invalid year' unless date.match?(/[12][90]\d{2}$/)
throw(:validation, errors) unless errors.empty?
end
def invoke_validation_and_do_stuff(date)
validate_date(date)
puts "I won't be called unless validation is successful for #{date}"
end
def meaningless_nesting(date)
invoke_validation_and_do_stuff(date)
end
def more_meaningless_nesting(date)
meaningless_nesting(date)
end
def top_level(date)
validation_errors = catch(:validation) do
more_meaningless_nesting(date)
nil
end
if validation_errors
puts validation_errors
else
puts 'Execution successful without errors'
end
end
top_level '20-10-2012'
# I won't be called unless validation is successful for 20-10-2012
# Execution successful without errors
top_level '55-50-2012'
# Invalid day
# Invalid month
I don't think you can raise multiple exceptions, it'll raise the first exception it finds and will be caught by the innermost rescue statement if multiple exist or depends on the type of exception you raise and type of rescue
No such concept exists, in any language that I am aware of.
You can raise a single exception consecutively, but not raising multiple exceptions at one time, and even if working on multiple threads to raise "simultaneously", it is still a single exception being raised on different control flows.
When an exception is raised, control flow goes to that exception. You have two options: do something about it, or crash. There is no third option, and no separate control flow popped up and is continuing on until this exception is dealt with accordingly.
If you want to see multiple failures as you stated in a comment, then you will still be doing it one at a time, as is how they would be raised. Exception gets raised, you inspect, log, do whatever, suppress it, and see the next if one gets raised for something else.
If you are asking how multiple unhandled exceptions can be raised, then it really doesn't make sense. It is akin to asking how to be in two places at once.

Create full Exception object in Ruby

I want a reference to an Exception object that includes all the normal information - message, backtrace, etc.
Exception has a new method, but it doesn't populate the backtrace:
exception = Exception.new("my message")
exception.backtrace
#=> nil
The docs say there is a set_backtrace method, but you have to supply the backtrace yourself (it doesn't just use the current stack).
I can work around this by raising and rescuing:
exception = begin
raise Exception, "my message"
rescue Exception => e
e
end
exception.backtrace
#=> ["(irb):4:in 'irb_binding'", ...]
but that doesn't feel very clean.
Is there a simple way to create a fully populated Exception object?
The current stack trace can be obtained from Kernel#caller, so you could do
exception = Exception.new("my message")
exception.set_backtrace(caller)
I guess creating an Exception object without raising it immediately basically only makes sense when you are planning to pass it around. Therefore it makes sense that the backtrace must be set manually.
I believe what you want is Kernel#caller_locations:
def a; b; end
def b; c; end
def c
caller_locations
end
a.each { |trace| p trace }
# =>
# "test.rb:2:in `b'"
# "test.rb:1:in `a'"
# "test.rb:8:in `<main>'"
The difference with Kernel#caller is caller delivers String objects. caller_locations gives Thread::Backtrace::Location objects. I don't see any other differences, so caller_locations seems a bit more versatile to me -- having methods like lineno, path, etc which might come in handy.

Resources