lab42_data_class 0.5.1 → 0.6.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 +42 -0
- data/lib/lab42/data_class/kernel.rb +17 -0
- data/lib/lab42/data_class/proxy/constraints.rb +11 -7
- data/lib/lab42/data_class/proxy/memos.rb +44 -0
- data/lib/lab42/data_class/proxy.rb +14 -23
- data/lib/lab42/data_class/version.rb +1 -1
- data/lib/lab42/data_class.rb +21 -11
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1819694c4684f3f4e377f04f4eb17d0842a1075ff7859eeef443c66dd7cfb458
|
4
|
+
data.tar.gz: 5af74933329ed924d51a1820d666c671973004edbd3ab00f6ba93378df71180f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e14f2656f03703aa867dce065bf9e4770cf043fd97d035cab9a6b869d9fb6e9749c2de3eef0b948e02c7ade07f85f75423c4b2412ed154574a6672e28411d9f
|
7
|
+
data.tar.gz: f831c9c8094f7224de088f6f03847791bcc91a33db6e565fc1ef3f45d5f3f5e29d18b05dfbcdbc7dcc422755d0ab87dc73f0c50a9de45dcd3df0916d87fb1c16
|
data/README.md
CHANGED
@@ -40,6 +40,7 @@ Well let us [speculate about](https://github.com/RobertDober/speculate_about) it
|
|
40
40
|
|
41
41
|
## Context `DataClass`
|
42
42
|
|
43
|
+
|
43
44
|
### Context: `DataClass` function
|
44
45
|
|
45
46
|
Given
|
@@ -452,6 +453,47 @@ And remark how bad unnamed validation errors might be
|
|
452
453
|
.to raise_error(validation_error, error_message_rgx)
|
453
454
|
```
|
454
455
|
|
456
|
+
### Context: Usage with `extend`
|
457
|
+
|
458
|
+
All the above mentioned features can be achieved with a more conventional syntax by extending a class
|
459
|
+
with `Lab42::DataClass`
|
460
|
+
|
461
|
+
Given a class that extends `DataClass`
|
462
|
+
```ruby
|
463
|
+
let :my_class do
|
464
|
+
Class.new do
|
465
|
+
extend Lab42::DataClass
|
466
|
+
attributes :age, member: false
|
467
|
+
constraint :member, Set.new([false, true])
|
468
|
+
validate(:too_young_for_member) { |instance| !(instance.member && instance.age < 18) }
|
469
|
+
end
|
470
|
+
end
|
471
|
+
let(:constraint_error) { Lab42::DataClass::ConstraintError }
|
472
|
+
let(:validation_error) { Lab42::DataClass::ValidationError }
|
473
|
+
let(:my_instance) { my_class.new(age: 42) }
|
474
|
+
let(:my_vip) { my_instance.merge(member: true) }
|
475
|
+
```
|
476
|
+
|
477
|
+
Then we can observe that instances of such a class
|
478
|
+
```ruby
|
479
|
+
expect(my_instance.to_h).to eq(age: 42, member: false)
|
480
|
+
expect(my_vip.to_h).to eq(age: 42, member: true)
|
481
|
+
expect(my_instance.member).to be_falsy
|
482
|
+
```
|
483
|
+
|
484
|
+
And we will get constraint errors if applicable
|
485
|
+
```ruby
|
486
|
+
expect{my_instance.merge(member: nil)}
|
487
|
+
.to raise_error(constraint_error)
|
488
|
+
```
|
489
|
+
|
490
|
+
And of course validations still work too
|
491
|
+
```ruby
|
492
|
+
expect{ my_vip.merge(age: 17) }
|
493
|
+
.to raise_error(validation_error, "too_young_for_member")
|
494
|
+
```
|
495
|
+
|
496
|
+
|
455
497
|
## Context: `Pair` and `Triple`
|
456
498
|
|
457
499
|
Two special cases of a `DataClass` which behave like `Tuple` of size 2 and 3 in _Elixir_
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kernel
|
4
|
+
def DataClass(*args, **kwds, &blk)
|
5
|
+
proxy = Lab42::DataClass::Proxy.new(*args, **kwds, &blk)
|
6
|
+
proxy.define_class!
|
7
|
+
end
|
8
|
+
|
9
|
+
def Pair(first, second)
|
10
|
+
Lab42::Pair.new(first, second)
|
11
|
+
end
|
12
|
+
|
13
|
+
def Triple(first, second, third)
|
14
|
+
Lab42::Triple.new(first, second, third)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -23,6 +23,16 @@ module Lab42
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
+
def define_constraints(constraints)
|
27
|
+
errors = constraints.map(&define_constraint).compact
|
28
|
+
unless errors.empty?
|
29
|
+
raise ArgumentError,
|
30
|
+
"constraints cannot be defined for undefined attributes #{errors.inspect}"
|
31
|
+
end
|
32
|
+
|
33
|
+
check_constraints_against_defaults(constraints)
|
34
|
+
end
|
35
|
+
|
26
36
|
private
|
27
37
|
|
28
38
|
def _check_constraint_against_default
|
@@ -68,13 +78,7 @@ module Lab42
|
|
68
78
|
proxy = self
|
69
79
|
->(*) do
|
70
80
|
define_method :with_constraint do |**constraints|
|
71
|
-
|
72
|
-
unless errors.empty?
|
73
|
-
raise ArgumentError,
|
74
|
-
"constraints cannot be defined for undefined attributes #{errors.inspect}"
|
75
|
-
end
|
76
|
-
|
77
|
-
proxy.check_constraints_against_defaults(constraints)
|
81
|
+
proxy.define_constraints(constraints)
|
78
82
|
self
|
79
83
|
end
|
80
84
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lab42
|
4
|
+
module DataClass
|
5
|
+
class Proxy
|
6
|
+
module Memos
|
7
|
+
def constraints
|
8
|
+
@__constraints__ ||= Hash.new { |h, k| h[k] = [] }
|
9
|
+
end
|
10
|
+
|
11
|
+
def defaults
|
12
|
+
@__defaults__ ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def members
|
16
|
+
@__members__ ||= unless (positionals + defaults.keys).empty?
|
17
|
+
Set.new(positionals + defaults.keys)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def positionals
|
22
|
+
@__positionals__ ||= []
|
23
|
+
end
|
24
|
+
|
25
|
+
def validations
|
26
|
+
@__validations__ ||= []
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def _missing_initializers
|
32
|
+
@___missing_initializers__ ||=
|
33
|
+
positionals - actual_params.keys
|
34
|
+
end
|
35
|
+
|
36
|
+
def _illegal_initializers
|
37
|
+
@___illegal_initializers__ ||=
|
38
|
+
actual_params.keys - positionals - defaults.keys
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -2,22 +2,22 @@
|
|
2
2
|
|
3
3
|
require 'set'
|
4
4
|
require_relative 'proxy/constraints'
|
5
|
+
require_relative 'proxy/memos'
|
5
6
|
require_relative 'proxy/validations'
|
6
7
|
require_relative 'proxy/mixin'
|
7
8
|
module Lab42
|
8
9
|
module DataClass
|
9
10
|
class Proxy
|
10
|
-
include Constraints, Validations
|
11
|
+
include Constraints, Memos, Validations
|
11
12
|
|
12
|
-
attr_reader :actual_params, :
|
13
|
+
attr_reader :actual_params, :block, :klass
|
13
14
|
|
14
15
|
def check!(**params)
|
15
16
|
@actual_params = params
|
16
|
-
@all_params = defaults.merge(params)
|
17
17
|
raise ArgumentError, "missing initializers for #{_missing_initializers}" unless _missing_initializers.empty?
|
18
18
|
raise ArgumentError, "illegal initializers #{_illegal_initializers}" unless _illegal_initializers.empty?
|
19
19
|
|
20
|
-
_check_constraints!(
|
20
|
+
_check_constraints!(defaults.merge(params))
|
21
21
|
end
|
22
22
|
|
23
23
|
def define_class!
|
@@ -39,21 +39,22 @@ module Lab42
|
|
39
39
|
.to_h
|
40
40
|
end
|
41
41
|
|
42
|
-
def
|
43
|
-
|
42
|
+
def update!(with_positionals, with_keywords)
|
43
|
+
positionals.push(*with_positionals)
|
44
|
+
defaults.update(with_keywords)
|
44
45
|
end
|
45
46
|
|
46
47
|
private
|
47
48
|
def initialize(*args, **kwds, &blk)
|
48
|
-
@klass = Class.
|
49
|
-
|
50
|
-
|
49
|
+
@klass = if Class === args.first
|
50
|
+
args.shift
|
51
|
+
else
|
52
|
+
Class.new
|
53
|
+
end
|
51
54
|
|
52
55
|
@block = blk
|
53
|
-
|
54
|
-
|
55
|
-
# TODO: Check for all symbols and no duplicates ⇒ v0.5.1
|
56
|
-
@positionals = args
|
56
|
+
defaults.update(kwds)
|
57
|
+
positionals.push(*args)
|
57
58
|
end
|
58
59
|
|
59
60
|
def _define_attr_reader
|
@@ -128,16 +129,6 @@ module Lab42
|
|
128
129
|
data_class_instance.instance_variable_set("@#{key}", value)
|
129
130
|
end
|
130
131
|
end
|
131
|
-
|
132
|
-
def _missing_initializers
|
133
|
-
@___missing_initializers__ ||=
|
134
|
-
positionals - actual_params.keys
|
135
|
-
end
|
136
|
-
|
137
|
-
def _illegal_initializers
|
138
|
-
@___illegal_initializers__ ||=
|
139
|
-
actual_params.keys - positionals - defaults.keys
|
140
|
-
end
|
141
132
|
end
|
142
133
|
end
|
143
134
|
end
|
data/lib/lab42/data_class.rb
CHANGED
@@ -1,24 +1,34 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative './data_class/constraint_error'
|
4
|
+
require_relative './data_class/kernel'
|
4
5
|
require_relative './data_class/validation_error'
|
5
6
|
require_relative './data_class/proxy'
|
6
7
|
require_relative './pair'
|
7
8
|
require_relative './triple'
|
8
9
|
|
9
|
-
module
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
10
|
+
module Lab42
|
11
|
+
module DataClass
|
12
|
+
def self.extended(extender)
|
13
|
+
proxy = Proxy.new(extender)
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
extender.module_eval do
|
16
|
+
define_singleton_method(:__data_class_proxy__){ proxy }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def attributes(*args, **kwds)
|
21
|
+
__data_class_proxy__.tap do |proxy|
|
22
|
+
proxy.update!(args, kwds)
|
23
|
+
proxy.define_class!
|
24
|
+
end
|
25
|
+
end
|
18
26
|
|
19
|
-
|
20
|
-
|
27
|
+
def constraint(member, constraint = nil, &block)
|
28
|
+
raise ArgumentError, "must not provide constraint (2nd argument) and a block" if block && constraint
|
29
|
+
|
30
|
+
__data_class_proxy__.define_constraints(member => constraint || block)
|
31
|
+
end
|
21
32
|
end
|
22
33
|
end
|
23
|
-
|
24
34
|
# SPDX-License-Identifier: Apache-2.0
|
metadata
CHANGED
@@ -1,11 +1,11 @@
|
|
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.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Dober
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 2022-02-24 00:00:00.000000000 Z
|
@@ -25,9 +25,11 @@ files:
|
|
25
25
|
- README.md
|
26
26
|
- lib/lab42/data_class.rb
|
27
27
|
- lib/lab42/data_class/constraint_error.rb
|
28
|
+
- lib/lab42/data_class/kernel.rb
|
28
29
|
- lib/lab42/data_class/proxy.rb
|
29
30
|
- lib/lab42/data_class/proxy/constraints.rb
|
30
31
|
- lib/lab42/data_class/proxy/constraints/maker.rb
|
32
|
+
- lib/lab42/data_class/proxy/memos.rb
|
31
33
|
- lib/lab42/data_class/proxy/mixin.rb
|
32
34
|
- lib/lab42/data_class/proxy/validations.rb
|
33
35
|
- lib/lab42/data_class/validation_error.rb
|
@@ -39,7 +41,7 @@ homepage: https://github.com/robertdober/lab42_data_class
|
|
39
41
|
licenses:
|
40
42
|
- Apache-2.0
|
41
43
|
metadata: {}
|
42
|
-
post_install_message:
|
44
|
+
post_install_message:
|
43
45
|
rdoc_options: []
|
44
46
|
require_paths:
|
45
47
|
- lib
|
@@ -55,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
55
57
|
version: '0'
|
56
58
|
requirements: []
|
57
59
|
rubygems_version: 3.3.3
|
58
|
-
signing_key:
|
60
|
+
signing_key:
|
59
61
|
specification_version: 4
|
60
62
|
summary: Finally a dataclass in ruby
|
61
63
|
test_files: []
|