Created
July 21, 2011 21:19
-
-
Save cqfd/1098237 to your computer and use it in GitHub Desktop.
Python-ish decorators for Ruby
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
# | |
# Adding Python-ish decorations to Ruby! | |
# | |
# Usage: | |
# | |
# class Foo | |
# extend WithDecorations | |
# | |
# decorate :bar do |b| | |
# puts "Do something here before bar gets invoked!" | |
# b.call | |
# puts "Do something here after bar gets invoked!" | |
# end | |
# | |
# def bar | |
# puts "My name's bar!" | |
# end | |
# end | |
module WithDecorations | |
# hash with method name keys pointing to arrays of decorators waiting to be | |
# applied | |
decorations = {} | |
# method_added is a Ruby runtime hook. If method_added is defined as a class | |
# method on class Foo, then it will be invoked immediately after a new | |
# instance method is added to Foo. | |
# | |
# The plan is to check whenever a new function is added if there are any | |
# decorations to apply. If so, dynamically create a new function with the | |
# same name that "does the right thing". | |
define_method :method_added do |m| | |
# grab a procified version of the old, undecorated method | |
old_m = instance_method m | |
unless decorations[m].nil? || decorations[m].empty? | |
decorated_m = decorations[m].pop | |
define_method m do |*args| | |
# old_m is actually an UnboundMethod. In order to call an unbound | |
# method you need to bind it to something, so we bind it to the object | |
# that called the decorated method, namely self. | |
decorated_m.call old_m.bind(self) | |
end | |
end | |
end | |
# calling decorate :foo with a block adds the block to the list of foo's | |
# decorations | |
define_method :decorate do |m, &block| | |
(decorations[m] ||= []) << block | |
end | |
end | |
class A | |
extend WithDecorations | |
def initialize | |
@foo = "Hi, I'm foo!" | |
end | |
decorate :foo do |f| | |
puts "Even futher outside foo!" | |
f.call | |
puts "Even futher outside foo!" | |
end | |
decorate :foo do |f| | |
puts "Outside foo!" | |
f.call | |
puts "Outside foo!" | |
end | |
def foo | |
puts @foo | |
end | |
end | |
a = A.new | |
a.foo |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment