Module: 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.2.0".freeze
@@type_signatures =

Any change of this doesn’t affect type checking

Hash.new({})

Class 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

.arg_message(idx) ⇒ Object



91
92
93
# File 'lib/rtype.rb', line 91

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



83
84
85
# File 'lib/rtype.rb', line 83

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



192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/rtype.rb', line 192

def self.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



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/rtype.rb', line 207

def self.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



230
231
232
233
234
235
236
237
238
239
240
# File 'lib/rtype.rb', line 230

def self.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



58
59
60
61
62
63
64
# File 'lib/rtype.rb', line 58

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:



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/rtype.rb', line 22

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



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

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

.kwarg_type_error_message(key, expected, value) ⇒ Object



87
88
89
# File 'lib/rtype.rb', line 87

def kwarg_type_error_message(key, expected, value)
  "#{kwarg_message(key)}\n" + type_error_message(expected, value)
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

.type_error_message(expected, value) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/rtype.rb', line 100

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



66
67
68
# File 'lib/rtype.rb', line 66

def type_signatures
  @@type_signatures
end

.valid?(expected, value) ⇒ Boolean

validate argument type

Returns:



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/rtype.rb', line 162

def self.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

.xor(*args) ⇒ Object



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

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