lab42_data_class 0.3.0 → 0.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.
- checksums.yaml +4 -4
- data/README.md +159 -2
- data/lib/lab42/data_class/proxy/mixin.rb +19 -0
- data/lib/lab42/data_class/proxy.rb +13 -11
- data/lib/lab42/data_class/version.rb +1 -1
- data/lib/lab42/data_class.rb +10 -0
- data/lib/lab42/eq_and_patterns.rb +19 -0
- data/lib/lab42/pair.rb +21 -0
- data/lib/lab42/triple.rb +22 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6be60bd36bb2a9a538eb968067ff72eec653a4e9bc7dc3c8904558f803668e3
|
4
|
+
data.tar.gz: 4c1e75ab8907ce56ebcb71dd56d705afd9a1127e905ffe5344d846ddd0ade90a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33d4995e0048d390486d01bc3f07ad1be0e0dd5efbcf9693f656162e8691645be2e0b11e60df41defc771f9fc7108f1b35cef58aa4e0102b4912136094e06202
|
7
|
+
data.tar.gz: 02e403af2357e1b6b06e34afe9909777ab3f64a27d51843387ed20d1d349d96f7c9383fe22da386b42e7e5510180e6bc9a6a3d17c0b86cf16d1ff3f6cd02c8c3
|
data/README.md
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
|
2
|
-
[](https://codeclimate.com/github/RobertDober/lab42_data_class)
|
3
3
|
[](https://github.com/robertdober/lab42_data_class/actions)
|
4
4
|
[](https://coveralls.io/github/RobertDober/lab42_data_class?branch=main)
|
5
|
+
[](https://rubygems.org/gems/lab42_data_class)
|
6
|
+
[](https://rubygems.org/gems/lab42_data_class)
|
5
7
|
|
6
8
|
|
7
9
|
# Lab42::DataClass
|
8
10
|
|
9
|
-
An immutable
|
11
|
+
An immutable Dataclass, Tuples and Triples
|
10
12
|
|
11
13
|
## Usage
|
12
14
|
|
@@ -31,6 +33,8 @@ require 'lab42/data_class'
|
|
31
33
|
|
32
34
|
Well let us [speculate about](https://github.com/RobertDober/speculate_about) it to find out:
|
33
35
|
|
36
|
+
## Context `DataClass`
|
37
|
+
|
34
38
|
### Context: `DataClass` function
|
35
39
|
|
36
40
|
Given
|
@@ -179,6 +183,159 @@ Then we can access the included method
|
|
179
183
|
expect(class_level.new.humanize).to eq("my value is 1")
|
180
184
|
```
|
181
185
|
|
186
|
+
### Context: Pattern Matching
|
187
|
+
|
188
|
+
A `DataClass` object behaves like the result of it's `to_h` in pattern matching
|
189
|
+
|
190
|
+
Given
|
191
|
+
```ruby
|
192
|
+
let(:numbers) { DataClass(:name, values: []) }
|
193
|
+
let(:odds) { numbers.new(name: "odds", values: (1..4).map{ _1 + _1 + 1}) }
|
194
|
+
let(:evens) { numbers.new(name: "evens", values: (1..4).map{ _1 + _1}) }
|
195
|
+
```
|
196
|
+
|
197
|
+
Then we can match accordingly
|
198
|
+
```ruby
|
199
|
+
match = case odds
|
200
|
+
in {name: "odds", values: [1, *]}
|
201
|
+
:not_really
|
202
|
+
in {name: "evens"}
|
203
|
+
:still_naaah
|
204
|
+
in {name: "odds", values: [hd, *]}
|
205
|
+
hd
|
206
|
+
else
|
207
|
+
:strange
|
208
|
+
end
|
209
|
+
expect(match).to eq(3)
|
210
|
+
```
|
211
|
+
|
212
|
+
And in `in` expressions
|
213
|
+
```ruby
|
214
|
+
evens => {values: [_, second, *]}
|
215
|
+
expect(second).to eq(4)
|
216
|
+
```
|
217
|
+
|
218
|
+
#### Context: In Case Statements
|
219
|
+
|
220
|
+
Given a nice little dataclass `Box`
|
221
|
+
```ruby
|
222
|
+
let(:box) { DataClass content: nil }
|
223
|
+
```
|
224
|
+
|
225
|
+
Then we can also use it in a case statement
|
226
|
+
```ruby
|
227
|
+
value = case box.new
|
228
|
+
when box
|
229
|
+
42
|
230
|
+
else
|
231
|
+
0
|
232
|
+
end
|
233
|
+
expect(value).to eq(42)
|
234
|
+
```
|
235
|
+
|
236
|
+
And all the associated methods
|
237
|
+
```ruby
|
238
|
+
expect(box.new).to be_a(box)
|
239
|
+
expect(box === box.new).to be_truthy
|
240
|
+
```
|
241
|
+
|
242
|
+
### Context: Behaving like a `Proc`
|
243
|
+
|
244
|
+
It is useful to be able to filter heterogeneous lists of `DataClass` instances by means of `&to_proc`, therefore
|
245
|
+
|
246
|
+
Given two different `DataClass` objects
|
247
|
+
```ruby
|
248
|
+
let(:class1) { DataClass :value }
|
249
|
+
let(:class2) { DataClass :value }
|
250
|
+
```
|
251
|
+
|
252
|
+
And a list of instances
|
253
|
+
```ruby
|
254
|
+
let(:list) {[class1.new(value: 1), class2.new(value: 2), class1.new(value: 3)]}
|
255
|
+
```
|
256
|
+
|
257
|
+
Then we can filter
|
258
|
+
```ruby
|
259
|
+
expect(list.filter(&class2)).to eq([class2.new(value: 2)])
|
260
|
+
```
|
261
|
+
|
262
|
+
### Context: Behaving like a `Hash`
|
263
|
+
|
264
|
+
We have already seen the `to_h` method, however if we want to pass an instance of `DataClass` as
|
265
|
+
keyword parameters we need an implementation of `to_hash`, which of course is just an alias
|
266
|
+
|
267
|
+
Given this keyword method
|
268
|
+
```ruby
|
269
|
+
def extract_value(value:, **others)
|
270
|
+
[value, others]
|
271
|
+
end
|
272
|
+
```
|
273
|
+
And this `DataClass`:
|
274
|
+
```ruby
|
275
|
+
let(:my_class) { DataClass(value: 1, base: 2) }
|
276
|
+
```
|
277
|
+
|
278
|
+
Then we can pass it as keyword arguments
|
279
|
+
```ruby
|
280
|
+
expect(extract_value(**my_class.new)).to eq([1, base: 2])
|
281
|
+
```
|
282
|
+
|
283
|
+
## Context: `Pair` and `Triple`
|
284
|
+
|
285
|
+
Two special cases of a `DataClass` which behave like `Tuple` of size 2 and 3 in _Elixir_
|
286
|
+
|
287
|
+
|
288
|
+
They distinguish themselves from `DataClass` classes by accepting only positional arguments, and
|
289
|
+
cannot be converted to hashes.
|
290
|
+
|
291
|
+
These are actually two classes and not class factories as they have a fixed interface , but let us speculate about them to learn what they can do for us.
|
292
|
+
|
293
|
+
### Context: Constructor functions
|
294
|
+
|
295
|
+
Given a pair
|
296
|
+
```ruby
|
297
|
+
let(:token) { Pair("12", 12) }
|
298
|
+
let(:node) { Triple("42", 4, 2) }
|
299
|
+
```
|
300
|
+
|
301
|
+
Then we can access their elements
|
302
|
+
```ruby
|
303
|
+
expect(token.first).to eq("12")
|
304
|
+
expect(token.second).to eq(12)
|
305
|
+
expect(node.first).to eq("42")
|
306
|
+
expect(node.second).to eq(4)
|
307
|
+
expect(node.third).to eq(2)
|
308
|
+
```
|
309
|
+
|
310
|
+
And we can treat them like _Indexable_
|
311
|
+
```ruby
|
312
|
+
expect(token[1]).to eq(12)
|
313
|
+
expect(token[-2]).to eq("12")
|
314
|
+
expect(node[2]).to eq(2)
|
315
|
+
```
|
316
|
+
|
317
|
+
And convert them to arrays of course
|
318
|
+
```ruby
|
319
|
+
expect(token.to_a).to eq(["12", 12])
|
320
|
+
expect(node.to_a).to eq(["42", 4, 2])
|
321
|
+
```
|
322
|
+
|
323
|
+
And they behave like arrays in pattern matching too
|
324
|
+
```ruby
|
325
|
+
token => [str, int]
|
326
|
+
node => [root, lft, rgt]
|
327
|
+
expect(str).to eq("12")
|
328
|
+
expect(int).to eq(12)
|
329
|
+
expect(root).to eq("42")
|
330
|
+
expect(lft).to eq(4)
|
331
|
+
expect(rgt).to eq(2)
|
332
|
+
```
|
333
|
+
|
334
|
+
And of course the factory functions are equivalent to the constructors
|
335
|
+
```ruby
|
336
|
+
expect(token).to eq(Lab42::Pair.new("12", 12))
|
337
|
+
expect(node).to eq(Lab42::Triple.new("42", 4, 2))
|
338
|
+
```
|
182
339
|
|
183
340
|
# LICENSE
|
184
341
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lab42
|
4
|
+
module DataClass
|
5
|
+
class Proxy
|
6
|
+
module Mixin
|
7
|
+
def ==(other)
|
8
|
+
other.is_a?(self.class) &&
|
9
|
+
to_h == other.to_h
|
10
|
+
end
|
11
|
+
|
12
|
+
def deconstruct_keys(*)
|
13
|
+
to_h
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'set'
|
4
|
+
require_relative 'proxy/mixin'
|
4
5
|
module Lab42
|
5
6
|
module DataClass
|
6
7
|
class Proxy
|
@@ -16,6 +17,8 @@ module Lab42
|
|
16
17
|
klass.module_eval(&_define_attr_reader)
|
17
18
|
klass.module_eval(&_define_initializer)
|
18
19
|
_define_methods
|
20
|
+
klass.include(Mixin)
|
21
|
+
klass.module_eval(&block) if block
|
19
22
|
klass
|
20
23
|
end
|
21
24
|
|
@@ -47,15 +50,6 @@ module Lab42
|
|
47
50
|
end
|
48
51
|
end
|
49
52
|
|
50
|
-
def _define_eql?
|
51
|
-
->(*) do
|
52
|
-
define_method :== do |other|
|
53
|
-
other.is_a?(self.class) &&
|
54
|
-
to_h == other.to_h
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
53
|
def _define_freezing_constructor
|
60
54
|
->(*) do
|
61
55
|
define_method :new do |*a, **p, &b|
|
@@ -87,10 +81,9 @@ module Lab42
|
|
87
81
|
|
88
82
|
def _define_methods
|
89
83
|
(class << klass; self end).module_eval(&_define_freezing_constructor)
|
84
|
+
(class << klass; self end).module_eval(&_define_to_proc)
|
90
85
|
klass.module_eval(&_define_to_h)
|
91
86
|
klass.module_eval(&_define_merge)
|
92
|
-
klass.module_eval(&_define_eql?)
|
93
|
-
klass.module_eval(&block) if block
|
94
87
|
end
|
95
88
|
|
96
89
|
def _define_to_h
|
@@ -99,6 +92,15 @@ module Lab42
|
|
99
92
|
define_method :to_h do
|
100
93
|
proxy.to_hash(self)
|
101
94
|
end
|
95
|
+
alias_method :to_hash, :to_h
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def _define_to_proc
|
100
|
+
->(*) do
|
101
|
+
define_method :to_proc do
|
102
|
+
->(other) { self === other }
|
103
|
+
end
|
102
104
|
end
|
103
105
|
end
|
104
106
|
|
data/lib/lab42/data_class.rb
CHANGED
@@ -1,12 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative './data_class/proxy'
|
4
|
+
require_relative './pair'
|
5
|
+
require_relative './triple'
|
4
6
|
|
5
7
|
module Kernel
|
6
8
|
def DataClass(*args, **kwds, &blk)
|
7
9
|
proxy = Lab42::DataClass::Proxy.new(*args, **kwds, &blk)
|
8
10
|
proxy.define_class!
|
9
11
|
end
|
12
|
+
|
13
|
+
def Pair(first, second)
|
14
|
+
Lab42::Pair.new(first, second)
|
15
|
+
end
|
16
|
+
|
17
|
+
def Triple(first, second, third)
|
18
|
+
Lab42::Triple.new(first, second, third)
|
19
|
+
end
|
10
20
|
end
|
11
21
|
|
12
22
|
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lab42
|
4
|
+
module EqAndPatterns
|
5
|
+
def [](idx)
|
6
|
+
to_a[idx]
|
7
|
+
end
|
8
|
+
|
9
|
+
def ==(other)
|
10
|
+
other.is_a?(self.class) &&
|
11
|
+
to_a == other.to_a
|
12
|
+
end
|
13
|
+
|
14
|
+
def deconstruct(*)
|
15
|
+
to_a
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
# SPDX-License-Identifier: Apache-2.0
|
data/lib/lab42/pair.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'eq_and_patterns'
|
4
|
+
module Lab42
|
5
|
+
class Pair
|
6
|
+
attr_reader :first, :second
|
7
|
+
include EqAndPatterns
|
8
|
+
|
9
|
+
def to_a
|
10
|
+
[first, second]
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def initialize(first, second)
|
16
|
+
@first = first
|
17
|
+
@second = second
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
# SPDX-License-Identifier: Apache-2.0
|
data/lib/lab42/triple.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'eq_and_patterns'
|
4
|
+
module Lab42
|
5
|
+
class Triple
|
6
|
+
attr_reader :first, :second, :third
|
7
|
+
include EqAndPatterns
|
8
|
+
|
9
|
+
def to_a
|
10
|
+
[first, second, third]
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def initialize(first, second, third)
|
16
|
+
@first = first
|
17
|
+
@second = second
|
18
|
+
@third = third
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
# SPDX-License-Identifier: Apache-2.0
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lab42_data_class
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Dober
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-02-19 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |
|
14
14
|
An Immutable DataClass for Ruby
|
@@ -24,7 +24,11 @@ files:
|
|
24
24
|
- README.md
|
25
25
|
- lib/lab42/data_class.rb
|
26
26
|
- lib/lab42/data_class/proxy.rb
|
27
|
+
- lib/lab42/data_class/proxy/mixin.rb
|
27
28
|
- lib/lab42/data_class/version.rb
|
29
|
+
- lib/lab42/eq_and_patterns.rb
|
30
|
+
- lib/lab42/pair.rb
|
31
|
+
- lib/lab42/triple.rb
|
28
32
|
homepage: https://github.com/robertdober/lab42_data_class
|
29
33
|
licenses:
|
30
34
|
- Apache-2.0
|