Skip to content

Instantly share code, notes, and snippets.

@henrik
Last active August 22, 2017 11:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save henrik/8163fa7ae4ac9a7478ac5c06a1f6bd16 to your computer and use it in GitHub Desktop.
Save henrik/8163fa7ae4ac9a7478ac5c06a1f6bd16 to your computer and use it in GitHub Desktop.
Ruby switch/case that complains if you forget to cover a case. Inspired by types in Elm. Just a for-fun experiment; not intended for real use.
# By Henrik Nyh <https://henrik.nyh.se> under the MIT license.
class Enum
class Switch
def initialize(value_to_match_against, *values)
@checked_values = values.map { |v| [ v, false ] }.to_h
values.each do |value|
define_singleton_method(value) { |&block|
@checked_values[value] = true
@block_to_call = block if value == value_to_match_against
}
end
end
def verify_we_covered_all_cases_and_call_block_for_matching_value
unchecked_values = @checked_values.reject { |_k, v| v }.keys
if unchecked_values.any?
raise "Did not cover these cases: #{unchecked_values.inspect}"
end
@block_to_call.call if @block_to_call
end
end
def initialize(*values)
@values = values
end
def switch(value)
s = Switch.new(value, *@values)
yield s
s.verify_we_covered_all_cases_and_call_block_for_matching_value
end
end
def Enum(*values)
Enum.new(*values)
end
states = Enum(:hungry, :thirsty)
# This works:
states.switch(:thirsty) do |s|
s.hungry { puts "hungry!" }
s.thirsty { puts "thirsty!" }
end
# This raises, catching the fact that you didn't define the "hungry" case:
states.switch(:thirsty) do |s|
#s.hungry { puts "hungry!" }
s.thirsty { puts "thirsty!" }
end
@mikker
Copy link

mikker commented Feb 9, 2017

👏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment