Can not catch exception in ruby - ruby

class Collector
class ContentNotFound < Exception
end
class DuplicateContent < Exception
end
end
begin
raise Collector::ContentNotFound.new
rescue
puts "catch"
end
When I run the script I don't get "catch" message I see error:
lib/collector/exception.rb:10:in `<main>': Collector::ContentNotFound (Collector::ContentNotFound)
Why? How Can I catch my exceptions without typing their classes in rescue?

If you really want to catch those exceptions as-is, use:
rescue Exception
The bare rescue keyword only catches derivatives of StandardError (with good reason).
However, a better solution is to have your custom exceptions derive from StandardError.
For an explanation on why this is so, see this section of the PickAxe.

See this post for an explanation:
https://stackoverflow.com/questions/383229/common-programming-mistakes-for-ruby-developers-to-avoid/2019170#2019170
Basically, you can do
class ContentNotFound < RuntimeError
end
to catch that without having to specify an exception class in the rescue statement.

Related

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

Tagging exceptions in Ruby, an explanation/alternative

I've read one tip for tagging your own exceptions in case you have your own library:
module AcmeHttp
module Error; end
end
begin
begin
raise IOError, "Some IO error"
rescue Exception => error
error.extend(AcmeHttp::Error)
raise
end
rescue AcmeHttp::Error => error
puts "Rescued AcmeHttp::Error: #{error}"
end
I'm not really sure how error.extend(AcmeHttp::Error) works and how later you can actually rescue using it. Also, is there some more elegant approach to tagging exceptions from your own library?
Object#extend adds the instance methods of one or more modules, to the receiver object.
So when you do error.extend(AcmeHttp::Error) you are actually adding the instance methods of the AcmeHttp::Error module to error which is an exception (actually a class object).

Reraise (same exception) after catching an exception in Ruby

I am trying to improve my Ruby skills by catching exceptions. I want to know if it is common to reraise the same kind of exception when you have several method calls. So, would the following code make sense? Is it ok to reraise the same kind of exception, or should I not catch it on the process method?
class Logo
def process
begin
#processed_logo = LogoProcessor::create_image(self.src)
rescue CustomException
raise CustomException
end
end
end
module LogoProcessor
def self.create_image
raise CustomException if some_condition
end
end
Sometimes we just want to know an error happened, without having to actually handle the error.
It is often the case that the one responsible for handling errors is user of the object: the caller. What if we are interested in the error, but don't want to assume that responsibility? We rescue the error, do whatever we need to do and then propagate the signal up the stack as if nothing had happened.
For example, what if we wanted to log the error message and then let the caller deal with it?
begin
this_will_fail!
rescue Failure => error
log.error error.message
raise
end
Calling raise without any arguments will raise the last error. In our case, we are re-raising error.
In the example you presented in your question, re-raising the error is simply not necessary. You could simply let it propagate up the stack naturally. The only difference in your example is you're creating a new error object and raising it instead of re-raising the last one.
This will raise the same type of error as the original, but you can customize the message.
rescue StandardError => e
raise e.class, "Message: #{e.message}"
I had the same question as in the comment thread here, i.e. What if the line before (re)raise fails?
My understanding was limited by the missing knowledge that the global variable of $! is "kinda garbage collected" // "scoped to its functional context", which the below example demonstrates:
def func
begin
raise StandardError, 'func!'
rescue StandardError => err
puts "$! = #{$!.inspect}"
end
end
begin
raise StandardError, 'oh no!'
rescue StandardError => err
func
puts "$! = #{$!.inspect}"
raise
end
The output of the above is:
$! = #<StandardError: func!>
$! = #<StandardError: oh no!>
StandardError: oh no!
from (pry):47:in `__pry__'
This behavior is different than how Python's (re)raise works.
The documentation for Exception states:
When an exception has been raised but not yet handled (in rescue,
ensure, at_exit and END blocks), two global variables are set:
$! contains the current exception.
$# contains its backtrace.
So these variables aren't true global variables, they are only defined inside the block that's handling the error.
begin
raise
rescue
p $! # StandardError
end
p $! # nil
A slightly better way to do the same thing as FreePender is to use the exception method from the Exception class, which is the ancestor class to any error classes, like StandardError, so that the method is available to any error classes.
Here the method's documentation that you can find on ApiDock:
With no argument, or if the argument is the same as the receiver, return the receiver. Otherwise, create a new exception object of the same class as the receiver, but with a message equal to string.to_str.
Now let's see how it works:
begin
this_will_fail!
rescue Failure => error
raise error.exception("Message: #{error.message}")
end
Adding to above answers here:
In some applications you may need to log the error twice.
For example exception need to be notified to monitoring tools like
Nagios/Newrelic/Cloudwatch.
At the same time you may have your own kibana backed summary logging
tool, internally for your reference.
In those cases you might want to log & handle the errors multiple times.
Example:
begin
begin
nil.to_sym
rescue => e
puts "inner block error message: #{e.message}"
puts "inner block backtrace: #{e.backtrace.join("\n")}"
raise e
end
rescue => e
puts "outer block error message: #{e.message}"
puts "outer block backtrace: #{e.backtrace.join("\n")}"
end
I am using puts here, for your the ease of verifying this code in
rails console, in actual production you may need to use rails logger

ruby - re-raise exception with sub-exception

I come from a C# background where I usually re-raise exceptions with the original exception contained inside the parent exception. Is a similar concept available in Ruby? How do I detect and raise an exception while maintaining the context of the lower level exception?
Take a look at the tricks from the talk Exceptional Ruby by Avdi Grimm:
class MyError < StandardError
attr_reader :original
def initialize(msg, original=nil);
super(msg);
#original = original;
end
end
# ...
rescue => error
raise MyError.new("Error B", error)
end
Ruby 2.1 added Exception#cause feature to solve this problem.
For Ruby prior 2.1, you may extend StandardError:
class StandardError
attr_accessor :original
end
and when you raise an exception, just set this property:
def reraise ex, original
ex.original = original
raise ex
end
rescue StandardError => e
reraise ArgumentError.new('Message'), e
end
With this approach you will be able to raise standard ruby errors and set parent error for them, not only your custom errors.
For ruby 2.1 and above you can use Exception#cause as mentioned in another answer.

Resources