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