Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement Enums in Ruby?

Tags:

enums

ruby

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.

like image 743
auramo Avatar asked Sep 16 '08 19:09

auramo


People also ask

Are there enums in Ruby?

Enums (introduced in Rails 4.1) are a feature of Rails, not Ruby.

How does enum work in 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.

How are enums implemented?

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).


2 Answers

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

like image 135
mlibby Avatar answered Sep 17 '22 13:09

mlibby


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.

like image 24
Charles Avatar answered Sep 20 '22 13:09

Charles