speculation 0.4.0 → 0.4.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.
@@ -19,12 +19,16 @@ module Speculation
19
19
  ms = @preds.map { |pred| S.dt(pred, x) }
20
20
 
21
21
  if ms.any?(&S.method(:invalid?))
22
- S::INVALID
22
+ :"Speculation/invalid"
23
23
  else
24
24
  ms.reduce(&:merge)
25
25
  end
26
26
  end
27
27
 
28
+ def unform(x)
29
+ @preds.reverse.map { |pred| S.unform(pred, x) }.reduce(&:merge)
30
+ end
31
+
28
32
  def explain(path, via, inn, x)
29
33
  @preds.
30
34
  flat_map { |pred| S.explain1(pred, path, via, inn, x) }.
@@ -36,7 +40,7 @@ module Speculation
36
40
  end
37
41
 
38
42
  def gen(overrides, path, rmap)
39
- return @gen if @gen
43
+ return @gen.call if @gen
40
44
 
41
45
  gens = @preds.
42
46
  map { |pred| S.gensub(pred, overrides, path, rmap) }
@@ -20,12 +20,16 @@ module Speculation
20
20
  value.nil? ? value : @delayed_spec.value!.conform(value)
21
21
  end
22
22
 
23
+ def unform(value)
24
+ value.nil? ? nil : @delayed_spec.value!.unform(value)
25
+ end
26
+
23
27
  def explain(path, via, inn, value)
24
28
  return if S.pvalid?(@delayed_spec.value!, value) || value.nil?
25
29
 
26
30
  Utils.conj(
27
- S.explain1(@pred, Utils.conj(path, ns(S, :pred)), via, inn, value),
28
- :path => Utils.conj(path, ns(S, :nil)), :pred => [NilClass, [value]], :val => value, :via => via, :in => inn
31
+ S.explain1(@pred, Utils.conj(path, :pred), via, inn, value),
32
+ :path => Utils.conj(path, :nil), :pred => [NilClass, [value]], :val => value, :via => via, :in => inn
29
33
  )
30
34
  end
31
35
 
@@ -34,11 +38,11 @@ module Speculation
34
38
  end
35
39
 
36
40
  def gen(overrides, path, rmap)
37
- return @gen if @gen
41
+ return @gen.call if @gen
38
42
 
39
43
  ->(rantly) do
40
44
  rantly.freq([1, Gen.delay { Utils.constantly(nil) }],
41
- [9, Gen.delay { S.gensub(@pred, overrides, Utils.conj(path, ns(S, :pred)), rmap) }])
45
+ [9, Gen.delay { S.gensub(@pred, overrides, Utils.conj(path, :pred), rmap) }])
42
46
  end
43
47
  end
44
48
  end
@@ -19,7 +19,11 @@ module Speculation
19
19
  def conform(value)
20
20
  ret = @delayed_spec.value!.conform(value)
21
21
 
22
- S.invalid?(ret) ? S::INVALID : value
22
+ S.invalid?(ret) ? :"Speculation/invalid" : value
23
+ end
24
+
25
+ def unform(value)
26
+ value
23
27
  end
24
28
 
25
29
  def explain(path, via, inn, value)
@@ -33,13 +33,20 @@ module Speculation
33
33
  end
34
34
  end
35
35
 
36
- S::INVALID
36
+ :"Speculation/invalid"
37
+ end
38
+
39
+ def unform(value)
40
+ spec_name, conformed_val = value
41
+ spec = @named_specs.fetch(spec_name)
42
+
43
+ S.unform(spec, conformed_val)
37
44
  end
38
45
 
39
46
  def explain(path, via, inn, value)
40
47
  return if S.pvalid?(self, value)
41
48
 
42
- @keys.zip(@preds).flat_map do |(key, pred)|
49
+ @named_specs.flat_map do |(key, pred)|
43
50
  next if S.pvalid?(pred, value)
44
51
  S.explain1(pred, Utils.conj(path, key), via, inn, value)
45
52
  end
@@ -50,7 +57,7 @@ module Speculation
50
57
  end
51
58
 
52
59
  def gen(overrides, path, rmap)
53
- return @gen if @gen
60
+ return @gen.call if @gen
54
61
 
55
62
  gs = @keys.zip(@preds).
56
63
  map { |(k, p)|
@@ -10,10 +10,11 @@ module Speculation
10
10
  include NamespacedSymbols
11
11
  S = Speculation
12
12
 
13
- def initialize(predicate, should_conform, gen = nil)
13
+ def initialize(predicate, should_conform, gen, unconformer)
14
14
  @predicate = predicate
15
15
  @should_conform = should_conform
16
16
  @gen = gen
17
+ @unconformer = unconformer
17
18
  end
18
19
 
19
20
  def conform(value)
@@ -26,7 +27,17 @@ module Speculation
26
27
  if @should_conform
27
28
  ret
28
29
  else
29
- ret ? value : S::INVALID
30
+ ret ? value : :"Speculation/invalid"
31
+ end
32
+ end
33
+
34
+ def unform(value)
35
+ return value unless @should_conform
36
+
37
+ if @unconformer
38
+ @unconformer.call(value)
39
+ else
40
+ raise "no unformer for conformer"
30
41
  end
31
42
  end
32
43
 
@@ -37,12 +48,12 @@ module Speculation
37
48
  end
38
49
 
39
50
  def with_gen(gen)
40
- self.class.new(@predicate, @should_conform, gen)
51
+ self.class.new(@predicate, @should_conform, gen, @unconformer)
41
52
  end
42
53
 
43
54
  def gen(_, _, _)
44
55
  if @gen
45
- @gen
56
+ @gen.call
46
57
  else
47
58
  Gen.gen_for_pred(@predicate)
48
59
  end
@@ -16,15 +16,19 @@ module Speculation
16
16
  end
17
17
 
18
18
  def conform(value)
19
- if value.nil? || Utils.collection?(value)
19
+ if value.nil? || Predicates.collection?(value)
20
20
  S.re_conform(@regex, value)
21
21
  else
22
- S::INVALID
22
+ :"Speculation/invalid"
23
23
  end
24
24
  end
25
25
 
26
+ def unform(value)
27
+ S.op_unform(@regex, value)
28
+ end
29
+
26
30
  def explain(path, via, inn, value)
27
- if value.nil? || Utils.collection?(value)
31
+ if value.nil? || Predicates.collection?(value)
28
32
  S.re_explain(path, via, inn, @regex, value || [])
29
33
  else
30
34
  [{ :path => path, :val => value, :via => via, :in => inn }]
@@ -36,7 +40,7 @@ module Speculation
36
40
  end
37
41
 
38
42
  def gen(overrides, path, rmap)
39
- return @gen if @gen
43
+ return @gen.call if @gen
40
44
 
41
45
  S.re_gen(@regex, overrides, path, rmap)
42
46
  end
@@ -22,8 +22,8 @@ module Speculation
22
22
  def conform(collection)
23
23
  specs = @delayed_specs.value!
24
24
 
25
- unless Utils.array?(collection) && collection.count == specs.count
26
- return S::INVALID
25
+ unless Predicates.array?(collection) && collection.count == specs.count
26
+ return :"Speculation/invalid"
27
27
  end
28
28
 
29
29
  return_value = collection.class.new
@@ -32,7 +32,7 @@ module Speculation
32
32
  conformed_value = spec.conform(value)
33
33
 
34
34
  if S.invalid?(conformed_value)
35
- return S::INVALID
35
+ return :"Speculation/invalid"
36
36
  else
37
37
  return_value += [conformed_value]
38
38
  end
@@ -41,9 +41,17 @@ module Speculation
41
41
  return_value
42
42
  end
43
43
 
44
+ def unform(value)
45
+ unless Predicates.array?(value) && value.count == @preds.count
46
+ raise ArgumentError, "unform value must be an array of length #{@preds.count}"
47
+ end
48
+
49
+ @preds.zip(value).map { |(pred, val)| S.unform(pred, val) }
50
+ end
51
+
44
52
  def explain(path, via, inn, value)
45
- if !Utils.array?(value)
46
- [{ :path => path, :val => value, :via => via, :in => inn, :pred => [Utils.method(:array?), [value]] }]
53
+ if !Predicates.array?(value)
54
+ [{ :path => path, :val => value, :via => via, :in => inn, :pred => [Predicates.method(:array?), [value]] }]
47
55
  elsif @preds.count != value.count
48
56
  [{ :path => path, :val => value, :via => via, :in => inn, :pred => [Utils.method(:count_eq), [@preds, value.count]] }]
49
57
  else
@@ -62,7 +70,7 @@ module Speculation
62
70
  end
63
71
 
64
72
  def gen(overrides, path, rmap)
65
- return @gen if @gen
73
+ return @gen.call if @gen
66
74
 
67
75
  gens = @preds.each_with_index.
68
76
  map { |p, i| S.gensub(p, overrides, Utils.conj(path, i), rmap) }
@@ -148,7 +148,7 @@ module Speculation
148
148
  # * :result optional boolean as to whether all generative tests passed
149
149
  # * :num_tests optional number of generative tests ran
150
150
  #
151
- # :failure is a hash that will contain a :"Speculation/failure" key with possible values:
151
+ # :failure is a hash that will contain a :failure key with possible values:
152
152
  #
153
153
  # * :check_failed at least one checked return did not conform
154
154
  # * :no_args_spec no :args spec provided
@@ -174,11 +174,11 @@ module Speculation
174
174
  # @return [Hash]
175
175
  def self.abbrev_result(x)
176
176
  if x[:failure]
177
- x.reject { |k, _| k == ns(:ret) }.
177
+ x.reject { |k, _| k == :ret }.
178
178
  merge(:spec => x[:spec].inspect,
179
179
  :failure => unwrap_failure(x[:failure]))
180
180
  else
181
- x.reject { |k, _| [:spec, ns(:ret)].include?(k) }
181
+ x.reject { |k, _| [:spec, :ret].include?(k) }
182
182
  end
183
183
  end
184
184
 
@@ -223,30 +223,30 @@ module Speculation
223
223
  conformed_args = S.conform(fspec.args, args)
224
224
  conformed_block = S.conform(fspec.block, block) if fspec.block
225
225
 
226
- if conformed_args == S::INVALID
226
+ if conformed_args == :"Speculation/invalid"
227
227
  backtrace = backtrace_relevant_to_instrument(caller)
228
228
 
229
229
  ed = S.
230
230
  _explain_data(fspec.args, [:args], [], [], args).
231
- merge(ns(S, :args) => args, ns(S, :failure) => :instrument, ns(:caller) => backtrace.first)
231
+ merge(:args => args, :failure => :instrument, :caller => backtrace.first)
232
232
 
233
233
  io = StringIO.new
234
234
  S.explain_out(ed, io)
235
235
  msg = io.string
236
236
 
237
- raise Speculation::Error.new("Call to '#{ident}' did not conform to spec:\n #{msg}", ed)
238
- elsif conformed_block == S::INVALID
237
+ raise Speculation::Error.new("Call to '#{ident}' did not conform to spec:\n#{msg}", ed)
238
+ elsif conformed_block == :"Speculation/invalid"
239
239
  backtrace = backtrace_relevant_to_instrument(caller)
240
240
 
241
241
  ed = S.
242
242
  _explain_data(fspec.block, [:block], [], [], block).
243
- merge(ns(S, :block) => block, ns(S, :failure) => :instrument, ns(:caller) => backtrace.first)
243
+ merge(:block => block, :failure => :instrument, :caller => backtrace.first)
244
244
 
245
245
  io = StringIO.new
246
246
  S.explain_out(ed, io)
247
247
  msg = io.string
248
248
 
249
- raise Speculation::Error.new("Call to '#{ident}' did not conform to spec:\n #{msg}", ed)
249
+ raise Speculation::Error.new("Call to '#{ident}' did not conform to spec:\n#{msg}", ed)
250
250
  end
251
251
  end
252
252
 
@@ -271,7 +271,7 @@ module Speculation
271
271
  end
272
272
 
273
273
  def no_fspec(ident, spec)
274
- S::Error.new("#{ident} not spec'ed", :method => ident, :spec => spec, ns(S, :failure) => :no_fspec)
274
+ S::Error.new("#{ident} not spec'ed", :method => ident, :spec => spec, :failure => :no_fspec)
275
275
  end
276
276
 
277
277
  def instrument1(ident, opts)
@@ -343,9 +343,9 @@ module Speculation
343
343
  def explain_check(args, spec, v, role)
344
344
  data = unless S.valid?(spec, v)
345
345
  S._explain_data(spec, [role], [], [], v).
346
- merge(ns(:args) => args,
347
- ns(:val) => v,
348
- ns(S, :failure) => :check_failed)
346
+ merge(:args => args,
347
+ :val => v,
348
+ :failure => :check_failed)
349
349
  end
350
350
 
351
351
  S::Error.new("Specification-based check failed", data).tap do |e|
@@ -355,17 +355,17 @@ module Speculation
355
355
 
356
356
  # Returns true if call passes specs, otherwise returns a hash with
357
357
  # :backtrace, :cause and :data keys. :data will have a
358
- # :"Speculation/failure" key.
358
+ # :failure key.
359
359
  def check_call(method, spec, args, block)
360
360
  conformed_args = S.conform(spec.args, args) if spec.args
361
361
 
362
- if conformed_args == S::INVALID
362
+ if conformed_args == :"Speculation/invalid"
363
363
  return explain_check(args, spec.args, args, :args)
364
364
  end
365
365
 
366
366
  conformed_block = S.conform(spec.block, block) if spec.block
367
367
 
368
- if conformed_block == S::INVALID
368
+ if conformed_block == :"Speculation/invalid"
369
369
  return explain_check(block, spec.block, block, :block)
370
370
  end
371
371
 
@@ -373,7 +373,7 @@ module Speculation
373
373
 
374
374
  conformed_ret = S.conform(spec.ret, ret) if spec.ret
375
375
 
376
- if conformed_ret == S::INVALID
376
+ if conformed_ret == :"Speculation/invalid"
377
377
  return explain_check(args, spec.ret, ret, :ret)
378
378
  end
379
379
 
@@ -415,9 +415,9 @@ module Speculation
415
415
  end
416
416
 
417
417
  def make_check_result(method, spec, check_result)
418
- result = { :spec => spec,
419
- ns(:ret) => check_result,
420
- :method => method }
418
+ result = { :spec => spec,
419
+ :ret => check_result,
420
+ :method => method }
421
421
 
422
422
  if check_result[:result] && check_result[:result] != true
423
423
  result[:failure] = check_result[:result]
@@ -440,8 +440,8 @@ module Speculation
440
440
  check_result = quick_check(method, spec, opts)
441
441
  make_check_result(method, spec, check_result)
442
442
  else
443
- failure = { :info => "No :args spec",
444
- ns(:failure) => :no_args_spec }
443
+ failure = { :info => "No :args spec",
444
+ :failure => :no_args_spec }
445
445
 
446
446
  { :failure => failure,
447
447
  :method => method,
@@ -540,7 +540,7 @@ module Speculation
540
540
  ### check reporting ###
541
541
 
542
542
  def failure_type(x)
543
- x.data[ns(S, :failure)] if x.is_a?(S::Error)
543
+ x.data[:failure] if x.is_a?(S::Error)
544
544
  end
545
545
 
546
546
  def unwrap_failure(x)
@@ -548,7 +548,7 @@ module Speculation
548
548
  end
549
549
 
550
550
  # Returns the type of the check result. This can be any of the
551
- # :"Speculation/failure" symbols documented in 'check', or:
551
+ # :failure symbols documented in 'check', or:
552
552
  #
553
553
  # :check_passed all checked method returns conformed
554
554
  # :check_raised checked fn threw an exception
@@ -5,18 +5,6 @@ require "set"
5
5
  module Speculation
6
6
  # @private
7
7
  module Utils
8
- def self.hash?(x)
9
- x.respond_to?(:store)
10
- end
11
-
12
- def self.array?(x)
13
- x.respond_to?(:at)
14
- end
15
-
16
- def self.collection?(xs)
17
- xs.respond_to?(:each)
18
- end
19
-
20
8
  def self.itself(x)
21
9
  x
22
10
  end
@@ -25,24 +13,6 @@ module Speculation
25
13
  ->(*) { x }
26
14
  end
27
15
 
28
- def self.complement(&f)
29
- ->(*args) { !f.call(*args) }
30
- end
31
-
32
- def self.distinct?(xs)
33
- seen = Set[]
34
-
35
- xs.each do |x|
36
- if seen.include?(x)
37
- return false
38
- else
39
- seen << x
40
- end
41
- end
42
-
43
- true
44
- end
45
-
46
16
  def self.ident?(x)
47
17
  x.is_a?(Symbol) || x.is_a?(MethodIdentifier)
48
18
  end
@@ -59,22 +29,6 @@ module Speculation
59
29
  from.reduce(to) { |memo, obj| conj(memo, obj) }
60
30
  end
61
31
 
62
- def self.count_eq?(coll, count)
63
- coll.count == count
64
- end
65
-
66
- def self.count_between?(coll, min_count, max_count)
67
- coll.count.between?(min_count, max_count)
68
- end
69
-
70
- def self.key?(hash, key)
71
- hash.key?(key)
72
- end
73
-
74
- def self.empty?(coll)
75
- coll.empty?
76
- end
77
-
78
32
  def self.conj(a, b)
79
33
  case a
80
34
  when Array, Set
@@ -87,5 +41,9 @@ module Speculation
87
41
  else raise ArgumentError, "#{a}: must be an Array, Set or Hash"
88
42
  end
89
43
  end
44
+
45
+ def self.sort_descending(coll)
46
+ coll.sort { |a, b| yield(b) <=> yield(a) }
47
+ end
90
48
  end
91
49
  end