literal 1.5.0 → 1.7.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/literal/array.rb +15 -15
  3. data/lib/literal/data_structure.rb +4 -3
  4. data/lib/literal/delegator.rb +25 -0
  5. data/lib/literal/enum.rb +4 -5
  6. data/lib/literal/flags/flags_16.rb +7 -0
  7. data/lib/literal/flags/flags_32.rb +7 -0
  8. data/lib/literal/flags/flags_64.rb +7 -0
  9. data/lib/literal/flags/flags_8.rb +7 -0
  10. data/lib/literal/flags.rb +0 -24
  11. data/lib/literal/hash.rb +1 -1
  12. data/lib/literal/properties/schema.rb +3 -3
  13. data/lib/literal/properties.rb +5 -6
  14. data/lib/literal/property.rb +3 -3
  15. data/lib/literal/rails/enum_type.rb +4 -0
  16. data/lib/literal/rails.rb +0 -3
  17. data/lib/literal/set.rb +1 -1
  18. data/lib/literal/transforms.rb +2 -2
  19. data/lib/literal/tuple.rb +2 -2
  20. data/lib/literal/types/array_type.rb +1 -0
  21. data/lib/literal/types/class_type.rb +3 -2
  22. data/lib/literal/types/constraint_type.rb +5 -4
  23. data/lib/literal/{deferred_type.rb → types/deferred_type.rb} +1 -1
  24. data/lib/literal/types/descendant_type.rb +2 -1
  25. data/lib/literal/types/enumerable_type.rb +2 -1
  26. data/lib/literal/types/frozen_type.rb +3 -2
  27. data/lib/literal/types/hash_type.rb +3 -2
  28. data/lib/literal/types/interface_type.rb +20 -14
  29. data/lib/literal/types/intersection_type.rb +4 -3
  30. data/lib/literal/types/map_type.rb +2 -1
  31. data/lib/literal/types/nilable_type.rb +3 -2
  32. data/lib/literal/types/not_type.rb +4 -3
  33. data/lib/literal/types/predicate_type.rb +22 -0
  34. data/lib/literal/types/range_type.rb +2 -1
  35. data/lib/literal/types/set_type.rb +2 -1
  36. data/lib/literal/types/tuple_type.rb +1 -0
  37. data/lib/literal/types/union_type.rb +46 -30
  38. data/lib/literal/types.rb +143 -87
  39. data/lib/literal/value.rb +65 -0
  40. data/lib/literal/version.rb +1 -1
  41. data/lib/literal.rb +82 -37
  42. metadata +26 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e3b0596d3836316a8c9f2dc13b42707da7e4b158cdf17af864c22be88856e76c
4
- data.tar.gz: be38909c66845eb4d09187803903f46cbe8d4392d0245d089aa7aec03c0ac7af
3
+ metadata.gz: 2c4292f514f586e16d0d477cc57e9a8b1f38771a8124f9ed933b67aa167550a0
4
+ data.tar.gz: 48dc63568c23368fb5c1577a57833720ca272c2aeca24dfc33e26271ed378497
5
5
  SHA512:
6
- metadata.gz: cfe44962b770aa87464249cd41540e2c26cce61a1b59292fca71b3d79331595483214e22eaa907e3b8453d49a75365219291d196854d843ec09ac1c4ed3b19dd
7
- data.tar.gz: 3b6cd6cfeb4fd440c43385462ac3147606389f2afe046eab45cdd3cd9b626dc1fd6e7cdce39c4c5f9b060ea659b99b6ffb7a9ab930a5edb393a11e64f7234557
6
+ metadata.gz: d79162f89f5088923b2a18ffb2d77214401e18f2d992d2535ab85a4f73b0adbf4430bf385dd846f860e7c0c984508bd6a4d48296a13883fefdb1eb8ef0ac51eb
7
+ data.tar.gz: d0cbebd25520b382b6ea73d22b9b2120b15831d7b51d6b2deb99bfd2adaa1c53cfeadbf4b6c3f7af6e97ea634991c49fe5e49d559c2efcf228ec2e9fe942daf1
data/lib/literal/array.rb CHANGED
@@ -17,13 +17,13 @@ class Literal::Array
17
17
  alias_method :[], :new
18
18
 
19
19
  def ===(value)
20
- Literal::Array === value && Literal.subtype?(value.__type__, of: @type)
20
+ Literal::Array === value && Literal.subtype?(value.__type__, @type)
21
21
  end
22
22
 
23
23
  def >=(other)
24
24
  case other
25
25
  when Literal::Array::Generic
26
- Literal.subtype?(other.type, of: @type)
26
+ Literal.subtype?(other.type, @type)
27
27
  else
28
28
  false
29
29
  end
@@ -51,7 +51,7 @@ class Literal::Array
51
51
  include Literal::Types
52
52
 
53
53
  def initialize(value, type:)
54
- Literal.check(actual: value, expected: _Array(type)) do |c|
54
+ Literal.check(value, _Array(type)) do |c|
55
55
  c.fill_receiver(receiver: self, method: "#initialize")
56
56
  end
57
57
 
@@ -98,7 +98,7 @@ class Literal::Array
98
98
  def +(other)
99
99
  case other
100
100
  when ::Array
101
- Literal.check(actual: other, expected: _Array(@__type__)) do |c|
101
+ Literal.check(other, _Array(@__type__)) do |c|
102
102
  c.fill_receiver(receiver: self, method: "#+")
103
103
  end
104
104
 
@@ -129,7 +129,7 @@ class Literal::Array
129
129
  end
130
130
 
131
131
  def <<(value)
132
- Literal.check(actual: value, expected: @__type__) do |c|
132
+ Literal.check(value, @__type__) do |c|
133
133
  c.fill_receiver(receiver: self, method: "#<<")
134
134
  end
135
135
 
@@ -157,7 +157,7 @@ class Literal::Array
157
157
  end
158
158
 
159
159
  def []=(index, value)
160
- Literal.check(actual: value, expected: @__type__) do |c|
160
+ Literal.check(value, @__type__) do |c|
161
161
  c.fill_receiver(receiver: self, method: "#[]=")
162
162
  end
163
163
 
@@ -284,7 +284,7 @@ class Literal::Array
284
284
  alias_method :index, :find_index
285
285
 
286
286
  def insert(index, *value)
287
- Literal.check(actual: value, expected: _Array(@__type__)) do |c|
287
+ Literal.check(value, _Array(@__type__)) do |c|
288
288
  c.fill_receiver(receiver: self, method: "#insert")
289
289
  end
290
290
 
@@ -344,9 +344,9 @@ class Literal::Array
344
344
 
345
345
  def map(type, &block)
346
346
  my_type = @__type__
347
- transform_type = Literal::TRANSFORMS.dig(my_type, block)
347
+ transform_type = Literal::Transforms.dig(my_type, block)
348
348
 
349
- if transform_type && Literal.subtype?(transform_type, of: my_type)
349
+ if transform_type && Literal.subtype?(transform_type, my_type)
350
350
  Literal::Array.allocate.__initialize_without_check__(
351
351
  @__value__.map(&block),
352
352
  type:,
@@ -392,13 +392,13 @@ class Literal::Array
392
392
  end
393
393
 
394
394
  def narrow(type)
395
- unless Literal.subtype?(type, of: @__type__)
395
+ unless Literal.subtype?(type, @__type__)
396
396
  raise ArgumentError.new("Cannot narrow #{@__type__} to #{type}")
397
397
  end
398
398
 
399
399
  if __type__ != type
400
400
  @__value__.each do |item|
401
- Literal.check(actual: item, expected: type) do |c|
401
+ Literal.check(item, type) do |c|
402
402
  c.fill_receiver(receiver: self, method: "#narrow")
403
403
  end
404
404
  end
@@ -453,7 +453,7 @@ class Literal::Array
453
453
  end
454
454
 
455
455
  def push(*value)
456
- Literal.check(actual: value, expected: _Array(@__type__)) do |c|
456
+ Literal.check(value, _Array(@__type__)) do |c|
457
457
  c.fill_receiver(receiver: self, method: "#push")
458
458
  end
459
459
 
@@ -475,7 +475,7 @@ class Literal::Array
475
475
  def replace(value)
476
476
  case value
477
477
  when Array
478
- Literal.check(actual: value, expected: _Array(@__type__)) do |c|
478
+ Literal.check(value, _Array(@__type__)) do |c|
479
479
  c.fill_receiver(receiver: self, method: "#replace")
480
480
  end
481
481
 
@@ -597,7 +597,7 @@ class Literal::Array
597
597
  end
598
598
 
599
599
  def unshift(value)
600
- Literal.check(actual: value, expected: @__type__) do |c|
600
+ Literal.check(value, @__type__) do |c|
601
601
  c.fill_receiver(receiver: self, method: "#unshift")
602
602
  end
603
603
 
@@ -636,7 +636,7 @@ class Literal::Array
636
636
  def |(other)
637
637
  case other
638
638
  when ::Array
639
- Literal.check(actual: other, expected: _Array(@__type__)) do |c|
639
+ Literal.check(other, _Array(@__type__)) do |c|
640
640
  c.fill_receiver(receiver: self, method: "#|")
641
641
  end
642
642
 
@@ -38,7 +38,7 @@ class Literal::DataStructure
38
38
  end
39
39
 
40
40
  def marshal_dump
41
- [1, to_h, frozen?]
41
+ [1, to_h, frozen?].freeze
42
42
  end
43
43
 
44
44
  def hash
@@ -46,9 +46,10 @@ class Literal::DataStructure
46
46
  end
47
47
 
48
48
  def ==(other)
49
- other.is_a?(self.class) && other.class.literal_properties.empty?
49
+ self.class === other && other.class.literal_properties.empty?
50
50
  end
51
- alias eql? ==
51
+
52
+ alias_method :eql?, :==
52
53
 
53
54
  def self.__generate_literal_methods__(new_property, buffer = +"")
54
55
  super
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Literal::Delegator < SimpleDelegator
4
+ def self.to_proc
5
+ -> (value) { new(value) }
6
+ end
7
+
8
+ def self.[](value)
9
+ new(value)
10
+ end
11
+
12
+ def initialize(value)
13
+ Literal.check(value, __type__)
14
+ super
15
+ freeze
16
+ end
17
+
18
+ def ===(other)
19
+ self.class === other && __getobj__ == other.__getobj__
20
+ end
21
+
22
+ alias_method :==, :===
23
+
24
+ freeze
25
+ end
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__
@@ -55,7 +55,7 @@ class Literal::Enum
55
55
 
56
56
  types = @indexes_definitions.fetch(key)
57
57
  type = types.first
58
- Literal.check(actual: value, expected: type) { |c| raise NotImplementedError }
58
+ Literal.check(value, type) { |c| raise NotImplementedError }
59
59
 
60
60
  @indexes.fetch(key)[value]
61
61
  end
@@ -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(value, type)
77
76
 
78
77
  @indexes.fetch(key)[value]&.first
79
78
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Literal::Flags16 < Literal::Flags
4
+ BYTES = 2
5
+ BITS = BYTES * 8
6
+ PACKER = "S"
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Literal::Flags32 < Literal::Flags
4
+ BYTES = 4
5
+ BITS = BYTES * 8
6
+ PACKER = "L"
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Literal::Flags64 < Literal::Flags
4
+ BYTES = 8
5
+ BITS = BYTES * 8
6
+ PACKER = "Q"
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Literal::Flags8 < Literal::Flags
4
+ BYTES = 1
5
+ BITS = BYTES * 8
6
+ PACKER = "C"
7
+ end
data/lib/literal/flags.rb CHANGED
@@ -219,27 +219,3 @@ class Literal::Flags
219
219
  2 ** self.class::FLAGS.fetch(key)
220
220
  end
221
221
  end
222
-
223
- class Literal::Flags8 < Literal::Flags
224
- BYTES = 1
225
- BITS = BYTES * 8
226
- PACKER = "C"
227
- end
228
-
229
- class Literal::Flags16 < Literal::Flags
230
- BYTES = 2
231
- BITS = BYTES * 8
232
- PACKER = "S"
233
- end
234
-
235
- class Literal::Flags32 < Literal::Flags
236
- BYTES = 4
237
- BITS = BYTES * 8
238
- PACKER = "L"
239
- end
240
-
241
- class Literal::Flags64 < Literal::Flags
242
- BYTES = 8
243
- BITS = BYTES * 8
244
- PACKER = "Q"
245
- end
data/lib/literal/hash.rb CHANGED
@@ -25,7 +25,7 @@ class Literal::Hash
25
25
  def initialize(value, key_type:, value_type:)
26
26
  collection_type = Literal::Types::HashType.new(key_type, value_type)
27
27
 
28
- Literal.check(actual: value, expected: collection_type) do |c|
28
+ Literal.check(value, collection_type) do |c|
29
29
  c.fill_receiver(receiver: self, method: "#initialize")
30
30
  end
31
31
 
@@ -62,7 +62,7 @@ class Literal::Properties::Schema
62
62
  end
63
63
 
64
64
  def generate_after_initializer(buffer = +"")
65
- buffer << " after_initialize if respond_to?(:after_initialize)\n"
65
+ buffer << " after_initialize if respond_to?(:after_initialize, true)\n"
66
66
  end
67
67
 
68
68
  def generate_to_h(buffer = +"")
@@ -96,13 +96,13 @@ class Literal::Properties::Schema
96
96
 
97
97
  def generate_eq(buffer = +"")
98
98
  buffer << "def ==(other)\n"
99
- buffer << " return false unless other.is_a?(self.class) && other.class.literal_properties.size == self.class.literal_properties.size\n"
99
+ buffer << " return false unless self.class === other && other.class.literal_properties.size == self.class.literal_properties.size\n"
100
100
 
101
101
  sorted_properties = @sorted_properties
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
@@ -1,9 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Literal::Properties
4
- autoload :Schema, "literal/properties/schema"
5
- autoload :DataSchema, "literal/properties/data_schema"
6
-
7
4
  include Literal::Types
8
5
 
9
6
  module DocString
@@ -30,7 +27,7 @@ module Literal::Properties
30
27
  end
31
28
 
32
29
  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(', ')}.")
30
+ raise Literal::ArgumentError.new("The predicate must be one of #{Literal::Property::VISIBILITY_OPTIONS.map(&:inspect).join(', ')}.")
34
31
  end
35
32
 
36
33
  if reader && :class == name
@@ -57,12 +54,14 @@ module Literal::Properties
57
54
  literal_properties << property
58
55
  __define_literal_methods__(property)
59
56
  include(__literal_extension__)
57
+
58
+ name
60
59
  end
61
60
 
62
61
  def literal_properties
63
62
  return @literal_properties if defined?(@literal_properties)
64
63
 
65
- if superclass.is_a?(Literal::Properties)
64
+ if Literal::Properties === superclass
66
65
  @literal_properties = superclass.literal_properties.dup
67
66
  else
68
67
  @literal_properties = Literal::Properties::Schema.new
@@ -90,7 +89,7 @@ module Literal::Properties
90
89
  end
91
90
 
92
91
  def to_h
93
- {}
92
+ {}
94
93
  end
95
94
 
96
95
  set_temporary_name "Literal::Properties(Extension)" if respond_to?(:set_temporary_name)
@@ -97,15 +97,15 @@ class Literal::Property
97
97
  def check(value, &)
98
98
  raise ArgumentError.new("Cannot check type without a block") unless block_given?
99
99
 
100
- Literal.check(actual: value, expected: @type, &)
100
+ Literal.check(value, @type, &)
101
101
  end
102
102
 
103
103
  def check_writer(receiver, value)
104
- Literal.check(actual: value, expected: @type) { |c| c.fill_receiver(receiver:, method: "##{@name.name}=(value)") }
104
+ Literal.check(value, @type) { |c| c.fill_receiver(receiver:, method: "##{@name.name}=(value)") }
105
105
  end
106
106
 
107
107
  def check_initializer(receiver, value)
108
- Literal.check(actual: value, expected: @type) { |c| c.fill_receiver(receiver:, method: "#initialize", label: param) }
108
+ Literal.check(value, @type) { |c| c.fill_receiver(receiver:, method: "#initialize", label: param) }
109
109
  end
110
110
 
111
111
  def generate_reader_method(buffer = +"")
@@ -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
data/lib/literal/rails.rb CHANGED
@@ -4,7 +4,4 @@ require_relative "railtie"
4
4
  require_relative "rails/patches/active_record"
5
5
 
6
6
  module Literal::Rails
7
- autoload :EnumType, "literal/rails/enum_type"
8
- autoload :FlagsType, "literal/rails/flags_type"
9
- autoload :EnumSerializer, "literal/rails/enum_serializer"
10
7
  end
data/lib/literal/set.rb CHANGED
@@ -26,7 +26,7 @@ class Literal::Set
26
26
  def initialize(value, type:)
27
27
  collection_type = Literal::Types::SetType.new(type)
28
28
 
29
- Literal.check(actual: value, expected: collection_type) do |c|
29
+ Literal.check(value, collection_type) do |c|
30
30
  c.fill_receiver(receiver: self, method: "#initialize")
31
31
  end
32
32
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # A map of core types to transform Procs mapping to the new type.
4
- Literal::TRANSFORMS = {
4
+ Literal::Transforms = {
5
5
  Integer => {
6
6
  abs: Integer,
7
7
  ceil: Integer,
@@ -140,4 +140,4 @@ Literal::TRANSFORMS = {
140
140
  to_s: String,
141
141
  year: Integer,
142
142
  },
143
- }.transform_values! { |it| it.transform_keys(&:to_proc) }.freeze
143
+ }.transform_values! { |it| it.transform_keys(&:to_proc).freeze }.freeze
data/lib/literal/tuple.rb CHANGED
@@ -19,7 +19,7 @@ class Literal::Tuple
19
19
 
20
20
  i, len = 0, types.size
21
21
  while i < len
22
- return false unless Literal.subtype?(other_types[i], of: types[i])
22
+ return false unless Literal.subtype?(other_types[i], types[i])
23
23
  i += 1
24
24
  end
25
25
 
@@ -36,7 +36,7 @@ class Literal::Tuple
36
36
 
37
37
  i, len = 0, types.size
38
38
  while i < len
39
- return false unless Literal.subtype?(other_types[i], of: types[i])
39
+ return false unless Literal.subtype?(other_types[i], types[i])
40
40
  i += 1
41
41
  end
42
42
 
@@ -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
@@ -21,9 +22,9 @@ class Literal::Types::ClassType
21
22
  def >=(other)
22
23
  case other
23
24
  when Literal::Types::ClassType
24
- Literal.subtype?(other.type, of: @type)
25
+ Literal.subtype?(other.type, @type)
25
26
  when Literal::Types::DescendantType
26
- (Class === other.type) && Literal.subtype?(other.type, of: @type)
27
+ (Class === other.type) && Literal.subtype?(other.type, @type)
27
28
  else
28
29
  false
29
30
  end
@@ -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
@@ -45,24 +46,24 @@ class Literal::Types::ConstraintType
45
46
  when Literal::Types::ConstraintType
46
47
  other_object_constraints = other.object_constraints
47
48
  return false unless @object_constraints.all? do |constraint|
48
- other_object_constraints.any? { |c| Literal.subtype?(c, of: constraint) }
49
+ other_object_constraints.any? { |c| Literal.subtype?(c, constraint) }
49
50
  end
50
51
 
51
52
  other_property_constraints = other.property_constraints
52
53
  return false unless @property_constraints.all? do |k, v|
53
- Literal.subtype?(other_property_constraints[k], of: v)
54
+ Literal.subtype?(other_property_constraints[k], v)
54
55
  end
55
56
 
56
57
  true
57
58
  when Literal::Types::IntersectionType
58
59
  other_object_constraints = other.types
59
60
  return false unless @object_constraints.all? do |constraint|
60
- other_object_constraints.any? { |c| Literal.subtype?(c, of: constraint) }
61
+ other_object_constraints.any? { |c| Literal.subtype?(c, constraint) }
61
62
  end
62
63
 
63
64
  true
64
65
  when Literal::Types::FrozenType
65
- @object_constraints.all? { |constraint| Literal.subtype?(other.type, of: constraint) }
66
+ @object_constraints.all? { |constraint| Literal.subtype?(other.type, constraint) }
66
67
  else
67
68
  false
68
69
  end
@@ -18,6 +18,6 @@ class Literal::Types::DeferredType
18
18
  end
19
19
 
20
20
  def >=(other)
21
- Literal.subtype?(other, of: @block.call)
21
+ Literal.subtype?(other, @block.call)
22
22
  end
23
23
  end
@@ -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
@@ -20,7 +21,7 @@ class Literal::Types::DescendantType
20
21
  def >=(other)
21
22
  case other
22
23
  when Literal::Types::DescendantType, Literal::Types::ClassType
23
- Literal.subtype?(other.type, of: @type)
24
+ Literal.subtype?(other.type, @type)
24
25
  else
25
26
  false
26
27
  end
@@ -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
@@ -21,7 +22,7 @@ class Literal::Types::EnumerableType
21
22
  def >=(other)
22
23
  case other
23
24
  when Literal::Types::EnumerableType
24
- Literal.subtype?(other.type, of: @type)
25
+ Literal.subtype?(other.type, @type)
25
26
  else
26
27
  false
27
28
  end
@@ -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
@@ -30,11 +31,11 @@ class Literal::Types::FrozenType
30
31
  @type >= other.type
31
32
  when Literal::Types::ConstraintType
32
33
  type_match = false
33
- frozen_match = Literal.subtype?(other.property_constraints[:frozen?], of: true)
34
+ frozen_match = Literal.subtype?(other.property_constraints[:frozen?], true)
34
35
 
35
36
  other.object_constraints.each do |constraint|
36
37
  frozen_match ||= ALWAYS_FROZEN.include?(constraint)
37
- type_match ||= Literal.subtype?(constraint, of: @type)
38
+ type_match ||= Literal.subtype?(constraint, @type)
38
39
  return true if frozen_match && type_match
39
40
  end
40
41
 
@@ -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
@@ -29,9 +30,9 @@ class Literal::Types::HashType
29
30
  case other
30
31
  when Literal::Types::HashType
31
32
  (
32
- Literal.subtype?(other.key_type, of: @key_type)
33
+ Literal.subtype?(other.key_type, @key_type)
33
34
  ) && (
34
- Literal.subtype?(other.value_type, of: @value_type)
35
+ Literal.subtype?(other.value_type, @value_type)
35
36
  )
36
37
  else
37
38
  false
@@ -2,18 +2,15 @@
2
2
 
3
3
  # @api private
4
4
  class Literal::Types::InterfaceType
5
- # TODO: We can generate this and make it much more extensive.
6
- METHOD_TYPE_MAPPINGS = {
7
- :call => Set[Proc, Method],
8
- :to_proc => Set[Proc, Method],
9
- :to_s => Set[String],
10
- }.freeze
11
-
12
5
  include Literal::Type
13
6
 
7
+ # List of `===` method owners where the comparison will only match for objects with the same class
8
+ OwnClassTypeMethodOwners = Set[String, Integer, Kernel, Float, NilClass, TrueClass, FalseClass].freeze
9
+
14
10
  def initialize(*methods)
15
11
  raise Literal::ArgumentError.new("_Interface type must have at least one method.") if methods.size < 1
16
- @methods = methods
12
+ @methods = methods.to_set.freeze
13
+ freeze
17
14
  end
18
15
 
19
16
  attr_reader :methods
@@ -23,21 +20,30 @@ class Literal::Types::InterfaceType
23
20
  end
24
21
 
25
22
  def ===(value)
26
- @methods.all? { |m| value.respond_to?(m) }
23
+ @methods.each do |method|
24
+ return false unless value.respond_to?(method)
25
+ end
26
+
27
+ true
27
28
  end
28
29
 
29
30
  def >=(other)
30
31
  case other
31
32
  when Literal::Types::InterfaceType
32
- @methods.all? { |m| other.methods.include?(m) }
33
+ @methods.subset?(other.methods)
33
34
  when Module
34
- @methods.map { |m| METHOD_TYPE_MAPPINGS[m] }.all? { |types| types&.include?(other) }
35
+ public_methods = other.public_instance_methods.to_set
36
+ @methods.subset?(public_methods)
35
37
  when Literal::Types::IntersectionType
36
- other.types.any? { |type| Literal.subtype?(type, of: self) }
38
+ other.types.any? { |type| Literal.subtype?(type, self) }
37
39
  when Literal::Types::ConstraintType
38
- other.object_constraints.any? { |type| Literal.subtype?(type, of: self) }
40
+ other.object_constraints.any? { |type| Literal.subtype?(type, self) }
39
41
  else
40
- false
42
+ if OwnClassTypeMethodOwners.include?(other.method(:===).owner)
43
+ self === other
44
+ else
45
+ false
46
+ end
41
47
  end
42
48
  end
43
49