rtype-legacy 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE +9 -0
- data/README.md +449 -0
- data/Rakefile +12 -0
- data/lib/rtype/argument_type_error.rb +4 -0
- data/lib/rtype/behavior/and.rb +24 -0
- data/lib/rtype/behavior/base.rb +17 -0
- data/lib/rtype/behavior/core_ext.rb +160 -0
- data/lib/rtype/behavior/nilable.rb +21 -0
- data/lib/rtype/behavior/not.rb +24 -0
- data/lib/rtype/behavior/typed_array.rb +25 -0
- data/lib/rtype/behavior/xor.rb +25 -0
- data/lib/rtype/behavior.rb +12 -0
- data/lib/rtype/core_ext.rb +137 -0
- data/lib/rtype/legacy/version.rb +9 -0
- data/lib/rtype/legacy.rb +417 -0
- data/lib/rtype/method_annotator.rb +55 -0
- data/lib/rtype/return_type_error.rb +4 -0
- data/lib/rtype/rtype_component.rb +49 -0
- data/lib/rtype/type_signature.rb +10 -0
- data/lib/rtype/type_signature_error.rb +4 -0
- data/spec/rtype_legacy_spec.rb +825 -0
- data/spec/spec_helper.rb +13 -0
- metadata +112 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
module Rtype
|
2
|
+
module Behavior
|
3
|
+
class Not < Base
|
4
|
+
def initialize(*types)
|
5
|
+
@types = types
|
6
|
+
end
|
7
|
+
|
8
|
+
def valid?(value)
|
9
|
+
@types.all? do |e|
|
10
|
+
!Rtype::valid?(e, value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def error_message(value)
|
15
|
+
arr = @types.map { |e| "NOT " + Rtype::type_error_message(e, value) }
|
16
|
+
arr.join "\nAND "
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def not(*args)
|
22
|
+
Behavior::Not[*args]
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Rtype
|
2
|
+
module Behavior
|
3
|
+
class TypedArray < Base
|
4
|
+
def initialize(type)
|
5
|
+
@type = type
|
6
|
+
Rtype.assert_valid_argument_type_sig_element(@type)
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid?(value)
|
10
|
+
if value.is_a?(Array)
|
11
|
+
any = value.any? do |e|
|
12
|
+
!Rtype::valid?(@type, e)
|
13
|
+
end
|
14
|
+
!any
|
15
|
+
else
|
16
|
+
false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def error_message(value)
|
21
|
+
"Expected #{value.inspect} to be an array with type #{@type.inspect}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Rtype
|
2
|
+
module Behavior
|
3
|
+
class Xor < Base
|
4
|
+
def initialize(*types)
|
5
|
+
@types = types
|
6
|
+
end
|
7
|
+
|
8
|
+
def valid?(value)
|
9
|
+
result = @types.map do |e|
|
10
|
+
Rtype::valid? e, value
|
11
|
+
end
|
12
|
+
result.count(true) == 1
|
13
|
+
end
|
14
|
+
|
15
|
+
def error_message(value)
|
16
|
+
arr = @types.map { |e| Rtype::type_error_message(e, value) }
|
17
|
+
arr.join "\nXOR "
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def xor(*args)
|
23
|
+
Behavior::Xor[*args]
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Rtype
|
2
|
+
module Behavior
|
3
|
+
end
|
4
|
+
end
|
5
|
+
|
6
|
+
require_relative 'behavior/base'
|
7
|
+
require_relative 'behavior/and'
|
8
|
+
require_relative 'behavior/xor'
|
9
|
+
require_relative 'behavior/not'
|
10
|
+
require_relative 'behavior/nilable'
|
11
|
+
require_relative 'behavior/typed_array'
|
12
|
+
require_relative 'behavior/core_ext'
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# true or false
|
2
|
+
module Boolean; end
|
3
|
+
class TrueClass; include Boolean; end
|
4
|
+
class FalseClass; include Boolean; end
|
5
|
+
|
6
|
+
Any = BasicObject
|
7
|
+
|
8
|
+
class Object
|
9
|
+
include ::Rtype::MethodAnnotator
|
10
|
+
end
|
11
|
+
|
12
|
+
module Kernel
|
13
|
+
private
|
14
|
+
def _rtype_component
|
15
|
+
unless @_rtype_component
|
16
|
+
@_rtype_component = ::Rtype::RtypeComponent.new
|
17
|
+
end
|
18
|
+
@_rtype_component
|
19
|
+
end
|
20
|
+
|
21
|
+
# Makes the method typed
|
22
|
+
#
|
23
|
+
# With 'annotation mode', this method works for both instance method and singleton method (class method).
|
24
|
+
# Without it (specifying method name), this method only works for instance method.
|
25
|
+
#
|
26
|
+
# @param [#to_sym, nil] method_name The name of method. If nil, annotation mode works
|
27
|
+
# @param [Hash] type_sig_info A type signature. e.g. [Integer] => Any
|
28
|
+
# @return [void]
|
29
|
+
#
|
30
|
+
# @note Annotation mode doesn't work in the outside of module
|
31
|
+
# @raise [RuntimeError] If called outside of module
|
32
|
+
# @raise [TypeSignatureError] If type_sig_info is invalid
|
33
|
+
def rtype(method_name=nil, type_sig_info)
|
34
|
+
if is_a?(Module)
|
35
|
+
if method_name.nil?
|
36
|
+
::Rtype::assert_valid_type_sig(type_sig_info)
|
37
|
+
_rtype_component.annotation_mode = true
|
38
|
+
_rtype_component.annotation_type_sig = type_sig_info
|
39
|
+
nil
|
40
|
+
else
|
41
|
+
::Rtype::define_typed_method(self, method_name, type_sig_info, false)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
raise "rtype doesn't work in the outside of module"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Makes the singleton method (class method) typed
|
49
|
+
#
|
50
|
+
# @param [#to_sym] method_name
|
51
|
+
# @param [Hash] type_sig_info A type signature. e.g. [Integer] => Any
|
52
|
+
# @return [void]
|
53
|
+
#
|
54
|
+
# @raise [ArgumentError] If method_name is nil
|
55
|
+
# @raise [TypeSignatureError] If type_sig_info is invalid
|
56
|
+
def rtype_self(method_name, type_sig_info)
|
57
|
+
::Rtype.define_typed_method(self, method_name, type_sig_info, true)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Makes the accessor methods (getter and setter) typed
|
61
|
+
#
|
62
|
+
# @param [Array<#to_sym>] accessor_names
|
63
|
+
# @param type_behavior A type behavior
|
64
|
+
# @return [void]
|
65
|
+
#
|
66
|
+
# @raise [ArgumentError] If accessor_names contains nil
|
67
|
+
# @raise [TypeSignatureError] If type_behavior is invalid
|
68
|
+
# @raise [RuntimeError] If called outside of module
|
69
|
+
# @see #rtype
|
70
|
+
def rtype_accessor(*accessor_names, type_behavior)
|
71
|
+
accessor_names.each do |accessor_name|
|
72
|
+
raise ArgumentError, "accessor_names contains nil" if accessor_name.nil?
|
73
|
+
|
74
|
+
accessor_name = accessor_name.to_sym
|
75
|
+
if !respond_to?(accessor_name) || !respond_to?(:"#{accessor_name}=")
|
76
|
+
attr_accessor accessor_name
|
77
|
+
end
|
78
|
+
|
79
|
+
if is_a?(Module)
|
80
|
+
::Rtype::define_typed_accessor(self, accessor_name, type_behavior, false)
|
81
|
+
else
|
82
|
+
raise "rtype_accessor doesn't work in the outside of module"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
nil
|
86
|
+
end
|
87
|
+
|
88
|
+
# Makes the accessor methods (getter and setter) typed
|
89
|
+
#
|
90
|
+
# @param [Array<#to_sym>] accessor_names
|
91
|
+
# @param type_behavior A type behavior
|
92
|
+
# @return [void]
|
93
|
+
#
|
94
|
+
# @raise [ArgumentError] If accessor_names contains nil
|
95
|
+
# @raise [TypeSignatureError] If type_behavior is invalid
|
96
|
+
# @see #rtype_self
|
97
|
+
def rtype_accessor_self(*accessor_names, type_behavior)
|
98
|
+
accessor_names.each do |accessor_name|
|
99
|
+
raise ArgumentError, "accessor_names contains nil" if accessor_name.nil?
|
100
|
+
|
101
|
+
accessor_name = accessor_name.to_sym
|
102
|
+
if !respond_to?(accessor_name) || !respond_to?(:"#{accessor_name}=")
|
103
|
+
singleton_class.send(:attr_accessor, accessor_name)
|
104
|
+
end
|
105
|
+
::Rtype::define_typed_accessor(self, accessor_name, type_behavior, true)
|
106
|
+
end
|
107
|
+
nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class Method
|
112
|
+
# @return [Boolean] Whether the method is typed with rtype
|
113
|
+
def typed?
|
114
|
+
!!::Rtype.type_signatures[owner][name]
|
115
|
+
end
|
116
|
+
|
117
|
+
# @return [TypeSignature]
|
118
|
+
def type_signature
|
119
|
+
::Rtype.type_signatures[owner][name]
|
120
|
+
end
|
121
|
+
|
122
|
+
# @return [Hash]
|
123
|
+
# @see TypeSignature#info
|
124
|
+
def type_info
|
125
|
+
::Rtype.type_signatures[owner][name].info
|
126
|
+
end
|
127
|
+
|
128
|
+
# @return [Array, Hash]
|
129
|
+
def argument_type
|
130
|
+
::Rtype.type_signatures[owner][name].argument_type
|
131
|
+
end
|
132
|
+
|
133
|
+
# @return A type behavior
|
134
|
+
def return_type
|
135
|
+
::Rtype.type_signatures[owner][name].return_type
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Rtype
|
2
|
+
module Legacy
|
3
|
+
VERSION = "0.0.2".freeze
|
4
|
+
# rtype java extension version. nil If the extension is not used
|
5
|
+
JAVA_EXT_VERSION = nil unless const_defined?(:JAVA_EXT_VERSION, false)
|
6
|
+
# rtype c extension version. nil If the extension is not used
|
7
|
+
NATIVE_EXT_VERSION = nil unless const_defined?(:NATIVE_EXT_VERSION, false)
|
8
|
+
end
|
9
|
+
end
|
data/lib/rtype/legacy.rb
ADDED
@@ -0,0 +1,417 @@
|
|
1
|
+
if Object.const_defined?(:RUBY_ENGINE)
|
2
|
+
case RUBY_ENGINE
|
3
|
+
when "jruby"
|
4
|
+
begin
|
5
|
+
require 'java'
|
6
|
+
require 'rtype/legacy/rtype_legacy_java'
|
7
|
+
# puts "Rtype Legacy with Java extension"
|
8
|
+
rescue LoadError
|
9
|
+
# puts "Rtype Legacy without native extension"
|
10
|
+
end
|
11
|
+
when "ruby"
|
12
|
+
begin
|
13
|
+
require 'rtype/legacy/rtype_legacy_native'
|
14
|
+
# puts "Rtype Legacy with C native extension"
|
15
|
+
rescue LoadError
|
16
|
+
# puts "Rtype Legacy without native extension"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
require_relative 'rtype_component'
|
22
|
+
require_relative 'method_annotator'
|
23
|
+
require_relative 'core_ext'
|
24
|
+
require_relative 'legacy/version'
|
25
|
+
require_relative 'type_signature_error'
|
26
|
+
require_relative 'argument_type_error'
|
27
|
+
require_relative 'return_type_error'
|
28
|
+
require_relative 'type_signature'
|
29
|
+
require_relative 'behavior'
|
30
|
+
|
31
|
+
module Rtype
|
32
|
+
extend self
|
33
|
+
|
34
|
+
# This is just 'information'
|
35
|
+
# Any change of this doesn't affect type checking
|
36
|
+
@@type_signatures = Hash.new
|
37
|
+
|
38
|
+
# Makes the method typed
|
39
|
+
# @param owner Owner of the method
|
40
|
+
# @param [#to_sym] method_name
|
41
|
+
# @param [Hash] type_sig_info A type signature. e.g. `[Integer, Float] => Float`
|
42
|
+
# @param [Boolean] singleton Whether the method is singleton method
|
43
|
+
# @return [void]
|
44
|
+
#
|
45
|
+
# @raise [ArgumentError] If method_name is nil, keyword argument signature is not empty, or singleton is not a boolean
|
46
|
+
# @raise [TypeSignatureError] If type_sig_info is invalid
|
47
|
+
def define_typed_method(owner, method_name, type_sig_info, singleton)
|
48
|
+
method_name = method_name.to_sym
|
49
|
+
raise ArgumentError, "method_name is nil" if method_name.nil?
|
50
|
+
raise ArgumentError, "singleton must be a boolean" unless singleton.is_a?(Boolean)
|
51
|
+
assert_valid_type_sig(type_sig_info)
|
52
|
+
|
53
|
+
el = type_sig_info.first
|
54
|
+
arg_sig = el[0]
|
55
|
+
return_sig = el[1]
|
56
|
+
|
57
|
+
if arg_sig.is_a?(Array)
|
58
|
+
expected_args = arg_sig.dup
|
59
|
+
if expected_args.last.is_a?(Hash)
|
60
|
+
kwargs = expected_args.pop
|
61
|
+
# empty kwargs signature
|
62
|
+
else
|
63
|
+
# empty kwargs signature
|
64
|
+
end
|
65
|
+
elsif arg_sig.is_a?(Hash)
|
66
|
+
# empty kwargs signature
|
67
|
+
expected_args = []
|
68
|
+
end
|
69
|
+
|
70
|
+
sig = TypeSignature.new
|
71
|
+
sig.argument_type = arg_sig
|
72
|
+
sig.return_type = return_sig
|
73
|
+
unless @@type_signatures.key?(owner)
|
74
|
+
@@type_signatures[owner] = {}
|
75
|
+
end
|
76
|
+
@@type_signatures[owner][method_name] = sig
|
77
|
+
|
78
|
+
redefine_method_to_typed(owner, method_name, expected_args, return_sig, singleton)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Calls `attr_accessor` if the accessor method(getter/setter) is not defined.
|
82
|
+
# and makes it typed.
|
83
|
+
#
|
84
|
+
# this method uses `define_typed_method` for getter and setter.
|
85
|
+
#
|
86
|
+
# @param owner Owner of the accessor
|
87
|
+
# @param [#to_sym] accessor_name
|
88
|
+
# @param type_behavior A type behavior. e.g. Integer
|
89
|
+
# @param [Boolean] singleton Whether the method is singleton method
|
90
|
+
# @return [void]
|
91
|
+
#
|
92
|
+
# @raise [ArgumentError] If accessor_name is nil or singleton is not a boolean
|
93
|
+
# @raise [TypeSignatureError]
|
94
|
+
def define_typed_accessor(owner, accessor_name, type_behavior, singleton)
|
95
|
+
raise ArgumentError, "accessor_name is nil" if accessor_name.nil?
|
96
|
+
getter = accessor_name.to_sym
|
97
|
+
setter = :"#{accessor_name}="
|
98
|
+
valid?(type_behavior, nil)
|
99
|
+
define_typed_method owner, getter, {[] => type_behavior}, singleton
|
100
|
+
define_typed_method owner, setter, {[type_behavior] => Any}, singleton
|
101
|
+
end
|
102
|
+
|
103
|
+
# This is just 'information'
|
104
|
+
# Any change of this doesn't affect type checking
|
105
|
+
#
|
106
|
+
# @return [Hash]
|
107
|
+
# @note type_signatures[owner][method_name]
|
108
|
+
def type_signatures
|
109
|
+
@@type_signatures
|
110
|
+
end
|
111
|
+
|
112
|
+
# @param [Integer] idx
|
113
|
+
# @param expected A type behavior
|
114
|
+
# @param value
|
115
|
+
# @return [String] A error message
|
116
|
+
#
|
117
|
+
# @raise [ArgumentError] If expected is invalid
|
118
|
+
def arg_type_error_message(idx, expected, value)
|
119
|
+
"#{arg_message(idx)}\n" + type_error_message(expected, value)
|
120
|
+
end
|
121
|
+
|
122
|
+
# @return [String]
|
123
|
+
def arg_message(idx)
|
124
|
+
"for #{ordinalize_number(idx+1)} argument:"
|
125
|
+
end
|
126
|
+
|
127
|
+
# Returns a error message for the pair of type behavior and value
|
128
|
+
#
|
129
|
+
# @param expected A type behavior
|
130
|
+
# @param value
|
131
|
+
# @return [String] error message
|
132
|
+
#
|
133
|
+
# @note This method doesn't check the value is valid
|
134
|
+
# @raise [TypeSignatureError] If expected is invalid
|
135
|
+
def type_error_message(expected, value)
|
136
|
+
case expected
|
137
|
+
when Rtype::Behavior::Base
|
138
|
+
expected.error_message(value)
|
139
|
+
when Module
|
140
|
+
"Expected #{value.inspect} to be a #{expected}"
|
141
|
+
when Symbol
|
142
|
+
"Expected #{value.inspect} to respond to :#{expected}"
|
143
|
+
when Regexp
|
144
|
+
"Expected stringified #{value.inspect} to match regexp #{expected.inspect}"
|
145
|
+
when Range
|
146
|
+
"Expected #{value.inspect} to be included in range #{expected.inspect}"
|
147
|
+
when Array
|
148
|
+
arr = expected.map { |e| type_error_message(e, value) }
|
149
|
+
arr.join("\nOR ")
|
150
|
+
when Hash
|
151
|
+
if value.is_a?(Hash)
|
152
|
+
arr = []
|
153
|
+
expected.each do |k, v|
|
154
|
+
if v.is_a?(Array) || v.is_a?(Hash)
|
155
|
+
arr << "- #{k} : {\n" + type_error_message(v, value[k]) + "\n}"
|
156
|
+
else
|
157
|
+
arr << "- #{k} : " + type_error_message(v, value[k])
|
158
|
+
end
|
159
|
+
end
|
160
|
+
"Expected #{value.inspect} to be a hash with #{expected.length} elements:\n" + arr.join("\n")
|
161
|
+
else
|
162
|
+
"Expected #{value.inspect} to be a hash"
|
163
|
+
end
|
164
|
+
when Proc
|
165
|
+
"Expected #{value.inspect} to return a truthy value for proc #{expected}"
|
166
|
+
when true
|
167
|
+
"Expected #{value.inspect} to be a truthy value"
|
168
|
+
when false
|
169
|
+
"Expected #{value.inspect} to be a falsy value"
|
170
|
+
when nil # for return
|
171
|
+
"Expected #{value.inspect} to be nil"
|
172
|
+
else
|
173
|
+
raise TypeSignatureError, "Invalid type behavior #{expected}"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Checks the type signature is valid
|
178
|
+
#
|
179
|
+
# e.g.
|
180
|
+
# `[Integer] => Any` is valid.
|
181
|
+
# `[Integer]` or `Any` are invalid
|
182
|
+
#
|
183
|
+
# @param sig A type signature
|
184
|
+
# @raise [TypeSignatureError] If sig is invalid
|
185
|
+
def assert_valid_type_sig(sig)
|
186
|
+
unless sig.is_a?(Hash)
|
187
|
+
raise TypeSignatureError, "Invalid type signature: type signature is not hash"
|
188
|
+
end
|
189
|
+
if sig.empty?
|
190
|
+
raise TypeSignatureError, "Invalid type signature: type signature is empty hash"
|
191
|
+
end
|
192
|
+
assert_valid_arguments_type_sig(sig.first[0])
|
193
|
+
assert_valid_return_type_sig(sig.first[1])
|
194
|
+
end
|
195
|
+
|
196
|
+
# Checks the arguments type signature is valid
|
197
|
+
#
|
198
|
+
# e.g.
|
199
|
+
# `[Integer]`, `{key: "value"}` are valid (the second is keyword argument signature and ignored in rtype-legacy).
|
200
|
+
# `Integer` is invalid.
|
201
|
+
#
|
202
|
+
# @param sig A arguments type signature
|
203
|
+
# @raise [TypeSignatureError] If sig is invalid
|
204
|
+
def assert_valid_arguments_type_sig(sig)
|
205
|
+
if sig.is_a?(Array)
|
206
|
+
sig = sig.dup
|
207
|
+
if sig.last.is_a?(Hash)
|
208
|
+
kwargs = sig.pop
|
209
|
+
else
|
210
|
+
kwargs = {}
|
211
|
+
end
|
212
|
+
sig.each { |e| assert_valid_argument_type_sig_element(e) }
|
213
|
+
unless kwargs.empty?
|
214
|
+
raise TypeSignatureError, "Invalid type signature: keyword arguments must be empty"
|
215
|
+
end
|
216
|
+
elsif sig.is_a?(Hash)
|
217
|
+
unless kwargs.empty?
|
218
|
+
raise TypeSignatureError, "Invalid type signature: keyword arguments must be empty"
|
219
|
+
end
|
220
|
+
else
|
221
|
+
raise TypeSignatureError, "Invalid type signature: arguments type signature is neither array nor hash"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Checks the type behavior is valid
|
226
|
+
#
|
227
|
+
# @param sig A type behavior
|
228
|
+
# @raise [TypeSignatureError] If sig is invalid
|
229
|
+
def assert_valid_argument_type_sig_element(sig)
|
230
|
+
case sig
|
231
|
+
when Rtype::Behavior::Base
|
232
|
+
when Module
|
233
|
+
when Symbol
|
234
|
+
when Regexp
|
235
|
+
when Range
|
236
|
+
when Array
|
237
|
+
sig.each do |e|
|
238
|
+
assert_valid_argument_type_sig_element(e)
|
239
|
+
end
|
240
|
+
when Hash
|
241
|
+
sig.each_value do |e|
|
242
|
+
assert_valid_argument_type_sig_element(e)
|
243
|
+
end
|
244
|
+
when Proc
|
245
|
+
when true
|
246
|
+
when false
|
247
|
+
when nil
|
248
|
+
else
|
249
|
+
raise TypeSignatureError, "Invalid type signature: Unknown type behavior #{sig}"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# @see #assert_valid_argument_type_sig_element
|
254
|
+
def assert_valid_return_type_sig(sig)
|
255
|
+
assert_valid_argument_type_sig_element(sig)
|
256
|
+
end
|
257
|
+
|
258
|
+
unless respond_to?(:valid?)
|
259
|
+
# Checks the value is valid for the type behavior
|
260
|
+
#
|
261
|
+
# @param expected A type behavior
|
262
|
+
# @param value
|
263
|
+
# @return [Boolean]
|
264
|
+
#
|
265
|
+
# @raise [TypeSignatureError] If expected is invalid
|
266
|
+
def valid?(expected, value)
|
267
|
+
case expected
|
268
|
+
when Module
|
269
|
+
value.is_a? expected
|
270
|
+
when Symbol
|
271
|
+
value.respond_to? expected
|
272
|
+
when Regexp
|
273
|
+
!!(expected =~ value.to_s)
|
274
|
+
when Range
|
275
|
+
expected.include?(value)
|
276
|
+
when Hash
|
277
|
+
return false unless value.is_a?(Hash)
|
278
|
+
return false unless expected.keys == value.keys
|
279
|
+
expected.all? { |k, v| valid?(v, value[k]) }
|
280
|
+
when Array
|
281
|
+
expected.any? { |e| valid?(e, value) }
|
282
|
+
when Proc
|
283
|
+
!!expected.call(value)
|
284
|
+
when true
|
285
|
+
!!value
|
286
|
+
when false
|
287
|
+
!value
|
288
|
+
when Rtype::Behavior::Base
|
289
|
+
expected.valid? value
|
290
|
+
when nil
|
291
|
+
value.nil?
|
292
|
+
else
|
293
|
+
raise TypeSignatureError, "Invalid type signature: Unknown type behavior #{expected}"
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
unless respond_to?(:assert_arguments_type)
|
299
|
+
# Validates arguments
|
300
|
+
#
|
301
|
+
# @param [Array] expected_args A type signature for non-keyword arguments
|
302
|
+
# @param [Array] args
|
303
|
+
# @return [void]
|
304
|
+
#
|
305
|
+
# @raise [TypeSignatureError] If expected_args is invalid
|
306
|
+
# @raise [ArgumentTypeError] If args is invalid
|
307
|
+
def assert_arguments_type(expected_args, args)
|
308
|
+
e_len = expected_args.length
|
309
|
+
# `length.times` is faster than `each_with_index`
|
310
|
+
args.length.times do |i|
|
311
|
+
break if i >= e_len
|
312
|
+
expected = expected_args[i]
|
313
|
+
value = args[i]
|
314
|
+
unless valid?(expected, value)
|
315
|
+
raise ArgumentTypeError, "#{arg_message(i)}\n" + type_error_message(expected, value)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
nil
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
# Validates result
|
323
|
+
#
|
324
|
+
# @param expected A type behavior
|
325
|
+
# @param result
|
326
|
+
# @return [void]
|
327
|
+
#
|
328
|
+
# @raise [TypeSignatureError] If expected is invalid
|
329
|
+
# @raise [ReturnTypeError] If result is invalid
|
330
|
+
unless respond_to?(:assert_return_type)
|
331
|
+
def assert_return_type(expected, result)
|
332
|
+
unless valid?(expected, result)
|
333
|
+
raise ReturnTypeError, "for return:\n" + type_error_message(expected, result)
|
334
|
+
end
|
335
|
+
nil
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
private
|
340
|
+
# @param owner
|
341
|
+
# @param [Symbol] method_name
|
342
|
+
# @param [Array] expected_args
|
343
|
+
# @param return_sig
|
344
|
+
# @param [Boolean] singleton
|
345
|
+
# @return [void]
|
346
|
+
def redefine_method_to_typed(owner, method_name, expected_args, return_sig, singleton)
|
347
|
+
compo = owner.send(:_rtype_component)
|
348
|
+
|
349
|
+
methods = owner.instance_methods if !singleton
|
350
|
+
methods = owner.methods if singleton
|
351
|
+
|
352
|
+
if methods.include?(method_name)
|
353
|
+
=begin
|
354
|
+
unless compo.has_old?(method_name, singleton)
|
355
|
+
old_method = owner.instance_method(method_name) if !singleton
|
356
|
+
old_method = owner.method(method_name) if singleton
|
357
|
+
compo.set_old(method_name, singleton, old_method)
|
358
|
+
end
|
359
|
+
=end
|
360
|
+
if compo.has_old?(method_name, singleton)
|
361
|
+
old_method = compo.get_old(method_name, singleton)
|
362
|
+
else
|
363
|
+
old_method = owner.instance_method(method_name) if !singleton
|
364
|
+
old_method = owner.method(method_name) if singleton
|
365
|
+
compo.set_old(method_name, singleton, old_method)
|
366
|
+
end
|
367
|
+
else # Undefined method
|
368
|
+
compo.add_undef(method_name, expected_args, return_sig, singleton)
|
369
|
+
return
|
370
|
+
end
|
371
|
+
|
372
|
+
owner = owner.singleton_class if singleton
|
373
|
+
priv = owner.private_method_defined?(method_name)
|
374
|
+
prot = owner.protected_method_defined?(method_name)
|
375
|
+
|
376
|
+
if !singleton
|
377
|
+
# `send` is faster than `method(...).call`
|
378
|
+
owner.send :define_method, method_name do |*args, &block|
|
379
|
+
::Rtype::assert_arguments_type(expected_args, args)
|
380
|
+
# result = compo.get_old(method_name, singleton).bind(self).call(*args, &block)
|
381
|
+
result = old_method.bind(self).call(*args, &block)
|
382
|
+
::Rtype::assert_return_type(return_sig, result)
|
383
|
+
result
|
384
|
+
end
|
385
|
+
else
|
386
|
+
owner.send :define_method, method_name do |*args, &block|
|
387
|
+
::Rtype::assert_arguments_type(expected_args, args)
|
388
|
+
# result = compo.get_old(method_name, singleton).call(*args, &block)
|
389
|
+
result = old_method.call(*args, &block)
|
390
|
+
::Rtype::assert_return_type(return_sig, result)
|
391
|
+
result
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
if priv
|
396
|
+
owner.send(:private, method_name)
|
397
|
+
elsif prot
|
398
|
+
owner.send(:protected, method_name)
|
399
|
+
end
|
400
|
+
nil
|
401
|
+
end
|
402
|
+
|
403
|
+
# @param [Integer] num
|
404
|
+
# @return [String]
|
405
|
+
def ordinalize_number(num)
|
406
|
+
if (11..13).include?(num % 100)
|
407
|
+
"#{num}th"
|
408
|
+
else
|
409
|
+
case num % 10
|
410
|
+
when 1; "#{num}st"
|
411
|
+
when 2; "#{num}nd"
|
412
|
+
when 3; "#{num}rd"
|
413
|
+
else "#{num}th"
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Rtype
|
2
|
+
module MethodAnnotator
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def method_added(name)
|
9
|
+
if @_rtype_component
|
10
|
+
compo = @_rtype_component
|
11
|
+
|
12
|
+
if compo.annotation_mode && !compo.ignoring
|
13
|
+
if compo.has_undef?(name, false)
|
14
|
+
compo.remove_undef(name, false)
|
15
|
+
end
|
16
|
+
|
17
|
+
compo.ignoring = true
|
18
|
+
::Rtype::define_typed_method(self, name, compo.annotation_type_sig, false)
|
19
|
+
compo.annotation_mode = false
|
20
|
+
compo.annotation_type_sig = nil
|
21
|
+
compo.ignoring = false
|
22
|
+
|
23
|
+
elsif compo.has_undef?(name, false)
|
24
|
+
info = compo.get_undef(name, false)
|
25
|
+
compo.remove_undef(name, false)
|
26
|
+
::Rtype.send(:redefine_method_to_typed, self, name, info[:expected], info[:result], false)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def singleton_method_added(name)
|
32
|
+
if @_rtype_component
|
33
|
+
compo = @_rtype_component
|
34
|
+
|
35
|
+
if compo.annotation_mode && !compo.ignoring
|
36
|
+
if compo.has_undef?(name, true)
|
37
|
+
compo.remove_undef(name, true)
|
38
|
+
end
|
39
|
+
|
40
|
+
compo.ignoring = true
|
41
|
+
::Rtype::define_typed_method(self, name, compo.annotation_type_sig, true)
|
42
|
+
compo.annotation_mode = false
|
43
|
+
compo.annotation_type_sig = nil
|
44
|
+
compo.ignoring = false
|
45
|
+
|
46
|
+
elsif compo.has_undef?(name, true)
|
47
|
+
info = compo.get_undef(name, true)
|
48
|
+
compo.remove_undef(name, true)
|
49
|
+
::Rtype.send(:redefine_method_to_typed, self, name, info[:expected], info[:result], true)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|