Conditional Specs - english/speculation GitHub Wiki

Speculation allows you to combine specs with S.or and S.and.

S.and

S.and allows you to construct a spec using multiple specs. For a given value to conform to the spec, it must conform to every constituent spec.

S.valid? S.and(Integer, ->(i) { i.positive? }), "foo" # => false
S.valid? S.and(Integer, ->(i) { i.positive? }), -1    # => false
S.valid? S.and(Integer, ->(i) { i.positive? }), 1     # => true

Combining specs with S.and allows you reuse specs in different contexts. It also makes it easy to avoid calling predicate methods on objects that might not implement them. For example:

is_positive = ->(i) { i.positive? }
S.valid? is_positive, "foo" # NoMethodError: undefined method `positive?' for "foo":String

S.or

Sometimes there are multiple ways a given data structure can be considered valid. S.or will consider a value valid if it conforms to any of its constituent specs:

S.valid? S.or(s: String, i: Integer), "foo" # => true
S.valid? S.or(s: String, i: Integer), 1     # => true
S.valid? S.or(s: String, i: Integer), 1.0   # => false

S.explain S.or(s: String, i: Integer), 1.0
# => val: 1.0 fails at: [:s] predicate: [String, [1.0]]
# => val: 1.0 fails at: [:i] predicate: [Integer, [1.0]]

It can be useful to know how a data structure conformed to an or spec. Because we've labelled our specs, S.conform will tell us which spec the value conformed to:

S.conform S.or(s: String, i: Integer), "foo" # => [:s, "foo"]
S.conform S.or(s: String, i: Integer), 1     # => [:i, 1]
⚠️ **GitHub.com Fallback** ⚠️