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.



181
182
183
184
185
186
187
188
# File 'lib/types/types/base.rb', line 181

def ==(other)
  case other
  when T::Types::Base
    other.name == self.name
  else
    false
  end
end

#describe_obj(obj) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/types/types/base.rb', line 126

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



148
149
150
151
152
153
154
# File 'lib/types/types/base.rb', line 148

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

#error_message_for_obj_recursive(obj) ⇒ Object



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

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`)



175
176
177
# File 'lib/types/types/base.rb', line 175

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
# File 'lib/types/types/base.rb', line 52

def subtype_of?(t2)
  t1 = self

  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



122
123
124
# File 'lib/types/types/base.rb', line 122

def to_s
  name
end

#validate!(obj) ⇒ Object

Raises:

  • (TypeError)


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

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