Skip to content

Instantly share code, notes, and snippets.

@vakiliy
Last active January 1, 2016 01:59
Show Gist options
  • Save vakiliy/8076412 to your computer and use it in GitHub Desktop.
Save vakiliy/8076412 to your computer and use it in GitHub Desktop.
Ruby Callback Example
# The MIT License (MIT) http://vakiliy.mit-license.org
# CallBack Module
module CallBackHelper
class TypeError < StandardError
def initialize(msg = 'Callback type error. Required: Symbol, String or Proc')
super msg
end
end
class UseError < StandardError
def initialize(msg)
super "Called same name method. Line: #{msg}"
end
end
def self.included(base)
base.extend(CallBackHelper::ClassMethods)
end
module ClassMethods
# Add callback handlers
[:before_method, :after_method, :around_method, :wrap_method].each do |m|
define_method(m) { |name, hook, *args| set_handler(m, name, hook, *args) }
end
def method_added(name)
# connect registered hook
callback_method, original_method = handler_method_names(name)
methods = instance_methods(false)
if methods.include?(callback_method) && !methods.include?(original_method)
alias_method original_method, name
alias_method name, callback_method
end
end
protected
def handler_method_names(name)
["callback_#{name}".to_sym, "original_#{name}".to_sym]
end
def set_handler(place, name, hook, *predefined_args)
# define handler
callback_method, original_method = handler_method_names(name)
define_method callback_method do |*args, &block|
handlers = {}
case hook.class.name
when 'Symbol', 'String' then
raise(UseError, __LINE__) if hook == name # prevent deep stack error
use_args = predefined_args.size > 0 ? predefined_args : args
handlers[:hook] = proc { __send__(hook, *use_args, &block) }
when 'Proc' then
handlers[:hook] = hook
else
raise TypeError
end
handlers[:orig] = proc { __send__(original_method, *args, &block) }
# call methods with position
order = []
case place
when :before_method then
order = [handlers[:hook], handlers[:orig]]
when :after_method then
order = [handlers[:orig], handlers[:hook]]
when :around_method then
order = [handlers[:hook], handlers[:orig], handlers[:hook]]
when :wrap_method then
order = [handlers[:orig], handlers[:hook], handlers[:orig]]
end
order.each do |handler|
handler.call(*args, &block)
end
end
end
end
end
# Test Class
class MyClass
include CallBackHelper
before_method :hello, :user_name, 'User'
def hello(msg)
puts " Say: #{msg}"
end
private
def user_name(name)
print name
end
end
# Test
a = MyClass.new()
a.hello('Hello!')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment