# frozen_string_literal: true module Kind module Types extend self COLONS = '::'.freeze KIND_OF = <<-RUBY def self.%{method_name}(object = Undefined, options = Empty::HASH) default = options[:or] return Kind::Of::%{kind_name} if Undefined == object && default.nil? is_instance = Kind::Of::%{kind_name}.__is_instance__(object) return object if is_instance Kind::Of.(::%{kind_name_to_check}, object && default ? default : object || default) end RUBY KIND_OF_IS = <<-RUBY def self.%{method_name}?(*args) Kind::Of::%{kind_name}.instance?(*args) end RUBY KIND_IS = <<-RUBY def self.%{method_name}(value = Undefined) return Kind::Is::%{kind_name} if Undefined == value Kind::Is.__call__(::%{kind_name_to_check}, value) end RUBY private_constant :KIND_OF, :KIND_IS, :COLONS def add(kind, name: nil) kind_name = Kind.of.Module(kind).name checker = name ? Kind::Of.(String, name) : kind_name if checker.include?(COLONS) add_kind_with_namespace(checker, method_name: name) else add_root(checker, kind_name, method_name: name, kind_name_to_check: kind_name, create_kind_is_mod: false) end end private def add_root(checker, kind_name, method_name:, create_kind_is_mod:, kind_name_to_check: nil) root_kind_name = method_name ? method_name : kind_name params = { method_name: method_name ? method_name : checker, kind_name: method_name ? method_name : kind_name, kind_name_to_check: kind_name_to_check || root_kind_name } add_kind(params, Kind::Of, Kind::Is, create_kind_is_mod) end def add_kind_with_namespace(checker, method_name:) raise NotImplementedError if method_name const_names = checker.split(COLONS) const_names.each_with_index do |const_name, index| if index == 0 add_root(const_name, const_name, method_name: nil, create_kind_is_mod: true) else add_node(const_names, index) end end end def add_node(const_names, index) namespace = const_names[0..(index-1)] namespace_name = namespace.join(COLONS) kind_of_mod = Kind::Of.const_get(namespace_name) kind_is_mod = Kind::Is.const_get(namespace_name) checker = const_names[index] kind_name = const_names[0..index].join(COLONS) params = { method_name: checker, kind_name: kind_name } add_kind(params, kind_of_mod, kind_is_mod, true) end def add_kind(params, kind_of_mod, kind_is_mod, create_kind_is_mod) method_name = params[:method_name] unless kind_of_mod.respond_to?(method_name) kind_name = params[:kind_name] params[:kind_name_to_check] ||= kind_name kind_checker = ::Module.new { extend Checker::Protocol } kind_checker.module_eval("def self.__kind; #{params[:kind_name_to_check]}; end") kind_of_mod.instance_eval(KIND_OF % params) kind_of_mod.const_set(method_name, kind_checker) kind_of_mod.instance_eval(KIND_OF_IS % params) end unless kind_is_mod.respond_to?(method_name) kind_is_mod.instance_eval(KIND_IS % params) kind_is_mod.const_set(method_name, Module.new) if create_kind_is_mod end end end end