Last active
January 1, 2016 01:59
-
-
Save vakiliy/8076412 to your computer and use it in GitHub Desktop.
Ruby Callback Example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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