How to implement Enums in Ruby?
Two ways. Symbols (:foo
notation) or constants (FOO
notation).
Symbols are appropriate when you want to enhance readability without littering code with literal strings.
postal_code[:minnesota] = "MN"postal_code[:new_york] = "NY"
Constants are appropriate when you have an underlying value that is important. Just declare a module to hold your constants and then declare the constants within that.
module Foo BAR = 1 BAZ = 2 BIZ = 4end flags = Foo::BAR | Foo::BAZ # flags = 3
Added 2021-01-17
If you are passing the enum value around (for example, storing it in a database) and you need to be able to translate the value back into the symbol, there's a mashup of both approaches
COMMODITY_TYPE = { currency: 1, investment: 2,}def commodity_type_string(value) COMMODITY_TYPE.key(value)endCOMMODITY_TYPE[:currency]
This approach inspired by andrew-grimm's answer https://stackoverflow.com/a/5332950/13468
I'd also recommend reading through the rest of the answers here since there are a lot of ways to solve this and it really boils down to what it is about the other language's enum that you care about
I'm surprised that no one has offered something like the following (harvested from the RAPI gem):
class Enum private def self.enum_attr(name, num) name = name.to_s define_method(name + '?') do @attrs & num != 0 end define_method(name + '=') do |set| if set @attrs |= num else @attrs &= ~num end end end public def initialize(attrs = 0) @attrs = attrs end def to_i @attrs endend
Which can be used like so:
class FileAttributes < Enum enum_attr :readonly, 0x0001 enum_attr :hidden, 0x0002 enum_attr :system, 0x0004 enum_attr :directory, 0x0010 enum_attr :archive, 0x0020 enum_attr :in_rom, 0x0040 enum_attr :normal, 0x0080 enum_attr :temporary, 0x0100 enum_attr :sparse, 0x0200 enum_attr :reparse_point, 0x0400 enum_attr :compressed, 0x0800 enum_attr :rom_module, 0x2000end
Example:
>> example = FileAttributes.new(3)=> #<FileAttributes:0x629d90 @attrs=3>>> example.readonly?=> true>> example.hidden?=> true>> example.system?=> false>> example.system = true=> true>> example.system?=> true>> example.to_i=> 7
This plays well in database scenarios, or when dealing with C style constants/enums (as is the case when using FFI, which RAPI makes extensive use of).
Also, you don't have to worry about typos causing silent failures, as you would with using a hash-type solution.
The most idiomatic way to do this is to use symbols. For example, instead of:
enum { FOO, BAR, BAZ}myFunc(FOO);
...you can just use symbols:
# You don't actually need to declare these, of course--this is# just to show you what symbols look like.:foo:bar:bazmy_func(:foo)
This is a bit more open-ended than enums, but it fits well with the Ruby spirit.
Symbols also perform very well. Comparing two symbols for equality, for example, is much faster than comparing two strings.