Search code examples
ruby-on-railsrubyswitch-statementcase

Why is my ruby case statement not working?


What's wrong with that? Yes, I could make an if else statement, but I wanna do that with case statements.

In my Controller

query_limit = case current_user
                when nil
                  return 5
                when is_admin?
                  return 200
                when has_role?('registered')
                  return 20
                else
                  return 5
              end


NoMethodError (undefined method `is_admin?' for #<V1::MyController:123123123>):

puts query_limit # is always ELSE when I do this:

 query_limit = case current_user
                    when nil
                      return 5
                    when current_user.is_admin?
                      return 200
                    when current_user.has_role?('registered')
                      return 20
                    else
                      return 5
                  end

Model User

class User
  def is_admin?
    self.has_role?('administrator')
  end

  def has_role?(the_role)
    self.roles.any? {|role| role.slug == the_role}
  end
end

Solution

  • According to the Ruby documentation:

    Case statements consist of an optional condition, which is in the position of an argument to case, and zero or more when clauses. The first when clause to match the condition (or to evaluate to Boolean truth, if the condition is null) “wins”, and its code stanza is executed.

    If you specify current_user as the case condition, then the first when expression that matches current_user will be executed. current_user.is_admin? returns a boolean value which will never be equal to current_user, so your second example will always take the else branch:

    case current_user
      when nil  # current_user != nil, skip condition
        return 5
      when current_user.is_admin?  # current_user != current_user.is_admin?, skip condition
        return 200
      when current_user.has_role?('registered')  # and so on
        return 20
      else
        return 5
    end
    

    Your first example is broken because there is no local is_admin? method defined. case does not know to call is_admin? on current_user in this case.

    To fix your code, you can remove case condition. In this case the first when clause that evaluates to a truthy value will be chosen:

    case  # no current_user here!
      when current_user.nil?  # current_user.nil? is false, skip condition
        return 5
      when current_user.is_admin?  # current_user.is_admin? is truthy, run this one!
        return 200
      when current_user.has_role?('registered')
        return 20
      else
        return 5
    end