literal 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e3b0596d3836316a8c9f2dc13b42707da7e4b158cdf17af864c22be88856e76c
4
- data.tar.gz: be38909c66845eb4d09187803903f46cbe8d4392d0245d089aa7aec03c0ac7af
3
+ metadata.gz: 98bc237de806f82733e8c812bccc840c5451183afd22f369ecffc6720c3b88ba
4
+ data.tar.gz: b340a2c25c5d23d033a83dbf3ab1483a9b071424f343f0edf5c104bee83402c2
5
5
  SHA512:
6
- metadata.gz: cfe44962b770aa87464249cd41540e2c26cce61a1b59292fca71b3d79331595483214e22eaa907e3b8453d49a75365219291d196854d843ec09ac1c4ed3b19dd
7
- data.tar.gz: 3b6cd6cfeb4fd440c43385462ac3147606389f2afe046eab45cdd3cd9b626dc1fd6e7cdce39c4c5f9b060ea659b99b6ffb7a9ab930a5edb393a11e64f7234557
6
+ metadata.gz: 17b16bdea93b6dff73b9fdcbdd269053648e996f3e2b5d6773933710281b756a50eb93684d727b46711aef990858985111f9d59d80250d20a3f71963767032ca
7
+ data.tar.gz: 67647cf3e25cdbdbdb12638081a7cbd40b5bee6771d9be54ca4100646740a97989259f24242b60064c41b016c7687d91a3969632d2195a0715c6015bede8e839
data/lib/literal/enum.rb CHANGED
@@ -24,7 +24,7 @@ class Literal::Enum
24
24
  @names = {}
25
25
  end
26
26
 
27
- if RUBY_ENGINE != "truffleruby"
27
+ if subclass.name && RUBY_ENGINE != "truffleruby"
28
28
  TracePoint.trace(:end) do |tp|
29
29
  if tp.self == subclass
30
30
  tp.self.__after_defined__
@@ -71,9 +71,8 @@ class Literal::Enum
71
71
  raise ArgumentError.new("You can only use `find_by` on unique indexes.")
72
72
  end
73
73
 
74
- unless (type = @indexes_definitions.fetch(key)[0]) === value
75
- raise Literal::TypeError.expected(value, to_be_a: type)
76
- end
74
+ type = @indexes_definitions.fetch(key)[0]
75
+ Literal.check(actual: value, expected: type)
77
76
 
78
77
  @indexes.fetch(key)[value]&.first
79
78
  end
@@ -102,7 +102,7 @@ class Literal::Properties::Schema
102
102
  i, n = 0, sorted_properties.size
103
103
  while i < n
104
104
  property = sorted_properties[i]
105
- buffer << " @" << property.name.name << " == other." << property.name.name
105
+ buffer << " @#{property.name.name} == other.instance_variable_get(:@#{property.name.name})"
106
106
  buffer << " &&\n " if i < n - 1
107
107
  i += 1
108
108
  end
@@ -30,7 +30,7 @@ module Literal::Properties
30
30
  end
31
31
 
32
32
  unless Literal::Property::VISIBILITY_OPTIONS.include?(predicate)
33
- raise Literal::ArgumentError.new("The predicate must be one of #{Literal::Property::VISIBILITY_OPTIONS.map(&:inspect).join(', ')}.")
33
+ raise Literal::ArgumentError.new("The predicate must be one of #{Literal::Property::VISIBILITY_OPTIONS.map(&:inspect).join(', ')}.")
34
34
  end
35
35
 
36
36
  if reader && :class == name
@@ -90,7 +90,7 @@ module Literal::Properties
90
90
  end
91
91
 
92
92
  def to_h
93
- {}
93
+ {}
94
94
  end
95
95
 
96
96
  set_temporary_name "Literal::Properties(Extension)" if respond_to?(:set_temporary_name)
@@ -6,6 +6,10 @@ class Literal::Rails::EnumType < ActiveModel::Type::Value
6
6
  super()
7
7
  end
8
8
 
9
+ def type
10
+ :literal_enum
11
+ end
12
+
9
13
  def cast(value)
10
14
  case value
11
15
  when nil
@@ -6,6 +6,7 @@ class Literal::Types::ArrayType
6
6
 
7
7
  def initialize(type)
8
8
  @type = type
9
+ freeze
9
10
  end
10
11
 
11
12
  attr_reader :type
@@ -6,6 +6,7 @@ class Literal::Types::ClassType
6
6
 
7
7
  def initialize(type)
8
8
  @type = type
9
+ freeze
9
10
  end
10
11
 
11
12
  attr_reader :type
@@ -7,6 +7,7 @@ class Literal::Types::ConstraintType
7
7
  def initialize(*object_constraints, **property_constraints)
8
8
  @object_constraints = object_constraints
9
9
  @property_constraints = property_constraints
10
+ freeze
10
11
  end
11
12
 
12
13
  attr_reader :object_constraints
@@ -5,6 +5,7 @@ class Literal::Types::DescendantType
5
5
 
6
6
  def initialize(type)
7
7
  @type = type
8
+ freeze
8
9
  end
9
10
 
10
11
  attr_reader :type
@@ -6,6 +6,7 @@ class Literal::Types::EnumerableType
6
6
 
7
7
  def initialize(type)
8
8
  @type = type
9
+ freeze
9
10
  end
10
11
 
11
12
  attr_reader :type
@@ -12,6 +12,7 @@ class Literal::Types::FrozenType
12
12
  end
13
13
 
14
14
  @type = type
15
+ freeze
15
16
  end
16
17
 
17
18
  attr_reader :type
@@ -7,6 +7,7 @@ class Literal::Types::HashType
7
7
  def initialize(key_type, value_type)
8
8
  @key_type = key_type
9
9
  @value_type = value_type
10
+ freeze
10
11
  end
11
12
 
12
13
  attr_reader :key_type, :value_type
@@ -14,6 +14,7 @@ class Literal::Types::InterfaceType
14
14
  def initialize(*methods)
15
15
  raise Literal::ArgumentError.new("_Interface type must have at least one method.") if methods.size < 1
16
16
  @methods = methods
17
+ freeze
17
18
  end
18
19
 
19
20
  attr_reader :methods
@@ -8,6 +8,7 @@ class Literal::Types::IntersectionType
8
8
  raise Literal::ArgumentError.new("_Intersection type must have at least one type.") if types.size < 1
9
9
 
10
10
  @types = types
11
+ freeze
11
12
  end
12
13
 
13
14
  attr_reader :types
@@ -6,6 +6,7 @@ class Literal::Types::MapType
6
6
 
7
7
  def initialize(**shape)
8
8
  @shape = shape
9
+ freeze
9
10
  end
10
11
 
11
12
  attr_reader :shape
@@ -6,6 +6,7 @@ class Literal::Types::NilableType
6
6
 
7
7
  def initialize(type)
8
8
  @type = type
9
+ freeze
9
10
  end
10
11
 
11
12
  attr_reader :type
@@ -6,6 +6,7 @@ class Literal::Types::NotType
6
6
 
7
7
  def initialize(type)
8
8
  @type = type
9
+ freeze
9
10
  end
10
11
 
11
12
  attr_reader :type
@@ -6,6 +6,7 @@ class Literal::Types::RangeType
6
6
 
7
7
  def initialize(type)
8
8
  @type = type
9
+ freeze
9
10
  end
10
11
 
11
12
  attr_reader :type
@@ -6,6 +6,7 @@ class Literal::Types::SetType
6
6
 
7
7
  def initialize(type)
8
8
  @type = type
9
+ freeze
9
10
  end
10
11
 
11
12
  attr_reader :type
@@ -8,6 +8,7 @@ class Literal::Types::TupleType
8
8
  raise Literal::ArgumentError.new("_Tuple type must have at least one type.") if types.size < 1
9
9
 
10
10
  @types = types
11
+ freeze
11
12
  end
12
13
 
13
14
  attr_reader :types
@@ -3,23 +3,43 @@
3
3
  class Literal::Types::UnionType
4
4
  include Enumerable
5
5
 
6
- def initialize(*types)
7
- raise Literal::ArgumentError.new("_Union type must have at least one type.") if types.size < 1
6
+ def initialize(*queue)
7
+ raise Literal::ArgumentError.new("_Union type must have at least one type.") if queue.size < 1
8
+ types = []
9
+ primitives = Set[]
10
+
11
+ while queue.length > 0
12
+ type = queue.shift
13
+ case type
14
+ when Literal::Types::UnionType
15
+ queue.concat(type.types, type.primitives.to_a)
16
+ when Array, Hash, String, Symbol, Integer, Float, Complex, Rational, true, false, nil
17
+ primitives << type
18
+ else
19
+ types << type
20
+ end
21
+ end
22
+
23
+ types.uniq!
24
+ @types = types
25
+ @primitives = primitives
8
26
 
9
- @types = []
10
- load_types(types)
11
- @types.uniq!
12
27
  @types.freeze
28
+ @primitives.freeze
29
+ freeze
13
30
  end
14
31
 
15
- attr_reader :types
32
+ attr_reader :types, :primitives
16
33
 
17
34
  def inspect
18
35
  "_Union(#{@types.inspect})"
19
36
  end
20
37
 
21
38
  def ===(value)
39
+ return true if @primitives.include?(value)
40
+
22
41
  types = @types
42
+
23
43
  i, len = 0, types.size
24
44
  while i < len
25
45
  return true if types[i] === value
@@ -28,48 +48,49 @@ class Literal::Types::UnionType
28
48
  end
29
49
 
30
50
  def each(&)
51
+ @primitives.each(&)
31
52
  @types.each(&)
32
53
  end
33
54
 
34
55
  def deconstruct
35
- @types.to_a
56
+ to_a
36
57
  end
37
58
 
38
59
  def [](key)
39
- if @types.include?(key)
60
+ if @primitives.include?(key) || @types.include?(key)
40
61
  key
41
- else
42
- raise ArgumentError.new("#{key} not in #{inspect}")
43
62
  end
44
63
  end
45
64
 
65
+ def fetch(key)
66
+ self[key] or raise KeyError.new("Key not found: #{key.inspect}")
67
+ end
68
+
46
69
  def record_literal_type_errors(ctx)
47
- @types.each do |type|
70
+ each do |type|
48
71
  ctx.add_child(label: type.inspect, expected: type, actual: ctx.actual)
49
72
  end
73
+
50
74
  ctx.children.clear if ctx.children.none? { |c| c.children.any? }
51
75
  end
52
76
 
53
77
  def >=(other)
78
+ types = @types
79
+ primitives = @primitives
80
+
54
81
  case other
55
82
  when Literal::Types::UnionType
56
- other.types.all? do |other_type|
57
- @types.any? do |type|
58
- Literal.subtype?(type, of: other_type)
59
- end
60
- end
61
- else
62
- @types.any? do |type|
63
- Literal.subtype?(other, of: type)
83
+ types_have_at_least_one_subtype = other.types.all? do |other_type|
84
+ primitives.any? { |p| Literal.subtype?(p, of: other_type) } || types.any? { |t| Literal.subtype?(t, of: other_type) }
64
85
  end
65
- end
66
- end
67
86
 
68
- private
87
+ primitives_have_at_least_one_subtype = other.primitives.all? do |other_primitive|
88
+ primitives.any? { |p| Literal.subtype?(p, of: other_primitive) } || types.any? { |t| Literal.subtype?(t, of: other_primitive) }
89
+ end
69
90
 
70
- def load_types(types)
71
- types.each do |type|
72
- (Literal::Types::UnionType === type) ? load_types(type.types) : @types << type
91
+ types_have_at_least_one_subtype && primitives_have_at_least_one_subtype
92
+ else
93
+ types.any? { |t| Literal.subtype?(other, of: t) } || primitives.any? { |p| Literal.subtype?(other, of: p) }
73
94
  end
74
95
  end
75
96
 
data/lib/literal/types.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Literal::Types
4
+ extend self
5
+
4
6
  autoload :AnyType, "literal/types/any_type"
5
7
  autoload :ArrayType, "literal/types/array_type"
6
8
  autoload :BooleanType, "literal/types/boolean_type"
@@ -26,125 +28,175 @@ module Literal::Types
26
28
  autoload :UnionType, "literal/types/union_type"
27
29
  autoload :VoidType, "literal/types/void_type"
28
30
 
29
- ProcableType = InterfaceType.new(:to_proc).freeze
30
- CallableType = InterfaceType.new(:call).freeze
31
- LambdaType = ConstraintType.new(Proc, lambda?: true).freeze
32
-
33
- NilableBooleanType = NilableType.new(BooleanType::Instance).freeze
34
- NilableCallableType = NilableType.new(CallableType).freeze
35
- NilableJSONDataType = NilableType.new(JSONDataType).freeze
36
- NilableLambdaType = NilableType.new(LambdaType).freeze
37
- NilableProcableType = NilableType.new(ProcableType).freeze
38
-
39
31
  # Matches any value except `nil`. Use `_Any?` or `_Void` to match any value including `nil`.
32
+ # ```ruby
33
+ # _Any
34
+ # ```
40
35
  def _Any
41
36
  AnyType::Instance
42
37
  end
43
38
 
39
+ # Matches any value including `nil`. This is the same as `_Void` and the opposite of `_Never`.
40
+ # ```ruby
41
+ # _Any?
42
+ # ```
44
43
  def _Any?
45
44
  VoidType::Instance
46
45
  end
47
46
 
48
- # Matches if the value is an `Array` and all the elements match the given type.
49
- def _Array(...)
50
- ArrayType.new(...)
47
+ # Matches if the value is an `Array` and all the elements of the array match the given type.
48
+ # ```ruby
49
+ # _Array(String)
50
+ # ```
51
+ def _Array(type)
52
+ ArrayType.new(type)
51
53
  end
52
54
 
53
- # Nilable version of `_Array`
54
- def _Array?(...)
55
- NilableType.new(
56
- ArrayType.new(...)
55
+ # Nilable version of `_Array`.
56
+ # ```ruby
57
+ # _Array?(String)
58
+ # ```
59
+ def _Array?(type)
60
+ _Nilable(
61
+ _Array(type)
57
62
  )
58
63
  end
59
64
 
60
- # Matches if the value is `true` or `false`.
65
+ # Matches if the value is either `true` or `false`. This is equivalent to `_Union(true, false)`.
66
+ # ```ruby
67
+ # _Boolean
68
+ # ```
61
69
  def _Boolean
62
70
  BooleanType::Instance
63
71
  end
64
72
 
65
- # Nilable version of `_Boolean`
73
+ # Nilable version of `_Boolean`.
74
+ # ```ruby
75
+ # _Boolean?
76
+ # ```
66
77
  def _Boolean?
67
78
  NilableBooleanType
68
79
  end
69
80
 
70
81
  # Matches if the value responds to `#call`.
82
+ # ```ruby
83
+ # _Callable
84
+ # ```
71
85
  def _Callable
72
86
  CallableType
73
87
  end
74
88
 
75
- # Nilabl version of `_Callable`
89
+ # Nilable version of `_Callable`.
90
+ # ```ruby
91
+ # _Callable?
92
+ # ```
76
93
  def _Callable?
77
94
  NilableCallableType
78
95
  end
79
96
 
80
97
  # Matches if the value either the given class or a subclass of it.
81
- def _Class(...)
82
- ClassType.new(...)
98
+ # ```ruby
99
+ # _Class(ActiveRecord::Base)
100
+ # ```
101
+ def _Class(expected_class)
102
+ ClassType.new(expected_class)
83
103
  end
84
104
 
85
- # Nilable version of `_Class`
105
+ # Nilable version of `_Class`.
106
+ # ```ruby
107
+ # _Class?(ActiveRecord::Base)
108
+ # ```
86
109
  def _Class?(...)
87
- NilableType.new(
88
- ClassType.new(...)
110
+ _Nilable(
111
+ _Class(...)
89
112
  )
90
113
  end
91
114
 
92
115
  # Similar to `_Intersection`, but allows you to specify attribute constraints as keyword arguments.
93
- # @example
94
- # _Constraint(Array, size: 1..3)
95
- def _Constraint(...)
96
- ConstraintType.new(...)
116
+ # ```ruby
117
+ # _Constraint(Array, size: 1..3)
118
+ # ```
119
+ def _Constraint(*a, **k)
120
+ if a.length == 1 && k.length == 0
121
+ a[0]
122
+ else
123
+ ConstraintType.new(*a, **k)
124
+ end
97
125
  end
98
126
 
99
127
  # Nilable version of `_Constraint`
128
+ # ```ruby
129
+ # _Constraint?(Array, size: 1..3)
130
+ # ```
100
131
  def _Constraint?(...)
101
- NilableType.new(
102
- ConstraintType.new(...)
132
+ _Nilable(
133
+ _Constraint(...)
103
134
  )
104
135
  end
105
136
 
106
137
  # Matches if the value is a `Date` and matches the given constraints.
107
- # If you don't need any constraints, use `Date` instead of `_Date`.
138
+ # If you don't need any constraints, use `Date` instead of `_Date`. See also `_Constraint`.
139
+ # ```ruby
140
+ # _Date((Date.today)..)
141
+ # _Date(year: 2025)
142
+ # ```
108
143
  def _Date(...)
109
144
  _Constraint(Date, ...)
110
145
  end
111
146
 
112
- # Nilable version of `_Date`
147
+ # Nilable version of `_Date`.
113
148
  def _Date?(...)
114
149
  _Nilable(
115
150
  _Date(...)
116
151
  )
117
152
  end
118
153
 
119
- def _Deferred(...)
120
- DeferredType.new(...)
154
+ # Takes a type as a block so it can be resolved when needed. This is useful if declaring your type now would cause an error because constants haven’t been defined yet.
155
+ # ```ruby
156
+ # _Deferred { _Class(SomeFutureConstant) }
157
+ # ```
158
+ def _Deferred(&type)
159
+ DeferredType.new(&type)
160
+ end
161
+
162
+ # Nilable version of `_Deferred`.
163
+ def _Deferred?(&type)
164
+ _Nilable(
165
+ _Deferred(&type)
166
+ )
121
167
  end
122
168
 
123
169
  # Matches if the value is a descendant of the given class.
170
+ # ```ruby
171
+ # _Descendant(ActiveRecord::Base)
172
+ # ```
124
173
  def _Descendant(...)
125
174
  DescendantType.new(...)
126
175
  end
127
176
 
128
- # Nilable version of `_Descendant`
177
+ # Nilable version of `_Descendant`.
129
178
  def _Descendant?(...)
130
- NilableType.new(
131
- DescendantType.new(...)
179
+ _Nilable(
180
+ _Descendant(...)
132
181
  )
133
182
  end
134
183
 
135
184
  #  Matches if the value is an `Enumerable` and all its elements match the given type.
136
- def _Enumerable(...)
137
- EnumerableType.new(...)
185
+ # ```ruby
186
+ # _Enumerable(String)
187
+ # ```
188
+ def _Enumerable(type)
189
+ EnumerableType.new(type)
138
190
  end
139
191
 
140
- # Nilable version of `_Enumerable`
192
+ # Nilable version of `_Enumerable`.
141
193
  def _Enumerable?(...)
142
- NilableType.new(
143
- EnumerableType.new(...)
194
+ _Nilable(
195
+ _Enumerable(...)
144
196
  )
145
197
  end
146
198
 
147
- # Matches *"falsy"* values (`nil` and `false`).
199
+ # Matches *"falsy"* values (`nil` and `false`). This is equivalent to `_Nilable(false)` or `_Union(nil, false)`.
148
200
  def _Falsy
149
201
  FalsyType::Instance
150
202
  end
@@ -152,11 +204,14 @@ module Literal::Types
152
204
  # Matches if the value is a `Float` and matches the given constraints.
153
205
  # You could use a `Range`, for example, as a constraint.
154
206
  # If you don't need a constraint, use `Float` instead of `_Float`.
207
+ # ```ruby
208
+ # _Float(5..10)
209
+ # ```
155
210
  def _Float(...)
156
211
  _Constraint(Float, ...)
157
212
  end
158
213
 
159
- # Nilable version of `_Float`
214
+ # Nilable version of `_Float`.
160
215
  def _Float?(...)
161
216
  _Nilable(
162
217
  _Float(...)
@@ -170,8 +225,8 @@ module Literal::Types
170
225
 
171
226
  # Nilable version of `_Frozen`
172
227
  def _Frozen?(...)
173
- NilableType.new(
174
- FrozenType.new(...)
228
+ _Nilable(
229
+ _Frozen(...)
175
230
  )
176
231
  end
177
232
 
@@ -182,8 +237,8 @@ module Literal::Types
182
237
 
183
238
  # Nilable version of `_Hash`
184
239
  def _Hash?(...)
185
- NilableType.new(
186
- HashType.new(...)
240
+ _Nilable(
241
+ _Hash(...)
187
242
  )
188
243
  end
189
244
 
@@ -210,8 +265,8 @@ module Literal::Types
210
265
 
211
266
  # Nilable version of `_Interface`
212
267
  def _Interface?(...)
213
- NilableType.new(
214
- InterfaceType.new(...)
268
+ _Nilable(
269
+ _Interface(...)
215
270
  )
216
271
  end
217
272
 
@@ -222,8 +277,8 @@ module Literal::Types
222
277
 
223
278
  # Nilable version of `_Intersection`
224
279
  def _Intersection?(...)
225
- NilableType.new(
226
- IntersectionType.new(...)
280
+ _Nilable(
281
+ _Intersection(...)
227
282
  )
228
283
  end
229
284
 
@@ -247,14 +302,20 @@ module Literal::Types
247
302
  NilableLambdaType
248
303
  end
249
304
 
305
+ # ```ruby
306
+ # _Map(name: String, age: Integer)
307
+ # ```
250
308
  def _Map(...)
251
309
  MapType.new(...)
252
310
  end
253
311
 
254
312
  # Nilable version of `_Map`
313
+ # ```ruby
314
+ # _Map?(name: String, age: Integer)
315
+ # ```
255
316
  def _Map?(...)
256
- NilableType.new(
257
- MapType.new(...)
317
+ _Nilable(
318
+ _Map(...)
258
319
  )
259
320
  end
260
321
 
@@ -295,8 +356,8 @@ module Literal::Types
295
356
 
296
357
  # Nilable version of `_Range`
297
358
  def _Range?(...)
298
- NilableType.new(
299
- RangeType.new(...)
359
+ _Nilable(
360
+ _Range(...)
300
361
  )
301
362
  end
302
363
 
@@ -307,8 +368,8 @@ module Literal::Types
307
368
 
308
369
  # Nilable version of `_Set`
309
370
  def _Set?(...)
310
- NilableType.new(
311
- SetType.new(...)
371
+ _Nilable(
372
+ _Set(...)
312
373
  )
313
374
  end
314
375
 
@@ -356,14 +417,20 @@ module Literal::Types
356
417
  end
357
418
 
358
419
  # Matches if the value is an `Array` and each element matches the given types in order.
420
+ # ```ruby
421
+ # _Tuple(String, Integer, Integer)
422
+ # ```
359
423
  def _Tuple(...)
360
424
  TupleType.new(...)
361
425
  end
362
426
 
363
427
  # Nilable version of `_Typle`
428
+ # ```ruby
429
+ # _Tuple?(String, Integer, Integer)
430
+ # ```
364
431
  def _Tuple?(...)
365
- NilableType.new(
366
- TupleType.new(...)
432
+ _Nilable(
433
+ _Tuple(...)
367
434
  )
368
435
  end
369
436
 
@@ -374,12 +441,22 @@ module Literal::Types
374
441
 
375
442
  # Nilable version of `_Union`
376
443
  def _Union?(...)
377
- NilableType.new(
378
- UnionType.new(...)
444
+ _Nilable(
445
+ _Union(...)
379
446
  )
380
447
  end
381
448
 
382
449
  def _Void
383
450
  VoidType::Instance
384
451
  end
452
+
453
+ ProcableType = _Interface(:to_proc)
454
+ CallableType = _Interface(:call)
455
+ LambdaType = _Constraint(Proc, lambda?: true)
456
+
457
+ NilableBooleanType = _Nilable(BooleanType::Instance)
458
+ NilableCallableType = _Nilable(CallableType)
459
+ NilableJSONDataType = _Nilable(JSONDataType)
460
+ NilableLambdaType = _Nilable(LambdaType)
461
+ NilableProcableType = _Nilable(ProcableType)
385
462
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Literal
4
- VERSION = "1.5.0"
4
+ VERSION = "1.6.0"
5
5
  end
data/lib/literal.rb CHANGED
@@ -63,15 +63,35 @@ module Literal
63
63
  end
64
64
 
65
65
  def self.subtype?(type, of:)
66
- type = type.block.call if Types::DeferredType === type
66
+ supertype = of
67
+ subtype = type
67
68
 
68
- (of == type) || case of
69
+ subtype = subtype.block.call if Types::DeferredType === subtype
70
+
71
+ return true if supertype == subtype
72
+
73
+ case supertype
69
74
  when Literal::Type
70
- of >= type
75
+ supertype >= subtype
71
76
  when Module
72
- (Module === type) ? of >= type : false
77
+ case subtype
78
+ when Module
79
+ supertype >= subtype
80
+ when Numeric
81
+ Numeric >= supertype
82
+ when String
83
+ String >= supertype
84
+ when Symbol
85
+ Symbol >= supertype
86
+ when ::Array
87
+ ::Array >= supertype
88
+ when ::Hash
89
+ ::Hash >= supertype
90
+ else
91
+ false
92
+ end
73
93
  when Range
74
- of.cover?(type)
94
+ supertype.cover?(subtype)
75
95
  else
76
96
  false
77
97
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: literal
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Drapper
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-01-17 00:00:00.000000000 Z
10
+ date: 2025-03-10 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: Enums, properties, generics, structured objects and runtime type checking.
13
13
  email: