Skip to content

Predicates

Jamie English edited this page Jul 21, 2017 · 4 revisions

Predicates are at the core of Speculation. They are used to validate simple values and are built upon to conform complex, nested and branching data structures. They enable runtime instrumentation of methods, verifying arguments and return values. In many cases they provide random data generation for free.

There are several ways to define a basic predicate:

Proc (call-able)

This is the most versatile kind of predicate. If the Proc's return value is truthy then it's valid, otherwise it's invalid.

  • S.valid?(->(x) { x.is_a?(Integer) }, 1) # => true
  • S.valid?(->(x) { x.even? }, 1) # => false
  • S.valid?(:odd?.to_proc, 1) # => true
  • S.valid?(->(x) { x.match?(/foo/) }, "bar") # => false
  • S.valid?(->(x) { x.nil? || x.even? }, nil) # => true

Moreover, anything that responds to #call can be used in this fashion.

class MyPredicate
  def call(x)
    x.between?(1..100)
  end
end

S.valid?(MyPredicate.new, 5) # => true

Regexp

A regular expression is a good choice when validating a string is in the expected format,.

S.valid?(/\Afoo\z/i, "FoO")  # => true
S.valid?(/\Afoo\z/i, "bar")  # => false
S.valid?(/baz/, "foobarbaz") # => true

Class or Module

is_a? checks are performed when supplying a Class or Module as the predicate.

S.valid?(Integer, 1)      # => true
S.valid?(Array, [1])      # => true
S.valid?(Enumerable, [1]) # => true
S.valid?(String, 1)       # => false

Set

Set membership is performed when a set is given as a predicate.

S.valid?(Set[1, 2, 3], 1)        # => true
S.valid?(Set[:a, :b, :c], 1)     # => false
S.valid?(Set.new("a".."z"), "g") # => true
Clone this wiki locally