Andrew
Andrew

Reputation: 238777

How to encapsulate many Ruby case conditions within a single object?

I would like to simplify my code that performs a single action when multiple conditions apply. Here's a simplified example:

case button
when 'up', 'upper-right', 'right', 'lower-right', 'down', 'lower-left', 'left', 'upper-left' 
  move_direction(button)
else
  do_something_else
end

If I could encapsulate those options into something like a class or a constant, that would be helpful, but I'm not sure how to do that or if it's even possible. How can I encapsulate these conditions?

Upvotes: 3

Views: 146

Answers (6)

pdoherty926
pdoherty926

Reputation: 10359

The solution below isn't particularly sophisticated, but it's arguably more readable than the original case statement.

def is_navigatable?(button)
  ['up', 
   'upper-right', 
   'right', 
   'lower-right', 
   'down', 
   'lower-left', 
   'left', 
   'upper-left'
  ].member? button
end

def main_method
  if is_navigatable? button
    move_direction button 
  else
    do_something_else
  end
end

Upvotes: 3

sawa
sawa

Reputation: 168131

In your particular case, you can also do this:

case button
when "up", "down", /\A(?:upper-|lower-|)(?:right|left)\z/
  move_direction(button)
else
  do_something_else
end

Upvotes: 0

peter
peter

Reputation: 42192

The fastest way will be using a set

require 'set'

def direction?(button)
  directions = Set[:up, 
                   :'upper-right', 
                   :right, 
                   :'lower-right', 
                   :down, 
                   :'lower-left', 
                   :left, 
                   :'upper-left']
  directions.include?(button)
end

button = 'up'
puts direction?(button.to_sym) #true

Upvotes: 0

Shoe
Shoe

Reputation: 76250

If I could encapsulate those options into something like a class or a constant, that would be helpful, but I'm not sure how to do that or if it's even possible. How can I encapsulate these conditions?

Why not simply refactor that code to drop the case-while condition and simply use if-else instead (since you don't seem to be using other options):

OPTIONS = 'up', 'upper-right', 'right', 'lower-right', 'down', 'lower-left', 'left', 'upper-left'

if OPTIONS.include? button
  move_direction(button)
else
  do_something_else
end

Upvotes: 1

Chris Heald
Chris Heald

Reputation: 62658

You can just use an array splat.

VALID_DIRECTION = %w( upper-left up upper-right left right
  lower-right down lower-left )

case button
when *VALID_DIRECTION
  move_direction(button)
else
  do_something_else
end

Upvotes: 7

Amadan
Amadan

Reputation: 198388

Not quite sure what you're looking for, but maybe something like this?

class CaseMatchingArray < Array
  def ===(element)
    self.include?(element)
  end
end

direction = CaseMatchingArray.new([
    'up', 'upper-right', 'right', 'lower-right',
    'down', 'lower-left', 'left', 'upper-left'])

case 'up'
when direction
  puts "Yup, it's a direction"
end

Remember that the Ruby case statement applies the === operator, and you can do whatever test you wish by matching against things that implement it. If you remember that Proc also implements === as a call, you could do:

direction = lambda { |x|
  [
    'up', 'upper-right', 'right', 'lower-right',
    'down', 'lower-left', 'left', 'upper-left'
  ].include?(x)
}

and have the same result, without defining a class. Or you could even do it in a singleton:

direction = [
    'up', 'upper-right', 'right', 'lower-right',
    'down', 'lower-left', 'left', 'upper-left'
]
def direction.===(other)
  self.include?(other)
end

EDIT: or what Chuck says :) Although, implementing objects that define === is more general, not limited to just membership of arrays.

Upvotes: 4

Related Questions