0

So I was writing a module function to convert 'falsey' and 'truthy' values into true and false. To do this I was using a case expression which as far as I can tell is basically just a big elseif statement that uses the === function with the === operator being meant to be used for comparing sets. From the Ruby documentation the only difference I can really find between == and === is that === can be overwritten for comparing decedents of classes. I also understand that everything is a method call in ruby so we can't assume that the equality/subsumption methods are commutative.

When using a case statement to sort by class I would have guessed that the functionality of == and === would be identical but what I found is that when sorting by class they case statement never put my inputs into the correct 'bin'. At that point I switched to using == and it did work so I was wondering why that was the case since I am comparing classes which I assumed would in fact be an "apples to apples" comparison.

module FirstTry
  def self.to_boolean(uncertain_value, to_integer: false)
    case uncertain_value.class
    when String
      value = ActiveRecord::Type::Boolean.new.cast(uncertain_value)
      return 1 if to_integer == true && value == true
      return 0 if to_integer == true && value == false
      return boolean_value
    when TrueClass
      return 1 if to_integer == true
      return true
    when FalseClass
      return 0 if to_integer == true
      return false
    when NilClass
      return 0 if to_integer == true
      return false
    end
    # I did not originally include this part of the code in the question
    raise 'Conversion Failed: No rules for converting that type of input into a boolean.'
  end
end

module SecondTry
  def self.to_boolean(uncertain_value, to_integer: false)
    if uncertain_value.class == String
      boolean_value = ActiveRecord::Type::Boolean.new.cast(uncertain_value)
      return 1 if to_integer == true && boolean_value == true
      return 0 if to_integer == true && boolean_value == false
      return boolean_value
    elsif uncertain_value.class == TrueClass
      return 1 if to_integer == true
      return true
    elsif uncertain_value.class == FalseClass
      return 0 if to_integer == true
      return false
    elsif uncertain_value.class == NilClass
      return 0 if to_integer == true
      return false
    end
    # I did not originally include this part of the code in the question
    raise 'Conversion Failed: No rules for converting that type of input into a boolean.'
  end
end
Mendoza
  • 82
  • 7
  • 2
    You are asking whether the class of something is a string. Obviously, it isn't. The class of something is a class, not a string. – Jörg W Mittag Jan 15 '18 at 22:26
  • 2
    BTW, you're doing a lot more work than you need to. `ActiveRecord::Type::Boolean.new.cast` takes care of strings, `nil`, booleans, ... for you so you don't need all those branches, just `value = ActiveRecord::Type::Boolean.new.cast(uncertain_value)` and the `to_integer` logic should be sufficient. – mu is too short Jan 15 '18 at 22:27
  • 1
    `return` is not needed in the last line of each clause (for example, `return false` can, and should be, written simply `false`). Also, instead of, say, `return 0 if to_integer == true; return false`, you can use a *ternary* expression: `to_integer == true ? 0 : false`. If you only care that `to_integer` is truthy, simplify that to `to_integer ? 0 : false`. – Cary Swoveland Jan 15 '18 at 23:01

2 Answers2

3

The triple equals === is called under the hood in a case expression in Ruby. We've found it convenient to be able to express something like:

case object
when String
  "object is an instance of String!"
when Enumerable
  "wow, object is actually a bunch of objects!"
end

This is convenient, of course, unless you're actually holding the class, not an instance of the class. But in your example, you call #class on the object in your case expression. Simply drop that call, and your cases will be put in the correct bins. :)

Martin Svalin
  • 2,227
  • 1
  • 17
  • 23
0

You can use .class.to_s for comparison in your case statement

Alex Dias
  • 1
  • 1