Class: T::Types::TypedEnumerable

Inherits:
Base
  • Object
show all
Defined in:
lib/types/types/typed_enumerable.rb

Overview

Note: All subclasses of Enumerable should add themselves to the ‘case` statement below in `describe_obj` in order to get better error messages.

Defined Under Namespace

Classes: Untyped

Instance Method Summary collapse

Methods inherited from Base

#==, #error_message_for_obj, #error_message_for_obj_recursive, #hash, method_added, #subtype_of?, #to_s, #validate!

Constructor Details

#initialize(type) ⇒ TypedEnumerable



9
10
11
# File 'lib/types/types/typed_enumerable.rb', line 9

def initialize(type)
  @inner_type = type
end

Instance Method Details

#build_typeObject



17
18
19
20
# File 'lib/types/types/typed_enumerable.rb', line 17

def build_type
  type
  nil
end

#describe_obj(obj) ⇒ Object

overrides Base



109
110
111
112
# File 'lib/types/types/typed_enumerable.rb', line 109

def describe_obj(obj)
  return super unless obj.is_a?(Enumerable)
  type_from_instance(obj).name
end

#nameObject

overrides Base



27
28
29
# File 'lib/types/types/typed_enumerable.rb', line 27

def name
  "T::Enumerable[#{type.name}]"
end

#recursively_valid?(obj) ⇒ Boolean

overrides Base



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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/types/types/typed_enumerable.rb', line 37

def recursively_valid?(obj)
  return false unless obj.is_a?(Enumerable)
  case obj
  when Array
    begin
      it = 0
      while it < obj.count
        return false unless type.recursively_valid?(obj[it])
        it += 1
      end
      true
    end
  when Hash
    return false unless type.is_a?(FixedArray)
    types = type.types
    return false if types.count != 2
    key_type = types[0]
    value_type = types[1]
    obj.each_pair do |key, val|
      # Some objects (I'm looking at you Rack::Utils::HeaderHash) don't
      # iterate over a [key, value] array, so we can't just use the type.recursively_valid?(v)
      return false if !key_type.recursively_valid?(key) || !value_type.recursively_valid?(val)
    end
    true
  when Enumerator::Lazy
    # Enumerators can be unbounded: see `[:foo, :bar].cycle`
    true
  when Enumerator::Chain
    # Enumerators can be unbounded: see `[:foo, :bar].cycle`
    true
  when Enumerator
    # Enumerators can be unbounded: see `[:foo, :bar].cycle`
    true
  when Range
    # A nil beginning or a nil end does not provide any type information. That is, nil in a range represents
    # boundlessness, it does not express a type. For example `(nil...nil)` is not a T::Range[NilClass], its a range
    # of unknown types (T::Range[T.untyped]).
    # Similarly, `(nil...1)` is not a `T::Range[T.nilable(Integer)]`, it's a boundless range of Integer.
    (obj.begin.nil? || type.recursively_valid?(obj.begin)) && (obj.end.nil? || type.recursively_valid?(obj.end))
  when Set
    obj.each do |item|
      return false unless type.recursively_valid?(item)
    end

    true
  else
    # We don't check the enumerable since it isn't guaranteed to be
    # rewindable (e.g. STDIN) and it may be expensive to enumerate
    # (e.g. an enumerator that makes an HTTP request)"
    true
  end
end

#typeObject



13
14
15
# File 'lib/types/types/typed_enumerable.rb', line 13

def type
  @type ||= T::Utils.coerce(@inner_type)
end

#underlying_classObject



22
23
24
# File 'lib/types/types/typed_enumerable.rb', line 22

def underlying_class
  Enumerable
end

#valid?(obj) ⇒ Boolean

overrides Base



32
33
34
# File 'lib/types/types/typed_enumerable.rb', line 32

def valid?(obj)
  obj.is_a?(Enumerable)
end