3

I have following if else statement that rubocop complained about Style/CaseLikeIf: Convert if-elsif to case-when

if obj.is_a? Rest::Headers
  obj
elsif obj.is_a? Hash
  Rest::Headers.new(**obj)
elsif obj.is_a? Array
  Rest::Headers.new(**obj.to_h)
else
  raise 'Invalid object type for Headers!'
end

I converted it into case statement

case
when (obj.is_a? Rest::Headers)
  obj
when (obj.is_a? Hash)
  Rest::Headers.new(**obj)
when (obj.is_a? Array)
  Rest::Headers.new(**obj.to_h)
else
  raise 'Invalid object type for Headers!'
end

But now rubocop complained Do not use empty case condition, instead use an if expression. Does anyone know what is wrong with my case statement?

user18646557
  • 95
  • 2
  • 8

1 Answers1

3

Ruby wants you to convert to a case that matches on the types themselves. case in Ruby doesn't just match on equality. A statement of the form

case a
when b
  ...
end

The case will call b === a (note the triple equals here, which does not mean equality) and trigger the when block if it's true.

=== is defined on Object to be equivalent to ==, but many classes override it.

  • Regular expressions use it to pattern match against the right-hand side
  • Ranges check whether the right-hand side is included in the range
  • Classes check whether the right-hand side is_a? instance of the class.

And it's that third bullet point that Rubocop suggests you use.

case obj
when Rest::Headers
  obj
when Hash
  Rest::Headers.new(**obj)
when Array
  Rest::Headers.new(**obj.to_h)
else
  raise 'Invalid object type for Headers!'
end

Note that I never explicitly call is_a? here; the case block automagically knows to do that.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
  • I did implement that earlier but would that work for Hash and Array? because is_a returns true if class is the class of obj – user18646557 Apr 14 '22 at 00:46
  • @user18646557 *"Classes check whether the right-hand side is_a? instance of the class"* So `Rest::Headers === obj` is identical to `obj.is_a?(Rest::Headers)`. See the the [`===`](https://ruby-doc.org/core-3.1.1/Module.html#method-i-3D-3D-3D) documentation of `Module` which is inherited by `Class`. – 3limin4t0r Apr 14 '22 at 00:54
  • 3
    @user18646557 Note that Ruby is a duck typing language. So you might be better of not explicitly checking for type, but instead check for available methods. The hash splat operator `**` can splat a given object if it responds to the `to_hash` method. So if `obj.respond_to(:to_hash)` then `Rest::Headers.new(**obj)`. The second check could also be `obj.respond_to(:to_h)` then `Rest::Headers.new(**obj.to_h)`. Just some food for thought. – 3limin4t0r Apr 14 '22 at 01:02