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