lib/rubype.rb in rubype-0.2.2 vs lib/rubype.rb in rubype-0.2.3

- old
+ new

@@ -4,75 +4,106 @@ TrueClass.send(:include, Boolean) FalseClass.send(:include, Boolean) class Module private - def __rubype__ - prepend (@__rubype__ = Module.new) unless @__rubype__ - @__rubype__ - end + # @return [Module] + def __rubype__ + prepend (@__rubype__ = Module.new) unless @__rubype__ + @__rubype__ + end - # @param hash [Hash] {method_name: [ArgClass1, ArgClass2, ... ArgClassn => RtnClass]} - def typesig(hash) - meth = hash.keys.first - *arg_types, type_pair = hash.values.first + # @param meth [Symbol] + # @param type_info_hash [Hash] { [ArgInfo_1, ArgInfo_2, ... ArgInfo_n] => RtnInfo } + # @return self + def typesig(meth, type_info_hash) + ::Rubype.send(:define_typed_method, self, meth, type_info_hash, __rubype__) + self + end +end - __rubype__.send(:define_method, meth) do |*args, &block| - ::Rubype.send(:assert_arg_type, self, meth, args, arg_types << type_pair.keys.first) - rtn = super(*args, &block) - ::Rubype.send(:assert_trn_type, self, meth, rtn, type_pair.values.first) - rtn +class Method + def type_info + if methods_hash = Rubype.typed_method_info[owner] + methods_hash[name] end - self end end module Rubype class ArgumentTypeError < ::TypeError; end class ReturnTypeError < ::TypeError; end + @@typed_method_info = Hash.new({}) class << self + def typed_method_info + @@typed_method_info + end + private + # @param caller [Object] + # @param type_info_hash [Hash] { [ArgInfo_1, ArgInfo_2, ... ArgInfo_n] => RtnInfo } + # @param module [Module] + def define_typed_method(meth_caller, meth, type_info_hash, __rubype__) + arg_types, rtn_type = *strip_type_info(type_info_hash) + @@typed_method_info[meth_caller][meth] = { + arg_types => rtn_type + } + __rubype__.send(:define_method, meth) do |*args, &block| + ::Rubype.send(:assert_arg_type, meth_caller, meth, args, arg_types) + rtn = super(*args, &block) + ::Rubype.send(:assert_trn_type, meth_caller, meth, rtn, rtn_type) + rtn + end + end - # @param caller [Module] - # @param meth [Symbol] - # @param args [Array<Object>] - # @param type_infos [Array<Class, Symbol>] - def assert_arg_type(caller, meth, args, type_infos) - args.each_with_index do |arg, i| - case type_check(arg, type_infos[i]) + # @param type_info_hash [Hash] { [ArgInfo_1, ArgInfo_2, ... ArgInfo_n] => RtnInfo } + # @return arg_types [Array<Class, Symbol>], rtn_type [Class, Symbol] + def strip_type_info(type_info_hash) + arg_types, rtn_type = type_info_hash.first + [arg_types, rtn_type] + end + + # @param caller [Module] + # @param meth [Symbol] + # @param args [Array<Object>] + # @param type_infos [Array<Class, Symbol>] + def assert_arg_type(caller, meth, args, type_infos) + args.zip(type_infos).each.with_index(1) do |(arg, type_info), i| + case type_check(arg, type_info) + when :need_correct_class + raise ArgumentTypeError, + "Expected #{caller.class}##{meth}'s #{i}th argument to be #{type_info} but got #{arg.inspect} instead" + when :need_correct_method + raise ArgumentTypeError, + "Expected #{caller.class}##{meth}'s #{i}th argument to have method ##{type_info} but got #{arg.inspect} instead" + end + end + end + + # @param caller [Module] + # @param rtn [Object] + # @param type_info [Class, Symbol] + def assert_trn_type(caller, meth, rtn, type_info) + case type_check(rtn, type_info) when :need_correct_class - raise ArgumentTypeError, - "Expected #{caller.class}##{meth}'s #{i+1}th argument to be #{type_infos[i]} but got #{arg.inspect} instead" + raise ReturnTypeError, + "Expected #{caller.class}##{meth} to return #{type_info} but got #{rtn.inspect} instead" when :need_correct_method - raise ArgumentTypeError, - "Expected #{caller.class}##{meth}'s #{i+1}th argument to have method ##{type_infos[i]} but got #{arg.inspect} instead" + raise ReturnTypeError, + "Expected #{caller.class}##{meth} to return object which has method ##{type_info} but got #{rtn.inspect} instead" end end - end - # @param caller [Module] - # @param rtn [Object] - # @param type_info [Class, Symbol] - def assert_trn_type(caller, meth, rtn, type_info) - case type_check(rtn, type_info) - when :need_correct_class - raise ReturnTypeError, - "Expected #{caller.class}##{meth} to return #{type_info} but got #{rtn.inspect} instead" - when :need_correct_method - raise ReturnTypeError, - "Expected #{caller.class}##{meth} to have method ##{type_info} but got #{rtn.inspect} instead" + # @param obj [Object] + # @param type_info [Class, Symbol] + # @return [Symbol] + def type_check(obj, type_info) + case type_info + when Module + :need_correct_class unless (obj.is_a?(type_info) || type_info == Any) + when Symbol + :need_correct_method unless (obj.respond_to?(type_info)) + end end - end - - # @param obj [Object] - # @param type_info [Class, Symbol] - # @return [Symbol] - def type_check(obj, type_info) - if type_info.is_a?(Module) && !(obj.is_a?(type_info) || type_info == Any) - :need_correct_class - elsif type_info.is_a?(Symbol) && !(obj.respond_to?(type_info)) - :need_correct_method - end - end end end