stevec
stevec

Reputation: 52308

How to make a ruby case statement use equals to (==) rather than threequals (===)

Ruby's case statement uses === by default. Is there a way to make it use 'equals to' (i.e. ==) instead?

The motivation for doing so is because I have 5 if statements that would very nicely be replaced by a switch, but I was a little surprised to learn that

datatype = "string".class
if datatype == String
puts "This will print"
end

is not the same as

case datatype
when String
puts "This will NOT print"
end

Upvotes: 5

Views: 524

Answers (4)

Peter Camilleri
Peter Camilleri

Reputation: 1912

You could also use the explicit form of case statement

datatype = test_object.class

case
when datatype == String
  puts "It's a string"
when datatype < Numeric
  puts "It's a number"
end

Note that the expression datatype < Numeric will be true for all numeric types.

Upvotes: 1

Kimmo Lehto
Kimmo Lehto

Reputation: 6041

Similar to Aleksei Matiushkin's answer, but without the curry:

is_class = ->(klass) { ->(item) { item == klass } }

10.times do
  case ['abc', 123].sample.class
  when is_class[String]
    puts "it's the abc"
  when is_class[Integer]
    puts "easy as 123"
  end
end

What happens here?

  • The first line defines a proc that returns another proc
  • proc[x] is the same as proc.call(x)
  • proc.===(x) is the same as proc.call(x)
  • is_class[Integer] returns a proc that does { |val| val == Integer }
  • ..which gets called by case with the case's parameter as argument because of === ==> call.

The big downside is it creates a lot of procs and looks weird.

Of course the obvious solution to your question would be to not do datatype = "string".class:

datatype = "string"
case datatype
when String
puts "This will print"
end

Upvotes: 2

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

This would be a more concise and cleaner (IMSO) approach to what @sawa had suggested. Use λ instead of the wrapper class.

META = ->(type, receiver) { receiver == type }

case "string".class
when META.curry[Integer] then puts 'integer'  
when META.curry[String] then puts 'string'  
end  

#⇒ "string"

This solution uses Proc#curry and Proc#=== under the hood.

Upvotes: 3

sawa
sawa

Reputation: 168101

You cannot let case to not use ===, but you can redefine === to use ==.

class Class
  alias === ==
end

case datatype
when String
  puts "This will print"
end
# >> This will print

Alternatively, you can create a specific class for doing that.

class MetaClass
  def initialize klass
    @klass = klass
  end

  def ===instance
    instance == @klass
  end
end

def meta klass
  MetaClass.new(klass)
end

case datatype
when meta(String)
  puts "This will print"
end
# >> This will print

Upvotes: 4

Related Questions