Skip to content

Instantly share code, notes, and snippets.

@rf-
Created October 8, 2011 04:24
Show Gist options
  • Save rf-/1271864 to your computer and use it in GitHub Desktop.
Save rf-/1271864 to your computer and use it in GitHub Desktop.
class Module
def hookable(method_name)
return nil if method_defined?("#{method_name}_without_hooks")
alias_method "#{method_name}_without_hooks", method_name
metaclass = class << self; self; end
hooks = []
define_method(method_name) do |*arguments|
hooks.inject(lambda { |*args| send("#{method_name}_without_hooks", *args) }) do |blk, next_blk|
lambda { |*args| next_blk.call(blk, *args) }
end.call(*arguments)
end
metaclass.class_eval do
define_method("before_#{method_name}") do |*arguments, &blk|
callable = blk || arguments.first || raise("no callable provided!")
hooks << lambda do |fn, *args|
callable.call(*args)
fn.call(*args)
end
end
define_method("around_#{method_name}") do |*arguments, &blk|
callable = blk || arguments.first || raise("no callable provided!")
hooks << lambda do |fn, *args|
callable.call(lambda do |*args_from_hook|
if args_from_hook.count > 0
fn.call(*args_from_hook)
else
fn.call(*args)
end
end, *args)
end
end
define_method("after_#{method_name}") do |*arguments, &blk|
callable = blk || arguments.first || raise("no callable provided!")
hooks << lambda do |fn, *args|
fn.call(*args)
callable.call(*args)
end
end
define_method("#{method_name}_hooks") do
hooks
end
end
end
end
class Foo
def foo
puts "called foo"
end
hookable :foo
def bar(arg1, arg2)
puts "called bar (arg1: #{arg1}, arg2: #{arg2})"
end
hookable :bar
end
obj = Foo.new
Foo.before_foo do
puts "i am before foo"
end
Foo.after_foo do
puts "i am after foo"
end
Foo.before_foo do
puts "i am also before foo"
end
Foo.around_foo do |fn|
puts "before foo..."
fn.call
puts "...and after foo"
end
obj.foo
# Result:
# before foo...
# i am also before foo
# i am before foo
# called foo
# i am after foo
# ...and after foo
Foo.before_bar do
puts "before bar, taking no arguments"
end
Foo.before_bar do |arg1, arg2|
puts "before bar, the values are #{arg1} and #{arg2}"
end
Foo.around_bar do |fn, arg1, arg2|
puts "about to alter the arguments..."
fn.call(arg1 * 2, arg2 / 2)
puts "the arguments are no longer altered"
end
Foo.after_bar do |arg1, arg2|
puts "after bar, the values are #{arg1} and #{arg2}"
end
Foo.around_bar do |fn|
puts "before......."
fn.call
puts "........after"
end
obj.bar(10, 24)
# Result:
# before.......
# about to alter the arguments...
# before bar, the values are 20 and 12
# before bar, taking no arguments
# called bar (arg1: 20, arg2: 12)
# the arguments are no longer altered
# after bar, the values are 10 and 24
# ........after
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment