Created
March 9, 2022 02:30
-
-
Save serradura/352432f0e163ebec554d3bdcbf5cb76c to your computer and use it in GitHub Desktop.
Kind::Interface prototype
This file contains hidden or 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 Kind | |
module Interface | |
module State | |
extend self | |
@current = :enabled | |
def disable! | |
@current = :disabled | |
end | |
def enabled? | |
@current == :enabled | |
end | |
end | |
module Methods | |
class Definition | |
attr_reader :name, :kargs | |
def initialize(name, kargs) | |
@name = name | |
@kargs = kargs | |
@void = true | |
@returns = nil | |
end | |
def void | |
freeze | |
end | |
def returns(kind) | |
@void = false | |
@returns = kind | |
freeze | |
end | |
def void? | |
@void | |
end | |
def kargs_names | |
@kargs.each_key.map { |name| ":#{name}" }.join(',') | |
end | |
def kargs_to_eval | |
@kargs.each_key.map { |name| "#{name}:" }.join(',') | |
end | |
def kargs_inputting_to_eval | |
@kargs.each_key.map { |name| "#{name}: #{name}" }.join(',') | |
end | |
InvalidType = ->(expected, value) { ::TypeError.new("#{value.inspect} expected to be a kind of #{expected}") } | |
def kargs_assert(hash) | |
hash.each do |key, value| | |
expected = @kargs[key] | |
expected === value or fail InvalidType[expected, value] | |
end | |
end | |
def returns_assert(value) | |
return value if @returns === value | |
fail InvalidType[@returns, value] | |
end | |
end | |
protected def __def_methods__ | |
@__def_methods__ | |
end | |
protected def def_method(name, **kargs) | |
__def_methods__[name] = Definition.new(name, kargs) | |
end | |
module MethodAddedHandler | |
def method_added(method_name) | |
return unless spec = __def_methods__[method_name] | |
parameters = instance_method(method_name).parameters | |
if parameters.empty? || !parameters.map(&:first).all?(:keyreq) || !(parameters.map(&:last) - spec.kargs.keys).empty? | |
fail ArgumentError, "method #{self}##{method_name} is expected to define the keywords: #{spec.kargs_names}" | |
end | |
alias_name = "__real_#{method_name}".to_sym | |
return if private_instance_methods.include?(alias_name) | |
self.alias_method(alias_name, method_name) | |
self.send(:private, alias_name) | |
method_def = | |
"def #{method_name}(#{spec.kargs_to_eval})\n" \ | |
" method_spec = self.class.__def_methods__[__method__]\n" \ | |
"\n" \ | |
" method_spec.kargs_assert(#{spec.kargs_inputting_to_eval})\n" \ | |
"\n" \ | |
" real_output = __real_#{method_name}(#{spec.kargs_inputting_to_eval})\n" \ | |
"\n" \ | |
" method_spec.returns_assert(real_output)\n" \ | |
"end" | |
self.class_eval(method_def, __FILE__, __LINE__ + 1) | |
end | |
end | |
def append_features(base) | |
__def_methods__.freeze | |
__def_methods__.each do |name, spec| | |
method_def = | |
"def #{name}(#{spec.kargs_to_eval})\n" \ | |
" raise NotImplementedError\n" \ | |
"end" | |
base.class_eval(method_def, __FILE__, __LINE__ + 1) | |
end | |
base.instance_variable_set(:@__def_methods__, __def_methods__.freeze) | |
base.instance_eval('def __def_methods__; @__def_methods__; end', __FILE__, __LINE__ + 1) | |
base.extend(MethodAddedHandler) | |
end | |
end | |
module NullMethods | |
module Definition | |
def self.returns(*) | |
end | |
end | |
def def_method(name, **) | |
Definition | |
end | |
end | |
def self.extended(base) | |
base.is_a?(Module) && !base.is_a?(Class) or fail TypeError, "#{base} must be a module" | |
strategy = State.enabled? ? Methods : NullMethods | |
base.instance_variable_set(:@__def_methods__, {}) | |
base.extend(strategy) | |
end | |
end | |
end | |
# Kind::Interface::State.disable! | |
module Greetable | |
extend Kind::Interface | |
def_method(:greet, name: String).returns(String) | |
end | |
class Person | |
include Greetable | |
def greet(name:) | |
"Hi, #{name}" | |
end | |
end | |
puts Person.new.greet(name: 1) | |
binding.irb |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment