1

Basically, this is normal code:

class Foo def hi # your code here.... rescue => e # Raise error here end def hello # your code here... rescue => e # Raise error here end end 

But in PHP, i can use __call magic method to create abstract class, look like this:

class FooAbstract { public function __call($name, $args) { # Try catch in here... } } class Foo extends FooAbstract { public function hi() { # Code with try catch... } } 

How can i use __call method in Ruby class???

7
  • If you want to define a class, which can not be instantiated except if subclassed, you could do a fail "Trying to instantiate abstract class" if self.class == FooAbstract in FooAbstract#initialize. Commented Jun 18, 2018 at 9:31
  • 3
    This looks like a XY problem. What are you trying to accomplish? Commented Jun 18, 2018 at 9:47
  • There is no direct equivalent to __call in ruby; the closest is probably method_missing. (And likewise, there's no direct equivalent to method_missing in PHP, but as far as I know __call is probably the closest.) But as Stefan says, what are you actually trying to achieve here? I can't really advise how to structure the code without knowing what problem you're trying to solve. Commented Jun 18, 2018 at 10:21
  • My guess is to accomplish something like the rails class level rescue_from so that you don't have to copy-paste the same rescue block to every method. Commented Jun 18, 2018 at 11:52
  • 1
    Not sure what you're trying to illustrate with that PHP code, but there method hi will not invoke __call in any way. What was __call supposed to do, add exception handling for all methods of child classes? Commented Jun 18, 2018 at 11:58

2 Answers 2

6

You could define a module that when included defines a method_added hook that wraps all new methods inside a begin..rescue block:

require 'set' module ExceptionHandler def self.included(klass) super klass.send(:extend, ClassMethods) end module ClassMethods def exception_handler(&block) @__exception_handler = block.to_proc end def handle_exception(exception) defined?(@__exception_handler) ? @__exception_handler.call(exception) : super end def handle_method_exceptions(method_name) old_method = instance_method(method_name) return if (@__hooked_methods ||= Set.new).include?(method_name) @__ignoring_added_methods = true # avoid infinite define_method/method_added loop define_method method_name do |*args, &block| begin old_method.bind(self).(*args, &block) rescue => ex self.class.handle_exception(ex) end end @__ignoring_added_methods = false @__hooked_methods << method_name end def method_added(method_name) super unless @__ignoring_added_methods handle_method_exceptions(method_name) end end end end 

This would be used like:

class Foo include ExceptionHandler exception_handler do |exception| puts "Catched an exception:" puts "---------------------" puts "Exception class: #{exception.class}" puts "Message: #{exception.message}" puts "Backtrace:" puts exception.backtrace.join("\n ") puts puts "reraising.." puts raise exception end def this_raises raise "somebody set up us the bomb" end end Foo.new.this_raises 

This would output:

Catched an exception: --------------------- Exception class: RuntimeError Message: somebody set up us the bomb Backtrace: errorhandler.rb:62:in `this_raises' errorhandler.rb:26:in `call' errorhandler.rb:26:in `block in handle_exceptions' errorhandler.rb:67:in `<main>' reraising.. 

I'm not sure if it is a good idea.

You could take out the method_added part and it would look something like:

class Foo with_rescue def foofoo(arg) puts arg.inspect end end 

(You can just rename the handle_method_exceptions to with_rescue and remove all the @__ignoring_added_methods trickery and the method_added method and it should work as described).

Sign up to request clarification or add additional context in comments.

2 Comments

This is great! But how is it possible to use an instance method as the exception_handler? I want to be able to log an exception in any method to my instance specific logger.
@meesern untested, but you can try to replace self.class.handle_exception(ex) with (respond_to?(:handle_exception) ? self : self.class).send(:handle_exception, ex) and define the exception handler as an instance method: def handle_exception(ex) ... end
1

I'm not sure what you want to achieve here, but the Ruby equivalent of PHP's __call() is method_missing.

By default, when you try to call a non-existing method you will get an exception. But if you want to implement an "abstract class". You could also try this solution: https://stackoverflow.com/a/512505/185870

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.