Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Created July 3, 2021 18:59
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 JoshCheek/df6154555387f966ef1808262e38f72e to your computer and use it in GitHub Desktop.
Save JoshCheek/df6154555387f966ef1808262e38f72e to your computer and use it in GitHub Desktop.
Using `method_added` to detect the next method
# https://twitter.com/dorianmariefr/status/1411306589739433984
# We'll ignore everything except required args, to keep it simpler
private def with_types(*ordtypes)
mod = is_a?(Module) ? self : Object # to handle edge-case of `main`
mod.module_eval { @next_types = ordtypes }
end
class Module
def method_added(name)
return unless @next_types
ordtypes = @next_types
remove_instance_variable :@next_types
meth = instance_method(name)
params = meth.parameters
ordtypes.size == params.size or raise TypeError, "#{ordtypes.inspect} does not match signature: #{params.inspect}"
define_method name do |*ords|
ords.size == ordtypes.size or raise ArgumentError, "wrong # of args, expected #{ordtypes.inspect}, got #{ords.inspect}"
ords.zip(ordtypes).each_with_index do |(ord, type), i|
type === ord or raise TypeError, "arg #{i} of `#{name}': #{type.inspect}, does not match #{ord.inspect}"
end
meth.bind(self).call(*ords)
end
end
end
# Provided example
with_types Integer, Integer
def add(a, b)
a + b
end
add 1, 2 # => 3
add 'a', 'b' rescue $! # => #<TypeError: arg 0 of `add': Integer, does not match "a">
# With mismatched signature
with_types Integer, Integer
def add2(arg) arg end rescue $! # => #<TypeError: [Integer, Integer] does not match signature: [[:req, :arg]]>
# On a class
class String
with_types String
def add(rhs)
self + rhs
end
end
"a".add "b" # => "ab"
"a".add 5 rescue $! # => #<TypeError: arg 0 of `add': String, does not match 5>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment