Skip to content

Instantly share code, notes, and snippets.

@garyharan
Last active May 12, 2022 16:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save garyharan/e576738a46742153c714a920909a74f0 to your computer and use it in GitHub Desktop.
Save garyharan/e576738a46742153c714a920909a74f0 to your computer and use it in GitHub Desktop.
Using a class comparison in a switch case condition
# Story time: Classy Switch Classes
# Say you have a bunch of messages coming in Slack and you'd like to add an automatic emoji
# of course your team is multicultural and you may want to cater to the diverse crowd you have.
# For the purpose of this exercise we will treat `message#emojis` and `message#replies` as arrays we can push to.
# You could write your code like this:
if message.text.include?("Good morning!")
message.emojis << ":goodmorning"
elsif message.text =~ /happy birthday|joyeux anniversare|feliz aniversario/i
message.emojis << ":birthdaycake:"
elsif message.text.downcase.include?("error") && message.sender == "production_bot"
message.emojis << ":redalert:"
message.replies << "If you need help with this error check the logs at this URL"
end
# As these conditions grow you may be compelled to rewrite it like this:
case message.text
when /Good morning/i
message.emojis << ":goodmorning"
when /happy birthday|joyeux anniversare|feliz aniversario/i
message.emojis << ":birthdaycake:"
when /error/i
if message.sender == "production_bot"
message.emojis << ":redalert:"
message.replies << "If you need help with this error check the logs at this URL"
end
end
# This looks like an improvement because it's more compact and switch cases in Ruby famously support
# regular expressions.
#
# However it does mean you now have a condition inside one of the blocks.
#
# Another issue with readability here is that users are looking at the logic rather than a story.
#
# When code tells a story you shouldn't have to read the details of a regular expression to know what is going on.
#
# We could be classier still with our fellow readers and use class comparison in our switch cases. The resulting
# code tells a much better story:
case message
when MorningGreeting
message.emojis << ":goodmorning"
when BirthdayWish
message.emojis << ":birthdaycake:"
when ProductionError
message.emojis << ":redalert:"
message.replies << "If you need help with this error check the logs at this URL"
end
# Then the classes to create this comparison can be written with all the conditions you need to compare on.
class MorningGreeting
def self.===(message)
message.text =~ /Good morning/i
end
end
class BirthdayWish
def self.===(message)
message.text =~ /happy birthday|joyeux anniversare|feliz aniversario/i
end
end
class ProductionError
def self.===(message)
message.text =~ /error/i && message.sender == "production_bot"
end
end
# Of course this is the start of strategy pattern... we could also encapsulate the resulting action in
# the class to that effect as follow:
case message
when MorningGreeting then MorningGreeting.react_to(message)
when BirthdayWish then BirthdayWish.react_to(message)
when ProductionError then ProductionError.react_to(message)
end
class MorningGreeting
def self.===(message)
message.text =~ /Good morning/i
end
def self.react_to(message)
message.emojis << ":goodmorning"
end
end
class BirthdayWish
def self.===(message)
message.text =~ /happy birthday|joyeux anniversare|feliz aniversario/i
end
def self.react_to(message)
message.emojis << ":birthdaycake:"
end
end
class ProductionError
def self.===(message)
message.text =~ /error/i && message.sender == "production_bot"
end
def self.react_to(message)
message.emojis << ":redalert:"
message.replies << "If you need help with this error check the logs at this URL"
end
end
# With that the dispatch switch case block becomes very readable and it makes it really easy to navigate the code.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment