Common Code:
class ThingA end
class ThingB end
class ThingC end
In order to set up conditional checks for the above types, I used the basic "if !..." construct that produced the accurate results as expected.
Sample Code if ! ...:
obj = ThingA.new
puts 'yes it is a ThingC' if !obj.is_a?(ThingA) && !obj.is_a?(ThingB) # works ok
# stdout => nothing
obj = ThingB.new
puts 'yes it is a ThingC' if !obj.is_a?(ThingA) && !obj.is_a?(ThingB) # works ok
# stdout => nothing
obj = ThingC.new
puts 'yes it is a ThingC' if !obj.is_a?(ThingA) && !obj.is_a?(ThingB) # works ok
# stdout => yes it is a ThingC
Considering the fact that "unless" is a more descriptive alternative to the basic "if !..." construct, I implemented the above using "unless", instead.
Sample Code unless:
obj = ThingA.new
puts 'yes it is a ThingC' unless obj.is_a?(ThingA) && obj.is_a?(ThingB) # BUG
# stdout => yes it is a ThingC
obj = ThingB.new
puts 'yes it is a ThingC' unless obj.is_a?(ThingA) && obj.is_a?(ThingB) # BUG
# stdout => yes it is a ThingC
obj = ThingC.new
puts 'yes it is a ThingC' unless obj.is_a?(ThingA) && obj.is_a?(ThingB) # ???
# stdout => yes it is a ThingC
Evidently, the "unless" version fails to produce the identical accurate results.
Based upon these simple and straight-forward results, would it be hard for anyone to conclude that "multiple conditions are not handled by unless accurately"?
Boolean logic becomes hard to parse with unless
. You can rewrite it with if
:
unless a || b
is equivalent to if !(a || b)
, which is equivalent to if !a && !b
unless a && b
is equivalent to if !(a && b)
, which is equivalent to if !a || !b
obj.is_a?(ThingA) && obj.is_a?(ThingB)
must be false
if ThingA
and ThingB
are independent classes.
It could only be true
if one class was a subclass of the other. In this case, you would only need to check if obj
is a Subclass instance :
obj.is_a?(ThingA) && obj.is_a?(Object)
is true if and only if obj
is a ThingA
object, so you could just write :
obj.is_a?(ThingA)
For your example, you should probably use case
:
obj = ThingA.new
case obj
when ThingA
puts "yes, it is a ThingA"
when ThingB
puts "yes, it is a ThingB"
when ThingC
puts "yes, it is a ThingC"
else
puts "no, it is some other Object"
end
or
case obj
when ThingA, ThingB, ThingC
puts "yes, it is a Thing A B or C"
else
puts "no, it is some other Object"
end
or
puts "yes, it is a #{obj.class}"
They return :
yes, it is a ThingA
yes, it is a Thing A B or C
yes, it is a ThingA
The problem here is with the logic and or or, when you negate a logical function the equivalent is the opossite and the precedence of operator: the logic functions before the modifier
De Morgan's laws provide a way of distributing negation over disjunction and conjunction :
¬ ( a ∨ b ) ≡ ( ¬ a ∧ ¬ b ),
and ¬ ( a ∧ b ) ≡ ( ¬ a ∨ ¬ b )
In your case,
(not(a) and not(b)) then you negate it => not(a or b)
and unless => not if
irb(main):001:0> class ThingA end
class ThingB end
class ThingC end
=> nil
irb(main):002:0> => nil
irb(main):003:0> => nil
irb(main):004:0> obj = ThingA.new
puts 'yes it is a ThingC' unless obj.is_a?(ThingA) || obj.is_a?(ThingB
obj = ThingB.new
puts 'yes it is a ThingC' unless obj.is_a?(ThingA) || obj.is_a?(ThingB)
obj = ThingC.new
puts 'yes it is a ThingC' unless obj.is_a?(ThingA) || obj.is_a?(ThingB)
<ThingA:0x000000025fc060>
irb(main):005:0> => nil
<ThingB:0x00000002749d00>
irb(main):009:0> => nil
<ThingC:0x000000027be420>
irb(main):013:0> yes it is a ThingC
=> nil
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With