constrain 0.1.0 → 0.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b37ca58d51de33d6c5d6810c5b8e4b77e0d1d19355d4e381241b923f44918817
4
- data.tar.gz: c64f19d94bb2e4dae6e4fbaf61a74d1fb6caee56f58a9006915a5749ee35b906
3
+ metadata.gz: ef3357b49faba9f9d3bfe5dbfb3d27b70d1493456b4dea29afb96c811b4eff8b
4
+ data.tar.gz: 590bb604f5566f76d4473f1f9191ef2ca21bfdcf9a904b92a0b6fa0388263c12
5
5
  SHA512:
6
- metadata.gz: bd6114ec7c7b5516c409ef41beae62d979c582f8f53c149634a1fc8ed8b4c595a7b3fd2d12d07a08fd2b7b587515adef890e6f68cdf6dd897d6ef79e8bad9c50
7
- data.tar.gz: 9eec4bc8dac6976bbe2eb22e45bcf57589b5f1e04092596e1cb99f658c0b9144a68182eab73e0e5ae3eb9be9bb9434184f8c71122aaddba284d15f90cac4600e
6
+ metadata.gz: ec0d44e4cb0a7b4c3c8291bc10d821000769d36fe30c44aed85c30b3993076796169f7efa394cd47b0694744b47e6354e7413fb1c8b3a814d3a42d91cdeff04d
7
+ data.tar.gz: 9b60ecec9cb4edd2dc507b10835df150094b9fc3a939735be6935a93df4d5f049bbf4fe963eebb7ebf07a4faf21fa13cf3908a4ea44ac93178f97ef9b29d2612
data/README.md CHANGED
@@ -4,6 +4,23 @@
4
4
  typically used to check the type of method parameters and is an alternative to
5
5
  using Ruby-3 .rbs files but with a different syntax and only dynamic checks
6
6
 
7
+ ```ruby
8
+ include Constrain
9
+
10
+ # f takes a String and an array of Integer objects and raises otherwise
11
+ def f(a, b)
12
+ constrain a, String
13
+ constrain b, [Integer]
14
+ ...
15
+ end
16
+
17
+ f("Hello", [1, 2]) # Doesn't raise
18
+ f("Hello", "world") # Boom
19
+ ```
20
+
21
+ It is intended to be an aid in development only and to be deactivated in
22
+ production (TODO: Make it possible to deactivate)
23
+
7
24
  Constrain works with ruby-2 (and maybe ruby-3)
8
25
 
9
26
  ## Installation
@@ -24,62 +41,139 @@ Or install it yourself as:
24
41
 
25
42
  ## Usage
26
43
 
27
- You'll typically include the Constrain module and use the #constrain method to chech values:
44
+ You will typically include Constrain globally to have #constrain available everywhere
45
+
46
+ ```ruby
47
+ require 'constrain'
48
+
49
+ # Include globally to make #constrain available everywhere
50
+ include Constrain
51
+
52
+ def f(a, b, c)
53
+ constrain a, Integer # An integer
54
+ constrain b, [Symbol, String] => Integer # Hash with String or Symbol keys
55
+ constrain c, [String], NilClass # Array of strings or nil
56
+ ...
57
+ end
58
+ ```
59
+
60
+ The alternative is to include the constrain Module in a common root class to
61
+ have it available in all child class
62
+
63
+ The #constrain method has the following signature
64
+
65
+ ```ruby
66
+ constrain(value, *class-expressions, message = nil)
67
+ ```
28
68
 
29
- include Constrain
69
+ It checks that the value matches at least one of the class-expressions
70
+ and raise a Constrain::TypeError if not. The error message can be customized by
71
+ added the message argument. #constrain also raise a Constrain::Error exception
72
+ if there is an error in the class expression. It is typically used to
73
+ type-check parameters in methods
30
74
 
31
- # f takes a String and an array of Integer objects
32
- def f(a, b)
33
- constrain a, String
34
- constrain b, [Integer]
35
- end
75
+ Constrain also defines a #check class method with the signature
36
76
 
37
- The constrain instance method raises a Constrain::TypeError if the value
38
- doesn't match the class expression. Constrain also defines the Constrain::check
39
- class method that returns true/false depending on if the value match the
40
- expression. Both methods raise a Constrain::Error if the expression is invalid
77
+ ```ruby
78
+ Constrain.check(value, *class-expression) -> true or false
79
+ ```
41
80
 
42
- ### Class Expressions
81
+ It matches value against the class expressions like #constrain but returns true
82
+ or false as result
83
+
84
+ ## Class Expressions
43
85
 
44
86
  Constrain#constrain and Constrain::check use class expressions composed of
45
- Class objects, Proc objects, or arrays and hashes of class objects. Class
87
+ Class objects, Proc objects, or arrays and hashes of class expressions. Class
46
88
  objects match if the value is an instance of the class:
47
89
 
48
- constrain 42, Integer # Success
49
- constrain 42, String # Failure
90
+ ```ruby
91
+ constrain 42, Integer # Success
92
+ constrain 42, String # Failure
93
+ ```
94
+
95
+ More than one class expression is allowed. It matches if at least one of the expressions match:
96
+
97
+ ```ruby
98
+ constrain "str", Symbol, String # Success
99
+ constrain :sym, Symbol, String # Success
100
+ constrain 42, Symbol, String # Failure
101
+ ```
50
102
 
51
- Note that NilClass and TrueClass and FalseClass are valid arguments and allows
52
- you to do value comparison for those types:
103
+ #### nil, true and false
53
104
 
54
- constrain nil, Integer # Failure
55
- constrain nil, Integer, NilClass # Success
105
+ NilClass is a valid argument and can be used to allow nil values:
106
+
107
+ ```ruby
108
+ constrain nil, Integer # Failure
109
+ constrain nil, Integer, NilClass # Success
110
+ ```
111
+
112
+ Boolean values are a special case since ruby doesn't have a boolean type use a
113
+ list to match for a boolean argument:
114
+
115
+ ```ruby
116
+ constrain true, TrueClass, FalseClass # Success
117
+ constrain false, TrueClass, FalseClass # Success
118
+ constrain nil, TrueClass, FalseClass # Failure
119
+ ```
120
+
121
+ #### Proc objects
56
122
 
57
123
  Proc objects are called with the value as argument and should return truish or falsy:
58
124
 
59
- proc = lambda { |value| value > 1 }
60
- constrain 42, proc # Success
61
- constrain 0, proc # Failure
125
+ ```ruby
126
+ constrain 42, lambda { |value| value > 1 } # Success
127
+ constrain 0, lambda { |value| value > 1 } # Failure
128
+ ```
129
+
130
+ Note that it is not possible to first match against a class expression and then use the proc object. You will either have to check for the type too in the proc object or make two calls to #constrain:
131
+
132
+ ```ruby
133
+ constrain 0, Integer # Success
134
+ constrain 0, lambda { |value| value > 1 } # Failure
135
+ ```
62
136
 
63
- Proc objects can check every aspect of an object and but you should not overuse
64
- them as `Constrain` is throught of as a poor-man's type checker. More elaborate
65
- constraints should be checked explicitly
137
+ Proc objects can check every aspect of an object but you should not overuse it
138
+ because as checks becomes more complex they tend to include business logic that
139
+ should be kept in the production code. Constrain is only thouhgt of as a tool
140
+ to catch developer errors - not errors that stem from corrupted data
141
+
142
+ #### Arrays
66
143
 
67
144
  Arrays match if the value is an Array and all its element match the given class expression:
68
145
 
69
- constrain [42], [Integer] # Success
70
- constrain [42], [String] # Failure
146
+ ```ruby
147
+ constrain [42], [Integer] # Success
148
+ constrain [42], [String] # Failure
149
+ ```
71
150
 
72
151
  Arrays can be nested
73
152
 
74
- constrain [[42]], [[Integer]]
153
+ ```ruby
154
+ constrain [[42]], [[Integer]] # Success
155
+ constrain [42], [[Integer]] # Failure
156
+ ```
157
+
158
+ More than one element class is allowed
159
+
160
+ ```ruby
161
+ constrain ["str"], [String, Symbol] # Success
162
+ constrain [:sym], [String, Symbol] # Success
163
+ constrain [42], [String, Symbol] # Failure
164
+ ```
165
+
166
+ Note that `[` ... `]` is treated specially in hashes
75
167
 
76
- Note that arrays are treated specially in hashes
168
+ #### Hashes
77
169
 
78
170
  Hashes match if value is a hash and every key/value pair match one of the given
79
171
  key-class/value-class expressions:
80
172
 
81
- constrain({"str" => 42}, String => Integer) # Success
82
- constrain({"str" => 42}, String => String) # Failure
173
+ ```ruby
174
+ constrain({"str" => 42}, String => Integer) # Success
175
+ constrain({"str" => 42}, String => String) # Failure
176
+ ```
83
177
 
84
178
  Note that the parenthesis are needed because otherwise the Ruby parser would
85
179
  interpret the hash as block argument to #constrain
@@ -89,13 +183,17 @@ expression match. List are annotated as an array but contains more than one
89
183
  element so that `[String, Symbol]` matches either a String or a Symbol value
90
184
  while `[String]` matches an array of String objects:
91
185
 
92
- constrain({ sym: 42 }, [Symbol, String] => Integer) # Success
93
- constrain({ [sym] => 42 }, [Symbol, String] => Integer) # Failure
186
+ ```ruby
187
+ constrain({ sym: 42 }, [Symbol, String] => Integer) # Success
188
+ constrain({ [sym] => 42 }, [Symbol, String] => Integer) # Failure
189
+ ```
94
190
 
95
191
  To specify an array of Symbol or String objects in hash keys or values, make
96
192
  sure the list expression is enclosed in an array:
97
193
 
98
- constrain({ [sym] => 42 }, [[Symbol, String]] => Integer) # Success
194
+ ```ruby
195
+ constrain({ [sym] => 42 }, [[Symbol, String]] => Integer) # Success
196
+ ```
99
197
 
100
198
  ## Contributing
101
199
 
data/TODO ADDED
@@ -0,0 +1,5 @@
1
+
2
+ o Class | Class syntax
3
+ o attr_reader :variable, ClassExpr
4
+
5
+ + constrain value, class-expr, "Error message"
data/lib/constrain.rb CHANGED
@@ -6,8 +6,8 @@ module Constrain
6
6
 
7
7
  # Raised if types doesn't match a class expression
8
8
  class TypeError < Error
9
- def initialize(value, exprs)
10
- super "Expected #{value.inspect} to match #{Constrain.fmt_exprs(exprs)}"
9
+ def initialize(value, exprs, msg = nil)
10
+ super msg || "Expected #{value.inspect} to match #{Constrain.fmt_exprs(exprs)}"
11
11
  end
12
12
  end
13
13
 
@@ -15,10 +15,14 @@ module Constrain
15
15
  # Constrain::Error if the expression is invalid and a Constrain::TypeError if
16
16
  # the value doesn't match
17
17
  def constrain(value, *exprs)
18
- return if exprs.any? { |expr| Constrain.check(value, expr) }
19
- error = TypeError.new(value, exprs)
20
- error.set_backtrace(caller[1..-1])
21
- raise error
18
+ msg = exprs.pop if exprs.last.is_a?(String)
19
+ begin
20
+ !exprs.empty? or raise Error, "Empty class expression"
21
+ exprs.any? { |expr| Constrain.check(value, expr) } or raise TypeError.new(value, exprs, msg)
22
+ rescue Error => ex
23
+ ex.set_backtrace(caller[1..-1])
24
+ raise
25
+ end
22
26
  end
23
27
 
24
28
  # Return true if the value matches the class expression. Raises a
@@ -1,3 +1,3 @@
1
1
  module Constrain
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
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.1.0
4
+ version: 0.1.1
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-15 00:00:00.000000000 Z
11
+ date: 2021-05-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: simplecov
@@ -46,6 +46,7 @@ files:
46
46
  - Gemfile
47
47
  - README.md
48
48
  - Rakefile
49
+ - TODO
49
50
  - bin/console
50
51
  - bin/setup
51
52
  - constrain.gemspec