literal 1.1.0 → 1.2.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: b459adebd387b12382d4e049a1f9211f6330bc6f77d66a8380bfbe758a4fb224
4
- data.tar.gz: d771162ac9e9939e20b9478225baa23dc5d3b4d2fd0f0daa3c821277053ea88f
3
+ metadata.gz: d40ba211b6f7b2ad3dad97feb118d3742b43dbd2f1773daa8d6979f597cbf428
4
+ data.tar.gz: 1fb316fac766b9b25041c2bc201c8ddaed8d3e59ed5f5aec9c7f93f0306df921
5
5
  SHA512:
6
- metadata.gz: 68c949539f7002388ccff9c0547c48988a3ccb9ae2cb3262f8ab9119f0187f8ff775c46d3b2aa8abbcc1bbb68a767427c99b48af898744c58bfa004fec3dd491
7
- data.tar.gz: 872aeda1f2efb4023e9c34c324e8fba149737d5744bc735e57b0ffdbb52be6001f39360fa86c5f981d70e70b5698394eef228eef9dde2d5ca387180414aac555
6
+ metadata.gz: 37fee8ee4d14f41573f5a4ce2d27d3d98bc1b78a54a35d850a41fbeedfc7e1b1f6f8a3533cbb82c35b81212079d4f495ad17cde8651a7b56fff3e6ecb99b5078
7
+ data.tar.gz: ca6fa6e45d9a344880ebbbfba69c82f9444809ad4b6645e89c4cc0c89c58e78ac66d2edf80612cb2084bac0b46b9c0a686458983f47ff5716f4016b9545f723c
@@ -0,0 +1,428 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Literal::Array
4
+ class Generic
5
+ include Literal::Type
6
+
7
+ def initialize(type)
8
+ @type = type
9
+ end
10
+
11
+ attr_reader :type
12
+
13
+ def new(*value)
14
+ Literal::Array.new(value, type: @type)
15
+ end
16
+
17
+ alias_method :[], :new
18
+
19
+ def ===(value)
20
+ Literal::Array === value && Literal.subtype?(value.__type__, of: @type)
21
+ end
22
+
23
+ def >=(other)
24
+ case other
25
+ when Literal::Array::Generic
26
+ @type >= other.type
27
+ else
28
+ false
29
+ end
30
+ end
31
+
32
+ def inspect
33
+ "Literal::Array(#{@type.inspect})"
34
+ end
35
+ end
36
+
37
+ include Enumerable
38
+ include Literal::Types
39
+
40
+ def initialize(value, type:)
41
+ Literal.check(actual: value, expected: _Array(type)) do |c|
42
+ c.fill_receiver(receiver: self, method: "#initialize")
43
+ end
44
+
45
+ @__type__ = type
46
+ @__value__ = value
47
+ end
48
+
49
+ def __initialize_without_check__(value, type:)
50
+ @__type__ = type
51
+ @__value__ = value
52
+ self
53
+ end
54
+
55
+ # Used to create a new Literal::Array with the same type and collection type but a new value. The value is not checked.
56
+ def __with__(value)
57
+ Literal::Array.allocate.__initialize_without_check__(
58
+ value,
59
+ type: @__type__,
60
+ )
61
+ end
62
+
63
+ attr_reader :__type__, :__value__
64
+
65
+ def &(other)
66
+ case other
67
+ when ::Array
68
+ __with__(@__value__ & other)
69
+ when Literal::Array
70
+ __with__(@__value__ & other.__value__)
71
+ else
72
+ raise ArgumentError.new("Cannot perform bitwise AND with #{other.class.name}.")
73
+ end
74
+ end
75
+
76
+ def *(times)
77
+ case times
78
+ when String
79
+ @__value__ * times
80
+ else
81
+ __with__(@__value__ * times)
82
+ end
83
+ end
84
+
85
+ def +(other)
86
+ case other
87
+ when ::Array
88
+ Literal.check(actual: other, expected: _Array(@__type__)) do |c|
89
+ c.fill_receiver(receiver: self, method: "#+")
90
+ end
91
+
92
+ __with__(@__value__ + other)
93
+ when Literal::Array(@__type__)
94
+ __with__(@__value__ + other.__value__)
95
+ when Literal::Array
96
+ raise Literal::TypeError.new(
97
+ context: Literal::TypeError::Context.new(
98
+ expected: Literal::Array(@__type__),
99
+ actual: other
100
+ )
101
+ )
102
+ else
103
+ raise ArgumentError.new("Cannot perform `+` with #{other.class.name}.")
104
+ end
105
+ end
106
+
107
+ def -(other)
108
+ case other
109
+ when ::Array
110
+ __with__(@__value__ - other)
111
+ when Literal::Array
112
+ __with__(@__value__ - other.__value__)
113
+ else
114
+ raise ArgumentError.new("Cannot perform `-` with #{other.class.name}.")
115
+ end
116
+ end
117
+
118
+ def <<(value)
119
+ Literal.check(actual: value, expected: @__type__) do |c|
120
+ c.fill_receiver(receiver: self, method: "#<<")
121
+ end
122
+
123
+ @__value__ << value
124
+ self
125
+ end
126
+
127
+ def <=>(other)
128
+ case other
129
+ when ::Array
130
+ @__value__ <=> other
131
+ when Literal::Array
132
+ @__value__ <=> other.__value__
133
+ else
134
+ raise ArgumentError.new("Cannot perform `<=>` with #{other.class.name}.")
135
+ end
136
+ end
137
+
138
+ def ==(other)
139
+ Literal::Array === other && @__value__ == other.__value__
140
+ end
141
+
142
+ def [](index)
143
+ @__value__[index]
144
+ end
145
+
146
+ def []=(index, value)
147
+ Literal.check(actual: value, expected: @__type__) do |c|
148
+ c.fill_receiver(receiver: self, method: "#[]=")
149
+ end
150
+
151
+ @__value__[index] = value
152
+ end
153
+
154
+ def all?(...)
155
+ @__value__.all?(...)
156
+ end
157
+
158
+ def any?(...)
159
+ @__value__.any?(...)
160
+ end
161
+
162
+ def assoc(...)
163
+ @__value__.assoc(...)
164
+ end
165
+
166
+ def at(...)
167
+ @__value__.at(...)
168
+ end
169
+
170
+ def bsearch(...)
171
+ @__value__.bsearch(...)
172
+ end
173
+
174
+ def clear(...)
175
+ @__value__.clear(...)
176
+ self
177
+ end
178
+
179
+ def compact
180
+ # @TODO if this is an array of nils, we should return an emtpy array
181
+ __with__(@__value__)
182
+ end
183
+
184
+ def compact!
185
+ # @TODO if this is an array of nils, we should set @__value__ = [] and return self
186
+ nil
187
+ end
188
+
189
+ def count(...)
190
+ @__value__.count(...)
191
+ end
192
+
193
+ def dig(...)
194
+ @__value__.dig(...)
195
+ end
196
+
197
+ def drop(...)
198
+ __with__(@__value__.drop(...))
199
+ end
200
+
201
+ def drop_while(...)
202
+ __with__(@__value__.drop_while(...))
203
+ end
204
+
205
+ def each(...)
206
+ @__value__.each(...)
207
+ end
208
+
209
+ def empty?
210
+ @__value__.empty?
211
+ end
212
+
213
+ alias_method :eql?, :==
214
+
215
+ def filter(...)
216
+ __with__(@__value__.filter(...))
217
+ end
218
+
219
+ def filter!(...)
220
+ @__value__.filter!(...)
221
+ self
222
+ end
223
+
224
+ def first(...)
225
+ @__value__.first(...)
226
+ end
227
+
228
+ def freeze
229
+ @__value__.freeze
230
+ super
231
+ end
232
+
233
+ def insert(index, *value)
234
+ Literal.check(actual: value, expected: _Array(@__type__)) do |c|
235
+ c.fill_receiver(receiver: self, method: "#insert")
236
+ end
237
+
238
+ @__value__.insert(index, *value)
239
+ self
240
+ end
241
+
242
+ def intersection(*values)
243
+ values.map! do |value|
244
+ case value
245
+ when Literal::Array
246
+ value.__value__
247
+ else
248
+ value
249
+ end
250
+ end
251
+
252
+ __with__(@__value__.intersection(*values))
253
+ end
254
+
255
+ def last(...)
256
+ @__value__.last(...)
257
+ end
258
+
259
+ def length(...)
260
+ @__value__.length(...)
261
+ end
262
+
263
+ def map(type, &block)
264
+ my_type = @__type__
265
+ transform_type = Literal::TRANSFORMS.dig(my_type, block)
266
+
267
+ if transform_type && Literal.subtype?(transform_type, of: my_type)
268
+ Literal::Array.allocate.__initialize_without_check__(
269
+ @__value__.map(&block),
270
+ type:,
271
+ )
272
+ else
273
+ Literal::Array.new(@__value__.map(&block), type:)
274
+ end
275
+ end
276
+
277
+ alias_method :collect, :map
278
+
279
+ # TODO: we can make this faster
280
+ def map!(&)
281
+ new_array = map(@__type__, &)
282
+ @__value__ = new_array.__value__
283
+ self
284
+ end
285
+
286
+ alias_method :collect!, :map!
287
+
288
+ def max(n = nil, &)
289
+ if n
290
+ __with__(@__value__.max(n, &))
291
+ else
292
+ @__value__.max(&)
293
+ end
294
+ end
295
+
296
+ def min(n = nil, &)
297
+ if n
298
+ __with__(@__value__.min(n, &))
299
+ else
300
+ @__value__.min(&)
301
+ end
302
+ end
303
+
304
+ def minmax(...)
305
+ __with__(@__value__.minmax(...))
306
+ end
307
+
308
+ def one?(...)
309
+ @__value__.one?(...)
310
+ end
311
+
312
+ def pop(...)
313
+ @__value__.pop(...)
314
+ end
315
+
316
+ def push(*value)
317
+ Literal.check(actual: value, expected: _Array(@__type__)) do |c|
318
+ c.fill_receiver(receiver: self, method: "#push")
319
+ end
320
+
321
+ @__value__.push(*value)
322
+ self
323
+ end
324
+
325
+ alias_method :append, :push
326
+
327
+ def reject(...)
328
+ __with__(@__value__.reject(...))
329
+ end
330
+
331
+ def reject!(...)
332
+ @__value__.reject!(...)
333
+ self
334
+ end
335
+
336
+ def replace(value)
337
+ case value
338
+ when Array
339
+ Literal.check(actual: value, expected: _Array(@__type__)) do |c|
340
+ c.fill_receiver(receiver: self, method: "#replace")
341
+ end
342
+
343
+ @__value__.replace(value)
344
+ when Literal::Array(@__type__)
345
+ @__value__.replace(value.__value__)
346
+ when Literal::Array
347
+ raise Literal::TypeError.new(
348
+ context: Literal::TypeError::Context.new(expected: @__type__, actual: value.__type__)
349
+ )
350
+ else
351
+ raise ArgumentError.new("#replace expects Array argument")
352
+ end
353
+
354
+ self
355
+ end
356
+
357
+ def sample(...)
358
+ @__value__.sample(...)
359
+ end
360
+
361
+ def shift(...)
362
+ @__value__.shift(...)
363
+ end
364
+
365
+ def size(...)
366
+ @__value__.size(...)
367
+ end
368
+
369
+ def sort(...)
370
+ __with__(@__value__.sort(...))
371
+ end
372
+
373
+ def sort!(...)
374
+ @__value__.sort!(...)
375
+ self
376
+ end
377
+
378
+ def to_a
379
+ @__value__.dup
380
+ end
381
+
382
+ alias_method :to_ary, :to_a
383
+
384
+ def uniq
385
+ __with__(@__value__.uniq)
386
+ end
387
+
388
+ def uniq!(...)
389
+ @__value__.uniq!(...) ? self : nil
390
+ end
391
+
392
+ def unshift(value)
393
+ Literal.check(actual: value, expected: @__type__) do |c|
394
+ c.fill_receiver(receiver: self, method: "#unshift")
395
+ end
396
+
397
+ @__value__.unshift(value)
398
+ self
399
+ end
400
+
401
+ alias_method :prepend, :unshift
402
+
403
+ def values_at(*indexes)
404
+ unless @__type__ === nil
405
+ max_value = length - 1
406
+ min_value = -length
407
+
408
+ indexes.each do |index|
409
+ case index
410
+ when Integer
411
+ if index < min_value || index > max_value
412
+ raise IndexError.new("index #{index} out of range")
413
+ end
414
+ when Range
415
+ if index.begin < min_value || index.end > max_value
416
+ raise IndexError.new("index #{index} out of range")
417
+ end
418
+ else
419
+ raise ArgumentError.new("Invalid index: #{index.inspect}")
420
+ end
421
+ end
422
+ end
423
+
424
+ __with__(
425
+ @__value__.values_at(*indexes)
426
+ )
427
+ end
428
+ end
@@ -10,16 +10,6 @@ class Literal::DataStructure
10
10
  object
11
11
  end
12
12
 
13
- def [](key)
14
- instance_variable_get(:"@#{key}")
15
- end
16
-
17
- def []=(key, value)
18
- # TODO: Sync error array w/ generated setter
19
- @literal_properties[key].check(value) { |c| raise NotImplementedError }
20
- instance_variable_set(:"@#{key}", value)
21
- end
22
-
23
13
  def to_h
24
14
  {}
25
15
  end
data/lib/literal/flags.rb CHANGED
@@ -9,7 +9,6 @@ class Literal::Flags
9
9
  end
10
10
 
11
11
  @value = value
12
-
13
12
  freeze
14
13
  end
15
14
 
@@ -138,6 +137,15 @@ class Literal::Flags
138
137
  [@value].pack(self.class::PACKER)
139
138
  end
140
139
 
140
+ def with(**attributes)
141
+ new_val = attributes.reduce(value) do |value, (k, v)|
142
+ v ? value |= bitmap(k) : value &= ~(bitmap(k))
143
+ value
144
+ end
145
+
146
+ self.class.allocate.__initialize_from_value__(new_val)
147
+ end
148
+
141
149
  # () -> String
142
150
  def inspect
143
151
  to_h.inspect
@@ -156,7 +164,7 @@ class Literal::Flags
156
164
 
157
165
  # (Symbol) -> Boolean
158
166
  def [](key)
159
- @value & (2 ** self.class::FLAGS.fetch(key)) > 0
167
+ @value & (bitmap(key)) > 0
160
168
  end
161
169
 
162
170
  def |(other)
@@ -197,14 +205,19 @@ class Literal::Flags
197
205
 
198
206
  def deconstruct_keys(keys = nil)
199
207
  if keys
200
- flags = self.class::FLAGS
201
208
  keys.to_h do |key|
202
- [key, @value & (2 ** flags.fetch(key)) > 0]
209
+ [key, @value & (bitmap(key)) > 0]
203
210
  end
204
211
  else
205
212
  to_h
206
213
  end
207
214
  end
215
+
216
+ private
217
+
218
+ def bitmap(key)
219
+ 2 ** self.class::FLAGS.fetch(key)
220
+ end
208
221
  end
209
222
 
210
223
  class Literal::Flags8 < Literal::Flags
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Literal::Hash
4
+ class Generic
5
+ def initialize(key_type, value_type)
6
+ @key_type = key_type
7
+ @value_type = value_type
8
+ end
9
+
10
+ def new(**value)
11
+ Literal::Hash.new(value, key_type: @key_type, value_type: @value_type)
12
+ end
13
+
14
+ def ===(value)
15
+ Literal::Hash === value && @type == value.__type__
16
+ end
17
+
18
+ def inspect
19
+ "Literal::Hash(#{@type.inspect})"
20
+ end
21
+ end
22
+
23
+ include Enumerable
24
+
25
+ def initialize(value, key_type:, value_type:)
26
+ collection_type = Literal::Types::HashType.new(key_type, value_type)
27
+
28
+ Literal.check(actual: value, expected: collection_type) do |c|
29
+ c.fill_receiver(receiver: self, method: "#initialize")
30
+ end
31
+
32
+ @__key_type__ = key_type
33
+ @__value_type__ = value_type
34
+ @__value__ = value
35
+ @__collection_type__ = collection_type
36
+ end
37
+
38
+ attr_reader :__key_type__, :__value_type__, :__value__
39
+
40
+ def freeze
41
+ @__value__.freeze
42
+ super
43
+ end
44
+
45
+ def each(...)
46
+ @__value__.each(...)
47
+ end
48
+ 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.escaped_name
105
+ buffer << " @" << property.name.name << " == other." << property.name.name
106
106
  buffer << " &&\n " if i < n - 1
107
107
  i += 1
108
108
  end
@@ -6,8 +6,13 @@ module Literal::Properties
6
6
 
7
7
  include Literal::Types
8
8
 
9
+ module DocString
10
+ # @!method initialize(...)
11
+ end
12
+
9
13
  def self.extended(base)
10
14
  super
15
+ base.include(DocString)
11
16
  base.include(base.__send__(:__literal_extension__))
12
17
  end
13
18
 
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Literal::Rails::FlagsType < ActiveModel::Type::Value
4
+ def initialize(flags_class)
5
+ @flags_class = flags_class
6
+ super()
7
+ end
8
+
9
+ def cast(value)
10
+ case value
11
+ when @flags_class
12
+ value
13
+ else
14
+ deserialize(value)
15
+ end
16
+ end
17
+
18
+ def serialize(value)
19
+ case value
20
+ when nil
21
+ nil
22
+ when @flags_class
23
+ value.to_bit_string
24
+ else
25
+ raise Literal::ArgumentError.new(
26
+ "Invalid value: #{value.inspect}. Expected an #{@flags_class.inspect}.",
27
+ )
28
+ end
29
+ end
30
+
31
+ def deserialize(value)
32
+ case value
33
+ when nil
34
+ nil
35
+ else
36
+ @flags_class.from_bit_string(value) || raise(
37
+ ArgumentError.new("Invalid value: #{value.inspect} for #{@flags_class}"),
38
+ )
39
+ end
40
+ end
41
+ end
data/lib/literal/rails.rb CHANGED
@@ -5,5 +5,6 @@ require_relative "rails/patches/active_record"
5
5
 
6
6
  module Literal::Rails
7
7
  autoload :EnumType, "literal/rails/enum_type"
8
+ autoload :FlagsType, "literal/rails/flags_type"
8
9
  autoload :EnumSerializer, "literal/rails/enum_serializer"
9
10
  end
@@ -6,8 +6,16 @@ class Literal::Railtie < Rails::Railtie
6
6
  Literal::Rails::EnumType.new(type)
7
7
  end
8
8
 
9
+ ActiveRecord::Type.register(:literal_flags) do |name, type:|
10
+ Literal::Rails::FlagsType.new(type)
11
+ end
12
+
9
13
  ActiveModel::Type.register(:literal_enum) do |name, type:|
10
14
  Literal::Rails::EnumType.new(type)
11
15
  end
16
+
17
+ ActiveModel::Type.register(:literal_flags) do |name, type:|
18
+ Literal::Rails::FlagsType.new(type)
19
+ end
12
20
  end
13
21
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Literal::Set
4
+ class Generic
5
+ def initialize(type)
6
+ @type = type
7
+ end
8
+
9
+ def new(*value)
10
+ Literal::Set.new(value.to_set, type: @type)
11
+ end
12
+
13
+ alias_method :[], :new
14
+
15
+ def ===(value)
16
+ Literal::Set === value && @type == value.__type__
17
+ end
18
+
19
+ def inspect
20
+ "Literal::Set(#{@type.inspect})"
21
+ end
22
+ end
23
+
24
+ include Enumerable
25
+
26
+ def initialize(value, type:)
27
+ collection_type = Literal::Types::SetType.new(type)
28
+
29
+ Literal.check(actual: value, expected: collection_type) do |c|
30
+ c.fill_receiver(receiver: self, method: "#initialize")
31
+ end
32
+
33
+ @__type__ = type
34
+ @__value__ = value
35
+ @__collection_type__ = collection_type
36
+ end
37
+
38
+ attr_reader :__type__, :__value__
39
+
40
+ def freeze
41
+ @__value__.freeze
42
+ super
43
+ end
44
+
45
+ def each(...)
46
+ @__value__.each(...)
47
+ end
48
+ end