speculation 0.1.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 +7 -0
- data/.gitignore +9 -0
- data/.rubocop.yml +87 -0
- data/.travis.yml +16 -0
- data/.yardopts +3 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +116 -0
- data/Rakefile +29 -0
- data/bin/bundler +17 -0
- data/bin/byebug +17 -0
- data/bin/coderay +17 -0
- data/bin/console +70 -0
- data/bin/cucumber-queue +17 -0
- data/bin/minitest-queue +17 -0
- data/bin/pry +17 -0
- data/bin/rake +17 -0
- data/bin/rspec-queue +17 -0
- data/bin/rubocop +17 -0
- data/bin/ruby-parse +17 -0
- data/bin/ruby-rewrite +17 -0
- data/bin/setup +8 -0
- data/bin/testunit-queue +17 -0
- data/bin/yard +17 -0
- data/bin/yardoc +17 -0
- data/bin/yri +17 -0
- data/lib/speculation/conj.rb +32 -0
- data/lib/speculation/error.rb +17 -0
- data/lib/speculation/gen.rb +106 -0
- data/lib/speculation/identifier.rb +47 -0
- data/lib/speculation/namespaced_symbols.rb +28 -0
- data/lib/speculation/pmap.rb +30 -0
- data/lib/speculation/spec_impl/and_spec.rb +39 -0
- data/lib/speculation/spec_impl/every_spec.rb +176 -0
- data/lib/speculation/spec_impl/f_spec.rb +121 -0
- data/lib/speculation/spec_impl/hash_spec.rb +215 -0
- data/lib/speculation/spec_impl/merge_spec.rb +40 -0
- data/lib/speculation/spec_impl/nilable_spec.rb +36 -0
- data/lib/speculation/spec_impl/or_spec.rb +62 -0
- data/lib/speculation/spec_impl/regex_spec.rb +35 -0
- data/lib/speculation/spec_impl/spec.rb +47 -0
- data/lib/speculation/spec_impl/tuple_spec.rb +67 -0
- data/lib/speculation/spec_impl.rb +36 -0
- data/lib/speculation/test.rb +553 -0
- data/lib/speculation/utils.rb +64 -0
- data/lib/speculation/utils_specs.rb +57 -0
- data/lib/speculation/version.rb +4 -0
- data/lib/speculation.rb +1308 -0
- data/speculation.gemspec +43 -0
- metadata +246 -0
@@ -0,0 +1,215 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Speculation
|
3
|
+
using Speculation::NamespacedSymbols.refine(self)
|
4
|
+
using Conj
|
5
|
+
|
6
|
+
# @private
|
7
|
+
class HashSpec < SpecImpl
|
8
|
+
S = Speculation
|
9
|
+
|
10
|
+
attr_reader :id
|
11
|
+
|
12
|
+
def initialize(req, opt, req_un, opt_un)
|
13
|
+
@id = SecureRandom.uuid
|
14
|
+
@req = req
|
15
|
+
@opt = opt
|
16
|
+
@req_un = req_un
|
17
|
+
@opt_un = opt_un
|
18
|
+
|
19
|
+
req_keys = req.flat_map(&method(:extract_keys))
|
20
|
+
req_un_specs = req_un.flat_map(&method(:extract_keys))
|
21
|
+
|
22
|
+
unless (req_keys + req_un_specs + opt + opt_un).all? { |s| s.is_a?(Symbol) && s.namespace }
|
23
|
+
raise "all keys must be namespaced Symbols"
|
24
|
+
end
|
25
|
+
|
26
|
+
req_specs = req_keys + req_un_specs
|
27
|
+
req_keys += req_un_specs.map(&method(:unqualify_key))
|
28
|
+
|
29
|
+
pred_exprs = [Utils.method(:hash?)]
|
30
|
+
pred_exprs.push(->(v) { parse_req(req, v, :itself.to_proc) == true }) if req.any?
|
31
|
+
pred_exprs.push(->(v) { parse_req(req_un, v, method(:unqualify_key)) == true }) if req_un.any?
|
32
|
+
|
33
|
+
@req_keys = req_keys
|
34
|
+
@req_specs = req_specs
|
35
|
+
@opt_keys = opt + opt_un.map(&method(:unqualify_key))
|
36
|
+
@opt_specs = opt + opt_un
|
37
|
+
@keys_pred = ->(v) { pred_exprs.all? { |p| p.call(v) } }
|
38
|
+
@key_to_spec_map = Hash[req_keys.concat(@opt_keys).zip(req_specs.concat(@opt_specs))]
|
39
|
+
end
|
40
|
+
|
41
|
+
def conform(value)
|
42
|
+
return :invalid.ns unless @keys_pred.call(value)
|
43
|
+
|
44
|
+
reg = S.registry
|
45
|
+
ret = value
|
46
|
+
|
47
|
+
value.each do |key, v|
|
48
|
+
spec_name = @key_to_spec_map.fetch(key, key)
|
49
|
+
spec = reg[spec_name]
|
50
|
+
|
51
|
+
next unless spec
|
52
|
+
|
53
|
+
conformed_value = S.conform(spec, v)
|
54
|
+
|
55
|
+
if S.invalid?(conformed_value)
|
56
|
+
return :invalid.ns
|
57
|
+
else
|
58
|
+
unless conformed_value.equal?(v)
|
59
|
+
ret = ret.merge(key => conformed_value)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
ret
|
65
|
+
end
|
66
|
+
|
67
|
+
def explain(path, via, inn, value)
|
68
|
+
unless Utils.hash?(value)
|
69
|
+
return [{ :path => path, :pred => :hash?, :val => value, :via => via, :in => inn }]
|
70
|
+
end
|
71
|
+
|
72
|
+
problems = []
|
73
|
+
|
74
|
+
if @req.any?
|
75
|
+
valid_or_failure = parse_req(@req, value, :itself.to_proc)
|
76
|
+
|
77
|
+
unless valid_or_failure == true
|
78
|
+
valid_or_failure.each do |failure_sexp|
|
79
|
+
pred = sexp_to_rb(failure_sexp)
|
80
|
+
problems << { :path => path, :pred => pred, :val => value, :via => via, :in => inn }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
if @req_un.any?
|
86
|
+
valid_or_failure = parse_req(@req_un, value, method(:unqualify_key))
|
87
|
+
|
88
|
+
unless valid_or_failure == true
|
89
|
+
valid_or_failure.each do |failure_sexp|
|
90
|
+
pred = sexp_to_rb(failure_sexp)
|
91
|
+
problems << { :path => path, :pred => pred, :val => value, :via => via, :in => inn }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
problems += value.flat_map { |(k, v)|
|
97
|
+
next unless S.registry.key?(@key_to_spec_map[k])
|
98
|
+
|
99
|
+
unless S.pvalid?(@key_to_spec_map.fetch(k), v)
|
100
|
+
S.explain1(@key_to_spec_map.fetch(k), path.conj(k), via, inn.conj(k), v)
|
101
|
+
end
|
102
|
+
}
|
103
|
+
|
104
|
+
problems.compact
|
105
|
+
end
|
106
|
+
|
107
|
+
def specize
|
108
|
+
self
|
109
|
+
end
|
110
|
+
|
111
|
+
def gen(overrides, path, rmap)
|
112
|
+
return @gen if @gen
|
113
|
+
|
114
|
+
rmap = S.inck(rmap, @id)
|
115
|
+
|
116
|
+
reqs = @req_keys.zip(@req_specs).
|
117
|
+
reduce({}) { |m, (k, s)|
|
118
|
+
m.merge(k => S.gensub(s, overrides, path.conj(k), rmap))
|
119
|
+
}
|
120
|
+
|
121
|
+
opts = @opt_keys.zip(@opt_specs).
|
122
|
+
reduce({}) { |m, (k, s)|
|
123
|
+
if S.recur_limit?(rmap, @id, path, k)
|
124
|
+
m
|
125
|
+
else
|
126
|
+
m.merge(k => Gen.delay { S.gensub(s, overrides, path.conj(k), rmap) })
|
127
|
+
end
|
128
|
+
}
|
129
|
+
|
130
|
+
->(rantly) do
|
131
|
+
count = rantly.range(0, opts.count)
|
132
|
+
opts = opts.to_a.shuffle.take(count).to_h
|
133
|
+
|
134
|
+
reqs.merge(opts).each_with_object({}) { |(k, spec_gen), h|
|
135
|
+
h[k] = spec_gen.call(rantly)
|
136
|
+
}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def sexp_to_rb(sexp, level = 0)
|
143
|
+
if sexp.is_a?(Array)
|
144
|
+
op, *keys = sexp
|
145
|
+
rb_string = String.new
|
146
|
+
|
147
|
+
rb_string << "(" unless level.zero?
|
148
|
+
|
149
|
+
keys.each_with_index do |key, i|
|
150
|
+
unless i.zero?
|
151
|
+
rb_string << " #{op.name} "
|
152
|
+
end
|
153
|
+
|
154
|
+
rb_string << sexp_to_rb(key, level + 1)
|
155
|
+
end
|
156
|
+
|
157
|
+
rb_string << ")" unless level.zero?
|
158
|
+
|
159
|
+
rb_string
|
160
|
+
else
|
161
|
+
":#{sexp}"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def extract_keys(symbol_or_arr)
|
166
|
+
if symbol_or_arr.is_a?(Array)
|
167
|
+
symbol_or_arr[1..-1].flat_map(&method(:extract_keys))
|
168
|
+
else
|
169
|
+
symbol_or_arr
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def unqualify_key(x)
|
174
|
+
x.name.to_sym
|
175
|
+
end
|
176
|
+
|
177
|
+
def parse_req(ks, v, f)
|
178
|
+
key, *ks = ks
|
179
|
+
|
180
|
+
ret = if key.is_a?(Array)
|
181
|
+
op, *kks = key
|
182
|
+
case op
|
183
|
+
when :or.ns
|
184
|
+
if kks.one? { |k| parse_req([k], v, f) == true }
|
185
|
+
true
|
186
|
+
else
|
187
|
+
[key]
|
188
|
+
end
|
189
|
+
when :and.ns
|
190
|
+
if kks.all? { |k| parse_req([k], v, f) == true }
|
191
|
+
true
|
192
|
+
else
|
193
|
+
[key]
|
194
|
+
end
|
195
|
+
else
|
196
|
+
raise "Expected or, and, got #{op}"
|
197
|
+
end
|
198
|
+
elsif v.key?(f.call(key))
|
199
|
+
true
|
200
|
+
else
|
201
|
+
[key]
|
202
|
+
end
|
203
|
+
|
204
|
+
if ks.any?
|
205
|
+
if ret == true
|
206
|
+
parse_req(ks, v, f)
|
207
|
+
else
|
208
|
+
ret + parse_req(ks, v, f)
|
209
|
+
end
|
210
|
+
else
|
211
|
+
ret
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Speculation
|
3
|
+
using NamespacedSymbols.refine(self)
|
4
|
+
|
5
|
+
# @private
|
6
|
+
class MergeSpec < SpecImpl
|
7
|
+
S = Speculation
|
8
|
+
|
9
|
+
def initialize(preds)
|
10
|
+
@preds = preds
|
11
|
+
end
|
12
|
+
|
13
|
+
def conform(x)
|
14
|
+
ms = @preds.map { |pred| S.dt(pred, x) }
|
15
|
+
|
16
|
+
if ms.any?(&S.method(:invalid?))
|
17
|
+
:invalid.ns(S)
|
18
|
+
else
|
19
|
+
ms.reduce(&:merge)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def explain(path, via, inn, x)
|
24
|
+
@preds.
|
25
|
+
flat_map { |pred| S.explain1(pred, path, via, inn, x) }.
|
26
|
+
compact
|
27
|
+
end
|
28
|
+
|
29
|
+
def gen(overrides, path, rmap)
|
30
|
+
return @gen if @gen
|
31
|
+
|
32
|
+
gens = @preds.
|
33
|
+
map { |pred| S.gensub(pred, overrides, path, rmap) }
|
34
|
+
|
35
|
+
->(r) do
|
36
|
+
gens.map { |gen| gen.call(r) }.reduce(&:merge)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Speculation
|
3
|
+
using NamespacedSymbols.refine(self)
|
4
|
+
using Conj
|
5
|
+
|
6
|
+
# @private
|
7
|
+
class NilableSpec < SpecImpl
|
8
|
+
S = Speculation
|
9
|
+
|
10
|
+
def initialize(pred)
|
11
|
+
@pred = pred
|
12
|
+
@delayed_spec = Concurrent::Delay.new { S.send(:specize, pred) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def conform(value)
|
16
|
+
value.nil? ? value : @delayed_spec.value.conform(value)
|
17
|
+
end
|
18
|
+
|
19
|
+
def explain(path, via, inn, value)
|
20
|
+
return if S.pvalid?(@delayed_spec.value, value) || value.nil?
|
21
|
+
|
22
|
+
S.
|
23
|
+
explain1(@pred, path.conj(:pred.ns), via, inn, value).
|
24
|
+
conj(:path => path.conj(:nil.ns), :pred => NilClass, :val => value, :via => via, :in => inn)
|
25
|
+
end
|
26
|
+
|
27
|
+
def gen(overrides, path, rmap)
|
28
|
+
return @gen if @gen
|
29
|
+
|
30
|
+
->(rantly) do
|
31
|
+
rantly.freq([1, Gen.delay { Utils.constantly(nil) }],
|
32
|
+
[9, Gen.delay { S.gensub(@pred, overrides, path.conj(:pred.ns), rmap) }])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Speculation
|
3
|
+
using NamespacedSymbols.refine(self)
|
4
|
+
using Conj
|
5
|
+
|
6
|
+
# @private
|
7
|
+
class OrSpec < SpecImpl
|
8
|
+
S = Speculation
|
9
|
+
|
10
|
+
attr_reader :id
|
11
|
+
|
12
|
+
def initialize(named_specs)
|
13
|
+
@id = SecureRandom.uuid
|
14
|
+
@named_specs = named_specs
|
15
|
+
@keys = named_specs.keys
|
16
|
+
@preds = preds = named_specs.values
|
17
|
+
|
18
|
+
@delayed_specs = Concurrent::Delay.new do
|
19
|
+
preds.map { |spec| S.send(:specize, spec) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def conform(value)
|
24
|
+
@delayed_specs.value.each_with_index do |spec, index|
|
25
|
+
conformed = spec.conform(value)
|
26
|
+
|
27
|
+
unless S.invalid?(conformed)
|
28
|
+
return [@keys[index], conformed]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
:invalid.ns
|
33
|
+
end
|
34
|
+
|
35
|
+
def explain(path, via, inn, value)
|
36
|
+
return if S.pvalid?(self, value)
|
37
|
+
|
38
|
+
@keys.zip(@preds).flat_map do |(key, pred)|
|
39
|
+
next if S.pvalid?(pred, value)
|
40
|
+
S.explain1(pred, path.conj(key), via, inn, value)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def gen(overrides, path, rmap)
|
45
|
+
return gen if @gen
|
46
|
+
|
47
|
+
gs = @keys.zip(@preds).
|
48
|
+
map { |(k, p)|
|
49
|
+
rmap = S.inck(rmap, @id)
|
50
|
+
|
51
|
+
unless S.recur_limit?(rmap, @id, path, k)
|
52
|
+
Gen.delay { S.gensub(p, overrides, path.conj(k), rmap) }
|
53
|
+
end
|
54
|
+
}.
|
55
|
+
compact
|
56
|
+
|
57
|
+
unless gs.empty?
|
58
|
+
->(rantly) { rantly.branch(*gs) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Speculation
|
3
|
+
using NamespacedSymbols.refine(self)
|
4
|
+
|
5
|
+
# @private
|
6
|
+
class RegexSpec < SpecImpl
|
7
|
+
S = Speculation
|
8
|
+
|
9
|
+
def initialize(regex)
|
10
|
+
@regex = regex
|
11
|
+
end
|
12
|
+
|
13
|
+
def conform(value)
|
14
|
+
if value.nil? || Utils.collection?(value)
|
15
|
+
S.re_conform(@regex, value)
|
16
|
+
else
|
17
|
+
:invalid.ns
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def explain(path, via, inn, value)
|
22
|
+
if value.nil? || Utils.collection?(value)
|
23
|
+
S.re_explain(path, via, inn, @regex, value || [])
|
24
|
+
else
|
25
|
+
[{ :path => path, :val => value, :via => via, :in => inn }]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def gen(overrides, path, rmap)
|
30
|
+
return @gen if @gen
|
31
|
+
|
32
|
+
S.re_gen(@regex, overrides, path, rmap)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Speculation
|
3
|
+
using Speculation::NamespacedSymbols.refine(self)
|
4
|
+
using Conj
|
5
|
+
|
6
|
+
# @private
|
7
|
+
class Spec < SpecImpl
|
8
|
+
S = Speculation
|
9
|
+
|
10
|
+
def initialize(predicate, should_conform)
|
11
|
+
@predicate = predicate
|
12
|
+
@should_conform = should_conform
|
13
|
+
end
|
14
|
+
|
15
|
+
def conform(value)
|
16
|
+
ret = case @predicate
|
17
|
+
when Set then @predicate.include?(value)
|
18
|
+
when Regexp, Module then @predicate === value
|
19
|
+
else @predicate.call(value)
|
20
|
+
end
|
21
|
+
|
22
|
+
if @should_conform
|
23
|
+
ret
|
24
|
+
else
|
25
|
+
ret ? value : :invalid.ns
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def explain(path, via, inn, value)
|
30
|
+
if S.invalid?(S.dt(@predicate, value))
|
31
|
+
[{ :path => path, :val => value, :via => via, :in => inn, :pred => @predicate }]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def gen(_, _, _)
|
36
|
+
if @gen
|
37
|
+
@gen
|
38
|
+
else
|
39
|
+
Gen.gen_for_pred(@predicate)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def inspect
|
44
|
+
"#{self.class}(#{@name || @predicate.inspect})"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Speculation
|
3
|
+
using Speculation::NamespacedSymbols.refine(self)
|
4
|
+
using Conj
|
5
|
+
|
6
|
+
# @private
|
7
|
+
class TupleSpec < SpecImpl
|
8
|
+
S = Speculation
|
9
|
+
|
10
|
+
def initialize(preds)
|
11
|
+
@preds = preds
|
12
|
+
|
13
|
+
@delayed_specs = Concurrent::Delay.new do
|
14
|
+
preds.map { |pred| S.send(:specize, pred) }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def conform(collection)
|
19
|
+
specs = @delayed_specs.value
|
20
|
+
|
21
|
+
unless Utils.array?(collection) && collection.count == specs.count
|
22
|
+
return :invalid.ns
|
23
|
+
end
|
24
|
+
|
25
|
+
return_value = collection.class.new
|
26
|
+
|
27
|
+
collection.zip(specs).each do |(value, spec)|
|
28
|
+
conformed_value = spec.conform(value)
|
29
|
+
|
30
|
+
if S.invalid?(conformed_value)
|
31
|
+
return :invalid.ns
|
32
|
+
else
|
33
|
+
return_value += [conformed_value]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
return_value
|
38
|
+
end
|
39
|
+
|
40
|
+
def explain(path, via, inn, value)
|
41
|
+
if !Utils.array?(value)
|
42
|
+
[{ :path => path, :val => value, :via => via, :in => inn, :pred => "array?" }]
|
43
|
+
elsif @preds.count != value.count
|
44
|
+
[{ :path => path, :val => value, :via => via, :in => inn, :pred => "count == predicates.count" }]
|
45
|
+
else
|
46
|
+
probs = @preds.zip(value).each_with_index.flat_map { |(pred, x), index|
|
47
|
+
unless S.pvalid?(pred, x)
|
48
|
+
S.explain1(pred, path.conj(index), via, inn.conj(index), x)
|
49
|
+
end
|
50
|
+
}
|
51
|
+
|
52
|
+
probs.compact
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def gen(overrides, path, rmap)
|
57
|
+
return @gen if @gen
|
58
|
+
|
59
|
+
gens = @preds.each_with_index.
|
60
|
+
map { |p, i| S.gensub(p, overrides, path.conj(i), rmap) }
|
61
|
+
|
62
|
+
->(rantly) do
|
63
|
+
gens.map { |g| g.call(rantly) }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Speculation
|
4
|
+
# @private
|
5
|
+
class SpecImpl
|
6
|
+
attr_accessor :name, :gen
|
7
|
+
attr_reader :id
|
8
|
+
|
9
|
+
def conform(_x)
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
12
|
+
|
13
|
+
def explain(_path, _via, _inn, _value)
|
14
|
+
raise NotImplementedError
|
15
|
+
end
|
16
|
+
|
17
|
+
def gen(_overrides, _path, _rmap)
|
18
|
+
raise NotImplementedError
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
"#{self.class}(#{name})"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
require_relative "spec_impl/hash_spec"
|
28
|
+
require_relative "spec_impl/spec"
|
29
|
+
require_relative "spec_impl/tuple_spec"
|
30
|
+
require_relative "spec_impl/or_spec"
|
31
|
+
require_relative "spec_impl/and_spec"
|
32
|
+
require_relative "spec_impl/merge_spec"
|
33
|
+
require_relative "spec_impl/every_spec"
|
34
|
+
require_relative "spec_impl/regex_spec"
|
35
|
+
require_relative "spec_impl/f_spec"
|
36
|
+
require_relative "spec_impl/nilable_spec"
|