Module: Speculation
- Extended by:
- NamespacedSymbols
- Defined in:
- lib/speculation/spec/f_spec.rb,
lib/speculation.rb,
lib/speculation/gen.rb,
lib/speculation/pmap.rb,
lib/speculation/spec.rb,
lib/speculation/test.rb,
lib/speculation/error.rb,
lib/speculation/utils.rb,
lib/speculation/version.rb,
lib/speculation/predicates.rb,
lib/speculation/spec/or_spec.rb,
lib/speculation/spec/and_spec.rb,
lib/speculation/spec/hash_spec.rb,
lib/speculation/spec/every_spec.rb,
lib/speculation/spec/merge_spec.rb,
lib/speculation/spec/regex_spec.rb,
lib/speculation/spec/tuple_spec.rb,
lib/speculation/method_identifier.rb,
lib/speculation/spec/nilable_spec.rb,
lib/speculation/namespaced_symbols.rb,
lib/speculation/spec/predicate_spec.rb,
lib/speculation/spec/nonconforming_spec.rb
Overview
This is a Ruby translation of clojure.spec:
https://github.com/clojure/clojure/blob/master/src/clj/clojure/spec.clj
All credit belongs with Rich Hickey and contributors for their original work.
Defined Under Namespace
Modules: Gen, NamespacedSymbols, Predicates, Test Classes: Error
Constant Summary collapse
- VERSION =
"0.4.2".freeze
Class Attribute Summary collapse
-
.check_asserts ⇒ Object
Enables or disables spec asserts.
-
.coll_check_limit ⇒ Object
The number of elements validated in a collection spec’ed with ‘every’.
-
.coll_error_limit ⇒ Object
The number of errors reported by explain in a collection spec’ed with ‘every’.
-
.fspec_iterations ⇒ Object
The number of times an anonymous fn specified by fspec will be (generatively) tested during conform.
-
.recursion_limit ⇒ Object
A soft limit on how many times a branching spec (or/alt/zero_or_more/opt keys) can be recursed through during generation.
Class Method Summary collapse
-
.alt(kv_specs) ⇒ Hash
Regex op that returns a two item array containing the key of the first matching pred and the corresponding value.
-
.and(*preds) ⇒ Spec
A spec that returns the conformed value.
- .and_keys(*ks) ⇒ Object
-
.assert(spec, x) ⇒ Object
Can be enabled or disabled at runtime: - enabled/disabled by setting ‘check_asserts`.
-
.cat(named_specs) ⇒ Hash
Regex op that matches (all) values in sequence, returning a map containing the keys of each pred and the corresponding value.
-
.coll_of(pred, opts = {}) ⇒ Spec
Returns a spec for a collection of items satisfying pred.
-
.conform(spec, value) ⇒ Symbol, Object
:Speculation/invalid if value does not match spec, else the (possibly destructured) value.
-
.conformer(f, unformer = nil) ⇒ Spec
A spec that uses pred as a predicate/conformer.
-
.constrained(re, *preds) ⇒ Hash
Regex-op that consumes input as per re but subjects the resulting value to the conjunction of the predicates, and any conforming they might perform.
-
.date_in(date_range) ⇒ Object
Spec that validates dates in the given range.
-
.def(key, spec) ⇒ Symbol, Method
Given a namespace-qualified symbol key, and a spec, spec name, predicate or regex-op makes an entry in the registry mapping key to the spec.
-
.every(pred, opts = {}) ⇒ Spec
Spec that validates collection elements against pred.
-
.every_kv(kpred, vpred, options = {}) ⇒ Spec
Like ‘every’ but takes separate key and val preds and works on associative collections.
-
.exercise(spec, n: 10, overrides: {}) ⇒ Array
Generates a number (default 10) of values compatible with spec and maps conform over them, returning a sequence of [val conformed-val] tuples.
-
.exercise_fn(method, n = 10, fspec = nil) ⇒ Array
Exercises the method by applying it to n (default 10) generated samples of its args spec.
-
.explain(spec, x) ⇒ Object
Given a spec and a value that fails to conform, prints an explaination to STDOUT.
-
.explain_data(spec, x) ⇒ nil, Hash
Given a spec and a value x which ought to conform, returns nil if x conforms, else a hash with at least the key :problems whose value is a collection of problem-hashes, where problem-hash has at least :path :pred and :val keys describing the predicate and the value that failed at that path.
- .explain_out(ed, out = STDOUT) ⇒ Object
-
.explain_str(spec, x) ⇒ String
A human readable explaination.
-
.fdef(method, spec) ⇒ Method
Once registered, specs are checked by instrument and tested by the runner Speculation::Test.check.
-
.float_in(min: nil, max: nil, infinite: true, nan: true) ⇒ Spec
That validates floats.
-
.fspec(args: nil, ret: nil, fn: nil, block: nil, gen: nil) ⇒ Spec
Takes :args :ret and (optional) :block and :fn kwargs whose values are preds and returns a spec whose conform/explain take a method/proc and validates it using generative testing.
-
.gen(spec, overrides = nil) ⇒ Proc
Given a spec, returns the generator for it, or raises if none can be constructed.
-
.get_spec(key) ⇒ Spec?
Spec registered for key, or nil.
-
.hash_of(kpred, vpred, options = {}) ⇒ Spec
Returns a spec for a hash whose keys satisfy kpred and vals satisfy vpred.
-
.int_in(range) ⇒ Object
Spec that validates ints in the given range.
-
.invalid?(value) ⇒ Boolean
True if value is the result of an unsuccessful conform.
-
.keys(req: [], opt: [], req_un: [], opt_un: [], gen: nil) ⇒ Object
Creates and returns a hash validating spec.
-
.merge(*preds) ⇒ Spec
A spec that returns a conformed hash satisfying all of the specs.
-
.nilable(pred) ⇒ Spec
A spec that accepts nil and values satisfying pred.
-
.nonconforming(spec) ⇒ Spec
A spec that has the same properies as the given spec, except ‘conform` will return the original (not the conformed) value.
-
.one_or_more(pred) ⇒ Hash
an array of matches.
-
.or(key_preds) ⇒ Spec
A destructuring spec that returns a two element array containing the key of the first matching pred and the corresponding value.
- .or_keys(*ks) ⇒ Object
-
.regex?(x) ⇒ Hash, false
X if x is a (Speculation) regex op, else logical false.
-
.registry ⇒ Hash
The registry hash.
-
.spec(pred, gen: nil) ⇒ Spec
NOTE: it is not generally necessary to wrap predicates in spec when using ‘S.def` etc., only to attach a unique generator.
-
.spec?(x) ⇒ Spec, false
X if x is a spec, else false.
-
.time_in(time_range) ⇒ Object
Spec that validates times in the given range.
-
.tuple(*preds) ⇒ Spec
A spec for a tuple, an array where each element conforms to the corresponding pred.
-
.unform(spec, value) ⇒ Object
Value with conform destructuring undone.
-
.valid?(spec, x) ⇒ Boolean
True when x is valid for spec.
-
.with_gen(spec, &gen) ⇒ Spec
Takes a spec and a no-arg generator returning block and returns a version of the spec that uses that generator.
-
.zero_or_more(pred) ⇒ Hash
Regex op that matches zero or more values matching pred.
-
.zero_or_one(pred) ⇒ Hash
single value (not a collection) if matched.
Methods included from NamespacedSymbols
namespace, namespaced_name, ns, symbol
Class Attribute Details
.check_asserts ⇒ Object
Enables or disables spec asserts. Defaults to false.
24 25 26 |
# File 'lib/speculation.rb', line 24 def check_asserts @check_asserts end |
.coll_check_limit ⇒ Object
The number of elements validated in a collection spec’ed with ‘every’.
35 36 37 |
# File 'lib/speculation.rb', line 35 def coll_check_limit @coll_check_limit end |
.coll_error_limit ⇒ Object
The number of errors reported by explain in a collection spec’ed with ‘every’
39 40 41 |
# File 'lib/speculation.rb', line 39 def coll_error_limit @coll_error_limit end |
.fspec_iterations ⇒ Object
The number of times an anonymous fn specified by fspec will be (generatively) tested during conform.
32 33 34 |
# File 'lib/speculation.rb', line 32 def fspec_iterations @fspec_iterations end |
.recursion_limit ⇒ Object
A soft limit on how many times a branching spec (or/alt/zero_or_more/opt keys) can be recursed through during generation. After this a non-recursive branch will be chosen.
28 29 30 |
# File 'lib/speculation.rb', line 28 def recursion_limit @recursion_limit end |
Class Method Details
.alt(kv_specs) ⇒ Hash
Returns regex op that returns a two item array containing the key of the first matching pred and the corresponding value. Thus can be destructured to refer generically to the components of the return.
526 527 528 |
# File 'lib/speculation.rb', line 526 def self.alt(kv_specs) _alt(kv_specs.values, kv_specs.keys).merge(:id => SecureRandom.uuid) end |
.and(*preds) ⇒ Spec
Returns a spec that returns the conformed value. Successive conformed values propagate through rest of predicates.
410 411 412 |
# File 'lib/speculation.rb', line 410 def self.and(*preds) AndSpec.new(preds) end |
.and_keys(*ks) ⇒ Object
391 392 393 |
# File 'lib/speculation.rb', line 391 def self.and_keys(*ks) [:"Speculation/and", *ks] end |
.assert(spec, x) ⇒ Object
Can be enabled or disabled at runtime:
-
enabled/disabled by setting ‘check_asserts`.
-
enabled by setting environment variable SPECULATION_CHECK_ASSERTS to the string “true”
Defaults to false if not set.
59 60 61 62 63 64 65 66 67 68 |
# File 'lib/speculation.rb', line 59 def self.assert(spec, x) return x unless check_asserts return x if valid?(spec, x) ed = _explain_data(spec, [], [], [], x).merge(:failure => :assertion_failed) out = StringIO.new explain_out(ed, out) raise Speculation::Error.new("Spec assertion failed\n#{out.string}", ed) end |
.cat(named_specs) ⇒ Hash
Returns regex op that matches (all) values in sequence, returning a map containing the keys of each pred and the corresponding value.
535 536 537 538 539 540 |
# File 'lib/speculation.rb', line 535 def self.cat(named_specs) keys = named_specs.keys predicates = named_specs.values pcat(:keys => keys, :predicates => predicates, :return_value => {}) end |
.coll_of(pred, opts = {}) ⇒ Spec
Returns a spec for a collection of items satisfying pred. Unlike ‘every’, coll_of will exhaustively conform every value.
Same options as ‘every’. conform will produce a collection corresponding to :into if supplied, else will match the input collection, avoiding rebuilding when possible.
476 477 478 |
# File 'lib/speculation.rb', line 476 def self.coll_of(pred, opts = {}) every(pred, :conform_all => true, **opts) end |
.conform(spec, value) ⇒ Symbol, Object
Returns :Speculation/invalid if value does not match spec, else the (possibly destructured) value.
136 137 138 139 |
# File 'lib/speculation.rb', line 136 def self.conform(spec, value) spec = MethodIdentifier(spec) specize(spec).conform(value) end |
.conformer(f, unformer = nil) ⇒ Spec
Returns a spec that uses pred as a predicate/conformer.
555 556 557 |
# File 'lib/speculation.rb', line 555 def self.conformer(f, unformer = nil) spec_impl(f, nil, true, unformer) end |
.constrained(re, *preds) ⇒ Hash
Returns regex-op that consumes input as per re but subjects the resulting value to the conjunction of the predicates, and any conforming they might perform.
547 548 549 |
# File 'lib/speculation.rb', line 547 def self.constrained(re, *preds) { :op => :amp, :p1 => re, :predicates => preds } end |
.date_in(date_range) ⇒ Object
Returns Spec that validates dates in the given range.
109 110 111 112 |
# File 'lib/speculation.rb', line 109 def self.date_in(date_range) spec(self.and(Date, ->(x) { date_range.cover?(x) }), :gen => ->() { ->(_) { rand(date_range) } }) end |
.def(key, spec) ⇒ Symbol, Method
Given a namespace-qualified symbol key, and a spec, spec name, predicate or regex-op makes an entry in the registry mapping key to the spec
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 |
# File 'lib/speculation.rb', line 292
def(key, spec)
key = MethodIdentifier(key)
unless Utils.ident?(key) && (!key.is_a?(Symbol) || NamespacedSymbols.namespace(key))
raise ArgumentError, "key must be a namespaced Symbol, e.g. #{ns(:my_spec)}, or a Method"
end
spec = if spec?(spec) || regex?(spec) || registry[spec]
spec
else
spec_impl(spec, nil, false)
end
@registry_ref.swap do |reg|
reg.merge(key => with_name(spec, key)).freeze
end
key.is_a?(MethodIdentifier) ? key.get_method : key
end
|
.every(pred, opts = {}) ⇒ Spec
that ‘every’ does not do exhaustive checking, rather it samples ‘coll_check_limit` elements. Nor (as a result) does it do any conforming of elements. ’explain’ will report at most coll_error_limit problems. Thus ‘every’ should be suitable for potentially large collections.
Returns spec that validates collection elements against pred.
443 444 445 446 447 |
# File 'lib/speculation.rb', line 443 def self.every(pred, opts = {}) gen = opts.delete(:gen) EverySpec.new(pred, opts, gen) end |
.every_kv(kpred, vpred, options = {}) ⇒ Spec
Like ‘every’ but takes separate key and val preds and works on associative collections.
Same options as ‘every’, :into defaults to {}
459 460 461 462 463 |
# File 'lib/speculation.rb', line 459 def self.every_kv(kpred, vpred, = {}) every(tuple(kpred, vpred), :kfn => ->(_i, v) { v.first }, :into => {}, **) end |
.exercise(spec, n: 10, overrides: {}) ⇒ Array
Generates a number (default 10) of values compatible with spec and maps conform over them, returning a sequence of [val conformed-val] tuples.
646 647 648 649 650 |
# File 'lib/speculation.rb', line 646 def self.exercise(spec, n: 10, overrides: {}) Gen.sample(gen(spec, overrides), n).map { |value| [value, conform(spec, value)] } end |
.exercise_fn(method, n = 10, fspec = nil) ⇒ Array
Exercises the method by applying it to n (default 10) generated samples of its args spec. When fspec is supplied its arg spec is used, and method can be a proc.
659 660 661 662 663 664 665 666 667 |
# File 'lib/speculation.rb', line 659 def self.exercise_fn(method, n = 10, fspec = nil) fspec ||= get_spec(method) raise ArgumentError, "No :args spec found for #{method}" unless fspec && fspec.args block_gen = fspec.block ? gen(fspec.block) : Utils.constantly(nil) gen = Gen.tuple(gen(fspec.args), block_gen) Gen.sample(gen, n).map { |(args, block)| [args, block, method.call(*args, &block)] } end |
.explain(spec, x) ⇒ Object
Given a spec and a value that fails to conform, prints an explaination to STDOUT
229 230 231 |
# File 'lib/speculation.rb', line 229 def self.explain(spec, x) explain_out(explain_data(spec, x)) end |
.explain_data(spec, x) ⇒ nil, Hash
Given a spec and a value x which ought to conform, returns nil if x conforms, else a hash with at least the key :problems whose value is a collection of problem-hashes, where problem-hash has at least :path :pred and :val keys describing the predicate and the value that failed at that path.
187 188 189 190 191 |
# File 'lib/speculation.rb', line 187 def self.explain_data(spec, x) spec = MethodIdentifier(spec) name = spec_name(spec) _explain_data(spec, [], Array(name), [], x) end |
.explain_out(ed, out = STDOUT) ⇒ Object
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/speculation.rb', line 195 def self.explain_out(ed, out = STDOUT) return out.puts("Success!") unless ed problems = Utils.sort_descending(ed.fetch(:problems)) { |prob| prob[:path] } problems.each do |prob| path, pred, val, reason, via, inn = prob.values_at(:path, :pred, :val, :reason, :via, :in) out.print("In: ", inn.to_a.inspect, " ") unless inn.empty? out.print("val: ", val.inspect, " fails") out.print(" spec: ", via.last.inspect) unless via.empty? out.print(" at: ", path.to_a.inspect) unless path.empty? out.print(" predicate: ", pred.inspect) out.print(", ", reason.inspect) if reason prob.each do |k, v| unless [:path, :pred, :val, :reason, :via, :in].include?(k) out.print("\n\t ", k.inspect, PP.pp(v, String.new)) end end out.puts end ed.each do |k, v| out.puts("#{k.inspect} #{PP.pp(v, String.new)}") unless k == :problems end nil end |
.explain_str(spec, x) ⇒ String
Returns a human readable explaination.
236 237 238 239 240 |
# File 'lib/speculation.rb', line 236 def self.explain_str(spec, x) out = StringIO.new explain_out(explain_data(spec, x), out) out.string end |
.fdef(method, spec) ⇒ Method
Note that :fn specs require the presence of :args and :ret specs to conform values, and so :fn specs will be ignored if :args or :ret are missing.
Once registered, specs are checked by instrument and tested by the runner Speculation::Test.check
610 611 612 613 |
# File 'lib/speculation.rb', line 610 def self.fdef(method, spec) self.def(MethodIdentifier(method), fspec(spec)) method end |
.float_in(min: nil, max: nil, infinite: true, nan: true) ⇒ Spec
Returns that validates floats.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/speculation.rb', line 75 def self.float_in(min: nil, max: nil, infinite: true, nan: true) preds = [Float] preds.push(->(x) { !x.nan? }) unless nan preds.push(->(x) { !x.infinite? }) unless infinite preds.push(->(x) { x <= max }) if max preds.push(->(x) { x >= min }) if min min ||= Float::MIN max ||= Float::MAX gens = [[20, ->(_) { rand(min.to_f..max.to_f) }]] gens << [1, ->(r) { r.choose(Float::INFINITY, -Float::INFINITY) }] if infinite gens << [1, ->(_) { Float::NAN }] if nan spec(self.and(*preds), :gen => ->() { ->(rantly) { rantly.freq(*gens) } }) end |
.fspec(args: nil, ret: nil, fn: nil, block: nil, gen: nil) ⇒ Spec
Takes :args :ret and (optional) :block and :fn kwargs whose values are preds and returns a spec whose conform/explain take a method/proc and validates it using generative testing. The conformed value is always the method itself.
fspecs can generate procs that validate the arguments and fabricate a return value compliant with the :ret spec, ignoring the :fn spec if present.
575 576 577 |
# File 'lib/speculation.rb', line 575 def self.fspec(args: nil, ret: nil, fn: nil, block: nil, gen: nil) FSpec.new(:args => spec(args), :ret => spec(ret), :fn => spec(fn), :block => spec(block), :gen => gen) end |
.gen(spec, overrides = nil) ⇒ Proc
Given a spec, returns the generator for it, or raises if none can be constructed.
Optionally an overrides hash can be provided which should map spec names or paths (array of symbols) to no-arg generator Procs. These will be used instead of the generators at those names/paths. Note that parent generator (in the spec or overrides map) will supersede those of any subtrees. A generator for a regex op must always return a sequential collection (i.e. a generator for Speculation.zero_or_more should return either an empty array or an array with one item in it)
273 274 275 276 |
# File 'lib/speculation.rb', line 273 def self.gen(spec, overrides = nil) spec = MethodIdentifier(spec) gensub(spec, overrides, [], :recursion_limit => recursion_limit) end |
.get_spec(key) ⇒ Spec?
Returns spec registered for key, or nil.
320 321 322 |
# File 'lib/speculation.rb', line 320 def self.get_spec(key) registry[MethodIdentifier(key)] end |
.hash_of(kpred, vpred, options = {}) ⇒ Spec
Returns a spec for a hash whose keys satisfy kpred and vals satisfy vpred. Unlike ‘every_kv’, hash_of will exhaustively conform every value.
Same options as ‘every’, :kind defaults to ‘Speculation::Predicates.hash?`, with the addition of:
:conform_keys - conform keys as well as values (default false)
493 494 495 496 497 |
# File 'lib/speculation.rb', line 493 def self.hash_of(kpred, vpred, = {}) every_kv(kpred, vpred, :kind => Predicates.method(:hash?), :conform_all => true, **) end |
.int_in(range) ⇒ Object
Returns Spec that validates ints in the given range.
95 96 97 98 |
# File 'lib/speculation.rb', line 95 def self.int_in(range) spec(self.and(Integer, ->(x) { range.include?(x) }), :gen => ->() { ->(_) { rand(range) } }) end |
.invalid?(value) ⇒ Boolean
Returns true if value is the result of an unsuccessful conform.
128 129 130 |
# File 'lib/speculation.rb', line 128 def self.invalid?(value) value.equal?(:"Speculation/invalid") end |
.keys(req: [], opt: [], req_un: [], opt_un: [], gen: nil) ⇒ Object
Creates and returns a hash validating spec. :req and :opt are both arrays of namespaced-qualified keywords (e.g. “:MyApp/foo”). The validator will ensure the :req keys are present. The :opt keys serve as documentation and may be used by the generator.
The :req key array supports ‘and_keys’ and ‘or_keys’ for key groups:
S.keys(req: [ns(:x), ns(:y), S.or_keys(ns(:secret), S.and_keys(ns(:user), ns(:pwd)))],
opt: [ns(:z)])
There are also _un versions of :req and :opt. These allow you to connect unqualified keys to specs. In each case, fully qualfied keywords are passed, which name the specs, but unqualified keys (with the same name component) are expected and checked at conform-time, and generated during gen:
S.keys(req_un: [:"MyApp/x", :"MyApp/y"])
The above says keys :x and :y are required, and will be validated and generated by specs (if they exist) named :“MyApp/x” :“MyApp/y” respectively.
In addition, the values of all namespace-qualified keys will be validated (and possibly destructured) by any registered specs. Note: there is no support for inline value specification, by design.
381 382 383 |
# File 'lib/speculation.rb', line 381 def self.keys(req: [], opt: [], req_un: [], opt_un: [], gen: nil) HashSpec.new(req, opt, req_un, opt_un, gen) end |
.merge(*preds) ⇒ Spec
Unlike ‘and’, merge can generate maps satisfying the union of the predicates.
Returns a spec that returns a conformed hash satisfying all of the specs.
417 418 419 |
# File 'lib/speculation.rb', line 417 def self.merge(*preds) MergeSpec.new(preds) end |
.nilable(pred) ⇒ Spec
Returns a spec that accepts nil and values satisfying pred.
627 628 629 |
# File 'lib/speculation.rb', line 627 def self.nilable(pred) NilableSpec.new(pred) end |
.nonconforming(spec) ⇒ Spec
Returns a spec that has the same properies as the given spec, except ‘conform` will return the original (not the conformed) value. Note, will specize regex ops.
635 636 637 |
# File 'lib/speculation.rb', line 635 def self.nonconforming(spec) NonconformingSpec.new(spec) end |
.one_or_more(pred) ⇒ Hash
an array of matches
509 510 511 |
# File 'lib/speculation.rb', line 509 def self.one_or_more(pred) pcat(:predicates => [pred, rep(pred, pred, [], true)], :return_value => []) end |
.or(key_preds) ⇒ Spec
Returns a destructuring spec that returns a two element array containing the key of the first matching pred and the corresponding value. Thus the ‘key’ and ‘val’ functions can be used to refer generically to the components of the tagged return.
401 402 403 |
# File 'lib/speculation.rb', line 401 def self.or(key_preds) OrSpec.new(key_preds) end |
.or_keys(*ks) ⇒ Object
386 387 388 |
# File 'lib/speculation.rb', line 386 def self.or_keys(*ks) [:"Speculation/or", *ks] end |
.regex?(x) ⇒ Hash, false
Returns x if x is a (Speculation) regex op, else logical false.
122 123 124 |
# File 'lib/speculation.rb', line 122 def self.regex?(x) x.is_a?(Hash) && x[:op] && x end |
.registry ⇒ Hash
Returns the registry hash.
314 315 316 |
# File 'lib/speculation.rb', line 314 def self.registry @registry_ref.value end |
.spec(pred, gen: nil) ⇒ Spec
NOTE: it is not generally necessary to wrap predicates in spec when using ‘S.def` etc., only to attach a unique generator.
Optionally takes :gen generator function, which must be a no-arg proc that returns a generator (proc that receives a Rantly instance) that generates a valid value.
347 348 349 |
# File 'lib/speculation.rb', line 347 def self.spec(pred, gen: nil) spec_impl(pred, gen, false) if pred end |
.spec?(x) ⇒ Spec, false
Returns x if x is a spec, else false.
116 117 118 |
# File 'lib/speculation.rb', line 116 def self.spec?(x) x if x.is_a?(Spec) end |
.time_in(time_range) ⇒ Object
Returns Spec that validates times in the given range.
102 103 104 105 |
# File 'lib/speculation.rb', line 102 def self.time_in(time_range) spec(self.and(Time, ->(x) { time_range.cover?(x) }), :gen => ->() { ->(_) { rand(time_range) } }) end |
.tuple(*preds) ⇒ Spec
Returns a spec for a tuple, an array where each element conforms to the corresponding pred. Each element will be referred to in paths using its ordinal.
583 584 585 |
# File 'lib/speculation.rb', line 583 def self.tuple(*preds) TupleSpec.new(preds) end |
.unform(spec, value) ⇒ Object
Returns value with conform destructuring undone.
144 145 146 |
# File 'lib/speculation.rb', line 144 def self.unform(spec, value) specize(spec).unform(value) end |
.valid?(spec, x) ⇒ Boolean
Returns true when x is valid for spec.
618 619 620 621 622 623 |
# File 'lib/speculation.rb', line 618 def self.valid?(spec, x) spec = MethodIdentifier(spec) spec = specize(spec) !invalid?(spec.conform(x)) end |
.with_gen(spec, &gen) ⇒ Spec
Takes a spec and a no-arg generator returning block and returns a version of the spec that uses
that generator
153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/speculation.rb', line 153 def self.with_gen(spec, &gen) if gen && !gen.arity.zero? raise ArgumentError, "gen must be a no-arg block that returns a generator" end if regex?(spec) spec.merge(:gfn => gen) else specize(spec).with_gen(gen) end end |
.zero_or_more(pred) ⇒ Hash
Returns regex op that matches zero or more values matching pred. Produces an array of matches iff there is at least one match.
502 503 504 |
# File 'lib/speculation.rb', line 502 def self.zero_or_more(pred) rep(pred, pred, [], false) end |
.zero_or_one(pred) ⇒ Hash
single value (not a collection) if matched.
516 517 518 |
# File 'lib/speculation.rb', line 516 def self.zero_or_one(pred) _alt([pred, accept(:nil)], nil) end |