Skip to content

Instantly share code, notes, and snippets.

@yugui
Created July 23, 2008 13:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yugui/1724 to your computer and use it in GitHub Desktop.
Save yugui/1724 to your computer and use it in GitHub Desktop.
Decorator for Ruby, inspired by Python
class Module
def declare(decorator, *args)
orig_method_added = method(:method_added)
metaclass = (class << self; self end)
defined_methods = []
metaclass.send(:define_method, :method_added) do |name|
orig_method_added.call(name)
defined_methods << name
end
begin
yield
ensure
metaclass.send(:remove_method, :method_added)
if orig_method_added.owner == metaclass
metaclass.send(:define_method, :method_added, orig_method_added)
end
end
decorator = method(decorator) unless decorator.respond_to?(:call)
defined_methods.each do |name|
method_body = decorator.call(name, *args)
define_method(name, method_body)
end
end
def before(name)
orig = instance_method(name)
proc {|*args|
yield(name, args)
orig.bind(self).call(*args)
}
end
def after(name)
orig = instance_method(name)
proc {|*args|
result = orig.bind(self).call(*args)
yield(name, args, result)
}
end
def around(name)
orig = instance_method(name)
proc {|*args|
yield(:before, name, args)
result = orig.bind(self).call(*args)
yield(:after, name, args, result)
}
end
def type(name, types)
before(name) do |name, args|
args.zip(types).each_with_index do |(arg, type), i|
raise TypeError, "#{i}th argument #{arg} should be #{type}" unless arg.instance_of?(type)
end
end
end
end
if $0 == __FILE__
class Calc
declare :type, [Float, Float] do
def add(x,y); x+y end
def sub(x,y); x-y end
def mul(x,y); x*y end
def div(x,y); x/y end
end
end
calc = Calc.new
p calc.add(1.0,2.0)
p calc.sub(1.0,2)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment