lab42_checked_class 0.1.0 → 0.2.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 +53 -1
- data/lib/lab42/checked_class/all.rb +9 -0
- data/lib/lab42/checked_class/constraint/predefined.rb +24 -0
- data/lib/lab42/checked_class/constraint.rb +6 -0
- data/lib/lab42/checked_class/injector.rb +7 -0
- data/lib/lab42/checked_class/mixin/class_methods.rb +3 -2
- data/lib/lab42/checked_class/proxy.rb +12 -0
- data/lib/lab42/checked_class/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ff1daba1d7c01bd5f4af095171c104f71fdaa6d180659ebdf6fdbb86a6f9c6e9
|
|
4
|
+
data.tar.gz: ac04957601c51e0a505dfb2621a5f44ff3664aeb09a0e00d412fba45de71bc33
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cae9380f7d40546c63af28f0abdb59bca958298208cc9b9ec39b1d0ca93acbdfd3eeec642af53e9eeceae9c75259abe79ce7b313dd6be33e6a4d52da7c94755c
|
|
7
|
+
data.tar.gz: a57c32b8094f45501ca71c7bbcf77fa3880732ecadcff5b56ce44a50df824b458cdc6ba4a391c2f12ce58dc32416776b833e0d9ca1f6ab1b159716bf17aead7d
|
data/README.md
CHANGED
|
@@ -58,8 +58,27 @@ These specs assume the following setup code
|
|
|
58
58
|
require 'lab42/checked_class'
|
|
59
59
|
CheckedClass = Lab42::CheckedClass
|
|
60
60
|
ConstraintError = CheckedClass::ConstraintError
|
|
61
|
+
Constraint = CheckedClass::Constraint
|
|
61
62
|
```
|
|
63
|
+
This is realized in the spec_helper, but if you want to have these three constants in your namespace you can
|
|
64
|
+
do the following:
|
|
62
65
|
|
|
66
|
+
## Context: Importing constants into your namespace
|
|
67
|
+
|
|
68
|
+
Then we will get them loaded
|
|
69
|
+
```ruby
|
|
70
|
+
expected_output = <<~EOT
|
|
71
|
+
#{ENV["PWD"]}/lib/lab42/checked_class/all.rb:5: warning: already initialized constant CheckedClass
|
|
72
|
+
#{ENV["PWD"]}/spec/spec_helper.rb:36: warning: previous definition of CheckedClass was here
|
|
73
|
+
#{ENV["PWD"]}/lib/lab42/checked_class/all.rb:6: warning: already initialized constant Constraint
|
|
74
|
+
#{ENV["PWD"]}/spec/spec_helper.rb:37: warning: previous definition of Constraint was here
|
|
75
|
+
#{ENV["PWD"]}/lib/lab42/checked_class/all.rb:7: warning: already initialized constant ConstraintError
|
|
76
|
+
#{ENV["PWD"]}/spec/spec_helper.rb:38: warning: previous definition of ConstraintError was here
|
|
77
|
+
EOT
|
|
78
|
+
expect {
|
|
79
|
+
require 'lab42/checked_class/all'
|
|
80
|
+
}.to output(expected_output).to_stderr
|
|
81
|
+
```
|
|
63
82
|
## Context: Attribute definitions and constraints
|
|
64
83
|
|
|
65
84
|
Given a checked class
|
|
@@ -119,7 +138,40 @@ And also we cannot create an instance that violates the constraints
|
|
|
119
138
|
expect { MyChecked.new(a: 10, c: -4, e: 90, f: 0) }.to raise_error(ConstraintError, "a + b > e")
|
|
120
139
|
```
|
|
121
140
|
|
|
122
|
-
|
|
141
|
+
### Context: Defining attributes with `method` missing.
|
|
142
|
+
|
|
143
|
+
As long as a method is called that is not defined inside the `attributes` block, it will be
|
|
144
|
+
interpreted as an attribute definition, as it cannot have a default value assigned, we can
|
|
145
|
+
also use the `defaults` macro
|
|
146
|
+
|
|
147
|
+
Given such implicit attribute definitions
|
|
148
|
+
```ruby
|
|
149
|
+
Implicit = Class.new do
|
|
150
|
+
extend CheckedClass
|
|
151
|
+
attributes do
|
|
152
|
+
alpha Numeric
|
|
153
|
+
beta Constraint.bool?
|
|
154
|
+
|
|
155
|
+
defaults alpha: 0, beta: true
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
let(:instance) { Implicit.new }
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Then we can access the implicitly defined attributes
|
|
162
|
+
```ruby
|
|
163
|
+
expect(instance.to_h).to eq(alpha: 0, beta: true)
|
|
164
|
+
```
|
|
165
|
+
And we will get a constraint error as expeced
|
|
166
|
+
```ruby
|
|
167
|
+
expected_message = "bool? constraint for beta"
|
|
168
|
+
expect { instance.update(beta: 42) }
|
|
169
|
+
.to raise_error(ConstraintError, expected_message)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
You can see all predefined builtin constraints [here](speculations/built_in_constraints.md)
|
|
173
|
+
|
|
174
|
+
## Context: Runtime Semantics of Constraints
|
|
123
175
|
|
|
124
176
|
### Context: Checked und unchecked attribute updates
|
|
125
177
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
module Lab42
|
|
4
|
+
module CheckedClass
|
|
5
|
+
class Constraint
|
|
6
|
+
module Predefined
|
|
7
|
+
|
|
8
|
+
def bool? =
|
|
9
|
+
tagged('bool?') { [false, true].member? it }
|
|
10
|
+
|
|
11
|
+
def match?(rgx) =
|
|
12
|
+
tagged("match?(#{rgx}") { rgx === it rescue false }
|
|
13
|
+
|
|
14
|
+
def member?(container) =
|
|
15
|
+
tagged("member?(#{container.inspect}") { container.member? it }
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
def tagged(name, &blk) = [self, blk, name]
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
require_relative 'constraint/predefined'
|
|
2
3
|
module Lab42
|
|
3
4
|
module CheckedClass
|
|
4
5
|
class Constraint
|
|
6
|
+
extend Predefined
|
|
7
|
+
|
|
5
8
|
|
|
6
9
|
def check(subject)
|
|
7
10
|
subject = @attr ? subject[@attr] : subject
|
|
@@ -31,6 +34,9 @@ module Lab42
|
|
|
31
34
|
-> { it.send(*args) }
|
|
32
35
|
in Module
|
|
33
36
|
-> { constraint === it }
|
|
37
|
+
in [Predefined, blk, name]
|
|
38
|
+
@name = [name, @name].join(' ')
|
|
39
|
+
blk
|
|
34
40
|
in nil
|
|
35
41
|
blk
|
|
36
42
|
in _
|
|
@@ -59,6 +59,13 @@ module Lab42
|
|
|
59
59
|
constraint_checker.check!(self)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
define_method :legal? do
|
|
63
|
+
check!
|
|
64
|
+
true
|
|
65
|
+
rescue ConstraintError
|
|
66
|
+
false
|
|
67
|
+
end
|
|
68
|
+
|
|
62
69
|
define_method :to_h do
|
|
63
70
|
attributes.inject(Hash.new) { |result, ele|
|
|
64
71
|
result.update(ele => self[ele])
|
|
@@ -52,12 +52,24 @@ module Lab42
|
|
|
52
52
|
_constraints["object"] << Constraint.new(name:, &blk)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
def defaults(**params)
|
|
56
|
+
params.each do |k, v|
|
|
57
|
+
raise ArgumentError, "must not redefine default for key #{k.inspect}" if _defaults.has_key?(k)
|
|
58
|
+
|
|
59
|
+
_defaults[k] = v
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
55
63
|
def init(&blk)
|
|
56
64
|
raise ArgumentError, "init needs a block" unless blk
|
|
57
65
|
|
|
58
66
|
@init = blk
|
|
59
67
|
end
|
|
60
68
|
|
|
69
|
+
def method_missing(name, *a, **k, &b)
|
|
70
|
+
attr(name, *a, **k, &b)
|
|
71
|
+
end
|
|
72
|
+
|
|
61
73
|
def _define_att(name, constraint = nil, default: None, &blk)
|
|
62
74
|
_attributes << name
|
|
63
75
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lab42_checked_class
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Robert Dober
|
|
@@ -20,8 +20,10 @@ files:
|
|
|
20
20
|
- LICENSE
|
|
21
21
|
- README.md
|
|
22
22
|
- lib/lab42/checked_class.rb
|
|
23
|
+
- lib/lab42/checked_class/all.rb
|
|
23
24
|
- lib/lab42/checked_class/argument_checker.rb
|
|
24
25
|
- lib/lab42/checked_class/constraint.rb
|
|
26
|
+
- lib/lab42/checked_class/constraint/predefined.rb
|
|
25
27
|
- lib/lab42/checked_class/constraint_checker.rb
|
|
26
28
|
- lib/lab42/checked_class/constraint_error.rb
|
|
27
29
|
- lib/lab42/checked_class/injector.rb
|