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 && !bunless a && b is equivalent to if !(a && b), which is equivalent to if !a || !bobj.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