speculation 0.4.0 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,5 +3,5 @@ gem 'sinatra'
3
3
  gem 'sequel'
4
4
  gem 'bcrypt'
5
5
  gem 'sqlite3'
6
- gem 'speculation', :git => "https://github.com/english/speculation.git", :ref => "ae3ee09"
6
+ gem 'speculation', :path => File.expand_path('../../..', __FILE__)
7
7
  gem 'pry'
@@ -1,9 +1,7 @@
1
- GIT
2
- remote: https://github.com/english/speculation.git
3
- revision: ae3ee095765816a8d5bfa5b29c808439f04633b0
4
- ref: ae3ee09
1
+ PATH
2
+ remote: /Users/jamie/Projects/speculation
5
3
  specs:
6
- speculation (0.2.0)
4
+ speculation (0.4.1)
7
5
  concurrent-ruby (~> 1.0)
8
6
  rantly (~> 1.0)
9
7
 
@@ -64,7 +64,7 @@ module User
64
64
 
65
65
  def self.serialize_validation_errors(user)
66
66
  data = S.explain_data(ns(:user), user)
67
- data[ns(S, :problems)].map { |problem| Validation.serialize_problem(problem) }
67
+ data[:problems].map { |problem| Validation.serialize_problem(problem) }
68
68
  end
69
69
 
70
70
  def self.fake
@@ -121,7 +121,7 @@ module User
121
121
 
122
122
  USER_ERROR_MESSAGE_MAP = {
123
123
  [] => {
124
- S::Utils.method(:key?) => ->(args) {
124
+ S::Predicates.method(:key?) => ->(args) {
125
125
  key = args.first
126
126
  if key == S.or_keys(:email, :username)
127
127
  "email or username is required"
@@ -146,8 +146,8 @@ module User
146
146
  }
147
147
  end
148
148
 
149
- S.def ns(:email), S.with_gen(S.and(String, Validation::EMAIL_REGEX), Generators.method(:email))
150
- S.def ns(:username), S.with_gen(S.and(String, Validation.method(:validate_username_length)), Generators.method(:username))
151
- S.def ns(:password), S.with_gen(S.and(String, Validation.method(:validate_password_length), Validation.method(:validate_password_complexity)), Generators.method(:password))
149
+ S.def ns(:email), S.with_gen(S.and(String, Validation::EMAIL_REGEX)) { Generators.method(:email) }
150
+ S.def ns(:username), S.with_gen(S.and(String, Validation.method(:validate_username_length))) { Generators.method(:username) }
151
+ S.def ns(:password), S.with_gen(S.and(String, Validation.method(:validate_password_length), Validation.method(:validate_password_complexity))) { Generators.method(:password) }
152
152
  S.def ns(:user), S.keys(:req_un => [S.or_keys(ns(:email), ns(:username)), ns(:password)])
153
153
  end
@@ -155,7 +155,7 @@ S.explain ns(:name_or_id), :foo
155
155
  # as a string or explain_data to receive the errors as data.
156
156
 
157
157
  S.explain_data ns(:name_or_id), :foo
158
- # => {:"Speculation/problems"=>
158
+ # => {:problems=>
159
159
  # [{:path=>[:name],
160
160
  # :val=>:foo,
161
161
  # :via=>[:"Object/name_or_id"],
@@ -214,8 +214,8 @@ S.valid? ns(:person), ns(:first_name) => "Elon", ns(:last_name) => "Musk", ns(:e
214
214
 
215
215
  # Fails required key check
216
216
  S.explain ns(:person), ns(:first_name) => "Elon"
217
- # >> val: {:"Object/first_name"=>"Elon"} fails spec: :"Object/person" predicate: [#<Method: Speculation::Utils.key?>, [:"Object/last_name"]]
218
- # >> val: {:"Object/first_name"=>"Elon"} fails spec: :"Object/person" predicate: [#<Method: Speculation::Utils.key?>, [:"Object/email"]]
217
+ # >> val: {:"Object/first_name"=>"Elon"} fails spec: :"Object/person" predicate: [#<Method: Speculation::Predicates.key?>, [:"Object/last_name"]]
218
+ # >> val: {:"Object/first_name"=>"Elon"} fails spec: :"Object/person" predicate: [#<Method: Speculation::Predicates.key?>, [:"Object/email"]]
219
219
 
220
220
  # Fails attribute conformance
221
221
  S.explain ns(:person), ns(:first_name) => "Elon", ns(:last_name) => "Musk", ns(:email) => "n/a"
@@ -248,8 +248,8 @@ S.explain :"unq/person", :first_name => "Elon", :last_name => "Musk", :email =>
248
248
  # >> In: [:email] val: "n/a" fails spec: :"Object/email_type" at: [:email] predicate: [/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$/, ["n/a"]]
249
249
 
250
250
  S.explain :"unq/person", :first_name => "Elon"
251
- # >> val: {:first_name=>"Elon"} fails spec: :"unq/person" predicate: [#<Method: Speculation::Utils.key?>, [:"Object/last_name"]]
252
- # >> val: {:first_name=>"Elon"} fails spec: :"unq/person" predicate: [#<Method: Speculation::Utils.key?>, [:"Object/email"]]
251
+ # >> val: {:first_name=>"Elon"} fails spec: :"unq/person" predicate: [#<Method: Speculation::Predicates.key?>, [:"Object/last_name"]]
252
+ # >> val: {:first_name=>"Elon"} fails spec: :"unq/person" predicate: [#<Method: Speculation::Predicates.key?>, [:"Object/email"]]
253
253
 
254
254
  # Unqualified keys can also be used to validate record attributes - don't support
255
255
  # Keyword args keys* - don't support
@@ -301,7 +301,7 @@ S.conform ns(:vnum3), [1, 2, 3] # => #<Set: {1, 2, 3}>
301
301
  S.explain ns(:vnum3), Set[1, 2, 3] # not an array
302
302
  # >> val: #<Set: {1, 2, 3}> fails spec: :"Object/vnum3" predicate: [Array, [#<Set: {1, 2, 3}>]]
303
303
  S.explain ns(:vnum3), [1, 1, 1] # not distinct
304
- # >> val: [1, 1, 1] fails spec: :"Object/vnum3" predicate: [#<Method: Speculation::Utils.distinct?>, [[1, 1, 1]]]
304
+ # >> val: [1, 1, 1] fails spec: :"Object/vnum3" predicate: [#<Method: Speculation::Predicates.distinct?>, [[1, 1, 1]]]
305
305
  S.explain ns(:vnum3), [1, 2, :a] # not a number
306
306
  # >> In: [2] val: :a fails spec: :"Object/vnum3" predicate: [Numeric, [:a]]
307
307
 
@@ -499,15 +499,15 @@ end
499
499
  S.check_asserts = true
500
500
  person_name 100 rescue $!
501
501
  # => #<Speculation::Error: Spec assertion failed
502
- # val: 100 fails predicate: [#<Method: Speculation::Utils.hash?>, [100]]
502
+ # val: 100 fails predicate: [#<Method: Speculation::Predicates.hash?>, [100]]
503
503
  # Speculation/failure :assertion_failed
504
- # {:"Speculation/problems"=>
504
+ # {:problems=>
505
505
  # [{:path=>[],
506
- # :pred=>[#<Method: Speculation::Utils.hash?>, [100]],
506
+ # :pred=>[#<Method: Speculation::Predicates.hash?>, [100]],
507
507
  # :val=>100,
508
508
  # :via=>[],
509
509
  # :in=>[]}],
510
- # :"Speculation/failure"=>:assertion_failed}
510
+ # :failure=>:assertion_failed}
511
511
  # >
512
512
 
513
513
  # A deeper level of integration is to call conform and use the return value to
@@ -925,7 +925,7 @@ S.exercise_fn(method(:ranged_rand))
925
925
  # predicate implicitly presumes values of a particular type but the spec does
926
926
  # not specify them:
927
927
 
928
- Gen.generate S.gen(:even?.to_proc) rescue $! # => #<Speculation::Error: unable to construct gen at: [] for: Speculation::Spec(#<Proc:0x007fb69b1908f8(&:even?)>) {:"Speculation/failure"=>:no_gen, :"Speculation/path"=>[]}\n>
928
+ Gen.generate S.gen(:even?.to_proc) rescue $! # => #<Speculation::Error: unable to construct gen at: [] for: Speculation::Spec(#<Proc:0x007fb69b1908f8(&:even?)>) {:failure=>:no_gen, :"Speculation/path"=>[]}\n>
929
929
 
930
930
  # In this case spec was not able to find a generator for the even? predicate.
931
931
  # Most of the primitive generators in spec are mapped to the common type
@@ -1013,8 +1013,10 @@ Gen.sample sym_gen, 5
1013
1013
  # To redefine our spec using this custom generator, use with_gen which takes a
1014
1014
  # spec and a replacement generator as a block:
1015
1015
 
1016
- gen = S.gen(Set[:"my.domain/name", :"my.domain/occupation", :"my.domain/id"])
1017
- S.def(ns(:syms), S.with_gen(S.and(Symbol, ->(s) { S::NamespacedSymbols.namespace(s) == "my.domain" }), gen))
1016
+ syms_spec = S.with_gen(S.and(Symbol, ->(s) { S::NamespacedSymbols.namespace(s) == "my.domain" })) do
1017
+ S.gen(Set[:"my.domain/name", :"my.domain/occupation", :"my.domain/id"])
1018
+ end
1019
+ S.def ns(:syms), syms_spec
1018
1020
 
1019
1021
  S.valid? ns(:syms), :"my.domain/name"
1020
1022
  Gen.sample S.gen(ns(:syms)), 5
@@ -1025,7 +1027,7 @@ Gen.sample S.gen(ns(:syms)), 5
1025
1027
  # :"my.domain/id"]
1026
1028
 
1027
1029
  # Note that with_gen (and other places that take a custom generator) take a
1028
- # one-arg function that returns the generator, allowing it to be lazily
1030
+ # no-arg block that returns the generator, allowing it to be lazily
1029
1031
  # realized.
1030
1032
 
1031
1033
  # One downside to this approach is we are missing what property testing is
@@ -1048,11 +1050,14 @@ Gen.sample sym_gen_2, 5 # => [:"my.domain/hLZnEpj", :"my.domain/kvy", :"my.domai
1048
1050
  # Returning to our "hello" example, we now have the tools to make that
1049
1051
  # generator:
1050
1052
 
1051
- S.def ns(:hello), S.with_gen(->(s) { s.include?("hello") }, ->(rantly) {
1052
- s1 = rantly.sized(rantly.range(0, 10)) { rantly.string(:alpha) }
1053
- s2 = rantly.sized(rantly.range(0, 10)) { rantly.string(:alpha) }
1054
- "#{s1}hello#{s2}"
1055
- })
1053
+ hello_spec = S.with_gen(->(s) { s.include?("hello") }) do
1054
+ ->(rantly) {
1055
+ s1 = rantly.sized(rantly.range(0, 10)) { rantly.string(:alpha) }
1056
+ s2 = rantly.sized(rantly.range(0, 10)) { rantly.string(:alpha) }
1057
+ "#{s1}hello#{s2}"
1058
+ }
1059
+ end
1060
+ S.def ns(:hello), hello_spec
1056
1061
 
1057
1062
  Gen.sample S.gen(ns(:hello))
1058
1063
  # => ["XRLhtLshelloaY",
@@ -1136,7 +1141,7 @@ ranged_rand 8, 5 rescue $!
1136
1141
  # Speculation/args [8, 5]
1137
1142
  # Speculation/failure :instrument
1138
1143
  # Speculation::Test/caller "/var/folders/4l/j2mycv0j4rx7z47sp01r93vc3kfxzs/T/seeing_is_believing_temp_dir20170304-91770-1jtmc1y/program.rb:901:in `<main>'"
1139
- # {:"Speculation/problems"=>
1144
+ # {:problems=>
1140
1145
  # [{:path=>[:args],
1141
1146
  # :val=>{:start=>8, :end=>5},
1142
1147
  # :via=>[],
@@ -1144,9 +1149,9 @@ ranged_rand 8, 5 rescue $!
1144
1149
  # :pred=>
1145
1150
  # [#<Proc:0x007fb69a8a0fb8@/var/folders/4l/j2mycv0j4rx7z47sp01r93vc3kfxzs/T/seeing_is_believing_temp_dir20170304-91770-1jtmc1y/program.rb:548 (lambda)>,
1146
1151
  # [{:start=>8, :end=>5}]]}],
1147
- # :"Speculation/args"=>[8, 5],
1148
- # :"Speculation/failure"=>:instrument,
1149
- # :"Speculation::Test/caller"=>
1152
+ # :args=>[8, 5],
1153
+ # :failure=>:instrument,
1154
+ # :caller=>
1150
1155
  # "/var/folders/4l/j2mycv0j4rx7z47sp01r93vc3kfxzs/T/seeing_is_believing_temp_dir20170304-91770-1jtmc1y/program.rb:901:in `<main>'"}
1151
1156
  # >
1152
1157
 
@@ -1172,7 +1177,7 @@ ranged_rand 8, 5 rescue $!
1172
1177
 
1173
1178
  STest.check method(:ranged_rand)
1174
1179
  # => [{:spec=>Speculation::FSpec(main.ranged_rand),
1175
- # :"Speculation::Test/ret"=>{:num_tests=>1000, :result=>true},
1180
+ # :ret=>{:num_tests=>1000, :result=>true},
1176
1181
  # :method=>#<Method: main.ranged_rand>}]
1177
1182
 
1178
1183
  # check also takes a number of options ~~that can be passed to test.check to
@@ -1193,7 +1198,7 @@ STest.abbrev_result STest.check(method(:ranged_rand)).first
1193
1198
  # >> {:spec=>"Speculation::FSpec(main.ranged_rand)",
1194
1199
  # >> :method=>#<Method: main.ranged_rand>,
1195
1200
  # >> :failure=>
1196
- # >> {:"Speculation/problems"=>
1201
+ # >> {:problems=>
1197
1202
  # >> [{:path=>[:fn],
1198
1203
  # >> :val=>{:args=>{:start=>-1, :end=>0}, :block=>nil, :ret=>0},
1199
1204
  # >> :via=>[],
@@ -1201,10 +1206,10 @@ STest.abbrev_result STest.check(method(:ranged_rand)).first
1201
1206
  # >> :pred=>
1202
1207
  # >> [#<Proc:0x007fb69a8a0c98@/var/folders/4l/j2mycv0j4rx7z47sp01r93vc3kfxzs/T/seeing_is_believing_temp_dir20170304-91770-1jtmc1y/program.rb:551 (lambda)>,
1203
1208
  # >> [{:args=>{:start=>-1, :end=>0}, :block=>nil, :ret=>0}]]}],
1204
- # >> :"Speculation::Test/args"=>[-1, 0],
1205
- # >> :"Speculation::Test/val"=>
1209
+ # >> :args=>[-1, 0],
1210
+ # >> :val=>
1206
1211
  # >> {:args=>{:start=>-1, :end=>0}, :block=>nil, :ret=>0},
1207
- # >> :"Speculation/failure"=>:check_failed}}
1212
+ # >> :failure=>:check_failed}}
1208
1213
 
1209
1214
  # check has reported an error in the :fn spec. We can see the arguments passed
1210
1215
  # were -1 and 0 and the return value was -0, which is out of the expected
@@ -12,6 +12,7 @@ require "speculation/version"
12
12
  require "speculation/namespaced_symbols"
13
13
  require "speculation/method_identifier"
14
14
  require "speculation/utils"
15
+ require "speculation/predicates"
15
16
  require "speculation/spec"
16
17
  require "speculation/error"
17
18
 
@@ -22,9 +23,8 @@ module Speculation
22
23
  # Enables or disables spec asserts. Defaults to false.
23
24
  attr_accessor :check_asserts
24
25
 
25
- # A soft limit on how many times a branching spec (or/alt/zero_or_more) can
26
- # be recursed through during generation. After this a non-recursive branch
27
- # will be chosen.
26
+ # A soft limit on how many times a branching spec (or/alt/zero_or_more/opt keys) can be recursed
27
+ # through during generation. After this a non-recursive branch will be chosen.
28
28
  attr_accessor :recursion_limit
29
29
 
30
30
  # The number of times an anonymous fn specified by fspec will be
@@ -47,29 +47,6 @@ module Speculation
47
47
 
48
48
  @registry_ref = Concurrent::Atom.new({})
49
49
 
50
- INVALID = ns(:invalid)
51
-
52
- # @private
53
- OP = ns(:op)
54
- # @private
55
- ALT = ns(:alt)
56
- # @private
57
- AMP = ns(:amp)
58
- # @private
59
- PCAT = ns(:pcat)
60
- # @private
61
- REP = ns(:rep)
62
- # @private
63
- ACCEPT = ns(:accept)
64
- # @private
65
- NIL = ns(:nil)
66
- # @private
67
- RECURSION_LIMIT = ns(:recursion_limit)
68
- # @private
69
- GEN = ns(:gen)
70
- # @private
71
- NAME = ns(:name)
72
-
73
50
  # Can be enabled or disabled at runtime:
74
51
  # - enabled/disabled by setting `check_asserts`.
75
52
  # - enabled by setting environment variable SPECULATION_CHECK_ASSERTS to the
@@ -83,7 +60,7 @@ module Speculation
83
60
  return x unless check_asserts
84
61
  return x if valid?(spec, x)
85
62
 
86
- ed = _explain_data(spec, [], [], [], x).merge(ns(:failure) => :assertion_failed)
63
+ ed = _explain_data(spec, [], [], [], x).merge(:failure => :assertion_failed)
87
64
  out = StringIO.new
88
65
  explain_out(ed, out)
89
66
 
@@ -110,28 +87,28 @@ module Speculation
110
87
  gens << [1, ->(r) { r.choose(Float::INFINITY, -Float::INFINITY) }] if infinite
111
88
  gens << [1, ->(_) { Float::NAN }] if nan
112
89
 
113
- spec(self.and(*preds), :gen => ->(rantly) { rantly.freq(*gens) })
90
+ spec(self.and(*preds), :gen => ->() { ->(rantly) { rantly.freq(*gens) } })
114
91
  end
115
92
 
116
93
  # @param range [Range<Integer>]
117
94
  # @return Spec that validates ints in the given range
118
95
  def self.int_in(range)
119
96
  spec(self.and(Integer, ->(x) { range.include?(x) }),
120
- :gen => ->(_) { rand(range) })
97
+ :gen => ->() { ->(_) { rand(range) } })
121
98
  end
122
99
 
123
100
  # @param time_range [Range<Time>]
124
101
  # @return Spec that validates times in the given range
125
102
  def self.time_in(time_range)
126
103
  spec(self.and(Time, ->(x) { time_range.cover?(x) }),
127
- :gen => ->(_) { rand(time_range) })
104
+ :gen => ->() { ->(_) { rand(time_range) } })
128
105
  end
129
106
 
130
107
  # @param date_range [Range<Date>]
131
108
  # @return Spec that validates dates in the given range
132
109
  def self.date_in(date_range)
133
110
  spec(self.and(Date, ->(x) { date_range.cover?(x) }),
134
- :gen => ->(_) { rand(date_range) })
111
+ :gen => ->() { ->(_) { rand(date_range) } })
135
112
  end
136
113
 
137
114
  # @param x [Spec, Object]
@@ -143,30 +120,43 @@ module Speculation
143
120
  # @param x [Hash, Object]
144
121
  # @return [Hash, false] x if x is a (Speculation) regex op, else logical false
145
122
  def self.regex?(x)
146
- Utils.hash?(x) && x[OP] && x
123
+ x.is_a?(Hash) && x[:op] && x
147
124
  end
148
125
 
149
126
  # @param value return value of a `conform` call
150
127
  # @return [Boolean] true if value is the result of an unsuccessful conform
151
128
  def self.invalid?(value)
152
- value.equal?(INVALID)
129
+ value.equal?(:"Speculation/invalid")
153
130
  end
154
131
 
155
132
  # @param spec [Spec]
156
133
  # @param value value to conform
157
- # @return [Symbol, Object] :Speculation/invalid if value does not match spec, else the (possibly destructured) value
134
+ # @return [Symbol, Object] :Speculation/invalid if value does not match spec, else the (possibly
135
+ # destructured) value
158
136
  def self.conform(spec, value)
159
137
  spec = MethodIdentifier(spec)
160
138
  specize(spec).conform(value)
161
139
  end
162
140
 
163
- # Takes a spec and a one-arg generator function and returns a version of the spec that uses that generator
164
141
  # @param spec [Spec]
165
- # @param gen [Proc] generator proc that receives a Rantly instance
142
+ # @value value [Object] value created by `conform` call and given `spec`
143
+ # @return value with conform destructuring undone
144
+ def self.unform(spec, value)
145
+ specize(spec).unform(value)
146
+ end
147
+
148
+ # Takes a spec and a no-arg generator returning block and returns a version of the spec that uses
149
+ # that generator
150
+ # @param spec [Spec]
151
+ # @yieldreturn Rantly generator
166
152
  # @return [Spec]
167
- def self.with_gen(spec, gen)
153
+ def self.with_gen(spec, &gen)
154
+ if gen && !gen.arity.zero?
155
+ raise ArgumentError, "gen must be a no-arg block that returns a generator"
156
+ end
157
+
168
158
  if regex?(spec)
169
- spec.merge(ns(:gfn) => gen)
159
+ spec.merge(:gfn => gen)
170
160
  else
171
161
  specize(spec).with_gen(gen)
172
162
  end
@@ -177,19 +167,21 @@ module Speculation
177
167
  probs = specize(spec).explain(path, via, inn, value)
178
168
 
179
169
  if probs && probs.any?
180
- { ns(:problems) => probs }
170
+ { :problems => probs,
171
+ :spec => spec,
172
+ :value => value }
181
173
  end
182
174
  end
183
175
 
184
176
  # Given a spec and a value x which ought to conform, returns nil if x
185
- # conforms, else a hash with at least the key :"Speculation/problems" whose
177
+ # conforms, else a hash with at least the key :problems whose
186
178
  # value is a collection of problem-hashes, where problem-hash has at least
187
179
  # :path :pred and :val keys describing the predicate and the value that failed
188
180
  # at that path.
189
181
  # @param spec [Spec]
190
182
  # @param x value which ought to conform
191
183
  # @return [nil, Hash] nil if x conforms, else a hash with at least the key
192
- # :Speculation/problems whose value is a collection of problem-hashes,
184
+ # :problems whose value is a collection of problem-hashes,
193
185
  # where problem-hash has at least :path :pred and :val keys describing the
194
186
  # predicate and the value that failed at that path.
195
187
  def self.explain_data(spec, x)
@@ -203,7 +195,9 @@ module Speculation
203
195
  def self.explain_out(ed, out = STDOUT)
204
196
  return out.puts("Success!") unless ed
205
197
 
206
- ed.fetch(ns(:problems)).each do |prob|
198
+ problems = Utils.sort_descending(ed.fetch(:problems)) { |prob| prob[:path] }
199
+
200
+ problems.each do |prob|
207
201
  path, pred, val, reason, via, inn = prob.values_at(:path, :pred, :val, :reason, :via, :in)
208
202
 
209
203
  out.print("In: ", inn.to_a.inspect, " ") unless inn.empty?
@@ -223,7 +217,7 @@ module Speculation
223
217
  end
224
218
 
225
219
  ed.each do |k, v|
226
- out.puts("#{k} #{PP.pp(v, String.new)}") unless k == ns(:problems)
220
+ out.puts("#{k.inspect} #{PP.pp(v, String.new)}") unless k == :problems
227
221
  end
228
222
 
229
223
  nil
@@ -251,13 +245,14 @@ module Speculation
251
245
 
252
246
  spec = specize(spec)
253
247
  gfn = overrides[spec_name(spec) || spec] || overrides[path]
248
+ gfn = gfn.call if gfn
254
249
  g = gfn || spec.gen(overrides, path, rmap)
255
250
 
256
251
  if g
257
252
  Gen.such_that(g) { |x| valid?(spec, x) }
258
253
  else
259
254
  raise Speculation::Error.new("unable to construct gen at: #{path.inspect} for: #{spec.inspect}",
260
- ns(:failure) => :no_gen, ns(:path) => path)
255
+ :failure => :no_gen, :path => path)
261
256
  end
262
257
  end
263
258
 
@@ -277,7 +272,7 @@ module Speculation
277
272
  # @return [Proc]
278
273
  def self.gen(spec, overrides = nil)
279
274
  spec = MethodIdentifier(spec)
280
- gensub(spec, overrides, [], RECURSION_LIMIT => recursion_limit)
275
+ gensub(spec, overrides, [], :recursion_limit => recursion_limit)
281
276
  end
282
277
 
283
278
  # @private
@@ -329,8 +324,8 @@ module Speculation
329
324
  # NOTE: it is not generally necessary to wrap predicates in spec when using
330
325
  # `S.def` etc., only to attach a unique generator.
331
326
  #
332
- # Optionally takes :gen generator function, which must be a proc of one arg
333
- # (Rantly instance) that generates a valid value.
327
+ # Optionally takes :gen generator function, which must be a no-arg proc that returns a generator
328
+ # (proc that receives a Rantly instance) that generates a valid value.
334
329
  #
335
330
  # @param pred [Proc, Method, Set, Class, Regexp, Hash] Takes a single predicate. A
336
331
  # predicate can be one of:
@@ -346,8 +341,8 @@ module Speculation
346
341
  # zero_or_more, one_or_more, zero_or_one, in which case it will return a
347
342
  # regex-conforming spec, useful when nesting an independent regex.
348
343
  #
349
- # @param gen [Proc] generator function, which must be a proc of one
350
- # arg (Rantly instance) that generates a valid value.
344
+ # @param gen [Proc] generator returning function, which must be a zero arg proc that returns a
345
+ # proc of one arg (Rantly instance) that generates a valid value.
351
346
  # @return [Spec]
352
347
  def self.spec(pred, gen: nil)
353
348
  spec_impl(pred, gen, false) if pred
@@ -381,20 +376,20 @@ module Speculation
381
376
  # @param opt [Array<Symbol>]
382
377
  # @param req_un [Array<Symbol>]
383
378
  # @param opt_un [Array<Symbol>]
384
- # @param gen [Proc] generator function, which must be a proc of one arg
385
- # (Rantly instance) that generates a valid value
379
+ # @param gen [Proc] generator returning function, which must be a zero arg proc that
380
+ # returns a proc of one arg (Rantly instance) that generates a valid value.
386
381
  def self.keys(req: [], opt: [], req_un: [], opt_un: [], gen: nil)
387
382
  HashSpec.new(req, opt, req_un, opt_un, gen)
388
383
  end
389
384
 
390
385
  # @see keys
391
386
  def self.or_keys(*ks)
392
- [ns(:or), *ks]
387
+ [:"Speculation/or", *ks]
393
388
  end
394
389
 
395
390
  # @see keys
396
391
  def self.and_keys(*ks)
397
- [ns(:and), *ks]
392
+ [:"Speculation/and", *ks]
398
393
  end
399
394
 
400
395
  # @param key_preds [Hash] Takes key+pred hash
@@ -440,8 +435,8 @@ module Speculation
440
435
  # @option opts :into [Array, Hash, Set] (Array) one of [], {}, Set[], the
441
436
  # default collection to generate into (default: empty coll as generated by
442
437
  # :kind pred if supplied, else [])
443
- # @option opts :gen [Proc] generator proc, which must be a proc of one arg
444
- # (Rantly instance) that generates a valid value.
438
+ # @option opts :gen [Proc] generator returning function, which must be a zero arg proc that
439
+ # returns a proc of one arg (Rantly instance) that generates a valid value.
445
440
  # @see coll_of
446
441
  # @see every_kv
447
442
  # @return [Spec] spec that validates collection elements against pred
@@ -461,9 +456,9 @@ module Speculation
461
456
  # @param vpred val pred
462
457
  # @param options [Hash]
463
458
  # @return [Spec] spec that validates associative collections
464
- def self.every_kv(kpred, vpred, options)
465
- every(tuple(kpred, vpred), ns(:kfn) => ->(_i, v) { v.first },
466
- :into => {},
459
+ def self.every_kv(kpred, vpred, options = {})
460
+ every(tuple(kpred, vpred), :kfn => ->(_i, v) { v.first },
461
+ :into => {},
467
462
  **options)
468
463
  end
469
464
 
@@ -479,13 +474,13 @@ module Speculation
479
474
  # @param opts [Hash]
480
475
  # @return [Spec]
481
476
  def self.coll_of(pred, opts = {})
482
- every(pred, ns(:conform_all) => true, **opts)
477
+ every(pred, :conform_all => true, **opts)
483
478
  end
484
479
 
485
480
  # Returns a spec for a hash whose keys satisfy kpred and vals satisfy vpred.
486
481
  # Unlike 'every_kv', hash_of will exhaustively conform every value.
487
482
  #
488
- # Same options as 'every', :kind defaults to `Speculation::Utils.hash?`, with
483
+ # Same options as 'every', :kind defaults to `Speculation::Predicates.hash?`, with
489
484
  # the addition of:
490
485
  #
491
486
  # :conform_keys - conform keys as well as values (default false)
@@ -496,8 +491,8 @@ module Speculation
496
491
  # @param options [Hash]
497
492
  # @return [Spec]
498
493
  def self.hash_of(kpred, vpred, options = {})
499
- every_kv(kpred, vpred, :kind => Utils.method(:hash?),
500
- ns(:conform_all) => true,
494
+ every_kv(kpred, vpred, :kind => Predicates.method(:hash?),
495
+ :conform_all => true,
501
496
  **options)
502
497
  end
503
498
 
@@ -519,7 +514,7 @@ module Speculation
519
514
  # @return [Hash] regex op that matches zero or one value matching pred. Produces a
520
515
  # single value (not a collection) if matched.
521
516
  def self.zero_or_one(pred)
522
- _alt([pred, accept(NIL)], nil)
517
+ _alt([pred, accept(:nil)], nil)
523
518
  end
524
519
 
525
520
  # @param kv_specs [Hash] key+pred pairs
@@ -550,14 +545,15 @@ module Speculation
550
545
  # resulting value to the conjunction of the predicates, and any conforming
551
546
  # they might perform.
552
547
  def self.constrained(re, *preds)
553
- { OP => AMP, :p1 => re, :predicates => preds }
548
+ { :op => :amp, :p1 => re, :predicates => preds }
554
549
  end
555
550
 
556
- # @param f predicate function with the semantics of conform i.e. it should
551
+ # @param f [#call] function with the semantics of conform i.e. it should
557
552
  # return either a (possibly converted) value or :"Speculation/invalid"
553
+ # @param unformer [#call] function that does the unform of the result of `f`
558
554
  # @return [Spec] a spec that uses pred as a predicate/conformer.
559
- def self.conformer(f)
560
- spec_impl(f, nil, true)
555
+ def self.conformer(f, unformer = nil)
556
+ spec_impl(f, nil, true, unformer)
561
557
  end
562
558
 
563
559
  # Takes :args :ret and (optional) :block and :fn kwargs whose values are preds and returns a spec
@@ -571,8 +567,8 @@ module Speculation
571
567
  # @param ret predicate
572
568
  # @param fn predicate
573
569
  # @param block predicate
574
- # @param gen [Proc] generator proc, which must be a proc of one arg (Rantly
575
- # instance) that generates a valid value.
570
+ # @param gen [Proc] generator returning function, which must be a zero arg proc that
571
+ # returns a proc of one arg (Rantly instance) that generates a valid value.
576
572
  # @return [Spec]
577
573
  # @see fdef See 'fdef' for a single operation that creates an fspec and registers it, as well as a
578
574
  # full description of :args, :block, :ret and :fn
@@ -662,7 +658,7 @@ module Speculation
662
658
  # @return [Array] an array of triples of [args, block, ret].
663
659
  def self.exercise_fn(method, n = 10, fspec = nil)
664
660
  fspec ||= get_spec(method)
665
- raise ArgumentError, "No fspec found for #{method}" unless fspec
661
+ raise ArgumentError, "No :args spec found for #{method}" unless fspec && fspec.args
666
662
 
667
663
  block_gen = fspec.block ? gen(fspec.block) : Utils.constantly(nil)
668
664
  gen = Gen.tuple(gen(fspec.args), block_gen)
@@ -674,7 +670,7 @@ module Speculation
674
670
 
675
671
  # @private
676
672
  def self.recur_limit?(rmap, id, path, k)
677
- rmap[id] > rmap[RECURSION_LIMIT] &&
673
+ rmap[id] > rmap[:recursion_limit] &&
678
674
  path.include?(k)
679
675
  end
680
676
 
@@ -692,11 +688,11 @@ module Speculation
692
688
  if spec
693
689
  conform(spec, x)
694
690
  elsif pred.is_a?(Module) || pred.is_a?(::Regexp)
695
- pred === x ? x : INVALID
691
+ pred === x ? x : :"Speculation/invalid"
696
692
  elsif pred.is_a?(Set)
697
- pred.include?(x) ? x : INVALID
693
+ pred.include?(x) ? x : :"Speculation/invalid"
698
694
  elsif pred.respond_to?(:call)
699
- pred.call(x) ? x : INVALID
695
+ pred.call(x) ? x : :"Speculation/invalid"
700
696
  else
701
697
  raise "#{pred} is not a class, proc, set or regexp"
702
698
  end
@@ -723,16 +719,16 @@ module Speculation
723
719
  end
724
720
 
725
721
  # @private
726
- def self.spec_impl(pred, gen, should_conform)
722
+ def self.spec_impl(pred, gen, should_conform, unconformer = nil)
727
723
  if spec?(pred)
728
- with_gen(pred, gen)
724
+ with_gen(pred, &gen)
729
725
  elsif regex?(pred)
730
726
  RegexSpec.new(pred, gen)
731
727
  elsif Utils.ident?(pred)
732
728
  spec = the_spec(pred)
733
- gen ? with_gen(spec, gen) : spec
729
+ gen ? with_gen(spec, &gen) : spec
734
730
  else
735
- PredicateSpec.new(pred, should_conform, gen)
731
+ PredicateSpec.new(pred, should_conform, gen, unconformer)
736
732
  end
737
733
  end
738
734
 
@@ -761,7 +757,7 @@ module Speculation
761
757
  p = reg_resolve!(p)
762
758
 
763
759
  id, op, ps, ks, p1, p2, ret, id, gen = p.values_at(
764
- :id, OP, :predicates, :keys, :p1, :p2, :return_value, :id, GEN
760
+ :id, :op, :predicates, :keys, :p1, :p2, :return_value, :id, :gfn
765
761
  ) if regex?(p)
766
762
 
767
763
  id = p.id if spec?(p)
@@ -786,19 +782,21 @@ module Speculation
786
782
  overrides[path]
787
783
 
788
784
  if ogen
785
+ gen = ogen.call
786
+
789
787
  if [:accept, nil].include?(op)
790
- return ->(rantly) { [*ogen.call(rantly)] }
788
+ return Gen.fmap(gen) { |x| [x] }
791
789
  else
792
- return ->(rantly) { ogen.call(rantly) }
790
+ return gen
793
791
  end
794
792
  end
795
793
 
796
- return gen if gen
794
+ return gen.call if gen
797
795
 
798
796
  if p
799
797
  case op
800
- when ACCEPT
801
- if ret == NIL
798
+ when :accept
799
+ if ret == :nil
802
800
  ->(_rantly) { [] }
803
801
  else
804
802
  ->(_rantly) { [ret] }
@@ -806,10 +804,10 @@ module Speculation
806
804
  when nil
807
805
  g = gensub(p, overrides, path, rmap)
808
806
 
809
- ->(rantly) { [g.call(rantly)] }
810
- when AMP
807
+ Gen.fmap(g) { |x| [x] }
808
+ when :amp
811
809
  re_gen(p1, overrides, path, rmap)
812
- when PCAT
810
+ when :pcat
813
811
  gens = ggens.call(ps, ks)
814
812
 
815
813
  if gens.all?
@@ -817,11 +815,11 @@ module Speculation
817
815
  gens.flat_map { |gg| gg.call(rantly) }
818
816
  end
819
817
  end
820
- when ALT
818
+ when :alt
821
819
  gens = ggens.call(ps, ks).compact
822
820
 
823
821
  ->(rantly) { rantly.branch(*gens) } unless gens.empty?
824
- when REP
822
+ when :rep
825
823
  if recur_limit?(rmap, id, [id], id)
826
824
  ->(_rantly) { [] }
827
825
  else
@@ -841,15 +839,45 @@ module Speculation
841
839
  def self.re_conform(regex, data)
842
840
  data.each do |x|
843
841
  regex = deriv(regex, x)
844
- return INVALID unless regex
842
+ return :"Speculation/invalid" unless regex
845
843
  end
846
844
 
847
845
  if accept_nil?(regex)
848
846
  return_value = preturn(regex)
849
847
 
850
- return_value == NIL ? nil : return_value
848
+ return_value == :nil ? nil : return_value
851
849
  else
852
- INVALID
850
+ :"Speculation/invalid"
851
+ end
852
+ end
853
+
854
+ # @private
855
+ def self.op_unform(regex, value)
856
+ return unform(regex, value) unless regex?(regex)
857
+
858
+ case regex[:op]
859
+ when :accept
860
+ [regex[:return_value]]
861
+ when :amp
862
+ px = regex[:predicates].reverse.reduce(value) { |val, pred| op_unform(pred, val) }
863
+ op_unform(regex[:p1], px)
864
+ when :rep
865
+ value.flat_map { |val| op_unform(regex[:p1], val) }
866
+ when :pcat
867
+ if regex[:keys] # it's a `cat`
868
+ kps = Hash[regex[:keys].zip(regex[:predicates])]
869
+ regex[:keys].flat_map { |key| value.include?(key) ? op_unform(kps[key], value[key]) : [] }
870
+ else # it's a `one_or_more`
871
+ value.flat_map { |val| op_unform(regex[:predicates].first, val) }
872
+ end
873
+ when :alt
874
+ if regex[:keys] # it's an `alt`
875
+ kps = Hash[regex[:keys].zip(regex[:predicates])]
876
+ k, v = value
877
+ op_unform(kps[k], v)
878
+ else # it's a `zero_or_one`
879
+ [unform(regex[:predicates].first, value)]
880
+ end
853
881
  end
854
882
  end
855
883
 
@@ -866,7 +894,7 @@ module Speculation
866
894
  end
867
895
 
868
896
  if accept?(p)
869
- if p[OP] == PCAT
897
+ if p[:op] == :pcat
870
898
  return op_explain(p, path, via, Utils.conj(inn, index), input[index..-1])
871
899
  else
872
900
  return [{ :path => path,
@@ -929,7 +957,7 @@ module Speculation
929
957
  if Utils.ident?(spec)
930
958
  spec
931
959
  elsif regex?(spec)
932
- spec.merge(NAME => name)
960
+ spec.merge(:name => name)
933
961
  else
934
962
  spec.tap { |s| s.name = name }
935
963
  end
@@ -939,7 +967,7 @@ module Speculation
939
967
  if Utils.ident?(spec)
940
968
  spec
941
969
  elsif regex?(spec)
942
- spec[NAME]
970
+ spec[:name]
943
971
  elsif spec.respond_to?(:name)
944
972
  spec.name
945
973
  end
@@ -973,7 +1001,7 @@ module Speculation
973
1001
  def and_preds(x, preds)
974
1002
  preds.each do |pred|
975
1003
  x = dt(pred, x)
976
- return INVALID if invalid?(x)
1004
+ return :"Speculation/invalid" if invalid?(x)
977
1005
  end
978
1006
 
979
1007
  x
@@ -986,6 +1014,8 @@ module Speculation
986
1014
  case spec
987
1015
  when Symbol, MethodIdentifier
988
1016
  specize(reg_resolve!(spec))
1017
+ when nil
1018
+ raise ArgumentError, "#{spec.inspect} can not be a spec"
989
1019
  else
990
1020
  spec_impl(spec, nil, false)
991
1021
  end
@@ -995,12 +1025,12 @@ module Speculation
995
1025
  ### regex ###
996
1026
 
997
1027
  def accept(x)
998
- { OP => ACCEPT, :return_value => x }
1028
+ { :op => :accept, :return_value => x }
999
1029
  end
1000
1030
 
1001
1031
  def accept?(hash)
1002
1032
  if hash.is_a?(Hash)
1003
- hash[OP] == ACCEPT
1033
+ hash[:op] == :accept
1004
1034
  end
1005
1035
  end
1006
1036
 
@@ -1013,7 +1043,7 @@ module Speculation
1013
1043
  return unless regex[:predicates].all?
1014
1044
 
1015
1045
  unless accept?(predicate)
1016
- return { OP => PCAT,
1046
+ return { :op => :pcat,
1017
1047
  :predicates => regex[:predicates],
1018
1048
  :keys => keys,
1019
1049
  :return_value => regex[:return_value] }
@@ -1034,7 +1064,7 @@ module Speculation
1034
1064
  def rep(p1, p2, return_value, splice)
1035
1065
  return unless p1
1036
1066
 
1037
- regex = { OP => REP, :p2 => p2, :splice => splice, :id => SecureRandom.uuid }
1067
+ regex = { :op => :rep, :p2 => p2, :splice => splice, :id => SecureRandom.uuid }
1038
1068
 
1039
1069
  if accept?(p1)
1040
1070
  regex.merge(:p1 => p2, :return_value => Utils.conj(return_value, p1[:return_value]))
@@ -1059,7 +1089,7 @@ module Speculation
1059
1089
  predicate, *rest_predicates = predicates
1060
1090
  key, *_rest_keys = keys
1061
1091
 
1062
- return_value = { OP => ALT, :predicates => predicates, :keys => keys }
1092
+ return_value = { :op => :alt, :predicates => predicates, :keys => keys }
1063
1093
  return return_value unless rest_predicates.empty?
1064
1094
 
1065
1095
  return predicate unless key
@@ -1077,24 +1107,24 @@ module Speculation
1077
1107
  end
1078
1108
 
1079
1109
  def no_ret?(p1, pret)
1080
- return true if pret == NIL
1110
+ return true if pret == :nil
1081
1111
 
1082
1112
  regex = reg_resolve!(p1)
1083
- op = regex[OP]
1113
+ op = regex[:op]
1084
1114
 
1085
- [REP, PCAT].include?(op) && pret.empty? || nil
1115
+ [:rep, :pcat].include?(op) && pret.empty? || nil
1086
1116
  end
1087
1117
 
1088
1118
  def accept_nil?(regex)
1089
1119
  regex = reg_resolve!(regex)
1090
1120
  return unless regex?(regex)
1091
1121
 
1092
- case regex[OP]
1093
- when ACCEPT then true
1094
- when PCAT then regex[:predicates].all? { |p| accept_nil?(p) }
1095
- when ALT then regex[:predicates].any? { |p| accept_nil?(p) }
1096
- when REP then (regex[:p1] == regex[:p2]) || accept_nil?(regex[:p1])
1097
- when AMP
1122
+ case regex[:op]
1123
+ when :accept then true
1124
+ when :pcat then regex[:predicates].all? { |p| accept_nil?(p) }
1125
+ when :alt then regex[:predicates].any? { |p| accept_nil?(p) }
1126
+ when :rep then (regex[:p1] == regex[:p2]) || accept_nil?(regex[:p1])
1127
+ when :amp
1098
1128
  p1 = regex[:p1]
1099
1129
 
1100
1130
  return false unless accept_nil?(p1)
@@ -1102,7 +1132,7 @@ module Speculation
1102
1132
  no_ret?(p1, preturn(p1)) ||
1103
1133
  !invalid?(and_preds(preturn(p1), regex[:predicates]))
1104
1134
  else
1105
- raise "Unexpected #{OP} #{regex[OP]}"
1135
+ raise "Unexpected op #{regex[:op]}"
1106
1136
  end
1107
1137
  end
1108
1138
 
@@ -1113,30 +1143,30 @@ module Speculation
1113
1143
  p0, *_pr = regex[:predicates]
1114
1144
  k, *_ks = regex[:keys]
1115
1145
 
1116
- case regex[OP]
1117
- when ACCEPT then regex[:return_value]
1118
- when PCAT then add_ret(p0, regex[:return_value], k)
1119
- when REP then add_ret(regex[:p1], regex[:return_value], k)
1120
- when AMP
1146
+ case regex[:op]
1147
+ when :accept then regex[:return_value]
1148
+ when :pcat then add_ret(p0, regex[:return_value], k)
1149
+ when :rep then add_ret(regex[:p1], regex[:return_value], k)
1150
+ when :amp
1121
1151
  pret = preturn(regex[:p1])
1122
1152
 
1123
1153
  if no_ret?(regex[:p1], pret)
1124
- NIL
1154
+ :nil
1125
1155
  else
1126
1156
  and_preds(pret, regex[:predicates])
1127
1157
  end
1128
- when ALT
1158
+ when :alt
1129
1159
  pred, key = regex[:predicates].zip(Array(regex[:keys])).find { |(p, _k)| accept_nil?(p) }
1130
1160
 
1131
1161
  r = if pred.nil?
1132
- NIL
1162
+ :nil
1133
1163
  else
1134
1164
  preturn(pred)
1135
1165
  end
1136
1166
 
1137
1167
  key ? [key, r] : r
1138
1168
  else
1139
- raise "Unexpected #{OP} #{regex[OP]}"
1169
+ raise "Unexpected op #{regex[:op]}"
1140
1170
  end
1141
1171
  end
1142
1172
 
@@ -1144,16 +1174,16 @@ module Speculation
1144
1174
  regex = reg_resolve!(regex)
1145
1175
  return r unless regex?(regex)
1146
1176
 
1147
- case regex[OP]
1148
- when ACCEPT, ALT, AMP
1177
+ case regex[:op]
1178
+ when :accept, :alt, :amp
1149
1179
  return_value = preturn(regex)
1150
1180
 
1151
- if return_value == NIL
1181
+ if return_value == :nil
1152
1182
  r
1153
1183
  else
1154
1184
  Utils.conj(r, key ? { key => return_value } : return_value)
1155
1185
  end
1156
- when PCAT, REP
1186
+ when :pcat, :rep
1157
1187
  return_value = preturn(regex)
1158
1188
 
1159
1189
  if return_value.empty?
@@ -1164,7 +1194,7 @@ module Speculation
1164
1194
  regex[:splice] ? Utils.into(r, val) : Utils.conj(r, val)
1165
1195
  end
1166
1196
  else
1167
- raise "Unexpected #{OP} #{regex[OP]}"
1197
+ raise "Unexpected op #{regex[:op]}"
1168
1198
  end
1169
1199
  end
1170
1200
 
@@ -1187,9 +1217,9 @@ module Speculation
1187
1217
  pred, *rest_preds = predicates
1188
1218
  key, *rest_keys = keys
1189
1219
 
1190
- case regex[OP]
1191
- when ACCEPT then nil
1192
- when PCAT
1220
+ case regex[:op]
1221
+ when :accept then nil
1222
+ when :pcat
1193
1223
  regex1 = pcat(:predicates => [deriv(pred, value), *rest_preds], :keys => keys, :return_value => return_value)
1194
1224
  regex2 = nil
1195
1225
 
@@ -1201,9 +1231,9 @@ module Speculation
1201
1231
  end
1202
1232
 
1203
1233
  alt2(regex1, regex2)
1204
- when ALT
1234
+ when :alt
1205
1235
  _alt(predicates.map { |p| deriv(p, value) }, keys)
1206
- when REP
1236
+ when :rep
1207
1237
  regex1 = rep(deriv(p1, value), p2, return_value, splice)
1208
1238
  regex2 = nil
1209
1239
 
@@ -1212,18 +1242,18 @@ module Speculation
1212
1242
  end
1213
1243
 
1214
1244
  alt2(regex1, regex2)
1215
- when AMP
1245
+ when :amp
1216
1246
  p1 = deriv(p1, value)
1217
1247
  return unless p1
1218
1248
 
1219
- if p1[OP] == ACCEPT
1249
+ if p1[:op] == :accept
1220
1250
  ret = and_preds(preturn(p1), predicates)
1221
1251
  accept(ret) unless invalid?(ret)
1222
1252
  else
1223
1253
  constrained(p1, *predicates)
1224
1254
  end
1225
1255
  else
1226
- raise "Unexpected #{OP} #{regex[OP]}"
1256
+ raise "Unexpected op #{regex[:op]}"
1227
1257
  end
1228
1258
  end
1229
1259
 
@@ -1251,9 +1281,9 @@ module Speculation
1251
1281
  end
1252
1282
  end
1253
1283
 
1254
- case p[OP]
1255
- when ACCEPT then nil
1256
- when AMP
1284
+ case p[:op]
1285
+ when :accept then nil
1286
+ when :amp
1257
1287
  if input.empty?
1258
1288
  if accept_nil?(p[:p1])
1259
1289
  explain_pred_list(p[:predicates], path, via, inn, preturn(p[:p1]))
@@ -1269,7 +1299,7 @@ module Speculation
1269
1299
  op_explain(p[:p1], path, via, inn, input)
1270
1300
  end
1271
1301
  end
1272
- when PCAT
1302
+ when :pcat
1273
1303
  pks = p[:predicates].zip(Array(p[:keys]))
1274
1304
  pred, k = if pks.count == 1
1275
1305
  pks.first
@@ -1284,7 +1314,7 @@ module Speculation
1284
1314
  else
1285
1315
  op_explain(pred, path, via, inn, input)
1286
1316
  end
1287
- when ALT
1317
+ when :alt
1288
1318
  return insufficient(p, path, via, inn) if input.empty?
1289
1319
 
1290
1320
  probs = p[:predicates].zip(Array(p[:keys])).flat_map { |(predicate, key)|
@@ -1292,21 +1322,21 @@ module Speculation
1292
1322
  }
1293
1323
 
1294
1324
  probs.compact
1295
- when REP
1325
+ when :rep
1296
1326
  op_explain(p[:p1], path, via, inn, input)
1297
1327
  else
1298
- raise "Unexpected #{OP} #{p[OP]}"
1328
+ raise "Unexpected :op #{p[:op]}"
1299
1329
  end
1300
1330
  end
1301
1331
  end
1302
1332
 
1303
1333
  @registry_ref.reset(
1304
- ns(:any) => with_gen(Utils.constantly(true), ->(r) { r.branch(*Gen::GEN_BUILTINS.values) }),
1334
+ ns(:any) => with_gen(Utils.constantly(true)) { ->(r) { r.branch(*Gen::GEN_BUILTINS.values) } },
1305
1335
  ns(:boolean) => Set[true, false],
1306
- ns(:positive_integer) => with_gen(self.and(Integer, ->(x) { x > 0 }), ->(r) { r.range(1) }),
1336
+ ns(:positive_integer) => with_gen(self.and(Integer, ->(x) { x > 0 })) { ->(r) { r.range(1) } },
1307
1337
  # Rantly#positive_integer is actually a natural integer
1308
- ns(:natural_integer) => with_gen(self.and(Integer, ->(x) { x >= 0 }), :positive_integer.to_proc),
1309
- ns(:negative_integer) => with_gen(self.and(Integer, ->(x) { x < 0 }), ->(r) { r.range(nil, -1) }),
1310
- ns(:empty) => with_gen(Utils.method(:empty?), Utils.constantly([]))
1338
+ ns(:natural_integer) => with_gen(self.and(Integer, ->(x) { x >= 0 })) { :positive_integer.to_proc },
1339
+ ns(:negative_integer) => with_gen(self.and(Integer, ->(x) { x < 0 })) { ->(r) { r.range(nil, -1) } },
1340
+ ns(:empty) => with_gen(Predicates.method(:empty?)) { Utils.constantly([]) }
1311
1341
  )
1312
1342
  end