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

def self.method_added(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?



135
136
137
# File 'lib/types/types/base.rb', line 135

def ==(other)
  other.class == self.class && other.name == self.name
end

#describe_obj(obj) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/types/types/base.rb', line 96

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}"
    else
      "type #{obj.class} with value #{T::Utils.string_truncate_middle(obj.inspect, 30, 30)}"
    end
  rescue StandardError, SystemStackError
    "type #{obj.class} with unprintable value"
  end
end

#error_message_for_obj(obj) ⇒ Object



116
117
118
119
120
121
122
# File 'lib/types/types/base.rb', line 116

def error_message_for_obj(obj)
  if valid?(obj)
    nil
  else
    "Expected type #{self.name}, got #{describe_obj(obj)}"
  end
end

#hashObject

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



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

def hash
  name.hash
end

#nameObject

Equality is based on name, so be sure the name reflects all relevant state when implementing.

Raises:

  • (NotImplementedError)


31
32
33
# File 'lib/types/types/base.rb', line 31

def name
  raise NotImplementedError
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:



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

def subtype_of?(t2)
  t1 = self

  # 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



92
93
94
# File 'lib/types/types/base.rb', line 92

def to_s
  name
end

#valid?(obj) ⇒ Boolean

Returns:

Raises:

  • (NotImplementedError)


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

def valid?(obj)
  raise NotImplementedError
end

#validate!(obj) ⇒ Object

Raises:

  • (TypeError)


124
125
126
127
# File 'lib/types/types/base.rb', line 124

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