Validates_mutual_exclusivity

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/
“Test Driven Ajax (on Rails)”
assert_xpath, assert_javascript, & assert_ajax

On 6/13/07, Phlip [email protected] 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

On 6/13/07, Bob S. [email protected] 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.

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
http://www.oreilly.com/catalog/9780596510657/
“Test Driven Ajax (on Rails)”
assert_xpath, assert_javascript, & assert_ajax

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

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
[email protected]

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

Phlip
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
[email protected]

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs