13

This is going to sound weird, but I would love to do something like this:

case cool_hash
  when cool_hash[:target] == "bullseye" then do_something_awesome
  when cool_hash[:target] == "2 pointer" then do_something_less_awesome
  when cool_hash[:crazy_option] == true then unleash_the_crazy_stuff
  else raise "Hell"
end

Ideally, I wouldn't even need to reference the has again since it's what the case statement is about. If I only wanted to use one option then I would "case cool_hash[:that_option]", but I'd like to use any number of options. Also, I know case statements in Ruby only evaluate the first true conditional block, is there a way to override this to evaluate every block that's true unless there is a break?

Michael K Madison
  • 2,242
  • 3
  • 21
  • 35

4 Answers4

22

You could also use a lambda:

case cool_hash
when -> (h) { h[:key] == 'something' }
  puts 'something'
else
  puts 'something else'
end
Undo
  • 25,519
  • 37
  • 106
  • 129
marknery
  • 1,533
  • 3
  • 19
  • 27
6

Your code is very close to being valid ruby code. Just remove the variable name on the first line, changing it to be:

case

However, there is no way to override the case statement to evaluate multiple blocks. I think what you want is to use if statements. Instead of a break, you use return to jump out of the method.

def do_stuff(cool_hash)
  did_stuff = false

  if cool_hash[:target] == "bullseye"
    do_something_awesome
    did_stuff = true
  end

  if cool_hash[:target] == "2 pointer"
    do_something_less_awesome
    return  # for example
  end

  if cool_hash[:crazy_option] == true
    unleash_the_crazy_stuff
    did_stuff = true
  end

  raise "hell" unless did_stuff
end
David Grayson
  • 84,103
  • 24
  • 152
  • 189
5

I think, following is the better way to do the stuff you want.

def do_awesome_stuff(cool_hash)
  case cool_hash[:target]
    when "bullseye"
      do_something_awesome
    when "2 pointer"
      do_something_less_awesome
    else
     if cool_hash[:crazy_option]
      unleash_the_crazy_stuff
     else
      raise "Hell"
     end
  end
end

Even in case's else part you can use 'case cool_hash[:crazy_option]' instead of 'if' if there are more conditions. I prefer you to use 'if' in this case because there is only one condition.

Data Don
  • 338
  • 2
  • 11
2

in ruby 3.0 you can do the following with pattern matching

# assuming you have these methods, ruby 3 syntax
def do_something_awesome = "something awesome "
def do_something_less_awesome = "something LESS awesome"
def unleash_the_crazy_stuff = "UNLEASH the crazy stuff "

you can do

def do_the_thing(cool_hash)
  case cool_hash
  in target: "bullseye" then do_something_awesome
  in target: "2 pointer" then do_something_less_awesome
  in crazy_option: true then unleash_the_crazy_stuff
  else raise "Hell"
  end
end

will return

do_the_thing(target: "bullseye")
=> "something awesome "

do_the_thing(target: "2 pointer")
=> "something LESS awesome"

do_the_thing(crazy_option: true)
=> "UNLEASH the crazy stuff "

in ruby 2.7 it still works

# need to define the methods differently
def do_something_awesome; "something awesome "; end
def do_something_less_awesome; "something LESS awesome"; end
def unleash_the_crazy_stuff; "UNLEASH the crazy stuff "; end

# and when calling the code above to do the switch statement
# you will get the following warning
warning: Pattern matching is experimental, and the behavior may change
         in future versions of Ruby!