Created
December 11, 2017 09:59
-
-
Save RaVbaker/5f17ab89bedbc38dc72d393d44e5d4fb to your computer and use it in GitHub Desktop.
Runtime type checking in Ruby - proof-of-concept
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
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