Skip to content

Instantly share code, notes, and snippets.

@havenwood
Last active March 3, 2022 21:03
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 havenwood/2588e1b62a611c722791b5b868bc161c to your computer and use it in GitHub Desktop.
Save havenwood/2588e1b62a611c722791b5b868bc161c to your computer and use it in GitHub Desktop.
A spike implementing Crystal's enum in Ruby
require_relative 'enum'
enum :Color do
value :Red # 0
value :Green # 1
value :Blue, 5 # overwritten to 5
value :Yellow # 6 (5 + 1)
def red?
self == Color::Red
end
end
Color::Red.value #=> 0
Color::Green.value #=> 1
Color::Blue.value #=> 5
Color::Yellow.value #=> 6
# Enums from integers
Color.new 1 #=> Color::Red
Color.new 10 #=> 10
# Methods
Color::Red.red? #=> true
Color::Blue.red? #=> false
# Flags
enum :IOMode, flags: true do
value :Read # 1
value :Write # 2
value :Async # 4
end
IOMode::Async.value #=> 4
# frozen_string_literal: true
module Kernel
def enum(const, flags: false, &block)
enum_class = Module.new do
@flags = flags
@value_counter = flags ? 1 : 0
class << self
def new(value)
matching_const = constants.find do |constant|
const_get(constant).value == value
end
return value unless matching_const
const_get matching_const
end
private
def value(value_const, value_counter = @value_counter)
@value_counter = if @flags
value_counter * 2
else
value_counter + 1
end
mixin = self
value_class = Module.new do
extend mixin
@value = value_counter
class << self
include Comparable
attr_reader :value
def <=>(other)
value <=> other.value
end
def to_s
name.rpartition('::').last
end
end
end
const_set value_const, value_class
end
end
end
Object.const_set(const.name, enum_class)
Object.const_get(const.name).module_eval(&block)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment