lab42_data_class 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|