ruby-on-railsrubyif-statementruby-on-rails-7

Ruby On Rails - Turn If Else Statement Wiith Match? To Case Statement


How do I turn the following if / else statement:

  if /_?town$/.match? name
    ['Springfield', 'Shelbyville', 'Kent', 'Carter', 'Benson'].sample
  elsif /_?state$/.match? name
    ['TX'].sample
  else
    'text'
  end

into a case statement in Rails / Ruby?

I tried

  case name
  when /_?town$/.match?
    ['Springfield', 'Shelbyville', 'Kent', 'Carter', 'Benson'].sample
  when /_?state$/.match?
    ['TX'].sample
  else
    'text'
  end

but I am getting a wrong number of arguments error related to match?


Solution

  • case statements do not work like that. The object behind the case is not passed as an argument to any method call that is found after each when. Instead, it is passed to a very special method (===) call as an argument.

    Instead, when there is a case block defined like this:

    case x
    when a
      # ...
    when b
      # ...
    else
      # ...
    end
    

    Then the equivalent if-else-block would look like this:

    if a === x
      # ...
    elsif b === x
      # ...
    else
      # ...
    end
    

    Note the method being called (===) and the order (it is, a === x, not x === a). The === method is defined on many types of classes and has differently behavior.

    For example:

    This means the behavior of a case-block depends on the objects and their === implementation. That makes case-blocks extremely powerful. But it doesn't support passing the object to any other method than the ===.

    That said, you can either still call match? (or any other method call), but then you have to write the full statement behind the when and leave the case blank. This might be preferable when you want to avoid having long if-elsif-blocks.

    case
    when /_?town$/.match? name
      ['Springfield', 'Shelbyville', 'Kent', 'Carter', 'Benson'].sample
    when /_?state$/.match? name
      ['TX'].sample
    else
      'text'
    end
    

    Or – in your specific example – you could write it like this, because regexp.match?(name) has almost the same behavior as regexp === name (See Regexp#=== in the docs):

    case name
    when /_?town$/
      ['Springfield', 'Shelbyville', 'Kent', 'Carter', 'Benson'].sample
    when /_?state$/
      ['TX'].sample
    else
      'text'
    end