Is there any way in Ruby to have a standard rescue strategy based in inheritance?
I'd like to have something like:
class Parent
def method
rescue StandardError => e
#handle error
end
end
class Child < Parent
def method
raise StandardError.new("ERROR") if error_present
#do some stuff
super
end
def error_present
#checks for errors
end
end
and the result I expect is that when the StandardError
is raised in the child's method it will be rescued from the parent's method definition.
If that's not possible, is there any other way I could achieve this behaviour?
CodePudding user response:
I am afraid this is not possible - one method's rescue
can only rescue errors raised within its body.
One way to deal with this would be to offer another method to override in subclasses:
class Parent
# This is the public interface of the class, it is not supposed to be overriden
def execute!
perform
rescue => e
# do_sth_with_error
end
private
# Private implementation detail, override at will
def perform
end
end
class Child < Parent
private
def perform
raise "Something" if something?
# do some more things
super
end
end
Child.new.execute!
That being said - please do not rescue StandardError
. It will make your future debugging a nightmare. Create your own error subclass instead.
CodePudding user response:
You can have such a strategy, but it depends how exactly it should work and how your code is organized. This pattern can be extended. For your particular case you can organize your code as follows:
class Parent
def my_method(arg1, arg2)
yield if block_given?
# some code that should be run by Parent#my_method
rescue StandardError => e
# handle the error
end
end
class Child < Parent
def my_method(arg1, arg2)
super do
# code that should be run by Child#my_method
raise StandardError.new('error message') if error?
# maybe more code that should be run by Child#my_method
end
end
def error?
true
end
end
With this strategy you have to inject all your code from the child method as a block passed to you parent method through a super
call that is the only statement (well, basically it's a closure passed up in the inheritance chain). Of course, this strategy assumes that your method doesn't use a block to implement its normal logic and you can use this feature specifically for this execution injection.
If you want to have more than two levels of inheritance using this strategy, you'll have to to the same trick with every next method:
class C
def m1
yield if block_given?
# ...
rescue SomeError
# ...
end
end
class B < C
def m1
super do
yield if block_given?
# ...
end
end
end
class A < B
def m1
super do
raise SomeError if condition
# ...
end
end
end
Most probably you can remove the if block_given?
parts if you apply this strategy.