lab42_data_class 0.7.1 → 0.8.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.
- checksums.yaml +4 -4
- data/README.md +51 -0
- data/lib/lab42/data_class/builtin_constraints.rb +17 -0
- data/lib/lab42/data_class/constraints/attribute_setters/attribute_setter.rb +29 -0
- data/lib/lab42/data_class/constraints/attribute_setters/list_of_attribute_setter.rb +26 -0
- data/lib/lab42/data_class/constraints/attribute_setters/pair_of_attribute_setter.rb +28 -0
- data/lib/lab42/data_class/constraints/attribute_setters/triple_of_attribute_setter.rb +34 -0
- data/lib/lab42/data_class/constraints/constraint.rb +28 -0
- data/lib/lab42/data_class/constraints/kernel.rb +106 -0
- data/lib/lab42/data_class/constraints/list_of_constraint.rb +17 -0
- data/lib/lab42/data_class/constraints/pair_of_constraint.rb +17 -0
- data/lib/lab42/data_class/constraints/setter_constraint.rb +28 -0
- data/lib/lab42/data_class/constraints/triple_of_constraint.rb +17 -0
- data/lib/lab42/data_class/kernel.rb +7 -0
- data/lib/lab42/data_class/proxy/constraints/maker.rb +11 -4
- data/lib/lab42/data_class/proxy/constraints.rb +20 -12
- data/lib/lab42/data_class/proxy/memos.rb +2 -0
- data/lib/lab42/data_class/proxy.rb +49 -8
- data/lib/lab42/data_class/undefined_setter_error.rb +9 -0
- data/lib/lab42/data_class/version.rb +1 -1
- data/lib/lab42/data_class.rb +3 -0
- data/lib/lab42/list/class_methods.rb +37 -0
- data/lib/lab42/list.rb +33 -0
- data/lib/lab42/nil.rb +13 -0
- data/lib/lab42/pair.rb +3 -0
- data/lib/lab42/triple.rb +4 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6534869faa807d9d3f774d0fd1deef2d12d23475f06ab07208a46f7ef998056b
|
4
|
+
data.tar.gz: f099a45edac8b2daf5d0637e7345ba3301c1ac379f2d0f4319b34172f847e431
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 005cd3a1d4a52bfabaa371fb36769bce9530ed061d331f0009d39342a5af70c9126373eda6164471c0a58a14e27039261568439d40742b35b18e426b1fa61733
|
7
|
+
data.tar.gz: ed3c5a31cc163448ba912e76284946c0f4ed47bd7b2ee3841e7e5918ad350f5763656da819bea96826da614285de7b981092ae4ee924dce574e638dd684919af
|
data/README.md
CHANGED
@@ -235,6 +235,57 @@ And of course the factory functions are equivalent to the constructors
|
|
235
235
|
expect(node).to eq(Lab42::Triple.new("42", 4, 2))
|
236
236
|
```
|
237
237
|
|
238
|
+
#### Context: Pseudo Assignments
|
239
|
+
|
240
|
+
... in reality return a new object
|
241
|
+
|
242
|
+
Given an instance of `Pair`
|
243
|
+
```ruby
|
244
|
+
let(:original) { Pair(1, 1) }
|
245
|
+
```
|
246
|
+
|
247
|
+
And one of `Triple`
|
248
|
+
```ruby
|
249
|
+
let(:xyz) { Triple(1, 1, 1) }
|
250
|
+
```
|
251
|
+
|
252
|
+
Then
|
253
|
+
```ruby
|
254
|
+
second = original.set_first(2)
|
255
|
+
third = second.set_second(2)
|
256
|
+
expect(original).to eq( Pair(1, 1) )
|
257
|
+
expect(second).to eq(Pair(2, 1))
|
258
|
+
expect(third).to eq(Pair(2, 2))
|
259
|
+
```
|
260
|
+
|
261
|
+
And also
|
262
|
+
```ruby
|
263
|
+
second = xyz.set_first(2)
|
264
|
+
third = second.set_second(2)
|
265
|
+
fourth = third.set_third(2)
|
266
|
+
expect(xyz).to eq(Triple(1, 1, 1))
|
267
|
+
expect(second).to eq(Triple(2, 1, 1))
|
268
|
+
expect(third).to eq(Triple(2, 2, 1))
|
269
|
+
expect(fourth).to eq(Triple(2, 2, 2))
|
270
|
+
```
|
271
|
+
|
272
|
+
## Context: `List`
|
273
|
+
|
274
|
+
A `List` is what a _list_ is in Lisp or Elixir it exposes the following API
|
275
|
+
|
276
|
+
Given such a _list_
|
277
|
+
```ruby
|
278
|
+
let(:three) { List(*%w[a b c]) }
|
279
|
+
```
|
280
|
+
|
281
|
+
Then this becomes really a _linked_list_
|
282
|
+
```ruby
|
283
|
+
expect(three.car).to eq("a")
|
284
|
+
expect(three.cdr).to eq(List(*%w[b c]))
|
285
|
+
```
|
286
|
+
|
287
|
+
For all details please consult the [List speculations](speculations/LIST.md)
|
288
|
+
|
238
289
|
# LICENSE
|
239
290
|
|
240
291
|
Copyright 2022 Robert Dober robert.dober@gmail.com
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "constraints/constraint"
|
4
|
+
require_relative "constraints/list_of_constraint"
|
5
|
+
require_relative "constraints/pair_of_constraint"
|
6
|
+
require_relative "constraints/triple_of_constraint"
|
7
|
+
require_relative "constraints/kernel"
|
8
|
+
|
9
|
+
module Lab42
|
10
|
+
module DataClass
|
11
|
+
module BuiltinConstraints
|
12
|
+
extend self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lab42
|
4
|
+
module DataClass
|
5
|
+
module Constraints
|
6
|
+
module AttributeSetters
|
7
|
+
module AttributeSetter
|
8
|
+
attr_reader :attribute, :constraint, :instance
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def initialize(attribute:, constraint:, instance:)
|
13
|
+
@attribute = attribute
|
14
|
+
@constraint = constraint
|
15
|
+
@instance = instance
|
16
|
+
end
|
17
|
+
|
18
|
+
def _set_attr!(value)
|
19
|
+
new_values = instance.to_h.merge(attribute => value)
|
20
|
+
instance.class.send(:_new_from_merge, {}, new_values)
|
21
|
+
end
|
22
|
+
|
23
|
+
def _value = @___value__ ||= instance[attribute]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "attribute_setter"
|
4
|
+
module Lab42
|
5
|
+
module DataClass
|
6
|
+
module Constraints
|
7
|
+
module AttributeSetters
|
8
|
+
class ListOfAttributeSetter
|
9
|
+
include AttributeSetter
|
10
|
+
|
11
|
+
def cons(value)
|
12
|
+
constraint.constraint.(value) or raise ConstraintError,
|
13
|
+
"cannot set value #{value} in set(#{attribute}).cons"
|
14
|
+
|
15
|
+
_set_attr!(_value.cons(value))
|
16
|
+
end
|
17
|
+
|
18
|
+
def cdr
|
19
|
+
_set_attr!(_value.cdr)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "attribute_setter"
|
4
|
+
module Lab42
|
5
|
+
module DataClass
|
6
|
+
module Constraints
|
7
|
+
module AttributeSetters
|
8
|
+
class PairOfAttributeSetter
|
9
|
+
include AttributeSetter
|
10
|
+
|
11
|
+
def first_element(value)
|
12
|
+
constraint.constraint.first.(value) or raise ConstraintError,
|
13
|
+
"cannot set value #{value} in set(#{attribute}).first_element"
|
14
|
+
|
15
|
+
_set_attr!(_value.set_first(value))
|
16
|
+
end
|
17
|
+
|
18
|
+
def second_element(value)
|
19
|
+
constraint.constraint.last.(value) or raise ConstraintError,
|
20
|
+
"cannot set value #{value} in set(#{attribute}).second_element"
|
21
|
+
_set_attr!(_value.set_second(value))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "attribute_setter"
|
4
|
+
module Lab42
|
5
|
+
module DataClass
|
6
|
+
module Constraints
|
7
|
+
module AttributeSetters
|
8
|
+
class TripleOfAttributeSetter
|
9
|
+
include AttributeSetter
|
10
|
+
|
11
|
+
def first_element(value)
|
12
|
+
constraint.constraint.first.(value) or raise ConstraintError,
|
13
|
+
"cannot set value #{value} in set(#{attribute}).first_element"
|
14
|
+
|
15
|
+
_set_attr!(_value.set_first(value))
|
16
|
+
end
|
17
|
+
|
18
|
+
def second_element(value)
|
19
|
+
constraint.constraint[1].(value) or raise ConstraintError,
|
20
|
+
"cannot set value #{value} in set(#{attribute}).second_element"
|
21
|
+
_set_attr!(_value.set_second(value))
|
22
|
+
end
|
23
|
+
|
24
|
+
def third_element(value)
|
25
|
+
constraint.constraint.last.(value) or raise ConstraintError,
|
26
|
+
"cannot set value #{value} in set(#{attribute}).third_element"
|
27
|
+
_set_attr!(_value.set_third(value))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "lab42/data_class"
|
4
|
+
module Lab42
|
5
|
+
module DataClass
|
6
|
+
module Constraints
|
7
|
+
class Constraint
|
8
|
+
attr_reader :name, :function
|
9
|
+
|
10
|
+
def call(value) = function.(value)
|
11
|
+
def setter_constraint? = false
|
12
|
+
def to_s = "Constraint<#{name}>"
|
13
|
+
|
14
|
+
private
|
15
|
+
def initialize(name:, function:)
|
16
|
+
raise ArgumentError, "name not a String, but #{name}" unless String === name
|
17
|
+
unless function.respond_to?(:arity) && function.arity == 1
|
18
|
+
raise ArgumentError, "function not a callable with arity 1 #{function}"
|
19
|
+
end
|
20
|
+
|
21
|
+
@name = name
|
22
|
+
@function = function
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kernel
|
4
|
+
Constraints = Lab42::DataClass::Constraints
|
5
|
+
Constraint = Constraints::Constraint
|
6
|
+
ListOfConstraint = Constraints::ListOfConstraint
|
7
|
+
PairOfConstraint = Constraints::PairOfConstraint
|
8
|
+
TripleOfConstraint = Constraints::TripleOfConstraint
|
9
|
+
|
10
|
+
Maker = Lab42::DataClass::Proxy::Constraints::Maker # TODO: Move Maker to Lab42::DataClass:ConstraintMaker
|
11
|
+
Anything = Constraint.new(name: "Anything", function: ->(_) { true })
|
12
|
+
Boolean = Constraint.new(name: "Boolean", function: -> { [false, true].member?(_1) })
|
13
|
+
|
14
|
+
def All?(constraint = nil, &blk)
|
15
|
+
constraint = Maker.make_constraint(constraint, &blk)
|
16
|
+
f = -> do
|
17
|
+
_1.all?(&constraint)
|
18
|
+
end
|
19
|
+
Constraint.new(name: "All?(#{constraint})", function: f)
|
20
|
+
end
|
21
|
+
|
22
|
+
def Any?(constraint = nil, &blk)
|
23
|
+
constraint = Maker.make_constraint(constraint, &blk)
|
24
|
+
f = -> do
|
25
|
+
_1.any?(&constraint)
|
26
|
+
end
|
27
|
+
Constraint.new(name: "Any?(#{constraint})", function: f)
|
28
|
+
end
|
29
|
+
|
30
|
+
def Choice(*constraints)
|
31
|
+
constraints = constraints.map{ Maker.make_constraint _1 }
|
32
|
+
f = ->(value) do
|
33
|
+
constraints.any?{ _1.(value) }
|
34
|
+
end
|
35
|
+
Constraint.new(name: "Choice(#{constraints.join(', ')})", function: f)
|
36
|
+
end
|
37
|
+
|
38
|
+
def Contains(str)
|
39
|
+
f = -> { _1.include?(str) rescue false }
|
40
|
+
Constraint.new(name: "Contains(#{str})", function: f)
|
41
|
+
end
|
42
|
+
|
43
|
+
def EndsWith(str)
|
44
|
+
f = -> { _1.end_with?(str) rescue false }
|
45
|
+
Constraint.new(name: "EndsWith(#{str})", function: f)
|
46
|
+
end
|
47
|
+
|
48
|
+
def Lambda(arity)
|
49
|
+
function = -> do
|
50
|
+
_1.arity == arity rescue false
|
51
|
+
end
|
52
|
+
Constraint.new(name: "Lambda(#{arity})", function:)
|
53
|
+
end
|
54
|
+
|
55
|
+
def ListOf(constraint, &blk)
|
56
|
+
constraint = Maker.make_constraint(constraint, &blk)
|
57
|
+
function = -> do
|
58
|
+
(Lab42::List === _1 || Lab42::Nil == _1) &&
|
59
|
+
_1.all?(&constraint)
|
60
|
+
end
|
61
|
+
ListOfConstraint.new(name: "ListOf(#{constraint})", constraint:, function:)
|
62
|
+
end
|
63
|
+
|
64
|
+
def NilOr(constraint = nil, &blk)
|
65
|
+
constraint = Maker.make_constraint(constraint, &blk)
|
66
|
+
f = -> { _1.nil? || constraint.(_1) }
|
67
|
+
Constraint.new(name: "NilOr(#{constraint})", function: f)
|
68
|
+
end
|
69
|
+
|
70
|
+
def Not(constraint = nil, &blk)
|
71
|
+
constraint = Maker.make_constraint(constraint, &blk)
|
72
|
+
f = -> { !constraint.(_1) }
|
73
|
+
Constraint.new(name: "Not(#{constraint})", function: f)
|
74
|
+
end
|
75
|
+
|
76
|
+
def PairOf(fst, snd)
|
77
|
+
fst_constraint = Maker.make_constraint(fst)
|
78
|
+
snd_constraint = Maker.make_constraint(snd)
|
79
|
+
constraint = [fst_constraint, snd_constraint]
|
80
|
+
f = -> do
|
81
|
+
Lab42::Pair === _1 && fst_constraint.(_1.first) && snd_constraint.(_1.second)
|
82
|
+
end
|
83
|
+
PairOfConstraint.new(name: "PairOf(#{fst_constraint}, #{snd_constraint})", function: f, constraint:)
|
84
|
+
end
|
85
|
+
|
86
|
+
def StartsWith(str)
|
87
|
+
f = -> { _1.start_with?(str) rescue false }
|
88
|
+
Constraint.new(name: "StartsWith(#{str})", function: f)
|
89
|
+
end
|
90
|
+
|
91
|
+
def TripleOf(fst, snd, trd)
|
92
|
+
fst_constraint = Maker.make_constraint(fst)
|
93
|
+
snd_constraint = Maker.make_constraint(snd)
|
94
|
+
trd_constraint = Maker.make_constraint(trd)
|
95
|
+
constraint = [fst_constraint, snd_constraint, trd_constraint]
|
96
|
+
f = -> do
|
97
|
+
Lab42::Triple === _1 && fst_constraint.(_1.first) && snd_constraint.(_1.second) && trd_constraint.(_1.third)
|
98
|
+
end
|
99
|
+
TripleOfConstraint.new(
|
100
|
+
name: "TripleOf(#{fst_constraint}, #{snd_constraint}, #{trd_constraint})",
|
101
|
+
function: f,
|
102
|
+
constraint:
|
103
|
+
)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "constraint"
|
4
|
+
require_relative "setter_constraint"
|
5
|
+
require_relative "attribute_setters/list_of_attribute_setter"
|
6
|
+
module Lab42
|
7
|
+
module DataClass
|
8
|
+
module Constraints
|
9
|
+
class ListOfConstraint < Constraint
|
10
|
+
include SetterConstraint
|
11
|
+
|
12
|
+
def attribute_setter = AttributeSetters::ListOfAttributeSetter
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "constraint"
|
4
|
+
require_relative "setter_constraint"
|
5
|
+
require_relative "attribute_setters/pair_of_attribute_setter"
|
6
|
+
module Lab42
|
7
|
+
module DataClass
|
8
|
+
module Constraints
|
9
|
+
class PairOfConstraint < Constraint
|
10
|
+
include SetterConstraint
|
11
|
+
|
12
|
+
def attribute_setter = AttributeSetters::PairOfAttributeSetter
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lab42
|
4
|
+
module DataClass
|
5
|
+
module Constraints
|
6
|
+
module SetterConstraint
|
7
|
+
attr_reader :constraint
|
8
|
+
|
9
|
+
def setter_constraint? = true
|
10
|
+
|
11
|
+
def setter_for(attribute:, instance:)
|
12
|
+
attribute_setter.new(
|
13
|
+
attribute:,
|
14
|
+
constraint: self,
|
15
|
+
instance:
|
16
|
+
)
|
17
|
+
end
|
18
|
+
private
|
19
|
+
|
20
|
+
def initialize(constraint:, **other)
|
21
|
+
super(**other)
|
22
|
+
@constraint = constraint
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "constraint"
|
4
|
+
require_relative "setter_constraint"
|
5
|
+
require_relative "attribute_setters/triple_of_attribute_setter"
|
6
|
+
module Lab42
|
7
|
+
module DataClass
|
8
|
+
module Constraints
|
9
|
+
class TripleOfConstraint < Constraint
|
10
|
+
include SetterConstraint
|
11
|
+
|
12
|
+
def attribute_setter = AttributeSetters::TripleOfAttributeSetter
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -1,11 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../nil'
|
3
4
|
module Kernel
|
5
|
+
Nil = Lab42::Nil
|
6
|
+
|
4
7
|
def DataClass(*args, **kwds, &blk)
|
5
8
|
proxy = Lab42::DataClass::Proxy.new(*args, **kwds, &blk)
|
6
9
|
proxy.define_class!
|
7
10
|
end
|
8
11
|
|
12
|
+
def List(*elements)
|
13
|
+
Lab42::List.new(*elements)
|
14
|
+
end
|
15
|
+
|
9
16
|
def Pair(first, second)
|
10
17
|
Lab42::Pair.new(first, second)
|
11
18
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "lab42/data_class/constraints/constraint"
|
3
4
|
module Lab42
|
4
5
|
module DataClass
|
5
6
|
class Proxy
|
@@ -7,9 +8,17 @@ module Lab42
|
|
7
8
|
module Maker
|
8
9
|
extend self
|
9
10
|
|
10
|
-
def make_constraint(constraint)
|
11
|
+
def make_constraint(constraint, &blk)
|
12
|
+
raise ArgumentError, "must not pass a callable #{constraint} and a block" if constraint && blk
|
13
|
+
|
14
|
+
_make_constraint(constraint || blk)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def _make_constraint(constraint)
|
11
20
|
case constraint
|
12
|
-
when Proc, Method
|
21
|
+
when Lab42::DataClass::Constraints::Constraint, Proc, Method
|
13
22
|
constraint
|
14
23
|
when Symbol
|
15
24
|
-> { _1.send(constraint) }
|
@@ -26,8 +35,6 @@ module Lab42
|
|
26
35
|
end
|
27
36
|
end
|
28
37
|
|
29
|
-
private
|
30
|
-
|
31
38
|
def _make_member_constraint(constraint)
|
32
39
|
if constraint.respond_to?(:member?)
|
33
40
|
-> { constraint.member?(_1) }
|
@@ -12,19 +12,8 @@ module Lab42
|
|
12
12
|
raise ConstraintError, errors.join("\n\n") unless errors.empty?
|
13
13
|
end
|
14
14
|
|
15
|
-
def define_constraint
|
16
|
-
->((attr, constraint)) do
|
17
|
-
if members!.member?(attr)
|
18
|
-
constraints[attr] << Maker.make_constraint(constraint)
|
19
|
-
nil
|
20
|
-
else
|
21
|
-
attr
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
15
|
def define_constraints(constraints)
|
27
|
-
errors = constraints.map(&
|
16
|
+
errors = constraints.map(&_define_constraint).compact
|
28
17
|
unless errors.empty?
|
29
18
|
raise UndefinedAttributeError,
|
30
19
|
"constraints cannot be defined for undefined attributes #{errors.inspect}"
|
@@ -74,6 +63,19 @@ module Lab42
|
|
74
63
|
raise ConstraintError, errors.join("\n\n") unless errors.empty?
|
75
64
|
end
|
76
65
|
|
66
|
+
def _define_constraint
|
67
|
+
->((attr, constraint)) do
|
68
|
+
if members!.member?(attr)
|
69
|
+
constraint = Maker.make_constraint(constraint)
|
70
|
+
_maybe_define_setter_constraint(attr, constraint)
|
71
|
+
constraints[attr] << constraint
|
72
|
+
nil
|
73
|
+
else
|
74
|
+
attr
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
77
79
|
def _define_with_constraint
|
78
80
|
proxy = self
|
79
81
|
->(*) do
|
@@ -83,6 +85,12 @@ module Lab42
|
|
83
85
|
end
|
84
86
|
end
|
85
87
|
end
|
88
|
+
|
89
|
+
def _maybe_define_setter_constraint(attr, constraint)
|
90
|
+
if Lab42::DataClass::Constraints::Constraint === constraint && constraint.setter_constraint?
|
91
|
+
setter_attributes.update(attr => constraint)
|
92
|
+
end
|
93
|
+
end
|
86
94
|
end
|
87
95
|
end
|
88
96
|
end
|
@@ -30,12 +30,11 @@ module Lab42
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
def check!(
|
34
|
-
@actual_params = params
|
33
|
+
def check!(params, merge_with = defaults)
|
35
34
|
raise ArgumentError, "missing initializers for #{_missing_initializers}" unless _missing_initializers.empty?
|
36
35
|
raise ArgumentError, "illegal initializers #{_illegal_initializers}" unless _illegal_initializers.empty?
|
37
36
|
|
38
|
-
_check_constraints!(
|
37
|
+
_check_constraints!(merge_with.merge(params))
|
39
38
|
end
|
40
39
|
|
41
40
|
def define_class!
|
@@ -63,6 +62,10 @@ module Lab42
|
|
63
62
|
_init(data_class, defaults.merge(params))
|
64
63
|
end
|
65
64
|
|
65
|
+
def set_actual_params(params)
|
66
|
+
@actual_params = params
|
67
|
+
end
|
68
|
+
|
66
69
|
def to_hash(data_class_instance)
|
67
70
|
all_attributes
|
68
71
|
.inject({}) { |result, (k, _)| result.merge(k => data_class_instance[k]) }
|
@@ -96,15 +99,40 @@ module Lab42
|
|
96
99
|
|
97
100
|
def _define_derived_attribute(name, &blk)
|
98
101
|
->(*) do
|
102
|
+
if instance_methods.include?(name)
|
103
|
+
begin
|
104
|
+
remove_method(name)
|
105
|
+
rescue StandardError
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
end
|
99
109
|
define_method(name) { blk.call(self) }
|
100
110
|
end
|
101
111
|
end
|
102
112
|
|
103
113
|
def _define_freezing_constructor
|
114
|
+
proxy = self
|
115
|
+
->(*) do
|
116
|
+
define_method :new do |**params, &b|
|
117
|
+
allocate.tap do |o|
|
118
|
+
proxy.set_actual_params(params)
|
119
|
+
proxy.check!(params)
|
120
|
+
o.send(:initialize, **params, &b)
|
121
|
+
end.freeze
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def _define_merging_constructor
|
127
|
+
proxy = self
|
104
128
|
->(*) do
|
105
|
-
define_method :
|
106
|
-
|
129
|
+
define_method :_new_from_merge do |new_params, params|
|
130
|
+
allocate.tap do |o|
|
131
|
+
proxy.check!(new_params, {})
|
132
|
+
o.send(:initialize, **params)
|
133
|
+
end.freeze
|
107
134
|
end
|
135
|
+
private :_new_from_merge
|
108
136
|
end
|
109
137
|
end
|
110
138
|
|
@@ -112,7 +140,6 @@ module Lab42
|
|
112
140
|
proxy = self
|
113
141
|
->(*) do
|
114
142
|
define_method :initialize do |**params|
|
115
|
-
proxy.check!(**params)
|
116
143
|
proxy.init(self, **params)
|
117
144
|
proxy.validate!(self)
|
118
145
|
end
|
@@ -123,7 +150,7 @@ module Lab42
|
|
123
150
|
->(*) do
|
124
151
|
define_method :merge do |**params|
|
125
152
|
values = to_h.merge(params)
|
126
|
-
self.class.
|
153
|
+
self.class.send(:_new_from_merge, params, values)
|
127
154
|
end
|
128
155
|
end
|
129
156
|
end
|
@@ -133,10 +160,12 @@ module Lab42
|
|
133
160
|
klass.module_eval(&_define_access)
|
134
161
|
klass.module_eval(&_define_to_h)
|
135
162
|
klass.module_eval(&_define_merge)
|
163
|
+
klass.module_eval(&_define_set)
|
136
164
|
end
|
137
165
|
|
138
166
|
def _define_singleton_methods(singleton)
|
139
167
|
singleton.module_eval(&_define_freezing_constructor)
|
168
|
+
singleton.module_eval(&_define_merging_constructor)
|
140
169
|
singleton.module_eval(&_define_to_proc)
|
141
170
|
singleton.module_eval(&_define_with_constraint)
|
142
171
|
singleton.module_eval(&_define_derived)
|
@@ -170,9 +199,21 @@ module Lab42
|
|
170
199
|
end
|
171
200
|
end
|
172
201
|
|
202
|
+
def _define_set
|
203
|
+
proxy = self
|
204
|
+
->(*) do
|
205
|
+
define_method :set do |attribute|
|
206
|
+
setter_constraint = proxy.setter_attributes.fetch(attribute) do
|
207
|
+
raise UndefinedSetterError, "There is no constraint implementing a setter for attribute #{attribute}"
|
208
|
+
end
|
209
|
+
setter_constraint.setter_for(attribute:, instance: self)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
173
214
|
def _init(data_class_instance, params)
|
174
215
|
params.each do |key, value|
|
175
|
-
data_class_instance.instance_variable_set("@#{key}", value)
|
216
|
+
data_class_instance.instance_variable_set("@#{key}", value.freeze)
|
176
217
|
end
|
177
218
|
end
|
178
219
|
end
|
data/lib/lab42/data_class.rb
CHANGED
@@ -4,8 +4,11 @@ require_relative './data_class/constraint_error'
|
|
4
4
|
require_relative './data_class/duplicate_definition_error'
|
5
5
|
require_relative './data_class/kernel'
|
6
6
|
require_relative './data_class/undefined_attribute_error'
|
7
|
+
require_relative './data_class/undefined_setter_error'
|
7
8
|
require_relative './data_class/validation_error'
|
8
9
|
require_relative './data_class/proxy'
|
10
|
+
require_relative './list'
|
11
|
+
require_relative './nil'
|
9
12
|
require_relative './pair'
|
10
13
|
require_relative './triple'
|
11
14
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lab42
|
4
|
+
class List
|
5
|
+
module ClassMethods
|
6
|
+
def cons(element, list)
|
7
|
+
raise ArgumentError, "list needs to be a list instance" unless list?(list)
|
8
|
+
|
9
|
+
allocate.tap do |o|
|
10
|
+
o.instance_variable_set("@car", element)
|
11
|
+
o.instance_variable_set("@cdr", list)
|
12
|
+
o.instance_variable_set("@length", list.length.succ)
|
13
|
+
o.freeze
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def each(subject, &blk)
|
18
|
+
unless subject.empty?
|
19
|
+
blk.(subject.car)
|
20
|
+
each(subject.cdr, &blk)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def list?(subject)
|
25
|
+
self === subject || Nil == subject
|
26
|
+
end
|
27
|
+
|
28
|
+
def new(*elements)
|
29
|
+
elements.reverse.inject(Nil) do |list, element|
|
30
|
+
cons(element, list)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
extend ClassMethods
|
35
|
+
end
|
36
|
+
end
|
37
|
+
# SPDX-License-Identifier: Apache-2.0
|
data/lib/lab42/list.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "nil"
|
4
|
+
require_relative "list/class_methods"
|
5
|
+
module Lab42
|
6
|
+
class List
|
7
|
+
include Enumerable
|
8
|
+
attr_reader :car, :cdr, :length
|
9
|
+
|
10
|
+
def ==(other) =
|
11
|
+
self.class.list?(other) &&
|
12
|
+
length == other.length &&
|
13
|
+
car == other.car &&
|
14
|
+
cdr == other.cdr
|
15
|
+
|
16
|
+
def cadr = cdr.car
|
17
|
+
|
18
|
+
def caddr = cdr.cdr.car
|
19
|
+
|
20
|
+
def cddr = cdr.cdr
|
21
|
+
|
22
|
+
def cdddr = cdr.cdr.cdr
|
23
|
+
|
24
|
+
def cons(new_head) = self.class.cons(new_head, self)
|
25
|
+
|
26
|
+
def deconstruct = [car, cdr]
|
27
|
+
|
28
|
+
def each(&blk) = self.class.each(self, &blk)
|
29
|
+
|
30
|
+
def empty? = false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
# SPDX-License-Identifier: Apache-2.0
|
data/lib/lab42/nil.rb
ADDED
data/lib/lab42/pair.rb
CHANGED
data/lib/lab42/triple.rb
CHANGED
@@ -10,6 +10,10 @@ module Lab42
|
|
10
10
|
[first, second, third]
|
11
11
|
end
|
12
12
|
|
13
|
+
def set_first(new_first) = self.class.new(new_first, second, third)
|
14
|
+
def set_second(new_second) = self.class.new(first, new_second, third)
|
15
|
+
def set_third(new_third) = self.class.new(first, second, new_third)
|
16
|
+
|
13
17
|
private
|
14
18
|
|
15
19
|
def initialize(first, second, third)
|
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.8.1
|
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-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |
|
14
14
|
An Immutable DataClass for Ruby
|
@@ -24,7 +24,18 @@ files:
|
|
24
24
|
- LICENSE
|
25
25
|
- README.md
|
26
26
|
- lib/lab42/data_class.rb
|
27
|
+
- lib/lab42/data_class/builtin_constraints.rb
|
27
28
|
- lib/lab42/data_class/constraint_error.rb
|
29
|
+
- lib/lab42/data_class/constraints/attribute_setters/attribute_setter.rb
|
30
|
+
- lib/lab42/data_class/constraints/attribute_setters/list_of_attribute_setter.rb
|
31
|
+
- lib/lab42/data_class/constraints/attribute_setters/pair_of_attribute_setter.rb
|
32
|
+
- lib/lab42/data_class/constraints/attribute_setters/triple_of_attribute_setter.rb
|
33
|
+
- lib/lab42/data_class/constraints/constraint.rb
|
34
|
+
- lib/lab42/data_class/constraints/kernel.rb
|
35
|
+
- lib/lab42/data_class/constraints/list_of_constraint.rb
|
36
|
+
- lib/lab42/data_class/constraints/pair_of_constraint.rb
|
37
|
+
- lib/lab42/data_class/constraints/setter_constraint.rb
|
38
|
+
- lib/lab42/data_class/constraints/triple_of_constraint.rb
|
28
39
|
- lib/lab42/data_class/duplicate_definition_error.rb
|
29
40
|
- lib/lab42/data_class/kernel.rb
|
30
41
|
- lib/lab42/data_class/proxy.rb
|
@@ -35,9 +46,13 @@ files:
|
|
35
46
|
- lib/lab42/data_class/proxy/mixin.rb
|
36
47
|
- lib/lab42/data_class/proxy/validations.rb
|
37
48
|
- lib/lab42/data_class/undefined_attribute_error.rb
|
49
|
+
- lib/lab42/data_class/undefined_setter_error.rb
|
38
50
|
- lib/lab42/data_class/validation_error.rb
|
39
51
|
- lib/lab42/data_class/version.rb
|
40
52
|
- lib/lab42/eq_and_patterns.rb
|
53
|
+
- lib/lab42/list.rb
|
54
|
+
- lib/lab42/list/class_methods.rb
|
55
|
+
- lib/lab42/nil.rb
|
41
56
|
- lib/lab42/pair.rb
|
42
57
|
- lib/lab42/triple.rb
|
43
58
|
homepage: https://github.com/robertdober/lab42_data_class
|