Andreas Launila wrote:
> Another variation would be to use a combination of constrain and RSpec's
> should/should_not.
>
> constrain (x + y).must == z
> constrain (x + y).must_not == z
>
> It might chop up the constraint a bit, but it reads well. One could
> possibly also drop the constraint part. It would be nicer with a
> negative form of constrain though (or some other fitting word) since it
> would make it easier to be consistent. As an example there's a domain
> constraint which basically says that x must be in a given range.
> Negating the constraint is possible and might be written as follows.
>
> constrain x.in(1..17)
> constrain x.not_in(1..17)
>
> The above takes a different form than the linear constraints. To make it
> consistent with the mixed variation of linear constraint syntax one
> could rewrite it as.
>
> constrain (1..17).must(include(x))
> constrain (1..17).must_not(include(x))
>

Not necessarily. It could also be written as

constrain x.must(be_in(1..17))
constrain x.must_not(be_in(1..17))

Which is both consistent and reads well (possibly apart from the
parenthesis).

Another way of making it clear that we're specifying constraints could
be to place them all in a block. E.g. something like the following.

in_a_solution do
  (x + y).must == z
  x.must > z
  y.must_not be_in(1..4)
  distinct x,y,z
end

Possibly also sending the variables through the method with the block
(which would clearly declare which variables that constitute a solution).

in_a_solution_with(x,y,z) do |x,y,z|
  (x + y).must == z
  x.must > z
  y.must_not be_in(1..4)
  distinct x,y,z
end

-- 
Andreas Launila