Home > database >  Rescue on parent method an exception raised in child
Rescue on parent method an exception raised in child

Time:11-10

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.

  • Related