Skip to content

Instantly share code, notes, and snippets.

@serradura
Created March 9, 2022 02:30
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save serradura/352432f0e163ebec554d3bdcbf5cb76c to your computer and use it in GitHub Desktop.
Save serradura/352432f0e163ebec554d3bdcbf5cb76c to your computer and use it in GitHub Desktop.
Kind::Interface prototype
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