# Validates\_mutual\_exclusivity

**URL:** <https://www.ruby-forum.com/t/validates-mutual-exclusivity/106754>\
**Category:** Rails\
**Created:** [June 13, 2007, 8:48pm UTC](https://www.ruby-forum.com/t/validates-mutual-exclusivity/106754 "2007-06-13T20:48:11Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Phlip](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/ed8c4c/32.png) [@Phlip](https://www.ruby-forum.com/u/Phlip)\
**Post date:** [June 13, 2007, 8:48pm UTC](https://www.ruby-forum.com/t/validates-mutual-exclusivity/106754/1 "2007-06-13T20:48:11Z")

</div>

Railsters:

We have a model with 2 variables. Neither may be nil or non-nil at the  
same time. They must pass this truth table:

def test\_mutually\_exclude  
assert\_equal false, Model.new.save  
assert\_equal false, Model.new(:thing1 =\> 2, :thing2 =\> 1).save  
assert\_equal true, Model.new(:thing1 =\> 2).save  
assert\_equal true, Model.new(:thing2 =\> 1).save  
end

thing1 and thing2 default to nil. We need only one Thing at a time,  
and having them both together causes trouble.

What’s the minimum set of validates\_presence\_of or similar validators  
required to enforce this? Note the error message itself is not  
important.

We have it down to 4 lines, which seems absurdly excessive for Rails,  
so what are we missing?

Even better, could someone write validates\_mutual\_exclusivity, in the  
usual 2 lines of code or less?

–  
Phlip  
[http://www.oreilly.com/catalog/9780596510657/](http://www.oreilly.com/catalog/9780596510657/)  
“Test Driven Ajax (on Rails)”  
assert\_xpath, assert\_javascript, & assert\_ajax

---

<div class="post-metadata">

**Author:** ![Phlip](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/ed8c4c/32.png) [@Phlip](https://www.ruby-forum.com/u/Phlip)\
**Post date:** [June 13, 2007, 9:32pm UTC](https://www.ruby-forum.com/t/validates-mutual-exclusivity/106754/2 "2007-06-13T21:32:23Z")

</div>

On 6/13/07, Phlip [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> ```
> assert_equal true, Model.new(:thing2 => 1).save
> 
> ```
> 
> so what are we missing?
> 
> Even better, could someone write validates\_mutual\_exclusivity, in the  
> usual 2 lines of code or less?

How about something like:

validates\_each :thing1 do |row, attr, value|  
row.errors.add\_to\_base ‘Supply thing1 or thing2, but not both’  
unless value.blank? ^ row.thing2.blank?  
end

---

<div class="post-metadata">

**Author:** ![Phlip](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/ed8c4c/32.png) [@Phlip](https://www.ruby-forum.com/u/Phlip)\
**Post date:** [June 13, 2007, 9:33pm UTC](https://www.ruby-forum.com/t/validates-mutual-exclusivity/106754/3 "2007-06-13T21:33:23Z")

</div>

On 6/13/07, Bob S. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> How about something like:
> 
> validates\_each :thing1 do |row, attr, value|  
> row.errors.add\_to\_base ‘Supply thing1 or thing2, but not both’  
> unless value.blank? ^ row.thing2.blank?  
> end

Darned line wrapping. That “unless” part goes with the line above.

---

<div class="post-metadata">

**Author:** ![Phlip](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/ed8c4c/32.png) [@Phlip](https://www.ruby-forum.com/u/Phlip)\
**Post date:** [June 14, 2007, 5:45am UTC](https://www.ruby-forum.com/t/validates-mutual-exclusivity/106754/4 "2007-06-14T05:45:22Z")

</div>

> > Even better, could someone write validates\_mutual\_exclusivity, in the  
> > usual 2 lines of code or less?
> 
> How about something like:

validates\_each :thing1 do |row, attr, value|  
row.errors.add\_to\_base ‘Supply thing1 or thing2, but not both’   
unless value.blank? ^ row.thing2.blank?  
end

Thanks! Now I remember why I don’t twiddle bits any more! I will report  
back  
if it works.

Next riddle: Our “Community Agreements” discourage ‘unless’. So if you  
distribute a ‘not’ into a boolean expression, you get if value.nil? Q  
value.nil?, where Q is the opposite operator from ^, right? If so,  
what’s Q?

–  
Phlip  
[Test Driven Ajax (on Rails) [Book]](http://www.oreilly.com/catalog/9780596510657/)  
“Test Driven Ajax (on Rails)”  
assert\_xpath, assert\_javascript, & assert\_ajax

---

<div class="post-metadata">

**Author:** ![Phlip](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/ed8c4c/32.png) [@Phlip](https://www.ruby-forum.com/u/Phlip)\
**Post date:** [June 14, 2007, 5:19pm UTC](https://www.ruby-forum.com/t/validates-mutual-exclusivity/106754/5 "2007-06-14T17:19:57Z")

</div>

Rob B. wrote:

> > validates\_each :thing1 do |row, attr, value|  
> > row.errors.add\_to\_base ‘Supply thing1 or thing2, but not both’   
> > unless value.blank? ^ row.thing2.blank?  
> > end

That one worked.

> > Next riddle: Our “Community Agreements” discourage ‘unless’. So if you  
> > distribute a ‘not’ into a boolean expression, you get if value.nil? Q  
> > value.nil?, where Q is the opposite operator from ^, right? If so,  
> > what’s Q?

> expression, then don’t take the last hop (or throw the !(a^b) after  
> the ‘if’).

Now now - someone with “agile” in their home URI should know better  
than to call any Community Agreement “silly”.

Our coding standard challenges us to reduce unless abuse, It’s still  
better than stating the negative at the end of an error line:

errors.add “bad thing should not happen” unless good\_thing()

We stayed with the unless. Thanks for the math - that was awesome!

–  
Phlip

---

<div class="post-metadata">

**Author:** ![Phlip](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/ed8c4c/32.png) [@Phlip](https://www.ruby-forum.com/u/Phlip)\
**Post date:** [June 14, 2007, 6:05pm UTC](https://www.ruby-forum.com/t/validates-mutual-exclusivity/106754/6 "2007-06-14T18:05:22Z")

</div>

On Jun 14, 2007, at 11:19 AM, Phlip wrote:

> errors.add “bad thing should not happen” unless good\_thing()
> 
> We stayed with the unless. Thanks for the math - that was awesome!
> 
> –  
> Phlip

Great to see that the “standard” isn’t absolute – I guess i read  
“discourage” as “prohibit”. Anyway, I wouldn’t be agile if I didn’t  
challenge things that conflicted with my own opinions. (And  
‘arbitrary’ limitations on the use of language features is one of  
those things that I despise.)

-Rob

Rob B. [http://agileconsultingllc.com](http://agileconsultingllc.com)  
removed\_email\_address@domain.invalid

---

<div class="post-metadata">

**Author:** ![Phlip](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/ed8c4c/32.png) [@Phlip](https://www.ruby-forum.com/u/Phlip)\
**Post date:** [June 14, 2007, 6:19am UTC](https://www.ruby-forum.com/t/validates-mutual-exclusivity/106754/7 "2007-06-14T06:19:24Z")

</div>

On Jun 13, 2007, at 11:44 PM, Phlip wrote:

> Phlip  
> [Test Driven Ajax (on Rails) [Book]](http://www.oreilly.com/catalog/9780596510657/)  
> “Test Driven Ajax (on Rails)”  
> assert\_xpath, assert\_javascript, & assert\_ajax

!(a ^ b)

expanding the exclusive-or  
!((a & !b) | (!a & b))

distribute the outer !  
!(a & !b) & !(!a & b)

distribute the inner !'s  
(!a | !(!b)) & (!(!a) | !b)

simplify  
(!a | b) & (a | !b)

and since: x & (y | z) =\> (x & y) | (x & z)

((!a | b) & a) | ((!a | b) & !b)

((!a & a) | (b & a)) | ((!a & !b) | (b & !b))

but: x & !x =\> false  
(false | (b & a)) | ((!a & !b) | false)

and: false | y =\> y  
(b & a) | (!a & !b)

and: x & y =\> y & x  
(a & b) | (!a & !b)

which is equivalent to: (if you take a small hop from pure boolean  
algebra to Ruby)  
a ? b : !b

so the original expression’s equivalent “if” form is:

```
 row.errors.add_to_base 'Supply thing1 or thing2, but not both' \
   if (value.blank? ? row.thing2.blank? : !row.thing2.blank?)

```

if you look back at the original expansion of ^  
[ok, here it is: a ^ b =\> (a & !b) | (!a & b)]

you’ll note a similar form in the ?: expression’s equivalent:  
a ? !b : b

If your “Community Agreements” have a similarly silly ban on the ?:  
expression, then don’t take the last hop (or throw the !(a^b) after  
the ‘if’).

-Rob

Rob B. [http://agileconsultingllc.com](http://agileconsultingllc.com)  
removed\_email\_address@domain.invalid
