Skip to content

Instantly share code, notes, and snippets.

@RaVbaker
Created December 11, 2017 09:59
Show Gist options
  • Save RaVbaker/5f17ab89bedbc38dc72d393d44e5d4fb to your computer and use it in GitHub Desktop.
Save RaVbaker/5f17ab89bedbc38dc72d393d44e5d4fb to your computer and use it in GitHub Desktop.
Runtime type checking in Ruby - proof-of-concept
module Types
InvalidTypeError = Class.new(ArgumentError)
def specify(spec, a_method)
args_spec = []
return_spec = nil
case spec.first
when Hash
args_spec = spec.first.keys.first
return_spec = spec.first.values.first
when Array
args_spec = spec.first
else
args_spec = spec
end
original_method = :"_#{a_method}_without_type_checking"
alias_method original_method, a_method
define_method(a_method) do |*args|
ensure_correct_types(args_spec, args)
result = send(original_method, *args)
ensure_correct_types(return_spec, [result], what: "return value") if return_spec
result
end
a_method
end
module InstanceMethods
def ensure_correct_types(spec, args, what: "argument")
Array(spec).each.with_index do |klass, index|
raise InvalidTypeError, ["Type for #{what}:", klass, "for" , args[index]&.inspect || "-missing #{index.succ} argument-", args[index].class].join(" ") if klass && !(klass === args[index])
end
end
end
private_constant :InstanceMethods
def self.extended(mod)
mod.include InstanceMethods
end
end
Object.extend Types
class Foo
specify [String],
def foo(arg)
42
end
specify [String => Numeric],
def bar(arg)
42
end
specify [[String, String]],
def foo2(arg1, arg2)
42
end
specify [[String, String] => Numeric],
def bar2(arg1, arg2)
42
end
end
Foo.new.foo("42")
Foo.new.bar2("42", ?x)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment