literal 1.2.0 → 1.4.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/literal/array.rb +183 -1
  3. data/lib/literal/data.rb +1 -7
  4. data/lib/literal/deferred_type.rb +23 -0
  5. data/lib/literal/enum.rb +9 -2
  6. data/lib/literal/properties/schema.rb +1 -1
  7. data/lib/literal/property.rb +4 -4
  8. data/lib/literal/transforms.rb +142 -0
  9. data/lib/literal/tuple.rb +60 -0
  10. data/lib/literal/types/any_type.rb +2 -2
  11. data/lib/literal/types/boolean_type.rb +2 -2
  12. data/lib/literal/types/constraint_type.rb +4 -2
  13. data/lib/literal/types/falsy_type.rb +2 -2
  14. data/lib/literal/types/frozen_type.rb +35 -1
  15. data/lib/literal/types/hash_type.rb +22 -1
  16. data/lib/literal/types/interface_type.rb +29 -1
  17. data/lib/literal/types/intersection_type.rb +25 -0
  18. data/lib/literal/types/json_data_type.rb +34 -4
  19. data/lib/literal/types/map_type.rb +18 -1
  20. data/lib/literal/types/never_type.rb +18 -3
  21. data/lib/literal/types/nilable_type.rb +18 -1
  22. data/lib/literal/types/not_type.rb +22 -1
  23. data/lib/literal/types/range_type.rb +18 -1
  24. data/lib/literal/types/set_type.rb +18 -1
  25. data/lib/literal/types/truthy_type.rb +17 -3
  26. data/lib/literal/types/tuple_type.rb +19 -2
  27. data/lib/literal/types/union_type.rb +21 -4
  28. data/lib/literal/types/void_type.rb +13 -3
  29. data/lib/literal/types.rb +51 -44
  30. data/lib/literal/version.rb +1 -1
  31. data/lib/literal.rb +11 -65
  32. metadata +7 -12
  33. data/lib/literal/data_property.rb +0 -16
  34. data/lib/literal/types/callable_type.rb +0 -31
  35. data/lib/literal/types/float_type.rb +0 -10
  36. data/lib/literal/types/integer_type.rb +0 -10
  37. data/lib/literal/types/lambda_type.rb +0 -12
  38. data/lib/literal/types/procable_type.rb +0 -12
  39. data/lib/literal/types/string_type.rb +0 -10
  40. data/lib/literal/types/symbol_type.rb +0 -10
data/lib/literal/types.rb CHANGED
@@ -4,36 +4,34 @@ module Literal::Types
4
4
  autoload :AnyType, "literal/types/any_type"
5
5
  autoload :ArrayType, "literal/types/array_type"
6
6
  autoload :BooleanType, "literal/types/boolean_type"
7
- autoload :CallableType, "literal/types/callable_type"
8
7
  autoload :ClassType, "literal/types/class_type"
9
8
  autoload :ConstraintType, "literal/types/constraint_type"
9
+ autoload :DeferredType, "literal/deferred_type"
10
10
  autoload :DescendantType, "literal/types/descendant_type"
11
11
  autoload :EnumerableType, "literal/types/enumerable_type"
12
12
  autoload :FalsyType, "literal/types/falsy_type"
13
- autoload :FloatType, "literal/types/float_type"
14
13
  autoload :FrozenType, "literal/types/frozen_type"
15
14
  autoload :HashType, "literal/types/hash_type"
16
- autoload :IntegerType, "literal/types/integer_type"
17
15
  autoload :InterfaceType, "literal/types/interface_type"
18
16
  autoload :IntersectionType, "literal/types/intersection_type"
19
17
  autoload :JSONDataType, "literal/types/json_data_type"
20
- autoload :LambdaType, "literal/types/lambda_type"
21
18
  autoload :MapType, "literal/types/map_type"
22
19
  autoload :NeverType, "literal/types/never_type"
23
20
  autoload :NilableType, "literal/types/nilable_type"
24
21
  autoload :NotType, "literal/types/not_type"
25
- autoload :ProcableType, "literal/types/procable_type"
26
22
  autoload :RangeType, "literal/types/range_type"
27
23
  autoload :SetType, "literal/types/set_type"
28
- autoload :StringType, "literal/types/string_type"
29
- autoload :SymbolType, "literal/types/symbol_type"
30
24
  autoload :TruthyType, "literal/types/truthy_type"
31
25
  autoload :TupleType, "literal/types/tuple_type"
32
26
  autoload :UnionType, "literal/types/union_type"
33
27
  autoload :VoidType, "literal/types/void_type"
34
28
 
29
+ ProcableType = InterfaceType.new(:to_proc).freeze
30
+ CallableType = InterfaceType.new(:call).freeze
31
+ LambdaType = ConstraintType.new(Proc, lambda?: true).freeze
32
+
35
33
  NilableBooleanType = NilableType.new(BooleanType::Instance).freeze
36
- NilableCallableType = NilableType.new(CallableType::Instance).freeze
34
+ NilableCallableType = NilableType.new(CallableType).freeze
37
35
  NilableJSONDataType = NilableType.new(JSONDataType).freeze
38
36
  NilableLambdaType = NilableType.new(LambdaType).freeze
39
37
  NilableProcableType = NilableType.new(ProcableType).freeze
@@ -44,7 +42,7 @@ module Literal::Types
44
42
  end
45
43
 
46
44
  def _Any?
47
- VoidType
45
+ VoidType::Instance
48
46
  end
49
47
 
50
48
  # Matches if the value is an `Array` and all the elements match the given type.
@@ -55,7 +53,7 @@ module Literal::Types
55
53
  # Nilable version of `_Array`
56
54
  def _Array?(...)
57
55
  NilableType.new(
58
- ArrayType.new(...),
56
+ ArrayType.new(...)
59
57
  )
60
58
  end
61
59
 
@@ -71,7 +69,7 @@ module Literal::Types
71
69
 
72
70
  # Matches if the value responds to `#call`.
73
71
  def _Callable
74
- CallableType::Instance
72
+ CallableType
75
73
  end
76
74
 
77
75
  # Nilabl version of `_Callable`
@@ -87,7 +85,7 @@ module Literal::Types
87
85
  # Nilable version of `_Class`
88
86
  def _Class?(...)
89
87
  NilableType.new(
90
- ClassType.new(...),
88
+ ClassType.new(...)
91
89
  )
92
90
  end
93
91
 
@@ -101,10 +99,14 @@ module Literal::Types
101
99
  # Nilable version of `_Constraint`
102
100
  def _Constraint?(...)
103
101
  NilableType.new(
104
- ConstraintType.new(...),
102
+ ConstraintType.new(...)
105
103
  )
106
104
  end
107
105
 
106
+ def _Deferred(...)
107
+ DeferredType.new(...)
108
+ end
109
+
108
110
  # Matches if the value is a descendant of the given class.
109
111
  def _Descendant(...)
110
112
  DescendantType.new(...)
@@ -113,7 +115,7 @@ module Literal::Types
113
115
  # Nilable version of `_Descendant`
114
116
  def _Descendant?(...)
115
117
  NilableType.new(
116
- DescendantType.new(...),
118
+ DescendantType.new(...)
117
119
  )
118
120
  end
119
121
 
@@ -125,7 +127,7 @@ module Literal::Types
125
127
  # Nilable version of `_Enumerable`
126
128
  def _Enumerable?(...)
127
129
  NilableType.new(
128
- EnumerableType.new(...),
130
+ EnumerableType.new(...)
129
131
  )
130
132
  end
131
133
 
@@ -134,17 +136,17 @@ module Literal::Types
134
136
  FalsyType::Instance
135
137
  end
136
138
 
137
- # Matches if the value is a `Float` and matches the given constraint.
139
+ # Matches if the value is a `Float` and matches the given constraints.
138
140
  # You could use a `Range`, for example, as a constraint.
139
141
  # If you don't need a constraint, use `Float` instead of `_Float`.
140
142
  def _Float(...)
141
- FloatType.new(...)
143
+ _Constraint(Float, ...)
142
144
  end
143
145
 
144
146
  # Nilable version of `_Float`
145
147
  def _Float?(...)
146
- NilableType.new(
147
- FloatType.new(...),
148
+ _Nilable(
149
+ _Float(...)
148
150
  )
149
151
  end
150
152
 
@@ -156,7 +158,7 @@ module Literal::Types
156
158
  # Nilable version of `_Frozen`
157
159
  def _Frozen?(...)
158
160
  NilableType.new(
159
- FrozenType.new(...),
161
+ FrozenType.new(...)
160
162
  )
161
163
  end
162
164
 
@@ -168,7 +170,7 @@ module Literal::Types
168
170
  # Nilable version of `_Hash`
169
171
  def _Hash?(...)
170
172
  NilableType.new(
171
- HashType.new(...),
173
+ HashType.new(...)
172
174
  )
173
175
  end
174
176
 
@@ -178,13 +180,13 @@ module Literal::Types
178
180
  # @example
179
181
  # attribute :age, _Integer(18..127)
180
182
  def _Integer(...)
181
- IntegerType.new(...)
183
+ _Constraint(Integer, ...)
182
184
  end
183
185
 
184
186
  # Nilable version of `_Integer`
185
187
  def _Integer?(...)
186
- NilableType.new(
187
- IntegerType.new(...),
188
+ _Nilable(
189
+ _Integer(...)
188
190
  )
189
191
  end
190
192
 
@@ -196,7 +198,7 @@ module Literal::Types
196
198
  # Nilable version of `_Interface`
197
199
  def _Interface?(...)
198
200
  NilableType.new(
199
- InterfaceType.new(...),
201
+ InterfaceType.new(...)
200
202
  )
201
203
  end
202
204
 
@@ -208,13 +210,13 @@ module Literal::Types
208
210
  # Nilable version of `_Intersection`
209
211
  def _Intersection?(...)
210
212
  NilableType.new(
211
- IntersectionType.new(...),
213
+ IntersectionType.new(...)
212
214
  )
213
215
  end
214
216
 
215
217
  # Ensures the value is valid JSON data (i.e. it came from JSON.parse).
216
218
  def _JSONData
217
- JSONDataType
219
+ JSONDataType::Instance
218
220
  end
219
221
 
220
222
  # Nilable version of `_JSONData`
@@ -239,13 +241,13 @@ module Literal::Types
239
241
  # Nilable version of `_Map`
240
242
  def _Map?(...)
241
243
  NilableType.new(
242
- MapType.new(...),
244
+ MapType.new(...)
243
245
  )
244
246
  end
245
247
 
246
248
  # Never matches any value.
247
249
  def _Never
248
- NeverType
250
+ NeverType::Instance
249
251
  end
250
252
 
251
253
  # Matches if the value is either `nil` or the given type.
@@ -254,8 +256,13 @@ module Literal::Types
254
256
  end
255
257
 
256
258
  # Matches if the given type is *not* matched.
257
- def _Not(...)
258
- NotType.new(...)
259
+ def _Not(type)
260
+ case type
261
+ when NotType
262
+ type.type
263
+ else
264
+ NotType.new(type)
265
+ end
259
266
  end
260
267
 
261
268
  # Matches if the value is a `Proc` or responds to `#to_proc`.
@@ -276,7 +283,7 @@ module Literal::Types
276
283
  # Nilable version of `_Range`
277
284
  def _Range?(...)
278
285
  NilableType.new(
279
- RangeType.new(...),
286
+ RangeType.new(...)
280
287
  )
281
288
  end
282
289
 
@@ -288,38 +295,38 @@ module Literal::Types
288
295
  # Nilable version of `_Set`
289
296
  def _Set?(...)
290
297
  NilableType.new(
291
- SetType.new(...),
298
+ SetType.new(...)
292
299
  )
293
300
  end
294
301
 
295
302
  # Matches if the value is a `String` and matches the given constraints.
296
303
  # If you don't need any constraints, use `String` instead of `_String`.
297
304
  def _String(...)
298
- StringType.new(...)
305
+ _Constraint(String, ...)
299
306
  end
300
307
 
301
308
  # Nilable version of `_String`
302
309
  def _String?(...)
303
- NilableType.new(
304
- StringType.new(...),
310
+ _Nilable(
311
+ _String(...)
305
312
  )
306
313
  end
307
314
 
308
- # Matches if the value is a `Symbol` and matches the given constraint.
315
+ # Matches if the value is a `Symbol` and matches the given constraints.
309
316
  def _Symbol(...)
310
- SymbolType.new(...)
317
+ _Constraint(Symbol, ...)
311
318
  end
312
319
 
313
320
  # Nilable version of `_Symbol`
314
321
  def _Symbol?(...)
315
- NilableType.new(
316
- SymbolType.new(...),
322
+ _Nilable(
323
+ _Symbol(...)
317
324
  )
318
325
  end
319
326
 
320
327
  # Matches *"truthy"* values (anything except `nil` and `false`).
321
328
  def _Truthy
322
- TruthyType
329
+ TruthyType::Instance
323
330
  end
324
331
 
325
332
  # Matches if the value is an `Array` and each element matches the given types in order.
@@ -330,7 +337,7 @@ module Literal::Types
330
337
  # Nilable version of `_Typle`
331
338
  def _Tuple?(...)
332
339
  NilableType.new(
333
- TupleType.new(...),
340
+ TupleType.new(...)
334
341
  )
335
342
  end
336
343
 
@@ -342,11 +349,11 @@ module Literal::Types
342
349
  # Nilable version of `_Union`
343
350
  def _Union?(...)
344
351
  NilableType.new(
345
- UnionType.new(...),
352
+ UnionType.new(...)
346
353
  )
347
354
  end
348
355
 
349
356
  def _Void
350
- VoidType
357
+ VoidType::Instance
351
358
  end
352
359
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Literal
4
- VERSION = "1.2.0"
4
+ VERSION = "1.4.0"
5
5
  end
data/lib/literal.rb CHANGED
@@ -22,76 +22,14 @@ module Literal
22
22
  autoload :Struct, "literal/struct"
23
23
  autoload :Type, "literal/type"
24
24
  autoload :Types, "literal/types"
25
+ autoload :Tuple, "literal/tuple"
25
26
 
26
27
  # Errors
27
28
  autoload :Error, "literal/errors/error"
28
29
  autoload :TypeError, "literal/errors/type_error"
29
30
  autoload :ArgumentError, "literal/errors/argument_error"
30
31
 
31
- TRANSFORMS = {
32
- Integer => {
33
- abs: Integer,
34
- ceil: Integer,
35
- chr: String,
36
- denominator: Integer,
37
- even?: Types::BooleanType::Instance,
38
- floor: Integer,
39
- hash: Integer,
40
- inspect: String,
41
- integer?: true,
42
- magnitude: Integer,
43
- negative?: Types::BooleanType::Instance,
44
- next: Integer,
45
- nonzero?: Types::BooleanType::Instance,
46
- numerator: Integer,
47
- odd?: Types::BooleanType::Instance,
48
- ord: Integer,
49
- positive?: Types::BooleanType::Instance,
50
- pred: Integer,
51
- round: Integer,
52
- size: Integer,
53
- succ: Integer,
54
- to_f: Float,
55
- to_i: Integer,
56
- to_int: Integer,
57
- to_r: Rational,
58
- to_s: String,
59
- truncate: Integer,
60
- zero?: Types::BooleanType::Instance,
61
- },
62
- String => {
63
- ascii_only?: Types::BooleanType::Instance,
64
- bytesize: Integer,
65
- capitalize: String,
66
- chomp: String,
67
- chop: String,
68
- downcase: String,
69
- dump: String,
70
- empty?: Types::BooleanType::Instance,
71
- hash: Integer,
72
- inspect: String,
73
- length: Integer,
74
- lstrip: String,
75
- ord: Integer,
76
- reverse: String,
77
- rstrip: String,
78
- scrub: String,
79
- size: Integer,
80
- strip: String,
81
- swapcase: String,
82
- to_str: String,
83
- upcase: String,
84
- valid_encoding?: Types::BooleanType::Instance,
85
- },
86
- Array => {
87
- size: Integer,
88
- length: Integer,
89
- empty?: Types::BooleanType::Instance,
90
- sort: Array,
91
- to_a: Array,
92
- to_ary: Array,
93
- },
94
- }.transform_values! { |it| it.transform_keys(&:to_proc) }.freeze
32
+ autoload :TRANSFORMS, "literal/transforms"
95
33
 
96
34
  def self.Enum(type)
97
35
  Class.new(Literal::Enum) do
@@ -111,6 +49,10 @@ module Literal
111
49
  Literal::Hash::Generic.new(key_type, value_type)
112
50
  end
113
51
 
52
+ def self.Tuple(*types)
53
+ Literal::Tuple::Generic.new(*types)
54
+ end
55
+
114
56
  def self.check(actual:, expected:)
115
57
  if expected === actual
116
58
  true
@@ -123,9 +65,13 @@ module Literal
123
65
  end
124
66
 
125
67
  def self.subtype?(type, of:)
68
+ type = type.block.call if Types::DeferredType === type
69
+
126
70
  (of == type) || case of
127
- when Literal::Type, Module
71
+ when Literal::Type
128
72
  of >= type
73
+ when Module
74
+ (Module === type) ? of >= type : false
129
75
  when Range
130
76
  of.cover?(type)
131
77
  else
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: literal
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Drapper
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-11-22 00:00:00.000000000 Z
11
+ date: 2024-12-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: A literal Ruby gem.
13
+ description: Enums, properties, generics, structured objects and runtime type checking.
14
14
  email:
15
15
  - joel@drapper.me
16
16
  executables: []
@@ -22,8 +22,8 @@ files:
22
22
  - lib/literal.rb
23
23
  - lib/literal/array.rb
24
24
  - lib/literal/data.rb
25
- - lib/literal/data_property.rb
26
25
  - lib/literal/data_structure.rb
26
+ - lib/literal/deferred_type.rb
27
27
  - lib/literal/enum.rb
28
28
  - lib/literal/errors/argument_error.rb
29
29
  - lib/literal/errors/error.rb
@@ -44,34 +44,29 @@ files:
44
44
  - lib/literal/railtie.rb
45
45
  - lib/literal/set.rb
46
46
  - lib/literal/struct.rb
47
+ - lib/literal/transforms.rb
48
+ - lib/literal/tuple.rb
47
49
  - lib/literal/type.rb
48
50
  - lib/literal/types.rb
49
51
  - lib/literal/types/any_type.rb
50
52
  - lib/literal/types/array_type.rb
51
53
  - lib/literal/types/boolean_type.rb
52
- - lib/literal/types/callable_type.rb
53
54
  - lib/literal/types/class_type.rb
54
55
  - lib/literal/types/constraint_type.rb
55
56
  - lib/literal/types/descendant_type.rb
56
57
  - lib/literal/types/enumerable_type.rb
57
58
  - lib/literal/types/falsy_type.rb
58
- - lib/literal/types/float_type.rb
59
59
  - lib/literal/types/frozen_type.rb
60
60
  - lib/literal/types/hash_type.rb
61
- - lib/literal/types/integer_type.rb
62
61
  - lib/literal/types/interface_type.rb
63
62
  - lib/literal/types/intersection_type.rb
64
63
  - lib/literal/types/json_data_type.rb
65
- - lib/literal/types/lambda_type.rb
66
64
  - lib/literal/types/map_type.rb
67
65
  - lib/literal/types/never_type.rb
68
66
  - lib/literal/types/nilable_type.rb
69
67
  - lib/literal/types/not_type.rb
70
- - lib/literal/types/procable_type.rb
71
68
  - lib/literal/types/range_type.rb
72
69
  - lib/literal/types/set_type.rb
73
- - lib/literal/types/string_type.rb
74
- - lib/literal/types/symbol_type.rb
75
70
  - lib/literal/types/truthy_type.rb
76
71
  - lib/literal/types/tuple_type.rb
77
72
  - lib/literal/types/union_type.rb
@@ -83,7 +78,7 @@ licenses:
83
78
  metadata:
84
79
  homepage_uri: https://literal.fun
85
80
  source_code_uri: https://github.com/joeldrapper/literal
86
- changelog_uri: https://github.com/joeldrapper/literal/blob/main/CHANGELOG.md
81
+ changelog_uri: https://github.com/joeldrapper/literal/releases
87
82
  funding_uri: https://github.com/sponsors/joeldrapper
88
83
  rubygems_mfa_required: 'true'
89
84
  post_install_message:
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Literal::DataProperty < Literal::Property
4
- def generate_initializer_assign_value(buffer = +"")
5
- buffer <<
6
- "@" <<
7
- @name.name <<
8
- " = " <<
9
- escaped_name <<
10
- ".frozen? ? " <<
11
- escaped_name <<
12
- " : " <<
13
- escaped_name <<
14
- ".dup.freeze\n"
15
- end
16
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # @api private
4
- class Literal::Types::CallableType
5
- include Literal::Type
6
-
7
- def inspect
8
- "_Callable"
9
- end
10
-
11
- def ===(value)
12
- value.respond_to?(:call)
13
- end
14
-
15
- def >=(other)
16
- (self == other) || (Proc == other) || (Method == other) || case other
17
- when Literal::Types::IntersectionType
18
- other.types.any? { |type| Literal.subtype?(type, of: self) }
19
- when Literal::Types::ConstraintType
20
- other.object_constraints.any? { |type| Literal.subtype?(type, of: self) }
21
- when Literal::Types::InterfaceType
22
- other.methods.include?(:call)
23
- else
24
- false
25
- end
26
- end
27
-
28
- Instance = new.freeze
29
-
30
- freeze
31
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # @api private
4
- class Literal::Types::FloatType < Literal::Types::ConstraintType
5
- def inspect = "_Float(#{inspect_constraints})"
6
-
7
- def ===(value)
8
- Float === value && super
9
- end
10
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # @api private
4
- class Literal::Types::IntegerType < Literal::Types::ConstraintType
5
- def inspect = "_Integer(#{inspect_constraints})"
6
-
7
- def ===(value)
8
- Integer === value && super
9
- end
10
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # @api private
4
- module Literal::Types::LambdaType
5
- def self.inspect = "_Lambda"
6
-
7
- def self.===(value)
8
- Proc === value && value.lambda?
9
- end
10
-
11
- freeze
12
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # @api private
4
- module Literal::Types::ProcableType
5
- def self.inspect = "_Procable"
6
-
7
- def self.===(value)
8
- Proc === value || value.respond_to?(:to_proc)
9
- end
10
-
11
- freeze
12
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # @api private
4
- class Literal::Types::StringType < Literal::Types::ConstraintType
5
- def inspect = "_String(#{inspect_constraints})"
6
-
7
- def ===(value)
8
- String === value && super
9
- end
10
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # @api private
4
- class Literal::Types::SymbolType < Literal::Types::ConstraintType
5
- def inspect = "_Symbol(#{inspect_constraints})"
6
-
7
- def ===(value)
8
- Symbol === value && super
9
- end
10
- end