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
Related
I have a module called Notifier.
module Notifier
def self.prepended(host_class)
host_class.extend(ClassMethods)
end
module ClassMethods
def emit_after(*methods)
methods.each do |method|
define_method(method) do |thing, block|
r = super(thing)
block.call
r
end
end
end
end
end
It exposes a class method emit_after. I use it like so:
class Player
prepend Notifier
attr_reader :inventory
emit_after :take
def take(thing)
# ...
end
end
The intention is that by calling emit_after :take, the module overrides #take with its own method.
But the instance method isn't being overridden.
I can however, override it explicitly without using ClassMethods
module Notifier
def self.prepended(host_class)
define_method(:take) do |thing, block|
r = super(thing)
block.call
r
end
end
class Player
prepend Notifier
attr_reader :inventory
def take(thing)
# ...
end
end
#> #player.take #apple, -> { puts "Taking apple" }
#Taking apple
#=> #<Inventory:0x00007fe35f608a98...
I know that ClassMethods#emit_after is called so I assume that the method is being defined but it just never gets called.
I want to create the methods dynamically. How can I ensure that the generate method overrides my instance method?
#Konstantin Strukov's solution is good but maybe a little confusing. So, I suggest another solution, which is more like the original one.
Your first goal is to add a class method (emit_after) to your class. To do that you should use extend method without any hooks such as self.prepended(), self.included() or self.extended().
prepend, as well as include, are used to add or override instance methods. But that is your second goal and it happens when you call emit_after. So you shouldn't use prepend or include when extending your class.
module Notifier
def emit_after(*methods)
prepend(Module.new do
methods.each do |method|
define_method(method) do |thing, &block|
super(thing)
block.call if block
end
end
end)
end
end
class Player
extend Notifier
emit_after :take
def take(thing)
puts thing
end
end
Player.new.take("foo") { puts "bar" }
# foo
# bar
# => nil
Now it is obvious that you call extend Notifier in order to add the emit_after class method and all the magic is hidden in the method.
What about this solution:
module Notifier
def self.[](*methods)
Module.new do
methods.each do |method|
define_method(method) do |thing, &block|
super(thing)
block.call if block
end
end
end
end
end
class Player
prepend Notifier[:take]
def take(thing)
puts "I'm explicitly defined"
end
end
Player.new.take(:foo) { puts "I'm magically prepended" }
# => I'm explicitly defined
# => I'm magically prepended
It's quite similar to the solution from Aleksei Matiushkin, but the ancestors' chain is a bit cleaner (no "useless" Notifier there)
Prepend to the currently opened class:
module Notifier
def self.prepended(host_class)
host_class.extend(ClassMethods)
end
module ClassMethods
def emit_after(*methods)
# ⇓⇓⇓⇓⇓⇓⇓ HERE
prepend(Module.new do
methods.each do |method|
define_method(method) do |thing, block = nil|
super(thing).tap { block.() if block }
end
end
end)
end
end
end
class Player
prepend Notifier
attr_reader :inventory
emit_after :take
def take(thing)
puts "foo"
end
end
Player.new.take :foo, -> { puts "Taking apple" }
#⇒ foo
# Taking apple
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.
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
class SomeClass
def initialize
yield
end
def test
puts 'test'
end
end
I want to execute the test function inside a block when I initialize a some SomeClass.
SomeClass.new { test() }
This gives me
NoMethodError: undefined method `test' for main:Object
You are looking for instance_eval:
class SomeClass
def initialize(&block)
instance_eval(&block) if block_given?
end
def test
puts 'test'
end
end
SomeClass.new { test() } #=> test
It is very easy, just pass the self:
class SomeClass
def initialize
yield self if block_given?
end
def test
puts 'test'
end
end
SomeClass.new { |ob| ob.test }
# >> test
Your one didn't work, because blocks are closure, and the self is set to main inside the block in your example. main is a instance of Object. You didn't defie the #test inside the Object, so main tries to call the #test and you got the genuine error.
Or you can use call, like this:
class SomeClass
def initialize(&block)
block.call(self) if block_given?
end
def test
puts 'test'
end
end
SomeClass.new {|s| s.test }
Two solutions:
1
class SomeClass
def initialize(&block)
instance_eval(&block)
end
def test
puts 'test'
end
end
SomeClass.new { test() } #=> test
2
class SomeClass
def initialize
yield self
end
def test
puts 'test'
end
end
SomeClass.new {|o| o.test() } #=> test
Some references hope to help you:
1.instance_eval no longer yielding self in ruby 1.9: https://www.ruby-forum.com/topic/189422
2.How do I build DSLs with yield and instance_eval?: http://rubylearning.com/blog/2010/11/30/how-do-i-build-dsls-with-yield-and-instance_eval/
I'm trying to create some kind of module or superclass that wraps one method call after each method of the subclass.
There are some constraints though: I wouldn't want the method to be run after initialize() is called nor after a few other methods of my choice is called.
Another constraint is that I would only want that method to be executed IF the flag #check_ec is set to true.
I have classes with more than 60 methods that I have hard-coded the same piece of code that ispasted all over the place.
Is there a way that I could make a wrapper that would automatically execute that method for my class methods?
So the idea is this:
class Abstract
def initialize(check_ec)
#check_ec = check_ec
end
def after(result) # this is the method that I'd like to be added to most methods
puts "ERROR CODE: #{result[EC]}"
end
def methods(method) # below each method it would execute after
result = method() # execute the given method normally
after(result) if #check_ec and method != :initialize and method != :has_valid_params
end
end
class MyClass < Abstract
def initialize(name, some_stuff, check_error_code)
# do some stuff...
#name = name
super(check_error_code)
end
def my_method_a() # execute after() after this method
return {EC: 0}
end
def my_method_b() # execute after() after this method
return {EC: 7}
end
def has_valid_params() # don't execute after() on this method
return true
end
end
This is trivially easy using method_missing, and composition instead of inheritance. You can build a very simple class which forwards method invocations, and then executes an after callback, except for specific method names:
class Abstract
def initialize(object)
#object = object
end
def method_missing(method, *arguments)
result = #object.send(method, *arguments)
after() unless method == "has_valid_params"
result
end
def after
# whatever
end
end
o = Abstract.new(MyClass.new)
A solution using singleton class.
class MyClass
def initialize(name, some_stuff)
# do some stuff...
#name = name
end
def my_method_a # execute after() after this method
return {EC: 0}
end
def my_method_b() # execute after() after this method
return {EC: 7}
end
def has_valid_params() # don't execute after() on this method
return true
end
end
module ErrorCodeChecker
def after(result) # this is the method that I'd like to be added to most methods
puts "ERROR CODE: #{result[:EC]}"
end
def addErrorCodeCheck(exclude = [])
methods = self.class.superclass.public_instance_methods(false) - exclude
class << self
self
end.class_exec {
methods.each {|method|
define_method(method) {|*p|
super(*p).tap {|res| after(res)}
}
}
}
end
end
class MyClassEC < MyClass
include ErrorCodeChecker
def initialize(name, some_stuff, check_error_code, exclude = [])
super name, some_stuff
addErrorCodeCheck(exclude) if check_error_code
end
end
'addErrorCodeCheck' opens up the singleton class of an instance of MyClassEC, and redefines instance methods of MyClass not in the exclude list. The redefined methods hide the original methods but call them via 'super' method inside before calling 'after'.
You can apply 'addErrorCodeCheck' repeatedly later if needed.
Execution example: (tested in Ruby 1.9.3)
my = MyClassEC.new('test', 'abc', true, [:has_valid_params])
my.my_method_a # => ERROR CODE: 0
my.my_method_b # => ERROR CODE: 7
my.has_valid_params # => (nothing)
What about this? It has a major drawback which is that your methods must be already defined before calling check_error_code, but it may suit your needs. You could look for inspiration for a better solution in Rails callbacks, or defer the redefinition of each method until that method is added using the method_added hook.
Include ErrorCodeChecker and call check_error_code in each class you want to check the error code (as in the last line of the snippet).
module ErrorCodeChecker
def self.included(base)
base.send(:extend, ClassMethods)
end
def after(result) # this is the method that I'd like to be added to most methods
puts "ERROR CODE: #{result[:ec]}"
end
module ClassMethods
def check_error_code(options = {})
check_on = instance_methods(false) - Array(options[:except])
check_on &= Array(options[:only]) if options[:only]
class_eval do
check_on.each do |method|
alias_method "#{ method }_without_ec", method
define_method(method) do |*args, &block|
send("#{ method }_without_ec", *args, &block).tap { |result| after(result) if #check_ec }
#if you want to actually return the return value of calling after:
#result = send("#{ method }_without_ec")
##check_ec ? after(result) : result
end
end
end
end
end
end
class Abstract
include ErrorCodeChecker
def initialize(check_ec)
#check_ec = check_ec
end
end
class MyClass < Abstract
def initialize(name, some_stuff, check_error_code)
# do some stuff...
#name = name
super(check_error_code)
end
def my_method_a # execute after() after this method
{ec: 0}
end
def my_method_b # execute after() after this method
{ec: 7}
end
def has_valid_params # don't execute after() on this method
true
end
check_error_code except: :has_valid_params
#or whitelisting:
#check_error_code only: [:my_method_a, :my_method_b]
#or both:
#check_error_code only: :my_method_a, except: [:has_valid_params, dont_check_this_one]
end