literal 1.6.0 → 1.7.1

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 (43) 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 +2 -2
  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 +2 -2
  13. data/lib/literal/properties.rb +3 -4
  14. data/lib/literal/property.rb +3 -3
  15. data/lib/literal/rails/active_record_relation_patch.rb +9 -0
  16. data/lib/literal/rails/relation_type.rb +28 -0
  17. data/lib/literal/railtie.rb +31 -4
  18. data/lib/literal/set.rb +1 -1
  19. data/lib/literal/transforms.rb +2 -2
  20. data/lib/literal/tuple.rb +2 -2
  21. data/lib/literal/types/class_type.rb +2 -2
  22. data/lib/literal/types/constraint_type.rb +4 -4
  23. data/lib/literal/{deferred_type.rb → types/deferred_type.rb} +1 -1
  24. data/lib/literal/types/descendant_type.rb +1 -1
  25. data/lib/literal/types/enumerable_type.rb +1 -1
  26. data/lib/literal/types/frozen_type.rb +2 -2
  27. data/lib/literal/types/hash_type.rb +2 -2
  28. data/lib/literal/types/interface_type.rb +19 -14
  29. data/lib/literal/types/intersection_type.rb +3 -3
  30. data/lib/literal/types/map_type.rb +1 -1
  31. data/lib/literal/types/nilable_type.rb +2 -2
  32. data/lib/literal/types/not_type.rb +3 -3
  33. data/lib/literal/types/predicate_type.rb +22 -0
  34. data/lib/literal/types/range_type.rb +1 -1
  35. data/lib/literal/types/set_type.rb +1 -1
  36. data/lib/literal/types/union_type.rb +7 -12
  37. data/lib/literal/types.rb +4 -25
  38. data/lib/literal/value.rb +65 -0
  39. data/lib/literal/version.rb +1 -1
  40. data/lib/literal.rb +62 -34
  41. metadata +28 -7
  42. data/lib/literal/rails/patches/active_record.rb +0 -32
  43. data/lib/literal/rails.rb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98bc237de806f82733e8c812bccc840c5451183afd22f369ecffc6720c3b88ba
4
- data.tar.gz: b340a2c25c5d23d033a83dbf3ab1483a9b071424f343f0edf5c104bee83402c2
3
+ metadata.gz: bfccfa4acf4ec640c3a47a2bab9d34aec7295a29abc387a20e81f6fb3beb085b
4
+ data.tar.gz: a5a029b15bfc36d930338fc5c2dd6b52abdab0ad1315d54725d2ad90e681bb97
5
5
  SHA512:
6
- metadata.gz: 17b16bdea93b6dff73b9fdcbdd269053648e996f3e2b5d6773933710281b756a50eb93684d727b46711aef990858985111f9d59d80250d20a3f71963767032ca
7
- data.tar.gz: 67647cf3e25cdbdbdb12638081a7cbd40b5bee6771d9be54ca4100646740a97989259f24242b60064c41b016c7687d91a3969632d2195a0715c6015bede8e839
6
+ metadata.gz: 9207d8ba24e146a2d9400c640b6d4c5d6fa97694d95097cf86ac09ebedf16613daa7b1cd4b7946b447809d71aef2e3dca8cd805c59902e3744d00d5b10da37a1
7
+ data.tar.gz: f02700e76bd9a65ef3d3885f3c79984cec164ac47af50f153bb034026d71576926245bc7a481f728c46ea6410a984d45d97d217d16ed1b34b6bf72233179033b
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
@@ -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
@@ -72,7 +72,7 @@ class Literal::Enum
72
72
  end
73
73
 
74
74
  type = @indexes_definitions.fetch(key)[0]
75
- Literal.check(actual: value, expected: type)
75
+ Literal.check(value, type)
76
76
 
77
77
  @indexes.fetch(key)[value]&.first
78
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,7 +96,7 @@ 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
@@ -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
@@ -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
@@ -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 = +"")
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Literal::Rails
4
+ module ActiveRecordRelationPatch
5
+ def Relation(model)
6
+ RelationType.new(model)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Literal::Rails
4
+ class RelationType
5
+ def initialize(model_class)
6
+ unless Class === model_class && model_class < ActiveRecord::Base
7
+ raise Literal::TypeError.new(
8
+ context: Literal::TypeError::Context.new(
9
+ expected: ActiveRecord::Base, actual: model_class
10
+ )
11
+ )
12
+ end
13
+
14
+ @model_class = model_class
15
+ end
16
+
17
+ def inspect = "ActiveRecord::Relation(#{@model_class.name})"
18
+
19
+ def ===(value)
20
+ case value
21
+ when ActiveRecord::Relation, ActiveRecord::Associations::CollectionProxy, ActiveRecord::AssociationRelation
22
+ @model_class == value.model || value.model < @model_class
23
+ else
24
+ false
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,15 +1,42 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ module Literal::Rails
4
+ if defined?(::ActiveJob)
5
+ require_relative "rails/enum_serializer"
6
+ end
7
+
8
+ if defined?(::ActiveRecord::Relation)
9
+ require_relative "rails/relation_type"
10
+ require_relative "rails/active_record_relation_patch"
11
+ ::ActiveRecord.extend(ActiveRecordRelationPatch)
12
+ end
13
+ end
14
+
3
15
  class Literal::Railtie < Rails::Railtie
4
- initializer "literal.register_literal_enum_type" do
5
- [ActiveRecord::Type, ActiveModel::Type].each do |registry|
6
- registry.register(:literal_enum) do |name, type:|
16
+ if defined?(::ActiveModel::Type)
17
+ require_relative "rails/enum_type"
18
+ require_relative "rails/flags_type"
19
+
20
+ initializer "literal.register_active_model_types" do
21
+ ActiveModel::Type.register(:literal_enum) do |name, type:|
7
22
  Literal::Rails::EnumType.new(type)
8
23
  end
9
24
 
10
- registry.register(:literal_flags) do |name, type:|
25
+ ActiveModel::Type.register(:literal_flags) do |name, type:|
11
26
  Literal::Rails::FlagsType.new(type)
12
27
  end
13
28
  end
29
+
30
+ if defined?(::ActiveRecord::Type)
31
+ initializer "literal.register_active_record_types" do
32
+ ActiveRecord::Type.register(:literal_enum) do |name, type:|
33
+ Literal::Rails::EnumType.new(type)
34
+ end
35
+
36
+ ActiveRecord::Type.register(:literal_flags) do |name, type:|
37
+ Literal::Rails::FlagsType.new(type)
38
+ end
39
+ end
40
+ end
14
41
  end
15
42
  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
 
@@ -22,9 +22,9 @@ class Literal::Types::ClassType
22
22
  def >=(other)
23
23
  case other
24
24
  when Literal::Types::ClassType
25
- Literal.subtype?(other.type, of: @type)
25
+ Literal.subtype?(other.type, @type)
26
26
  when Literal::Types::DescendantType
27
- (Class === other.type) && Literal.subtype?(other.type, of: @type)
27
+ (Class === other.type) && Literal.subtype?(other.type, @type)
28
28
  else
29
29
  false
30
30
  end
@@ -46,24 +46,24 @@ class Literal::Types::ConstraintType
46
46
  when Literal::Types::ConstraintType
47
47
  other_object_constraints = other.object_constraints
48
48
  return false unless @object_constraints.all? do |constraint|
49
- other_object_constraints.any? { |c| Literal.subtype?(c, of: constraint) }
49
+ other_object_constraints.any? { |c| Literal.subtype?(c, constraint) }
50
50
  end
51
51
 
52
52
  other_property_constraints = other.property_constraints
53
53
  return false unless @property_constraints.all? do |k, v|
54
- Literal.subtype?(other_property_constraints[k], of: v)
54
+ Literal.subtype?(other_property_constraints[k], v)
55
55
  end
56
56
 
57
57
  true
58
58
  when Literal::Types::IntersectionType
59
59
  other_object_constraints = other.types
60
60
  return false unless @object_constraints.all? do |constraint|
61
- other_object_constraints.any? { |c| Literal.subtype?(c, of: constraint) }
61
+ other_object_constraints.any? { |c| Literal.subtype?(c, constraint) }
62
62
  end
63
63
 
64
64
  true
65
65
  when Literal::Types::FrozenType
66
- @object_constraints.all? { |constraint| Literal.subtype?(other.type, of: constraint) }
66
+ @object_constraints.all? { |constraint| Literal.subtype?(other.type, constraint) }
67
67
  else
68
68
  false
69
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
@@ -21,7 +21,7 @@ class Literal::Types::DescendantType
21
21
  def >=(other)
22
22
  case other
23
23
  when Literal::Types::DescendantType, Literal::Types::ClassType
24
- Literal.subtype?(other.type, of: @type)
24
+ Literal.subtype?(other.type, @type)
25
25
  else
26
26
  false
27
27
  end
@@ -22,7 +22,7 @@ class Literal::Types::EnumerableType
22
22
  def >=(other)
23
23
  case other
24
24
  when Literal::Types::EnumerableType
25
- Literal.subtype?(other.type, of: @type)
25
+ Literal.subtype?(other.type, @type)
26
26
  else
27
27
  false
28
28
  end
@@ -31,11 +31,11 @@ class Literal::Types::FrozenType
31
31
  @type >= other.type
32
32
  when Literal::Types::ConstraintType
33
33
  type_match = false
34
- frozen_match = Literal.subtype?(other.property_constraints[:frozen?], of: true)
34
+ frozen_match = Literal.subtype?(other.property_constraints[:frozen?], true)
35
35
 
36
36
  other.object_constraints.each do |constraint|
37
37
  frozen_match ||= ALWAYS_FROZEN.include?(constraint)
38
- type_match ||= Literal.subtype?(constraint, of: @type)
38
+ type_match ||= Literal.subtype?(constraint, @type)
39
39
  return true if frozen_match && type_match
40
40
  end
41
41
 
@@ -30,9 +30,9 @@ class Literal::Types::HashType
30
30
  case other
31
31
  when Literal::Types::HashType
32
32
  (
33
- Literal.subtype?(other.key_type, of: @key_type)
33
+ Literal.subtype?(other.key_type, @key_type)
34
34
  ) && (
35
- Literal.subtype?(other.value_type, of: @value_type)
35
+ Literal.subtype?(other.value_type, @value_type)
36
36
  )
37
37
  else
38
38
  false
@@ -2,18 +2,14 @@
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
17
13
  freeze
18
14
  end
19
15
 
@@ -24,21 +20,30 @@ class Literal::Types::InterfaceType
24
20
  end
25
21
 
26
22
  def ===(value)
27
- @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
28
28
  end
29
29
 
30
30
  def >=(other)
31
31
  case other
32
32
  when Literal::Types::InterfaceType
33
- @methods.all? { |m| other.methods.include?(m) }
33
+ @methods.subset?(other.methods)
34
34
  when Module
35
- @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)
36
37
  when Literal::Types::IntersectionType
37
- other.types.any? { |type| Literal.subtype?(type, of: self) }
38
+ other.types.any? { |type| Literal.subtype?(type, self) }
38
39
  when Literal::Types::ConstraintType
39
- other.object_constraints.any? { |type| Literal.subtype?(type, of: self) }
40
+ other.object_constraints.any? { |type| Literal.subtype?(type, self) }
40
41
  else
41
- false
42
+ if OwnClassTypeMethodOwners.include?(other.method(:===).owner)
43
+ self === other
44
+ else
45
+ false
46
+ end
42
47
  end
43
48
  end
44
49
 
@@ -36,17 +36,17 @@ class Literal::Types::IntersectionType
36
36
  when Literal::Types::IntersectionType
37
37
  @types.all? do |type|
38
38
  other.types.any? do |other_type|
39
- Literal.subtype?(other_type, of: type)
39
+ Literal.subtype?(other_type, type)
40
40
  end
41
41
  end
42
42
  when Literal::Types::ConstraintType
43
43
  @types.all? do |type|
44
44
  other.object_constraints.any? do |object_constraint|
45
- Literal.subtype?(object_constraint, of: type)
45
+ Literal.subtype?(object_constraint, type)
46
46
  end
47
47
  end
48
48
  when Literal::Types::FrozenType
49
- @types.all? { |type| Literal.subtype?(other.type, of: type) }
49
+ @types.all? { |type| Literal.subtype?(other.type, type) }
50
50
  else
51
51
  false
52
52
  end
@@ -45,7 +45,7 @@ class Literal::Types::MapType
45
45
  other_shape = other.shape
46
46
 
47
47
  @shape.all? do |k, v|
48
- Literal.subtype?(other_shape[k], of: v)
48
+ Literal.subtype?(other_shape[k], v)
49
49
  end
50
50
  else
51
51
  false
@@ -26,11 +26,11 @@ class Literal::Types::NilableType
26
26
  def >=(other)
27
27
  case other
28
28
  when Literal::Types::NilableType
29
- Literal.subtype?(other.type, of: @type)
29
+ Literal.subtype?(other.type, @type)
30
30
  when nil
31
31
  true
32
32
  else
33
- Literal.subtype?(other, of: @type)
33
+ Literal.subtype?(other, @type)
34
34
  end
35
35
  end
36
36
 
@@ -22,11 +22,11 @@ class Literal::Types::NotType
22
22
  def >=(other)
23
23
  case other
24
24
  when Literal::Types::NotType
25
- Literal.subtype?(other.type, of: @type)
25
+ Literal.subtype?(other.type, @type)
26
26
  when Literal::Types::ConstraintType
27
- other.object_constraints.any? { |constraint| Literal.subtype?(constraint, of: self) }
27
+ other.object_constraints.any? { |constraint| Literal.subtype?(constraint, self) }
28
28
  when Literal::Types::IntersectionType
29
- other.types.any? { |type| Literal.subtype?(type, of: self) }
29
+ other.types.any? { |type| Literal.subtype?(type, self) }
30
30
  else
31
31
  false
32
32
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Literal::Types::PredicateType
4
+ include Literal::Type
5
+
6
+ def initialize(message:, block:)
7
+ @message = message
8
+ @block = block
9
+
10
+ freeze
11
+ end
12
+
13
+ def inspect
14
+ %(_Predicate("#{@message}"))
15
+ end
16
+
17
+ def ===(other)
18
+ @block === other
19
+ end
20
+
21
+ freeze
22
+ end
@@ -28,7 +28,7 @@ class Literal::Types::RangeType
28
28
  def >=(other)
29
29
  case other
30
30
  when Literal::Types::RangeType
31
- Literal.subtype?(other.type, of: @type)
31
+ Literal.subtype?(other.type, @type)
32
32
  else
33
33
  false
34
34
  end
@@ -38,7 +38,7 @@ class Literal::Types::SetType
38
38
  def >=(other)
39
39
  case other
40
40
  when Literal::Types::SetType
41
- Literal.subtype?(other.type, of: @type)
41
+ Literal.subtype?(other.type, @type)
42
42
  else
43
43
  false
44
44
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  class Literal::Types::UnionType
4
4
  include Enumerable
5
+ include Literal::Type
5
6
 
6
7
  def initialize(*queue)
7
8
  raise Literal::ArgumentError.new("_Union type must have at least one type.") if queue.size < 1
@@ -32,7 +33,7 @@ class Literal::Types::UnionType
32
33
  attr_reader :types, :primitives
33
34
 
34
35
  def inspect
35
- "_Union(#{@types.inspect})"
36
+ "_Union(#{to_a.map(&:inspect).join(', ')})"
36
37
  end
37
38
 
38
39
  def ===(value)
@@ -45,6 +46,8 @@ class Literal::Types::UnionType
45
46
  return true if types[i] === value
46
47
  i += 1
47
48
  end
49
+
50
+ false
48
51
  end
49
52
 
50
53
  def each(&)
@@ -66,14 +69,6 @@ class Literal::Types::UnionType
66
69
  self[key] or raise KeyError.new("Key not found: #{key.inspect}")
67
70
  end
68
71
 
69
- def record_literal_type_errors(ctx)
70
- each do |type|
71
- ctx.add_child(label: type.inspect, expected: type, actual: ctx.actual)
72
- end
73
-
74
- ctx.children.clear if ctx.children.none? { |c| c.children.any? }
75
- end
76
-
77
72
  def >=(other)
78
73
  types = @types
79
74
  primitives = @primitives
@@ -81,16 +76,16 @@ class Literal::Types::UnionType
81
76
  case other
82
77
  when Literal::Types::UnionType
83
78
  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) }
79
+ primitives.any? { |p| Literal.subtype?(other_type, p) } || types.any? { |t| Literal.subtype?(other_type, t) }
85
80
  end
86
81
 
87
82
  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) }
83
+ primitives.any? { |p| Literal.subtype?(other_primitive, p) } || types.any? { |t| Literal.subtype?(other_primitive, t) }
89
84
  end
90
85
 
91
86
  types_have_at_least_one_subtype && primitives_have_at_least_one_subtype
92
87
  else
93
- types.any? { |t| Literal.subtype?(other, of: t) } || primitives.any? { |p| Literal.subtype?(other, of: p) }
88
+ types.any? { |t| Literal.subtype?(other, t) } || primitives.any? { |p| Literal.subtype?(other, p) }
94
89
  end
95
90
  end
96
91
 
data/lib/literal/types.rb CHANGED
@@ -3,31 +3,6 @@
3
3
  module Literal::Types
4
4
  extend self
5
5
 
6
- autoload :AnyType, "literal/types/any_type"
7
- autoload :ArrayType, "literal/types/array_type"
8
- autoload :BooleanType, "literal/types/boolean_type"
9
- autoload :ClassType, "literal/types/class_type"
10
- autoload :ConstraintType, "literal/types/constraint_type"
11
- autoload :DeferredType, "literal/deferred_type"
12
- autoload :DescendantType, "literal/types/descendant_type"
13
- autoload :EnumerableType, "literal/types/enumerable_type"
14
- autoload :FalsyType, "literal/types/falsy_type"
15
- autoload :FrozenType, "literal/types/frozen_type"
16
- autoload :HashType, "literal/types/hash_type"
17
- autoload :InterfaceType, "literal/types/interface_type"
18
- autoload :IntersectionType, "literal/types/intersection_type"
19
- autoload :JSONDataType, "literal/types/json_data_type"
20
- autoload :MapType, "literal/types/map_type"
21
- autoload :NeverType, "literal/types/never_type"
22
- autoload :NilableType, "literal/types/nilable_type"
23
- autoload :NotType, "literal/types/not_type"
24
- autoload :RangeType, "literal/types/range_type"
25
- autoload :SetType, "literal/types/set_type"
26
- autoload :TruthyType, "literal/types/truthy_type"
27
- autoload :TupleType, "literal/types/tuple_type"
28
- autoload :UnionType, "literal/types/union_type"
29
- autoload :VoidType, "literal/types/void_type"
30
-
31
6
  # Matches any value except `nil`. Use `_Any?` or `_Void` to match any value including `nil`.
32
7
  # ```ruby
33
8
  # _Any
@@ -339,6 +314,10 @@ module Literal::Types
339
314
  end
340
315
  end
341
316
 
317
+ def _Predicate(message, &block)
318
+ PredicateType.new(message:, block:)
319
+ end
320
+
342
321
  # Matches if the value is a `Proc` or responds to `#to_proc`.
343
322
  def _Procable
344
323
  ProcableType
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Literal::Value
4
+ def self.to_proc
5
+ -> (value) { new(value) }
6
+ end
7
+
8
+ def self.[](value)
9
+ new(value)
10
+ end
11
+
12
+ def self.from_pack(payload)
13
+ object = allocate
14
+ object.marshal_load(payload)
15
+ object
16
+ end
17
+
18
+ # Takes a list of method names and delegates them to the underlying value.
19
+ def self.delegate(*methods)
20
+ methods.each do |method_name|
21
+ class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
22
+ # frozen_string_literal: true
23
+
24
+ def #{method_name}(...)
25
+ @value.#{method_name}(...)
26
+ end
27
+ RUBY
28
+ end
29
+ end
30
+
31
+ def initialize(value)
32
+ Literal.check(value, __type__)
33
+ @value = value
34
+ freeze
35
+ end
36
+
37
+ attr_reader :value
38
+
39
+ def inspect
40
+ "#{self.class.name}(#{value.inspect})"
41
+ end
42
+
43
+ def ===(other)
44
+ self.class === other && @value == other.value
45
+ end
46
+
47
+ alias_method :==, :===
48
+
49
+ def as_pack
50
+ marshal_dump
51
+ end
52
+
53
+ def marshal_load(payload)
54
+ _version, value, was_frozen = payload
55
+
56
+ @value = value
57
+ freeze if was_frozen
58
+ end
59
+
60
+ def marshal_dump
61
+ [1, @value, frozen?].freeze
62
+ end
63
+
64
+ freeze
65
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Literal
4
- VERSION = "1.6.0"
4
+ VERSION = "1.7.1"
5
5
  end
data/lib/literal.rb CHANGED
@@ -1,33 +1,58 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "zeitwerk"
4
+ require_relative "literal/version"
5
+
3
6
  module Literal
4
- autoload :Array, "literal/array"
5
- autoload :Data, "literal/data"
6
- autoload :DataProperty, "literal/data_property"
7
- autoload :DataStructure, "literal/data_structure"
8
- autoload :Enum, "literal/enum"
9
- autoload :Flags, "literal/flags"
10
- autoload :Flags16, "literal/flags"
11
- autoload :Flags32, "literal/flags"
12
- autoload :Flags64, "literal/flags"
13
- autoload :Flags8, "literal/flags"
14
- autoload :Hash, "literal/hash"
15
- autoload :Null, "literal/null"
16
- autoload :Object, "literal/object"
17
- autoload :Properties, "literal/properties"
18
- autoload :Property, "literal/property"
19
- autoload :Set, "literal/set"
20
- autoload :Struct, "literal/struct"
21
- autoload :Type, "literal/type"
22
- autoload :Types, "literal/types"
23
- autoload :Tuple, "literal/tuple"
24
-
25
- # Errors
26
- autoload :Error, "literal/errors/error"
27
- autoload :TypeError, "literal/errors/type_error"
28
- autoload :ArgumentError, "literal/errors/argument_error"
29
-
30
- autoload :TRANSFORMS, "literal/transforms"
7
+ Loader = Zeitwerk::Loader.for_gem.tap do |loader|
8
+ loader.ignore("#{__dir__}/literal/rails")
9
+ loader.ignore("#{__dir__}/literal/railtie.rb")
10
+
11
+ loader.inflector.inflect(
12
+ "json_data_type" => "JSONDataType"
13
+ )
14
+
15
+ loader.collapse("#{__dir__}/literal/flags")
16
+ loader.collapse("#{__dir__}/literal/errors")
17
+
18
+ loader.setup
19
+ end
20
+
21
+ def self.Value(*args, **kwargs, &block)
22
+ value_class = Class.new(Literal::Value)
23
+
24
+ type = Literal::Types._Constraint(*args, **kwargs)
25
+ value_class.define_method(:__type__) { type }
26
+
27
+ if subtype?(type, Integer)
28
+ value_class.alias_method :to_i, :value
29
+ elsif subtype?(type, String)
30
+ value_class.alias_method :to_s, :value
31
+ value_class.alias_method :to_str, :value
32
+ elsif subtype?(type, Array)
33
+ value_class.alias_method :to_a, :value
34
+ value_class.alias_method :to_ary, :value
35
+ elsif subtype?(type, Hash)
36
+ value_class.alias_method :to_h, :value
37
+ elsif subtype?(type, Float)
38
+ value_class.alias_method :to_f, :value
39
+ elsif subtype?(type, Set)
40
+ value_class.alias_method :to_set, :value
41
+ end
42
+
43
+ value_class.class_eval(&block) if block
44
+ value_class.freeze
45
+ end
46
+
47
+ def self.Delegator(*args, **kwargs, &block)
48
+ delegator_class = Class.new(Literal::Delegator)
49
+
50
+ type = Literal::Types._Constraint(*args, **kwargs)
51
+ delegator_class.define_method(:__type__) { type }
52
+
53
+ delegator_class.class_eval(&block) if block
54
+ delegator_class.freeze
55
+ end
31
56
 
32
57
  def self.Enum(type)
33
58
  Class.new(Literal::Enum) do
@@ -51,19 +76,22 @@ module Literal
51
76
  Literal::Tuple::Generic.new(*types)
52
77
  end
53
78
 
54
- def self.check(actual:, expected:)
55
- if expected === actual
79
+ def self.Brand(...)
80
+ Literal::Brand.new(...)
81
+ end
82
+
83
+ def self.check(value, type)
84
+ if type === value
56
85
  true
57
86
  else
58
- context = Literal::TypeError::Context.new(expected:, actual:)
59
- expected.record_literal_type_errors(context) if expected.respond_to?(:record_literal_type_errors)
87
+ context = Literal::TypeError::Context.new(expected: type, actual: value)
88
+ type.record_literal_type_errors(context) if type.respond_to?(:record_literal_type_errors)
60
89
  yield context if block_given?
61
90
  raise Literal::TypeError.new(context:)
62
91
  end
63
92
  end
64
93
 
65
- def self.subtype?(type, of:)
66
- supertype = of
94
+ def self.subtype?(type, supertype)
67
95
  subtype = type
68
96
 
69
97
  subtype = subtype.block.call if Types::DeferredType === subtype
@@ -98,4 +126,4 @@ module Literal
98
126
  end
99
127
  end
100
128
 
101
- require_relative "literal/rails" if defined?(Rails)
129
+ require_relative "literal/railtie" if defined?(Rails)
metadata CHANGED
@@ -1,14 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: literal
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0
4
+ version: 1.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Drapper
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-10 00:00:00.000000000 Z
11
- dependencies: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: zeitwerk
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
12
26
  description: Enums, properties, generics, structured objects and runtime type checking.
13
27
  email:
14
28
  - joel@drapper.me
@@ -22,13 +36,17 @@ files:
22
36
  - lib/literal/array.rb
23
37
  - lib/literal/data.rb
24
38
  - lib/literal/data_structure.rb
25
- - lib/literal/deferred_type.rb
39
+ - lib/literal/delegator.rb
26
40
  - lib/literal/enum.rb
27
41
  - lib/literal/errors/argument_error.rb
28
42
  - lib/literal/errors/error.rb
29
43
  - lib/literal/errors/type_error.rb
30
44
  - lib/literal/failure.rb
31
45
  - lib/literal/flags.rb
46
+ - lib/literal/flags/flags_16.rb
47
+ - lib/literal/flags/flags_32.rb
48
+ - lib/literal/flags/flags_64.rb
49
+ - lib/literal/flags/flags_8.rb
32
50
  - lib/literal/hash.rb
33
51
  - lib/literal/null.rb
34
52
  - lib/literal/object.rb
@@ -36,11 +54,11 @@ files:
36
54
  - lib/literal/properties/data_schema.rb
37
55
  - lib/literal/properties/schema.rb
38
56
  - lib/literal/property.rb
39
- - lib/literal/rails.rb
57
+ - lib/literal/rails/active_record_relation_patch.rb
40
58
  - lib/literal/rails/enum_serializer.rb
41
59
  - lib/literal/rails/enum_type.rb
42
60
  - lib/literal/rails/flags_type.rb
43
- - lib/literal/rails/patches/active_record.rb
61
+ - lib/literal/rails/relation_type.rb
44
62
  - lib/literal/railtie.rb
45
63
  - lib/literal/result.rb
46
64
  - lib/literal/set.rb
@@ -55,6 +73,7 @@ files:
55
73
  - lib/literal/types/boolean_type.rb
56
74
  - lib/literal/types/class_type.rb
57
75
  - lib/literal/types/constraint_type.rb
76
+ - lib/literal/types/deferred_type.rb
58
77
  - lib/literal/types/descendant_type.rb
59
78
  - lib/literal/types/enumerable_type.rb
60
79
  - lib/literal/types/falsy_type.rb
@@ -67,12 +86,14 @@ files:
67
86
  - lib/literal/types/never_type.rb
68
87
  - lib/literal/types/nilable_type.rb
69
88
  - lib/literal/types/not_type.rb
89
+ - lib/literal/types/predicate_type.rb
70
90
  - lib/literal/types/range_type.rb
71
91
  - lib/literal/types/set_type.rb
72
92
  - lib/literal/types/truthy_type.rb
73
93
  - lib/literal/types/tuple_type.rb
74
94
  - lib/literal/types/union_type.rb
75
95
  - lib/literal/types/void_type.rb
96
+ - lib/literal/value.rb
76
97
  - lib/literal/version.rb
77
98
  homepage: https://literal.fun
78
99
  licenses:
@@ -97,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
97
118
  - !ruby/object:Gem::Version
98
119
  version: '0'
99
120
  requirements: []
100
- rubygems_version: 3.6.2
121
+ rubygems_version: 3.6.7
101
122
  specification_version: 4
102
123
  summary: Enums, properties, generics, structured objects and runtime type checking.
103
124
  test_files: []
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActiveRecord
4
- class RelationType
5
- def initialize(model_class)
6
- unless Class === model_class && model_class < ActiveRecord::Base
7
- raise Literal::TypeError.new(
8
- context: Literal::TypeError::Context.new(
9
- expected: ActiveRecord::Base, actual: model_class
10
- )
11
- )
12
- end
13
-
14
- @model_class = model_class
15
- end
16
-
17
- def inspect = "ActiveRecord::Relation(#{@model_class.name})"
18
-
19
- def ===(value)
20
- case value
21
- when ActiveRecord::Relation, ActiveRecord::Associations::CollectionProxy, ActiveRecord::AssociationRelation
22
- @model_class == value.model || value.model < @model_class
23
- else
24
- false
25
- end
26
- end
27
- end
28
-
29
- def self.Relation(model)
30
- RelationType.new(model)
31
- end
32
- end
data/lib/literal/rails.rb DELETED
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "railtie"
4
- require_relative "rails/patches/active_record"
5
-
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
- end