literal 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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