What's the best way to implement the enum idiom in Ruby? I'm looking for something which I can use (almost) like the Java/C# enums.
Enums (introduced in Rails 4.1) are a feature of Rails, not Ruby.
In Ruby on Rails, an enum is an attribute where the values map to integers in the database and can be queried by name. For example, we could define an enum for the status attribute, where the possible values are pending , active , or archived . Ruby on Rails added support for enums in Rails 4.1.
Enums are statically created when the enum class is first loaded and are immutable. You must have a constructor in the enum class if you want to assign different values to your enum. After the constructor was finished you cannot change the enums value (immutable as said).
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 = 4 end 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) end COMMODITY_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 end end
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, 0x2000 end
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With