Created
October 8, 2011 04:24
-
-
Save rf-/1271864 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| 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