taipo 1.1.1 → 1.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 +22 -14
- data/lib/taipo.rb +17 -3
- data/lib/taipo/parser.rb +67 -20
- data/lib/taipo/parser/validater.rb +31 -8
- data/lib/taipo/type_element.rb +9 -6
- data/lib/taipo/type_element/constraint.rb +9 -1
- data/lib/taipo/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4be2ae174cd1d29fb2d7e4088ca69c9da0aecfb3
|
4
|
+
data.tar.gz: ac87cf9977ef502fe802459da7ea0855587fecc2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bafc5ce5e262afe6a17ca0bb06476ba4f2120d337de2e44b9bef5d0814ce556466d70a0efbe99c9df7095e0588cba45db874328d1d0dab73a03e140887f5a85c
|
7
|
+
data.tar.gz: b67fcb77105810336e8616b83a188115ddcde6ecc202221960a5c7e299be3a61a49e7b03e85a22cd998301a15c6cf456c7f0ba374b2986f2c2d2744db5db77d3
|
data/README.md
CHANGED
@@ -12,9 +12,9 @@ Taipo is a simple library for checking the types of variables.
|
|
12
12
|
|
13
13
|
## Overview
|
14
14
|
|
15
|
-
When we deal with variables in our code, we have certain expectations about what those variables can and can
|
15
|
+
When we deal with variables in our code, we have certain expectations about what those variables can and can't do.
|
16
16
|
|
17
|
-
Taipo provides a simple way to make those expectations explicit. If an expectation isn
|
17
|
+
Taipo provides a simple way to make those expectations explicit. If an expectation isn't met, Taipo can either raise an exception or return the problematic variables for us to handle.
|
18
18
|
|
19
19
|
## Installation
|
20
20
|
|
@@ -27,23 +27,23 @@ Taipo provides two methods in the `Taipo::Check` module that we can mix into our
|
|
27
27
|
### #check
|
28
28
|
|
29
29
|
```ruby
|
30
|
-
require
|
30
|
+
require 'taipo'
|
31
31
|
|
32
32
|
class Foo
|
33
33
|
include Taipo::Check
|
34
34
|
|
35
35
|
def double(val)
|
36
|
-
check types, val:
|
36
|
+
check types, val: 'Integer'
|
37
37
|
val * 2
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
41
|
foo = Foo.new
|
42
42
|
foo.double 5 #=> 10
|
43
|
-
foo.double
|
43
|
+
foo.double 'Oops' #=> Taipo::TypeError
|
44
44
|
```
|
45
45
|
|
46
|
-
The method `#check` will raise an exception as soon as one of its arguments doesn
|
46
|
+
The method `#check` will raise an exception as soon as one of its arguments doesn't match its type definition.
|
47
47
|
|
48
48
|
[More information about `#check`][rdc] is available in the documentation.
|
49
49
|
|
@@ -52,24 +52,24 @@ The method `#check` will raise an exception as soon as one of its arguments does
|
|
52
52
|
### #review
|
53
53
|
|
54
54
|
```ruby
|
55
|
-
require
|
55
|
+
require 'taipo'
|
56
56
|
|
57
57
|
class Foo
|
58
58
|
include Taipo::Check
|
59
59
|
|
60
60
|
def add(x, y)
|
61
|
-
errors = review types, x:
|
61
|
+
errors = review types, x: 'Integer', y: 'Integer'
|
62
62
|
if errors.empty?
|
63
63
|
x + y
|
64
64
|
else
|
65
|
-
|
65
|
+
'Oops'
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
70
|
foo = Foo.new
|
71
71
|
foo.add 4, 5 #=> 9
|
72
|
-
foo.add 2,
|
72
|
+
foo.add 2, 'OK' #=> 'Oops'
|
73
73
|
```
|
74
74
|
|
75
75
|
The method `#review` will put the invalid arguments into an array and return that to the user. If there are no errors, the array is empty.
|
@@ -88,7 +88,7 @@ The information in this README is only meant as an introduction. [More informati
|
|
88
88
|
|
89
89
|
### Names
|
90
90
|
|
91
|
-
The simplest case is to write the name of a class. For example,
|
91
|
+
The simplest case is to write the name of a class. For example, `'String'`. Inherited class names can also be used.
|
92
92
|
|
93
93
|
```ruby
|
94
94
|
check types, a: 'String', b: 'Numeric'
|
@@ -126,19 +126,27 @@ Type definitions can be combined to form sum types. Sum types consist of two or
|
|
126
126
|
check types, a: 'String|Float', b: 'Boolean|Array<String|Hash<Symbol,Point>|Array<String>>', c: 'Integer(max: 100)|Float(max: 100)'
|
127
127
|
```
|
128
128
|
|
129
|
+
#### Enums
|
130
|
+
|
131
|
+
It's possible to approximate an enum by writing a sum type consisting of Symbols.
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
check types, a: ':foo|:bar', b: ':one|:two|:three'
|
135
|
+
```
|
136
|
+
|
129
137
|
## Requirements
|
130
138
|
|
131
139
|
Taipo has been tested with Ruby version 2.4.2.
|
132
140
|
|
133
141
|
## Bugs
|
134
142
|
|
135
|
-
Found a bug? I
|
143
|
+
Found a bug? I'd love to know about it. The best way is to report them in the [Issues section][ghi] on GitHub.
|
136
144
|
|
137
145
|
[ghi]: https://github.com/pyrmont/taipo/issues
|
138
146
|
|
139
147
|
## Contributing
|
140
148
|
|
141
|
-
If you
|
149
|
+
If you're interested in contributing to Taipo, feel free to fork and submit a pull request.
|
142
150
|
|
143
151
|
## Versioning
|
144
152
|
|
@@ -159,4 +167,4 @@ Taipo began as, and remains primarily, an exercise to improve my programming ski
|
|
159
167
|
|
160
168
|
Taipo is released into the public domain. See [LICENSE.md][lc] for more details.
|
161
169
|
|
162
|
-
[lc]: https://github.com/pyrmont/taipo/blob/master/LICENSE.md
|
170
|
+
[lc]: https://github.com/pyrmont/taipo/blob/master/LICENSE.md
|
data/lib/taipo.rb
CHANGED
@@ -70,7 +70,7 @@ module Taipo
|
|
70
70
|
# @note All this does is check whether the given string begins with a hash
|
71
71
|
# symbol.
|
72
72
|
#
|
73
|
-
# @param str [String] the
|
73
|
+
# @param str [String] the string to check
|
74
74
|
#
|
75
75
|
# @return [Boolean] the result
|
76
76
|
#
|
@@ -85,7 +85,7 @@ module Taipo
|
|
85
85
|
# @note This assume that each element returned by Enumerator#each has the same
|
86
86
|
# number of components.
|
87
87
|
#
|
88
|
-
# @param
|
88
|
+
# @param obj [Object] the object
|
89
89
|
#
|
90
90
|
# @return [String] a type definition of the object
|
91
91
|
#
|
@@ -123,9 +123,23 @@ module Taipo
|
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
126
|
+
# Check if a string is the name of a symbol
|
127
|
+
#
|
128
|
+
# @note All this does is check whether the given string begins with a colon.
|
129
|
+
#
|
130
|
+
# @param str [String] the string to check
|
131
|
+
#
|
132
|
+
# @return [Boolean] the result
|
133
|
+
#
|
134
|
+
# @since 1.2.0
|
135
|
+
# @api private
|
136
|
+
def self.symbol?(str)
|
137
|
+
str[0] == ':'
|
138
|
+
end
|
139
|
+
|
126
140
|
# Return a String representation of an array of {Taipo::TypeElement}
|
127
141
|
#
|
128
|
-
# @param
|
142
|
+
# @param types [Array<Taipo::TypeElement>] the array of {Taipo::TypeElement}
|
129
143
|
#
|
130
144
|
# @return [String] the String representation
|
131
145
|
#
|
data/lib/taipo/parser.rb
CHANGED
@@ -22,6 +22,7 @@ module Taipo
|
|
22
22
|
Taipo::Parser::Validater.validate str
|
23
23
|
|
24
24
|
content = ''
|
25
|
+
previous = ''
|
25
26
|
is_fallthrough = false
|
26
27
|
fallthroughs = [ '/', '"' ]
|
27
28
|
closing_symbol = ''
|
@@ -34,12 +35,13 @@ module Taipo
|
|
34
35
|
|
35
36
|
case c
|
36
37
|
when '|'
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
unless attached? previous # content.empty? # Previous character must have been '>' or ')'.
|
39
|
+
el = Taipo::TypeElement.new name: content
|
40
|
+
content = ''
|
41
|
+
elements = stack.pop
|
42
|
+
elements.push el
|
43
|
+
stack.push elements
|
44
|
+
end
|
43
45
|
when '<'
|
44
46
|
el = Taipo::TypeElement.new name: content
|
45
47
|
content = ''
|
@@ -49,7 +51,7 @@ module Taipo
|
|
49
51
|
first_component = Array.new
|
50
52
|
stack.push first_component
|
51
53
|
when '>'
|
52
|
-
if content.empty? # Previous character must have been '>' or ')'.
|
54
|
+
if attached? previous # content.empty? # Previous character must have been '>' or ')'.
|
53
55
|
last_component = stack.pop
|
54
56
|
else
|
55
57
|
el = Taipo::TypeElement.new name: content.strip
|
@@ -65,7 +67,10 @@ module Taipo
|
|
65
67
|
elements.push parent_el
|
66
68
|
stack.push elements
|
67
69
|
when '('
|
68
|
-
if
|
70
|
+
if unattached? previous
|
71
|
+
el = Taipo::TypeElement.new name: 'Object'
|
72
|
+
content = ''
|
73
|
+
elsif attached_collection? previous # Previous character must have been '>'.
|
69
74
|
elements = stack.pop
|
70
75
|
el = elements.pop
|
71
76
|
stack.push elements
|
@@ -77,8 +82,8 @@ module Taipo
|
|
77
82
|
cst_collection = Array.new
|
78
83
|
stack.push cst_collection
|
79
84
|
when '#'
|
80
|
-
if
|
81
|
-
content =
|
85
|
+
if unattached? previous
|
86
|
+
content = '#'
|
82
87
|
else
|
83
88
|
cst = Taipo::TypeElement::Constraint.new
|
84
89
|
content = ''
|
@@ -87,11 +92,18 @@ module Taipo
|
|
87
92
|
stack.push cst_collection
|
88
93
|
end
|
89
94
|
when ':'
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
+
if unattached? previous
|
96
|
+
content = ':'
|
97
|
+
elsif content.strip.empty?
|
98
|
+
content = ':'
|
99
|
+
else
|
100
|
+
content = content
|
101
|
+
cst = Taipo::TypeElement::Constraint.new name: content.strip
|
102
|
+
content = ''
|
103
|
+
cst_collection = stack.pop
|
104
|
+
cst_collection.push cst
|
105
|
+
stack.push cst_collection
|
106
|
+
end
|
95
107
|
when ',' # We could be inside a collection or a set of constraints
|
96
108
|
if inside_collection? stack
|
97
109
|
previous_component = stack.pop
|
@@ -132,6 +144,7 @@ module Taipo
|
|
132
144
|
end
|
133
145
|
content = content + c
|
134
146
|
end
|
147
|
+
previous = c
|
135
148
|
end
|
136
149
|
|
137
150
|
unless content.empty?
|
@@ -143,6 +156,37 @@ module Taipo
|
|
143
156
|
|
144
157
|
stack.pop
|
145
158
|
end
|
159
|
+
|
160
|
+
# Check whether the current element is 'attached' to anything
|
161
|
+
#
|
162
|
+
# This check is performed by checking whether +char+ is the final character
|
163
|
+
# in a collection or constraint.
|
164
|
+
#
|
165
|
+
# @param char [String] the character to use in the test
|
166
|
+
#
|
167
|
+
# @return [Boolean] the result
|
168
|
+
#
|
169
|
+
# @since 1.2.0
|
170
|
+
# @api private
|
171
|
+
def self.attached?(char)
|
172
|
+
char == '>' || char == ')'
|
173
|
+
end
|
174
|
+
|
175
|
+
# Check whether the current element is 'attached' to a collection
|
176
|
+
#
|
177
|
+
# Like {self.attached?}, this check is performed by checking +char+. In
|
178
|
+
# this case, the check is whether +char+ is the final character in a
|
179
|
+
# collection.
|
180
|
+
#
|
181
|
+
# @param char [String] the character to use in the test
|
182
|
+
#
|
183
|
+
# @return [Boolean] the result
|
184
|
+
#
|
185
|
+
# @since 1.2.0
|
186
|
+
# @api private
|
187
|
+
def self.attached_collection?(char)
|
188
|
+
char == '>'
|
189
|
+
end
|
146
190
|
|
147
191
|
# Check if the parser is inside a collection
|
148
192
|
#
|
@@ -156,16 +200,19 @@ module Taipo
|
|
156
200
|
stack[-2]&.class == Taipo::TypeElement::ChildType
|
157
201
|
end
|
158
202
|
|
159
|
-
# Check
|
203
|
+
# Check whether the current element is 'unattached' to anything
|
160
204
|
#
|
161
|
-
#
|
205
|
+
# This check checks whether +char+ represents the beginning of a discrete
|
206
|
+
# type definition.
|
207
|
+
#
|
208
|
+
# @param char [String] the character to use in the test
|
162
209
|
#
|
163
210
|
# @return [Boolean] the result
|
164
211
|
#
|
165
|
-
# @since 1.
|
212
|
+
# @since 1.2.0
|
166
213
|
# @api private
|
167
|
-
def self.
|
168
|
-
|
214
|
+
def self.unattached?(char)
|
215
|
+
char.empty? || char == '|' || char == '<'
|
169
216
|
end
|
170
217
|
end
|
171
218
|
end
|
@@ -103,6 +103,15 @@ module Taipo
|
|
103
103
|
# The sum comprises two or more type definitions, each separated by a bar
|
104
104
|
# (ie. +|+).
|
105
105
|
#
|
106
|
+
# ==== Enums
|
107
|
+
#
|
108
|
+
# ':foo|:bar', ':one|:two|:three'
|
109
|
+
#
|
110
|
+
# It's possible to approximate the enum idiom available in many languages
|
111
|
+
# by creating a sum type consisting of Symbols. As a convenience, Taipo
|
112
|
+
# parses these values as constraints on the Object class. In other words,
|
113
|
+
# +':foo|:bar'+ is really +'Object(val: :foo)|Object(val: :bar)'+.
|
114
|
+
#
|
106
115
|
# @since 1.0.0
|
107
116
|
module Validater
|
108
117
|
|
@@ -132,7 +141,7 @@ module Taipo
|
|
132
141
|
chars = str.chars
|
133
142
|
str_length = chars.size
|
134
143
|
|
135
|
-
state.prohibit_all except: [ :hsh, :oth ]
|
144
|
+
state.prohibit_all except: [ :lpr, :hsh, :cln, :oth ]
|
136
145
|
|
137
146
|
while (i < str_length)
|
138
147
|
msg = "The string '#{str}' has an error here: #{str[0, i+1]}"
|
@@ -142,11 +151,11 @@ module Taipo
|
|
142
151
|
raise Taipo::SyntaxError, msg unless conditions.all?
|
143
152
|
state.enable :lab
|
144
153
|
state.enable :lpr
|
145
|
-
state.prohibit_all except: [ :hsh, :oth ]
|
154
|
+
state.prohibit_all except: [ :lpr, :hsh, :cln, :oth ]
|
146
155
|
when '<' # lab
|
147
156
|
conditions = [ state.allowed?(:lab) ]
|
148
157
|
raise Taipo::SyntaxError, msg unless conditions.all?
|
149
|
-
state.prohibit_all except: [ :hsh, :oth ]
|
158
|
+
state.prohibit_all except: [ :lpr, :hsh, :cln, :oth ]
|
150
159
|
state.increment :angle
|
151
160
|
when '>' # rab
|
152
161
|
conditions = [ state.allowed?(:rab), state.inside?(:angle) ]
|
@@ -176,10 +185,20 @@ module Taipo
|
|
176
185
|
state.decrement :const
|
177
186
|
end
|
178
187
|
when ':' # cln
|
179
|
-
conditions = [ state.allowed?(:cln)
|
188
|
+
conditions = [ state.allowed?(:cln) ]
|
180
189
|
raise Taipo::SyntaxError, msg unless conditions.all?
|
181
|
-
state.
|
182
|
-
|
190
|
+
if state.outside? :paren
|
191
|
+
state.disable :lab
|
192
|
+
state.disable :lpr
|
193
|
+
state.prohibit_all except: [ :oth ]
|
194
|
+
else
|
195
|
+
if state.count(:const) == 0 # This is a symbol.
|
196
|
+
state.prohibit_all except: [ :qut, :oth ]
|
197
|
+
else
|
198
|
+
state.prohibit_all except: [ :cln, :sls, :qut, :spc, :oth ]
|
199
|
+
state.decrement :const
|
200
|
+
end
|
201
|
+
end
|
183
202
|
when '/' #sls
|
184
203
|
conditions = [ state.allowed?(:sls), state.inside?(:paren),
|
185
204
|
state.outside?(:const) ]
|
@@ -201,11 +220,15 @@ module Taipo
|
|
201
220
|
when ' ' # spc
|
202
221
|
conditions = [ state.allowed?(:spc) ]
|
203
222
|
raise Taipo::SyntaxError, msg unless conditions.all?
|
204
|
-
state.prohibit_all except: [ :hsh, :sls, :qut, :oth ]
|
223
|
+
state.prohibit_all except: [ :hsh, :cln, :sls, :qut, :oth ]
|
205
224
|
else # oth
|
206
225
|
conditions = [ state.allowed?(:oth) ]
|
207
226
|
raise Taipo::SyntaxError, msg unless conditions.all?
|
208
|
-
state.
|
227
|
+
if state.inside? :paren
|
228
|
+
state.allow_all except: [ :hsh, :spc ]
|
229
|
+
else
|
230
|
+
state.allow_all except: [ :hsh, :cln, :spc ]
|
231
|
+
end
|
209
232
|
end
|
210
233
|
i += 1
|
211
234
|
end
|
data/lib/taipo/type_element.rb
CHANGED
@@ -40,7 +40,7 @@ module Taipo
|
|
40
40
|
# wrong type
|
41
41
|
# @raise [::ArgumentError] if +name+, +child_type+ or +constraints+ was
|
42
42
|
# blank or empty, or +child_type+ or +constraints+ was non-nil and this
|
43
|
-
# is a duck type (ie. a method the type responds to)
|
43
|
+
# is a duck type (ie. a method the type responds to) or a symbol
|
44
44
|
#
|
45
45
|
# @since 1.0.0
|
46
46
|
# @api private
|
@@ -61,15 +61,19 @@ module Taipo
|
|
61
61
|
msg = 'Argument constraints was empty.'
|
62
62
|
raise ::ArgumentError, msg if constraints&.empty?
|
63
63
|
|
64
|
-
if Taipo.instance_method? name
|
64
|
+
if Taipo.instance_method?(name) || Taipo.symbol?(name)
|
65
65
|
msg = 'Argument child_type should have been nil.'
|
66
66
|
raise ::ArgumentError, msg unless child_type.nil?
|
67
67
|
msg = 'Argument constraints should have been nil.'
|
68
68
|
raise ::ArgumentError, msg unless constraints.nil?
|
69
69
|
|
70
|
-
constraints =
|
71
|
-
|
72
|
-
|
70
|
+
constraints = if Taipo.instance_method? name
|
71
|
+
[Taipo::TypeElement::Constraint.new(name: nil,
|
72
|
+
value: name[1..-1])]
|
73
|
+
elsif Taipo.symbol? name
|
74
|
+
[Taipo::TypeElement::Constraint.new(name: 'val',
|
75
|
+
value: name)]
|
76
|
+
end
|
73
77
|
name = 'Object'
|
74
78
|
end
|
75
79
|
@name = name
|
@@ -214,6 +218,5 @@ module Taipo
|
|
214
218
|
end
|
215
219
|
name_str + child_type_str + constraints_str
|
216
220
|
end
|
217
|
-
|
218
221
|
end
|
219
222
|
end
|
@@ -44,7 +44,13 @@ module Taipo
|
|
44
44
|
msg = 'Argument name was an empty string.'
|
45
45
|
raise ::ArgumentError, msg if name&.empty?
|
46
46
|
|
47
|
-
@name =
|
47
|
+
@name = if name.nil?
|
48
|
+
Constraint::METHOD
|
49
|
+
elsif name == ':'
|
50
|
+
'val'
|
51
|
+
else
|
52
|
+
name
|
53
|
+
end
|
48
54
|
@value = self.parse_value value
|
49
55
|
end
|
50
56
|
|
@@ -79,6 +85,8 @@ module Taipo
|
|
79
85
|
when 'val'
|
80
86
|
if @value[0] == '"' && @value[-1] == '"'
|
81
87
|
arg.to_s == @value.slice(1..-2)
|
88
|
+
elsif arg.is_a? Symbol
|
89
|
+
":" + arg.to_s == @value
|
82
90
|
else
|
83
91
|
arg.to_s == @value
|
84
92
|
end
|
data/lib/taipo/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: taipo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Camilleri
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-01-
|
11
|
+
date: 2018-01-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|