Skip to content

Instantly share code, notes, and snippets.

@mrbrdo
Created June 6, 2012 05:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrbrdo/2880057 to your computer and use it in GitHub Desktop.
Save mrbrdo/2880057 to your computer and use it in GitHub Desktop.
require 'rspec'
# TODO: class vars?
# TODO: better just alias_method_chain method_missing for the duration of running the method
module Tdef
def self.check_args(args, rules)
rules.each_pair do |name, klass|
unless args[name.to_s].kind_of?(klass)
raise "Wrong type: #{name} should be #{klass.to_s}, but is #{args[name].class.to_s}"
end
end
end
def tdef name, args, &block
self.send(:define_method, name.to_sym) do |*arg_values|
raise "Wrong number of arguments" if args.count != arg_values.count
args_map = Hash.new
(0...arg_values.count).each do |i|
args_map[args.keys[i].to_s] = arg_values[i]
end
Tdef::check_args(args_map, args)
self.class.send(:alias_method, :method_missing_tmp, :method_missing)
self.class.send(:define_method, :method_missing) do |meth, *args, &block|
if args_map.keys.include?(meth.to_s)
args_map[meth.to_s]
else
if self.respond_to?(:method_missing_tmp)
method_missing_tmp meth, *args, &block
else
super(meth, *args, &block)
end
end
end
result = self.instance_eval &block
self.class.send(:remove_method, :method_missing)
self.class.send(:alias_method, :method_missing, :method_missing_tmp)
# No harm in leaving the method_missing_tmp there
# self.class.send(:remove_method, :method_missing_tmp)
result
end
end
end
# Test class
class TestClass
extend Tdef
tdef :hello, :a1 => Integer, :a2 => String do
a2 = "Goodbye"
@lala = 999
[a1, a2, missing_arg]
end
def method_missing meth, *args, &block
if meth.to_s == "missing_arg"
"lolario"
else
super
end
end
end
# Tests
describe TestClass do
it "works" do
k = TestClass.new
expect { k.hello }.should_not raise_error(NoMethodError)
r1, r2, r3 = k.hello(10, "Hello")
r1.should eq(10)
r2.should eq("Goodbye")
r3.should eq("lolario")
k.instance_variable_get("@lala").should eq(999)
expect { k.a1 }.should raise_error(NoMethodError)
expect { k.a2 }.should raise_error(NoMethodError)
defined?(a1).should be_nil
defined?(a2).should be_nil
end
it "detects wrong arguments" do
k = TestClass.new
expect { k.hello(10, 20) }.should raise_error
expect { k.hello("Hello", "World") }.should raise_error
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment