Module: Rtype

Extended by:
Rtype
Included in:
Rtype
Defined in:
lib/rtype.rb,
lib/rtype/version.rb,
lib/rtype/behavior.rb,
lib/rtype/behavior/or.rb,
lib/rtype/behavior/and.rb,
lib/rtype/behavior/not.rb,
lib/rtype/behavior/xor.rb,
lib/rtype/behavior/base.rb,
lib/rtype/type_signature.rb,
lib/rtype/behavior/nilable.rb,
lib/rtype/return_type_error.rb,
lib/rtype/argument_type_error.rb,
lib/rtype/type_signature_error.rb

Defined Under Namespace

Modules: Behavior Classes: ArgumentTypeError, ReturnTypeError, TypeSignature, TypeSignatureError

Constant Summary collapse

VERSION =
"0.3.0".freeze
@@type_signatures =

This is just the ‘information’ Any change of this doesn’t affect type checking

Hash.new({})

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.and(*args) ⇒ Object



21
22
23
# File 'lib/rtype/behavior/and.rb', line 21

def self.and(*args)
	Behavior::And[*args]
end

.nilable(*args) ⇒ Object



18
19
20
# File 'lib/rtype/behavior/nilable.rb', line 18

def self.nilable(*args)
	Behavior::Nilable[*args]
end

.not(*args) ⇒ Object



21
22
23
# File 'lib/rtype/behavior/not.rb', line 21

def self.not(*args)
	Behavior::Not[*args]
end

.or(*args) ⇒ Object



21
22
23
# File 'lib/rtype/behavior/or.rb', line 21

def self.or(*args)
	Behavior::Or[*args]
end

.xor(*args) ⇒ Object



22
23
24
# File 'lib/rtype/behavior/xor.rb', line 22

def self.xor(*args)
	Behavior::Xor[*args]
end

Instance Method Details

#arg_message(idx) ⇒ Object



102
103
104
# File 'lib/rtype.rb', line 102

def arg_message(idx)
	"for #{(idx+1).ordinalize} argument:"
end

#arg_type_error_message(idx, expected, value) ⇒ Object

def assert_keyword_arguments_type(expected_kwargs, kwargs) kwargs.each do |key, value| expected = expected_kwargs unless expected.nil? unless valid?(expected, value) raise ArgumentTypeError, “for ‘#key’ argument:n” + type_error_message(expected, value) end end end end



94
95
96
# File 'lib/rtype.rb', line 94

def arg_type_error_message(idx, expected, value)
	"#{arg_message(idx)}\n" + type_error_message(expected, value)
end

#assert_arguments_type(expected_args, args) ⇒ Object



203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/rtype.rb', line 203

def assert_arguments_type(expected_args, args)
	# `length.times` is faster than `each_with_index`
	args.length.times do |i|
		expected = expected_args[i]
		value = args[i]
		unless expected.nil?
			unless valid?(expected, value)
				raise ArgumentTypeError, "for #{(i+1).ordinalize} argument:\n" + type_error_message(expected, value)
			end
		end
	end
end

#assert_arguments_type_with_keywords(expected_args, args, expected_kwargs, kwargs) ⇒ Object



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/rtype.rb', line 218

def assert_arguments_type_with_keywords(expected_args, args, expected_kwargs, kwargs)
	# `length.times` is faster than `each_with_index`
	args.length.times do |i|
		expected = expected_args[i]
		value = args[i]
		unless expected.nil?
			unless valid?(expected, value)
				raise ArgumentTypeError, "#{arg_message(idx)}\n" + type_error_message(expected, value)
			end
		end
	end
	kwargs.each do |key, value|
		expected = expected_kwargs[key]
		unless expected.nil?
			unless valid?(expected, value)
				raise ArgumentTypeError, "#{kwarg_message(key)}\n" + type_error_message(expected, value)
			end
		end
	end
end

#assert_return_type(expected, result) ⇒ Object



241
242
243
244
245
246
247
248
249
250
251
# File 'lib/rtype.rb', line 241

def assert_return_type(expected, result)
	if expected.nil?
		unless result.nil?
			raise ReturnTypeError, "for return:\n" + type_error_message(expected, result)
		end
	else
		unless valid?(expected, result)
			raise ReturnTypeError, "for return:\n" + type_error_message(expected, result)
		end
	end
end

#define_typed_accessor(owner, accessor_name, type_behavior) ⇒ Object



69
70
71
72
73
74
75
# File 'lib/rtype.rb', line 69

def define_typed_accessor(owner, accessor_name, type_behavior)
	getter = accessor_name.to_sym
	setter = :"#{accessor_name}="
	valid?(type_behavior, nil)
	define_typed_method owner, getter, [] => type_behavior
	define_typed_method owner, setter, [type_behavior] => Any
end

#define_typed_method(owner, method_name, type_sig_info) ⇒ Object

Raises:



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/rtype.rb', line 33

def define_typed_method(owner, method_name, type_sig_info)
	raise TypeSignatureError, "Invalid type signature" unless valid_type_sig_info_form?(type_sig_info)
	method_name = method_name.to_sym
	raise ArgumentError, "method_name is nil" if method_name.nil?

	el = type_sig_info.first
	arg_sig = el[0]
	return_sig = el[1]

	if arg_sig.is_a?(Array)
		expected_args = arg_sig
		if expected_args.last.is_a?(Hash)
			expected_kwargs = expected_args.pop
		else
			expected_kwargs = {}
		end
	elsif arg_sig.is_a?(Hash)
		expected_args = []
		expected_kwargs = arg_sig
	end

	expected_args.each { |e| valid?(e, nil) }
	if expected_kwargs.keys.any? { |e| !e.is_a?(Symbol) }
		raise TypeSignatureError, "Invalid type signature: keyword arguments contain non-symbol key"
	end
	expected_kwargs.each_value { |e| valid?(e, nil) }
	valid?(return_sig, nil) unless return_sig.nil?

	sig = TypeSignature.new
	sig.argument_type = arg_sig
	sig.return_type = return_sig
	@@type_signatures[owner][method_name] = sig

	define_typed_method_to_proxy(owner, method_name, expected_args, expected_kwargs, return_sig)
end

#kwarg_message(key) ⇒ Object



106
107
108
# File 'lib/rtype.rb', line 106

def kwarg_message(key)
	"for '#{key}' argument:"
end

#kwarg_type_error_message(key, expected, value) ⇒ Object



98
99
100
# File 'lib/rtype.rb', line 98

def kwarg_type_error_message(key, expected, value)
	"#{kwarg_message(key)}\n" + type_error_message(expected, value)
end

#type_error_message(expected, value) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/rtype.rb', line 111

def type_error_message(expected, value)
	case expected
	when Rtype::Behavior::Base
		expected.error_message(value)
	when Module
		"Expected #{value.inspect} to be a #{expected}"
	when Symbol
		"Expected #{value.inspect} to respond to :#{expected}"
	when Regexp
		"Expected stringified #{value.inspect} to match regexp #{expected.inspect}"
	when Range
		"Expected #{value.inspect} to be included in range #{expected.inspect}"
	when Array
		if value.is_a?(Array)
			arr = expected.map.with_index do |e, idx|
				if e.is_a?(Array)
					"- [#{idx}] index : {\n" + type_error_message(e, value[idx]) + "\n}"
				else
					"- [#{idx}] index : " + type_error_message(e, value[idx])
				end
			end
			"Expected #{value.inspect} to be an array with #{expected.length} elements:\n" + arr.join("\n")
		else
			"Expected #{value.inspect} to be an array"
		end
	when Proc
		"Expected #{value.inspect} to return a truthy value for proc #{expected}"
	when true
		"Expected #{value.inspect} to be a truthy value"
	when false
		"Expected #{value.inspect} to be a falsy value"
	when nil # for return
		"Expected #{value.inspect} to be nil"
	end
end

#type_signaturesObject



77
78
79
# File 'lib/rtype.rb', line 77

def type_signatures
	@@type_signatures
end

#valid?(expected, value) ⇒ Boolean

validate argument type

Returns:



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/rtype.rb', line 173

def valid?(expected, value)
	case expected
	when Module
		value.is_a? expected
	when Symbol
		value.respond_to? expected
	when Regexp
		!!(expected =~ value.to_s)
	when Range
		expected.include?(value)
	when Array
		return false unless value.is_a?(Array)
		return false unless expected.length == value.length
		idx = -1
		expected.all? { |e| idx += 1; valid?(e, value[idx]) }
	when Proc
		!!expected.call(value)
	when true
		!!value
	when false
		!value
	when Rtype::Behavior::Base
		expected.valid? value
	else
		raise TypeSignatureError, "Invalid type signature: Unknown type behavior #{expected}"
	end
end