Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A callbacks DSL creation example with ruby metaprogramming
require './wrappable-other'
class CallbackTest
extend Wrappable
def original
puts "Original method"
end
def before
puts "Before method"
end
wrap :original do
before_run :before
after_run lambda { puts "After with lambda" }
end
end
CallbackTest.new.original
module Wrappable
class WrapperOptions
attr_accessor :before, :after
def initialize(&block)
instance_eval(&block)
end
private
def before_run(before)
@before = before
end
def after_run(after)
@after = after
end
end
def wrap(original_method, &block)
wrapper_options = WrapperOptions.new(&block)
wrapper_options.before = normalize_call(wrapper_options.before)
wrapper_options.after = normalize_call(wrapper_options.after)
alias_method :old_method, original_method
define_method original_method do
send(wrapper_options.before)
send(:old_method)
send(wrapper_options.after)
end
end
private
def normalize_call(method)
class_eval do
if method.respond_to? :call
method_name = "_#{method}"
define_method(method_name , &method)
method_name
else
method
end
end
end
end
module Wrappable
def self.included(klass)
klass.extend ClassMethods
end
class Wrapper
def initialize(original_method, receiver, &block)
instance_eval &block
create(receiver, original_method)
end
def create(receiver, original_method)
receiver.send(:alias_method, :original_method, original_method)
before_method = receiver.instance_method(@before)
after_method = receiver.instance_method(@after)
receiver.send(:define_method, original_method) do
before_method.bind(self).call
send :original_method
after_method.bind(self).call
end
end
def before_run(method)
@before = method
end
def after_run(method)
@after = method
end
end
module ClassMethods
def wrap(original_method, &block)
Wrapper.new(original_method, self, &block)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.