Skip to content

Instantly share code, notes, and snippets.

@bgerstle
Last active April 25, 2019 17:03
Show Gist options
  • Save bgerstle/e18732ab77b44ceff92e702091872885 to your computer and use it in GitHub Desktop.
Save bgerstle/e18732ab77b44ceff92e702091872885 to your computer and use it in GitHub Desktop.
Proof of concept for an exhaustive enum micro-lib in Ruby
module ExhaustiveEnum
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def switch(value, &block)
matcher = ExhaustiveMatcher.new(self)
matcher.match(value, &block)
end
def cases
self.constants(false).map { |c| const_get(c) }
end
end
class ExhaustiveMatcher
def initialize(cls)
@cls = cls
@statements = {}
end
def match(value, &block)
raise "No case #{value} defined" unless @cls.cases.include?(value)
instance_eval(&block)
missing_cases = @cls.cases - @statements.keys
raise "Missing cases for #{missing_cases}" unless missing_cases.empty?
@statements[value].call
end
def on(kase, &block)
raise "No case #{kase} defined" unless @cls.cases.include?(kase)
@statements[kase] = block
end
end
end
## Examples
module Gear
include ExhaustiveEnum
PARK = :park
NEUTRAL = :neutral
#REVERSE = :reverse
#DRIVE = :drive
end
def example_one(val)
Gear.switch(val) do
on(:park) { puts 'value is park!' }
on(:neutral) { puts 'value is neutral!' }
end
end
example_one(:park) # => value is park!
example_one(:neutral) # => value is neutral!
begin
example_one(:reverse)
rescue StandardError => e
puts e.inspect # => #<RuntimeError: No case reverse defined>
end
def not_exhaustive_example(val)
Gear.switch(val) do
on(:park) { puts 'value is park!' }
end
end
begin
not_exhaustive_example(:park)
rescue StandardError => e
puts e.inspect # => #<RuntimeError: Missing cases for [:neutral]>
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment