Class: T::Types::Base

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

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.method_added(method_name) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
# File 'lib/types/types/base.rb', line 6

def self.method_added(method_name)
  super(method_name)
  # What is now `subtype_of_single?` used to be named `subtype_of?`. Make sure people don't
  # override the wrong thing.
  #
  # NB: Outside of T::Types, we would enforce this by using `sig` and not declaring the method
  # as overridable, but doing so here would result in a dependency cycle.
  if method_name == :subtype_of? && self != T::Types::Base
    raise "`subtype_of?` should not be overridden. You probably want to override " \
          "`subtype_of_single?` instead."
  end
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?

Type equivalence, defined by serializing the type to a string (with ‘#name`) and comparing the resulting strings for equality.



191
192
193
194
195
196
197
198
199
200
201
# File 'lib/types/types/base.rb', line 191

def ==(other)
  case other
  when T::Types::Base
    # Performance fast path: pooled and memoized type instances (e.g. the
    # results of repeated T.nilable(X) calls) are the same object, so they
    # can compare equal without computing and comparing their names.
    other.equal?(self) || other.name == self.name
  else
    false
  end
end

#describe_obj(obj) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/types/types/base.rb', line 136

def describe_obj(obj)
  # Would be redundant to print class and value in these common cases.
  case obj
  when nil, true, false
    return "type #{obj.class}"
  end

  # In rare cases, obj.inspect may fail, or be undefined, so rescue.
  begin
    # Default inspect behavior of, eg; `#<Object:0x0...>` is ugly; just print the hash instead, which is more concise/readable.
    if obj.method(:inspect).owner == Kernel
      "type #{obj.class} with hash #{obj.hash}"
    elsif T::Configuration.include_value_in_type_errors?
      "type #{obj.class} with value #{T::Utils.string_truncate_middle(obj.inspect, 30, 30)}"
    else
      "type #{obj.class}"
    end
  rescue StandardError, SystemStackError
    "type #{obj.class} with unprintable value"
  end
end

#error_message_for_obj(obj) ⇒ Object



158
159
160
161
162
163
164
# File 'lib/types/types/base.rb', line 158

def error_message_for_obj(obj)
  if valid?(obj)
    nil
  else
    error_message(obj)
  end
end

#error_message_for_obj_recursive(obj) ⇒ Object



166
167
168
169
170
171
172
# File 'lib/types/types/base.rb', line 166

def error_message_for_obj_recursive(obj)
  if recursively_valid?(obj)
    nil
  else
    error_message(obj)
  end
end

#hashObject

Equality methods (necessary for deduping types with ‘uniq`)



185
186
187
# File 'lib/types/types/base.rb', line 185

def hash
  name.hash
end

#recursively_valid?(obj) ⇒ Boolean

this will be redefined in certain subclasses

Returns:



20
21
22
# File 'lib/types/types/base.rb', line 20

def recursively_valid?(obj)
  valid?(obj)
end

#subtype_of?(t2) ⇒ Boolean

Mirrors ruby_typer::core::Types::isSubType See git.corp.stripe.com/stripe-internal/ruby-typer/blob/9fc8ed998c04ac0b96592ae6bb3493b8a925c5c1/core/types/subtyping.cc#L912-L950

This method cannot be overridden (see ‘method_added` above). Subclasses only need to implement `subtype_of_single?`).

Returns:



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
89
90
91
92
93
94
95
96
97
98
99
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
# File 'lib/types/types/base.rb', line 52

def subtype_of?(t2)
  t1 = self

  # Fast path over the isSubType mirror below: the dominant pair during
  # override validation is two plain Simples, which match none of the
  # branches in the walk. instance_of? (never
  # is_a?) so that any hypothetical Simple subclass takes the full walk,
  # and the raw subtype_of_single? result is returned unmodified
  # (Module#<= yields nil for unrelated modules, which callers observe).
  if t1.instance_of?(T::Types::Simple) && t2.instance_of?(T::Types::Simple)
    return subtype_of_single?(t2)
  end

  if t2.is_a?(T::Private::Types::TypeAlias)
    t2 = t2.aliased_type
  end

  if t2.is_a?(T::Types::Anything)
    return true
  end

  if t1.is_a?(T::Private::Types::TypeAlias)
    return t1.aliased_type.subtype_of?(t2)
  end

  if t1.is_a?(T::Types::TypeVariable) || t2.is_a?(T::Types::TypeVariable)
    # Generics are erased at runtime. Let's treat them like `T.untyped` for
    # the purpose of things like override checking.
    return true
  end

  # pairs to cover: 1  (_, _)
  #                 2  (_, And)
  #                 3  (_, Or)
  #                 4  (And, _)
  #                 5  (And, And)
  #                 6  (And, Or)
  #                 7  (Or, _)
  #                 8  (Or, And)
  #                 9  (Or, Or)

  # Note: order of cases here matters!
  if t1.is_a?(T::Types::Union) # 7, 8, 9
    # this will be incorrect if/when we have Type members
    return t1.types.all? { |t1_member| t1_member.subtype_of?(t2) }
  end

  if t2.is_a?(T::Types::Intersection) # 2, 5
    # this will be incorrect if/when we have Type members
    return t2.types.all? { |t2_member| t1.subtype_of?(t2_member) }
  end

  if t2.is_a?(T::Types::Union)
    if t1.is_a?(T::Types::Intersection) # 6
      # dropping either of parts eagerly make subtype test be too strict.
      # we have to try both cases, when we normally try only one
      return t2.types.any? { |t2_member| t1.subtype_of?(t2_member) } ||
          t1.types.any? { |t1_member| t1_member.subtype_of?(t2) }
    end
    return t2.types.any? { |t2_member| t1.subtype_of?(t2_member) } # 3
  end

  if t1.is_a?(T::Types::Intersection) # 4
    # this will be incorrect if/when we have Type members
    return t1.types.any? { |t1_member| t1_member.subtype_of?(t2) }
  end

  # 1; Start with some special cases
  if t1.is_a?(T::Private::Types::Void)
    return t2.is_a?(T::Private::Types::Void)
  end

  if t1.is_a?(T::Types::Untyped) || t2.is_a?(T::Types::Untyped)
    return true
  end

  # Rest of (1)
  subtype_of_single?(t2)
end

#to_sObject



132
133
134
# File 'lib/types/types/base.rb', line 132

def to_s
  name
end

#validate!(obj) ⇒ Object

Raises:

  • (TypeError)


178
179
180
181
# File 'lib/types/types/base.rb', line 178

def validate!(obj)
  err = error_message_for_obj(obj)
  raise TypeError.new(err) if err
end