class A
def a_method
#..
end
end
class B < A
def method_1
# ...
a_method
end
def method_2
# ...
a_method
end
# ...
def method_n
# ...
a_method
end
end
The a_method ocassionally throws an AException.
I want to rescue from that exception, like:
class B < A
def method_1
# ...
a_method
rescue AException => e
p e.message
end
# ...
end
I want to rescue the same way in each methods inside class B (method_1, method_2, ..., method_n). I'm stuck on figuring out a nice and clean solution, that would not require to duplicate the rescue code block. Can you help me with that?
How about to use a block:
class B < A
def method_1
# some code here which do not raised an exception
with_rescue do
# method which raised exception
a_method
end
end
def method_2
with_rescue do
# ...
a_method
end
end
private
def with_rescue
yield
rescue => e
...
end
end
If you want to always rescue the exception, you could just override a_method in B:
class B < A
def a_method
super
rescue AException => e
p e.message
end
# ...
end
In addition you might want to return a value (like nil or false) to indicate the failure.
Like this, perhaps?
class B < A
def method_1
# ...
safe_a_method
end
private
def safe_a_method
a_method
rescue AException => e
...
end
end
You can wrap your methods using a Module like this.
The benefit is that unlike the other solutions you can call your methods with their regular names and the methods themselves don't have to be changed.
Just extend the class with the ErrorHandler method en at the end enumerate the methods to wrap them with your errorhandling logic.
module ErrorHandler
def wrap(method)
old = "_#{method}".to_sym
alias_method old, method
define_method method do |*args|
begin
send(old, *args)
rescue => e
puts "ERROR FROM ERRORHANDLER #{e.message}"
end
end
end
end
class A
extend ErrorHandler
def a_method v
"a_method gives #{v.length}"
end
(self.instance_methods - Object.methods).each {|method| wrap method}
end
class B < A
extend ErrorHandler
def method_1 v
"method_1 gives #{v.length}"
end
(self.instance_methods - Object.methods).each {|method| wrap method}
end
puts A.new.a_method "aa" # a_method gives 2
puts A.new.a_method 1 # ERROR FROM ERRORHANDLER undefined method `length' for 1:Fixnum
puts B.new.method_1 1 # ERROR FROM ERRORHANDLER undefined method `length' for 1:Fixnum
Related
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???
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).
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
How can i catch all exception in ruby class like this:
class ServiceException
rescue => e
puts 'Error!!!'
end
class ServiceA < ServiceException
def say_hello
error_code_here
end
end
ServiceA.new.say_hello
# => Error!!!
OK so i managed do accomplish this with some metaprogramming. First define this module:
module MetaprogrammingStuff
def rescue_all_methods(klass, *errors_to_rescue, &blk)
patch_all_methods(klass) do |orig_method, *args, &caller_blk|
begin
orig_method.call *args, &caller_blk
rescue *errors_to_rescue => e
blk.call e
end
end
end
def patch_all_methods(klass, &blk)
patch_class_methods(klass, &blk)
patch_instance_methods(klass, &blk)
end
def patch_class_methods(klass, &blk)
klass.methods(false).each do |fn_name|
orig_method = klass.method fn_name
klass.singleton_class.send(:define_method, fn_name) do |*args, &caller_blk|
blk.call orig_method, *args, &caller_blk
end
end
end
def patch_instance_methods(klass, &blk)
klass.instance_methods(false).each do |fn_name|
orig_method = klass.instance_method fn_name
klass.send(:define_method, fn_name) do |*args, &caller_blk|
blk.call orig_method.bind(self), *args, &caller_blk
end
end
end
end
then extend it to a class and run rescue_all_methods:
class TestClass
extend MetaprogrammingStuff
def foo
raise ArgumentError
end
def self.foo
raise RuntimeError
end
rescue_all_methods(self, ArgumentError, RuntimeError) do |error|
puts "#{error.class} was raised."
end
end
It's important that rescue_all_methods be called after the methods are defined.
You can see it inserts a rescue block around all instance and class methods:
TestClass.foo
# => RuntimeError was raised.
TestClass.new.foo
# => ArgumentError was raised.
How it works - basically it turns the methods into anonymous fuctions using the method and instance_method methods, then re-assigns those method names to new methods which call the original but with a begin..rescue wrapper.
I have a parent class:
class Base
def my_method
block_method do
# EXECUTE WHATEVER'S IN THE CHILD VERSION OF my_method
# HOW TO DO?
end
end
def block_method
original_foo = 'foo'
foo = 'CUSTOM FOO'
yield
foo = original_foo
end
end
and some child classes--currently five and there will be more:
class ChildA < Base
def my_method
puts 'apple'
puts 'aardvark'
end
end
class ChildE < Base
def my_method
puts 'eel'
puts 'elephant'
end
end
I want to change what a variable refers to just for the duration of each Child's #my_method.
My thought is to do this by wrapping the functionality of each Child's #my_method in a block. But I'd rather do that in the parent class than have to wrap each child class's #my_method in the exact same block.
Any insights on how I can do this?
If there's some opposite of super, I guess that would be one way to accomplish what I want to do.
You could give the idea of what you are calling "some opposite of super" using a module and prepend like so:
module MethodThing
# added to remove binding from class since
# #my_method relies on the existence of #foo and #foo=
def self.prepended(base)
base.attr_reader(:foo) unless base.method_defined?(:foo)
base.attr_writer(:foo) unless base.method_defined?(:foo=)
end
def my_method
puts "Before: #{foo}"
original_foo = foo
self.foo= 'CUSTOM FOO'
begin
super
rescue NoMethodError
warn "(skipped) #{self.class}##{__method__} not defined"
end
self.foo = original_foo
puts "After: #{foo}"
end
end
Prepend the module on inheritance
class Base
def self.inherited(child)
child.prepend(MethodThing)
end
attr_accessor :foo
def initialize
#foo = 12
end
end
class ChildA < Base
def my_method
puts 'apple'
puts "During: #{foo}"
puts 'aardvark'
end
end
class ChildE < Base
end
Output:
ChildA.new.my_method
# Before: 12
# apple
# During: CUSTOM FOO
# aardvark
# After: 12
ChildE.new.my_method
# Before: 12
# (skipped) ChildE#my_method not defined
# After: 12
There are other strange ways to accomplish this with inheritance as well such as
class Base
class << self
attr_accessor :delegate_my_method
def method_added(method_name)
if method_name.to_s == "my_method" && self.name != "Base"
warn "#{self.name}#my_method has been overwritten use delegate_my_method instead"
end
end
end
attr_accessor :foo
def my_method
puts "Before: #{foo}"
original_foo = foo
self.foo= 'CUSTOM FOO'
begin
method(self.class.delegate_my_method.to_s).()
rescue NameError, TypeError
warn "(skipped) #{self.class} method delegation not defined"
end
self.foo = original_foo
puts "After: #{foo}"
end
end
class ChildA < Base
self.delegate_my_method = :delegation_method
def delegation_method
puts 'apple'
puts "During: #{foo}"
puts 'aardvark'
end
end
I could probably keep going with stranger and stranger ways to solve this problem but I think these will get you where you need to go.
One option would be to define a private or nodoc method which the parent class can call and is defined in each of the children.
class Parent
def my_method
block_method do
my_method_behavior
end
end
def block_method
original_foo = #foo
#foo = 'CUSTOM FOO'
yield
#foo = original_foo
end
end
class Child1 < Parent
def my_method_behavior
puts #foo
end
end
class Child2 < Parent
def my_method_behavior
puts #foo
end
end
I have a module that defines a method if it is not already defined. This is the case of ActiveRecord's attributes as theirs getters and setters are not defined as methods.
module B
def create_say_hello_if_not_exists
puts respond_to?(:say_hello)
define_method :say_hello do
puts 'hello'
end unless respond_to?(:say_hello)
end
end
class A
def say_hello
puts 'hi'
end
puts respond_to?(:say_hello, true)
extend B
create_say_hello_if_not_exists
end
A.new.say_hello
The expected result is hi, but ruby prints hello. Why?
Maybe related to Confused about "respond_to?" method
Try this.
module B
def create_say_hello_if_not_exists
puts method_defined?(:say_hello)
define_method :say_hello do
puts 'hello'
end unless method_defined?(:say_hello)
end
end
class A
def say_hello
puts 'hi'
end
puts method_defined?( :say_hello )
extend B
create_say_hello_if_not_exists
end
A.new.say_hello
The reason why respond_to?(:say_hello) is returning false is due to the fact class A has say_hello as instance method and since you are extending class B the create_say_hello_if_not_exists is declared as class method and it does not find say_hello.
Changing the code to the following would do the trick. I'm declaring say_hello in class A as class method and am calling it in a static manner.
module B
def create_say_hello_if_not_exists
puts respond_to?(:say_hello)
define_method :say_hello do
puts 'hello'
end unless respond_to?(:say_hello)
end
end
class A
def self.say_hello
puts 'hi'
end
extend B
create_say_hello_if_not_exists
end
A.say_hello
How can I define Thread#initialize? I tried the following:
(1)
class Thread
def initialize
super
#foo = []
end
end
Thread.new{}.join
(2)
class Thread
def initialize &pr
super(&pr)
#foo = []
end
end
Thread.new{}.join
(3)
class Thread
def initialize
end
end
Thread.new{}.join
but they return an error:
Uninitialized thread - check `Thread#initialize'.
You're destroying the original Thread.initialize when open up the class this way, the call to super is Object.initialize, not to what was previously in Thread.initialize.
It's similar to what is happening here:
class Dog
def initialize
puts 'Arf'
end
end
class Dog
def initialize
super
puts 'I did not arf'
end
end
#dog.new "I did not arf"
You could subclass Thread and then use super, or you could use alias_method
class Thread
alias_method :old_initialize, :initialize
def initialize(*args,&blk)
puts 'Before initialize'
old_initialize(*args,&blk)
end
end