constrain 0.1.1 → 0.2.2
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 +40 -24
- data/constrain.gemspec +1 -1
- data/lib/constrain.rb +73 -13
- data/lib/constrain/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 21ca1b88c0ffb91b40a2a2335ead91e2570108f8b67eaa9ea7fa3ebe00ada4c4
|
4
|
+
data.tar.gz: '083da1149466231ca8de0dc216cb00c46c540ebc1b7558a0d26422c33b5af9c4'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b40d638495bf36bd661fd1f8ab7845ec177a3f5cb2f73ddfd400976a9b02f9f8b2f16b2b0301b287cd419c43a2e961e895f96b6e9fd7a46545c96c71a954a28
|
7
|
+
data.tar.gz: 7be37bc44024db00f04d1355cc4bf340ca8728b2ff3014e27bda54f551bab85843f383f61890a8eaf63c65a14907061b3ce49f676732e1b0ad774b0e0e727541
|
data/README.md
CHANGED
@@ -58,38 +58,49 @@ end
|
|
58
58
|
```
|
59
59
|
|
60
60
|
The alternative is to include the constrain Module in a common root class to
|
61
|
-
have it available in all child
|
61
|
+
have it available in all child classes
|
62
62
|
|
63
|
-
|
63
|
+
## Methods
|
64
64
|
|
65
|
-
|
66
|
-
constrain(value, *class-expressions, message = nil)
|
67
|
-
```
|
65
|
+
#### constrain(value, \*class-expressions, message = nil, unwind: 0)
|
68
66
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
67
|
+
Return the given value if it matches at least one of the class-expressions and raise a
|
68
|
+
Constrain::TypeError if not. The error message can be customized by added the
|
69
|
+
message argument and a number of backtrace leves can be skipped by setting
|
70
|
+
:unwind option. By default the backtrace will refer to the point of the call of
|
71
|
+
\#constrain. \#constrain raises a Constrain::Error exception if there is
|
72
|
+
an error in the syntax of the class expression
|
74
73
|
|
75
|
-
|
74
|
+
\#constrain is typically used to type-check parameters in methods where you
|
75
|
+
want an exception if the parameters doesn't match the expected, but because it
|
76
|
+
returns the value if successful it can be used to check the validity of
|
77
|
+
variables in expressions too, eg. `return constrain(result_of_complex_computation, Integer)`
|
78
|
+
to check the return value of a method
|
76
79
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
+
#### Constrain.constrain(value, \*class-expressions, message = nil, unwind: 0)
|
81
|
+
|
82
|
+
Class method version of #constrain. It is automatically added to classes that
|
83
|
+
include Constrain
|
84
|
+
|
85
|
+
|
86
|
+
#### Constrain.constrain?(value, \*class-expression) -> true or false
|
80
87
|
|
81
88
|
It matches value against the class expressions like #constrain but returns true
|
82
|
-
or false as result
|
89
|
+
or false as result and can be used to handle complex type expressions
|
90
|
+
dynamically. It is automatically added to classes that include Constrain.
|
91
|
+
Constrain.constrain? raises a Constrain::Error exception if there is an error
|
92
|
+
in the syntax of the class expression
|
83
93
|
|
84
94
|
## Class Expressions
|
85
95
|
|
86
|
-
Constrain#constrain and Constrain::
|
87
|
-
|
88
|
-
objects match if
|
96
|
+
Constrain#constrain and Constrain::constrain? use class expressions composed of
|
97
|
+
class or module objects, Proc objects, or arrays and hashes of class expressions. Class or module
|
98
|
+
objects match if `value.is_a?(class_or_module)` returns true:
|
89
99
|
|
90
100
|
```ruby
|
91
|
-
constrain 42, Integer
|
92
|
-
constrain 42,
|
101
|
+
constrain 42, Integer # Success
|
102
|
+
constrain 42, Comparable # Success
|
103
|
+
constrain nil, Comparable # Failure
|
93
104
|
```
|
94
105
|
|
95
106
|
More than one class expression is allowed. It matches if at least one of the expressions match:
|
@@ -134,10 +145,15 @@ constrain 0, Integer # Success
|
|
134
145
|
constrain 0, lambda { |value| value > 1 } # Failure
|
135
146
|
```
|
136
147
|
|
137
|
-
Proc objects
|
138
|
-
|
139
|
-
|
140
|
-
|
148
|
+
Proc objects are a little more verbose than checking the constraint without
|
149
|
+
\#constrain but it allows the use of the :unwind option to manipulate the
|
150
|
+
apparent origin in the source of the exception
|
151
|
+
|
152
|
+
Note that even though Proc objects can check every aspect of an object but you
|
153
|
+
should not overuse it because as checks becomes more complex they tend to
|
154
|
+
include business logic that should be kept in the production code. Constrain is
|
155
|
+
only thouhgt of as a tool to catch developer errors - not errors that stem from
|
156
|
+
corrupted data
|
141
157
|
|
142
158
|
#### Arrays
|
143
159
|
|
data/constrain.gemspec
CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
|
27
27
|
Constrain works with ruby-2 (and maybe ruby-3)
|
28
28
|
}
|
29
|
-
spec.homepage = "
|
29
|
+
spec.homepage = "https://github.com/clrgit/constrain/"
|
30
30
|
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
31
31
|
|
32
32
|
spec.metadata["homepage_uri"] = spec.homepage
|
data/lib/constrain.rb
CHANGED
@@ -1,4 +1,18 @@
|
|
1
1
|
require "constrain/version"
|
2
|
+
module Foo
|
3
|
+
|
4
|
+
module InstanceMethods
|
5
|
+
def bar1
|
6
|
+
'bar1'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def bar2
|
12
|
+
'bar2'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
2
16
|
|
3
17
|
module Constrain
|
4
18
|
# Raised on any error
|
@@ -6,42 +20,88 @@ module Constrain
|
|
6
20
|
|
7
21
|
# Raised if types doesn't match a class expression
|
8
22
|
class TypeError < Error
|
9
|
-
def initialize(value, exprs, msg = nil)
|
23
|
+
def initialize(value, exprs, msg = nil, unwind: 0)
|
10
24
|
super msg || "Expected #{value.inspect} to match #{Constrain.fmt_exprs(exprs)}"
|
11
25
|
end
|
12
26
|
end
|
13
27
|
|
28
|
+
def self.included base
|
29
|
+
base.extend ClassMethods
|
30
|
+
end
|
31
|
+
|
32
|
+
# See Constrain.constrain
|
33
|
+
def constrain(value, *exprs)
|
34
|
+
Constrain.do_constrain(value, *exprs)
|
35
|
+
end
|
36
|
+
|
37
|
+
def constrain?(value, expr)
|
38
|
+
Constrain.do_constrain?(value, expr)
|
39
|
+
end
|
40
|
+
|
41
|
+
# :call-seq:
|
42
|
+
# constrain(value, *class-expressions, unwind: 0)
|
43
|
+
#
|
14
44
|
# Check that value matches one of the class expressions. Raises a
|
15
45
|
# Constrain::Error if the expression is invalid and a Constrain::TypeError if
|
16
|
-
# the value doesn't match
|
17
|
-
|
46
|
+
# the value doesn't match. The exception's backtrace skips :unwind number of
|
47
|
+
# entries
|
48
|
+
def self.constrain(value, *exprs)
|
49
|
+
do_constrain(value, *exprs)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Return true if the value matches the class expression. Raises a
|
53
|
+
# Constrain::Error if the expression is invalid
|
54
|
+
#
|
55
|
+
# TODO: Allow *exprs
|
56
|
+
def self.constrain?(value, expr)
|
57
|
+
do_constrain?(value, expr)
|
58
|
+
end
|
59
|
+
|
60
|
+
module ClassMethods
|
61
|
+
# See Constrain.constrain
|
62
|
+
def constrain(*args) Constrain.do_constrain(*args) end
|
63
|
+
|
64
|
+
# See Constrain.constrain?
|
65
|
+
def constrain?(*args) Constrain.do_constrain?(*args) end
|
66
|
+
end
|
67
|
+
|
68
|
+
# unwind is automatically incremented by one because ::do_constrain is always
|
69
|
+
# called from one of the other constrain methods
|
70
|
+
#
|
71
|
+
def self.do_constrain(value, *exprs)
|
72
|
+
if exprs.last.is_a?(Hash)
|
73
|
+
unwind = (exprs.last.delete(:unwind) || 0) + 1
|
74
|
+
!exprs.last.empty? or exprs.pop
|
75
|
+
else
|
76
|
+
unwind = 1
|
77
|
+
end
|
18
78
|
msg = exprs.pop if exprs.last.is_a?(String)
|
19
79
|
begin
|
20
80
|
!exprs.empty? or raise Error, "Empty class expression"
|
21
|
-
exprs.any? { |expr| Constrain.
|
81
|
+
exprs.any? { |expr| Constrain.do_constrain?(value, expr) } or
|
82
|
+
raise TypeError.new(value, exprs, msg, unwind: unwind)
|
22
83
|
rescue Error => ex
|
23
|
-
ex.set_backtrace(caller[1..-1])
|
84
|
+
ex.set_backtrace(caller[1 + unwind..-1])
|
24
85
|
raise
|
25
86
|
end
|
87
|
+
value
|
26
88
|
end
|
27
89
|
|
28
|
-
|
29
|
-
# Constrain::Error if the expression is invalid
|
30
|
-
def self.check(value, expr)
|
90
|
+
def self.do_constrain?(value, expr)
|
31
91
|
case expr
|
32
|
-
when Class
|
92
|
+
when Class, Module
|
33
93
|
value.is_a?(expr)
|
34
94
|
when Array
|
35
95
|
!expr.empty? or raise Error, "Empty array"
|
36
|
-
value.is_a?(Array) && value.all? { |elem| expr.any? { |e|
|
96
|
+
value.is_a?(Array) && value.all? { |elem| expr.any? { |e| Constrain.constrain?(elem, e) } }
|
37
97
|
when Hash
|
38
98
|
value.is_a?(Hash) && value.all? { |key, value|
|
39
99
|
expr.any? { |key_expr, value_expr|
|
40
100
|
[[key, key_expr], [value, value_expr]].all? { |value, expr|
|
41
101
|
if expr.is_a?(Array) && (expr.size > 1 || expr.first.is_a?(Array))
|
42
|
-
expr.any? { |e|
|
102
|
+
expr.any? { |e| Constrain.do_constrain?(value, e) }
|
43
103
|
else
|
44
|
-
|
104
|
+
Constrain.constrain?(value, expr)
|
45
105
|
end
|
46
106
|
}
|
47
107
|
}
|
@@ -65,7 +125,7 @@ module Constrain
|
|
65
125
|
#
|
66
126
|
def self.fmt_expr(expr)
|
67
127
|
case expr
|
68
|
-
when Class; expr.to_s
|
128
|
+
when Class, Module; expr.to_s
|
69
129
|
when Array; "[" + expr.map { |expr| fmt_expr(expr) }.join(", ") + "]"
|
70
130
|
when Hash; "{" + expr.map { |k,v| "#{fmt_expr(k)} => #{fmt_expr(v)}" }.join(", ") + "}"
|
71
131
|
when Proc; "Proc@#{expr.source_location.first}:#{expr.source_location.last}"
|
data/lib/constrain/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: constrain
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Claus Rasmussen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-05-
|
11
|
+
date: 2021-05-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: simplecov
|
@@ -52,10 +52,10 @@ files:
|
|
52
52
|
- constrain.gemspec
|
53
53
|
- lib/constrain.rb
|
54
54
|
- lib/constrain/version.rb
|
55
|
-
homepage:
|
55
|
+
homepage: https://github.com/clrgit/constrain/
|
56
56
|
licenses: []
|
57
57
|
metadata:
|
58
|
-
homepage_uri:
|
58
|
+
homepage_uri: https://github.com/clrgit/constrain/
|
59
59
|
post_install_message:
|
60
60
|
rdoc_options: []
|
61
61
|
require_paths:
|