369

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.

NoDataDumpNoContribution
  • 10,591
  • 9
  • 64
  • 104
auramo
  • 13,167
  • 13
  • 66
  • 88
  • 7
    @auramo, good question, and great choice for the best answer. Love it or hate it, you get no type-safety and (at least in Ruby) no typo-safety. I was thrilled when I discovered enums in C# and later in Java (pick a value, but from these!), Ruby doesn't provide a real way to do that in any case at all. – Dan Rosenstark Mar 11 '10 at 09:57
  • 3
    The problem with this question is that Java and C# enums are dramatically different things. A Java enum member is an object instance and a singleton. A Java enum can have a constructor. In contrast, C# enums are based off Primitive values. Which behaviour is the questioner looking for? While it's likely the case that the C# case is wanted, Java is explicitly mentioned, rather than C or C++, so there is some doubt. As for suggesting that there's no way to be 'safe' in Ruby, that's transparently false, but you have to implement something more sophisticated. – user1164178 Feb 14 '15 at 06:33

25 Answers25

367

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

mlibby
  • 6,567
  • 1
  • 32
  • 41
  • 2
    What if these enum is too be stored to the database? Will symbol notation works? I doubt... – Phương Nguyễn Jun 11 '10 at 03:44
  • I would use the constants approach if I were saving to a database. Of course then you have to do some sort of lookup when pulling the data back out of the DB. You could also use something like `:minnesota.to_s` when saving to a database to save the string version of the symbol. Rails, I believe, has some helper methods to deal with some of this. – mlibby Jun 11 '10 at 12:40
  • @dfrankow, what do you mean? I've never heard of order as a concern in the context of C# enums. – mlibby Feb 21 '12 at 14:09
  • Don't know about C#, but Java enums have order, and it is used: http://docs.oracle.com/javase/1.5.0/docs/guide/language/enums.html. "Note that each enum type has a static values method that returns an array containing all of the values of the enum type in the order they are declared. This method is commonly used in combination with the for-each loop to iterate over the values of an enumerated type." – dfrankow Feb 21 '12 at 14:11
  • @drankow, ah. For that to be automatic you would need to look at something like http://www.rubyfleebie.com/enumerations-and-ruby/ or http://code.dblock.org/how-to-define-enums-in-ruby which support this behavior. Otherwise, if you use the constants approach, you can define a method like `Foo.values` that returns the constants as an array. – mlibby Feb 21 '12 at 14:21
  • 8
    Wouldn't a module be better to group constants - as you're not going to be making any instances of it? – thomthom Mar 14 '12 at 13:37
  • @thomthom absolutely, yes. I've changed the answer. If there is no close coupling of the enums to the context of a class where it would make sense to declare them, then a module makes more sense. – mlibby Mar 23 '12 at 12:51
  • 1
    As well, the module would be faster, as the other one has a hash lookup. Small hash, sure, but still. – baash05 Jul 06 '12 at 00:04
  • 3
    Just a comment. Ruby's a bit of a pain about naming conventions but not really obvious about them until you trip over them. The names of the enums must be all caps and the first letter of the module name must be capitalized for ruby to know that the module is a module of constants. – Rokujolady Apr 08 '13 at 18:41
  • 5
    Not entirely true. The first letter of the constant must be capitalized, but not all letters need to be. This is a matter of convention preference. For example, all module names and class names are actually constants too. – Michael Brown Mar 10 '14 at 23:02
  • What if I want to do a type check as these are just constants? – Gayan Weerakutti Apr 20 '18 at 13:20
63

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.

Charles
  • 6,199
  • 6
  • 50
  • 66
  • 1
    That's a great way to solve that particular problem, but the reason no one suggested it probably has to do with the fact that it's not much like C#/Java enums. – mlibby Feb 17 '12 at 14:21
  • 1
    This is a bit incomplete, but serves as nice hint as to how you might implement solutions with a dynamic approach. It bears some resemblance to a C# enum with the FlagsAttribute set, but like the symbol/constant based solutions above, it's one answer of many. The problem is the original question, which is muddled in its intent (C# and Java aren't interchangeable). There are many ways to itemize objects in Ruby; selecting the right one depends on the problem being solved. Slavishly replicating features you don't need is misguided. The correct answer has to depend on the context. – user1164178 Feb 14 '15 at 06:46
  • One problem with this design, if I'm understanding it correctly, is that unlike something like C#, your solution requires a heap allocation every time you want to use an enum. That means garbage collection and reduced read times one cache misses. I like the way it works and how it looks when defining an enum, but I can't accept unnecessary heap allocations in many scenarios. – Greg Cobb Sep 30 '22 at 09:27
56

I use the following approach:

class MyClass
  MY_ENUM = [MY_VALUE_1 = 'value1', MY_VALUE_2 = 'value2']
end

I like it for the following advantages:

  1. It groups values visually as one whole
  2. It does some compilation-time checking (in contrast with just using symbols)
  3. I can easily access the list of all possible values: just MY_ENUM
  4. I can easily access distinct values: MY_VALUE_1
  5. It can have values of any type, not just Symbol

Symbols may be better cause you don't have to write the name of outer class, if you are using it in another class (MyClass::MY_VALUE_1)

Alexey
  • 9,197
  • 5
  • 64
  • 76
  • 7
    I think this is the best answer. The functionality, syntax and minimal code overhead come closest to Java/C#. Also you can nest the definitions even deeper than one level and still recover all values with MyClass::MY_ENUM.flatten. As a side note I would use uppercased names here as is the standard for constants in Ruby. MyClass::MyEnum might be mistaken for a reference to a subclass. – jaynetics Apr 29 '15 at 12:10
  • @Janosch, I've updated the names. thanks for suggestion – Alexey Sep 12 '15 at 08:28
  • 2
    I'm still a little confused, and the link 410'd (no, not 404). Could you give examples as to how this enum would be used? – Shelvacu Oct 21 '15 at 17:18
52

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
:baz

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

emk
  • 60,150
  • 6
  • 45
  • 50
  • 124
    So the Ruby spirit is: "Typos will compile" – mxcl Aug 03 '09 at 14:02
  • 92
    Popular Ruby frameworks rely heavily on runtime metaprogramming, and performing too much load-time checking would take away most of Ruby's expressive power. To avoid problems, most Ruby programmers practice test-driven design, which will find not just typos but also logic errors. – emk Aug 20 '09 at 20:14
  • 1
    @emk, thanks for your comment, it really sums up the dynamic-lang crowd's thought on the thing. While I do think it's wrong, that's how it works :) – Dan Rosenstark Mar 05 '10 at 14:09
  • 11
    @yar: Well, language design is a series of tradeoffs, and language features interact. If you want a good, highly-dynamic language, go with Ruby, write your unit tests first, and go with the spirit of the language. :-) If that's not what you're looking for, there are dozens of other excellent languages out there, each of which makes different tradeoffs. – emk Mar 10 '10 at 17:51
  • 10
    @emk, I agree, but my personal issue is that I feel quite comfortable in Ruby, but I do not feel comfortable refactoring in Ruby. And now that I've started writing unit tests (finally), I realize that they are not a panacea: my guess is 1) that Ruby code doesn't get massively refactored that often, in practice and 2) Ruby is not the end-of-the-line in terms of dynamic languages, precisely because it's hard to refactor automatically. See my question 2317579 which got taken over, strangely, by the Smalltalk folks. – Dan Rosenstark Mar 10 '10 at 23:52
  • 2
    @Max, the way Ruby works, what appears to be a typo at compile time may not be a typo at run time. Using symbols in Ruby is merely an idiomatic and performant way to do this. In a language like C, enums are really the only way to solve this problem. But in C# I see lots of people using strings to solve the "named index" problem rather than taking the time to build an enum. Strings have the same "typos will compile" problems as Ruby's symbols *and* confounds the IDE's autocomplete functionality. – mlibby Mar 28 '10 at 11:20
  • 4
    Yeah, but using those strings would not be in the spirit of the C# language, it is simply a bad practice. – Ed S. Apr 04 '10 at 00:11
  • 2
    @yar: For Ruby (and similar languages) to be truly usable and refactorable, you really do need excellent test coverage. The easiest way to do this is to never add a feature except to fix a failing test case. This will give near-complete coverage, and it completely transforms the experience of working in a dynamic language. And yes, I've done some pretty serious refactorings of Ruby code. They're a bit harder than Eclipse refactorings, but there's usually far fewer lines of code, so it balances out. – emk Apr 07 '10 at 14:18
  • 4
    @emk, thanks for that: so basically you're replacing compile-time type checks with test-time human-written test code. Except for the fact that the latter is much more comprehensive, it doesn't sound like a very good tradeoff (more work). But I'll let you know what I think after a few months of Ruby with tests. – Dan Rosenstark Apr 08 '10 at 08:01
  • 2
    @yar, no problem! But even working in static languages, I tend to prefer very comprehensive test suites, because I design my APIs working from the test suites inwards to the actual implementation. So for people who've adopted this style, Ruby is literally no extra work—and we get some cool dynamic features, too. (There are some exceptions to this in my experience, particularly higher-order mathematical code in Haskell, where types _and_ tests are invaluable.) Good luck with Ruby! – emk Apr 10 '10 at 12:32
  • @MaxHowell Actually there is no compilation, it just would be evaluated and there is nothing wrong with that. – Jahan Zinedine Oct 17 '12 at 09:04
  • 2
    Well, since ruby doesn't compile, I think the ruby way is "Convention over Configuration" which means "We hardcode stuff and you guess what what we hardcoded." – Rokujolady Apr 08 '13 at 18:43
19

If you are using Rails 4.2 or greater you can use Rails enums.

Rails now has enums by default without the need for including any gems.

This is very similar (and more with features) to Java, C++ enums.

Quoted from http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html :

class Conversation < ActiveRecord::Base
  enum status: [ :active, :archived ]
end

# conversation.update! status: 0
conversation.active!
conversation.active? # => true
conversation.status  # => "active"

# conversation.update! status: 1
conversation.archived!
conversation.archived? # => true
conversation.status    # => "archived"

# conversation.update! status: 1
conversation.status = "archived"

# conversation.update! status: nil
conversation.status = nil
conversation.status.nil? # => true
conversation.status      # => nil
Vedant Agarwala
  • 18,146
  • 4
  • 66
  • 89
  • 8
    As you said - not useful if the OP is not using Rails (or more accurately the object is not of type ActiveRecord). Just explaining my downvote is all. – Gerard Dec 08 '15 at 20:25
  • 4
    These are not enums in Ruby, it is an ActiveRecord interface to Enums in your database. Not a generalizable solution that can be applied in any other use-case. – Adam Lassek May 09 '16 at 06:08
  • 1
    I have aleady mentioned that in my answer. – Vedant Agarwala May 13 '16 at 05:34
  • 1
    This is the best answer IFF using Rails. – theUtherSide Nov 15 '16 at 04:10
  • 1
    I don't like it because it must be stored in a Rails database (to work) and because it allows to create many instances of the `Conversation` class - I believe it must allow only 1 instance. – prograils Jun 29 '17 at 09:42
11

This is my approach to enums in Ruby. I was going for short and sweet, not necessarily the the most C-like. Any thoughts?

module Kernel
  def enum(values)
    Module.new do |mod|
      values.each_with_index{ |v,i| mod.const_set(v.to_s.capitalize, 2**i) }

      def mod.inspect
        "#{self.name} {#{self.constants.join(', ')}}"
      end
    end
  end
end

States = enum %w(Draft Published Trashed)
=> States {Draft, Published, Trashed} 

States::Draft
=> 1

States::Published
=> 2

States::Trashed
=> 4

States::Draft | States::Trashed
=> 5
akim
  • 8,255
  • 3
  • 44
  • 60
johnnypez
  • 151
  • 1
  • 6
11

I know it's been a long time since the guy posted this question, but I had the same question and this post didn't give me the answer. I wanted an easy way to see what the number represents, easy comparison, and most of all ActiveRecord support for lookup using the column representing the enum.

I didn't find anything, so I made an awesome implementation called yinum which allowed everything I was looking for. Made ton of specs, so I'm pretty sure it's safe.

Some example features:

COLORS = Enum.new(:COLORS, :red => 1, :green => 2, :blue => 3)
=> COLORS(:red => 1, :green => 2, :blue => 3)
COLORS.red == 1 && COLORS.red == :red
=> true

class Car < ActiveRecord::Base    
  attr_enum :color, :COLORS, :red => 1, :black => 2
end
car = Car.new
car.color = :red / "red" / 1 / "1"
car.color
=> Car::COLORS.red
car.color.black?
=> false
Car.red.to_sql
=> "SELECT `cars`.* FROM `cars` WHERE `cars`.`color` = 1"
Car.last.red?
=> true
Oded Niv
  • 2,557
  • 2
  • 22
  • 20
11

Check out the ruby-enum gem, https://github.com/dblock/ruby-enum.

class Gender
  include Enum

  Gender.define :MALE, "male"
  Gender.define :FEMALE, "female"
end

Gender.all
Gender::MALE
dB.
  • 4,700
  • 2
  • 46
  • 51
9

Perhaps the best lightweight approach would be

module MyConstants
  ABC = Class.new
  DEF = Class.new
  GHI = Class.new
end

This way values have associated names, as in Java/C#:

MyConstants::ABC
=> MyConstants::ABC

To get all values, you can do

MyConstants.constants
=> [:ABC, :DEF, :GHI] 

If you want an enum's ordinal value, you can do

MyConstants.constants.index :GHI
=> 2
Daniel Lubarov
  • 7,796
  • 1
  • 37
  • 56
  • 1
    IMHO this very closely replicates the use and purpose (type safety) from Java, also, as a matter of preference, constants can be defined like this: `class ABC; end` – wik Dec 21 '19 at 16:12
7

If you're worried about typos with symbols, make sure your code raises an exception when you access a value with a non-existent key. You can do this by using fetch rather than []:

my_value = my_hash.fetch(:key)

or by making the hash raise an exception by default if you supply a non-existent key:

my_hash = Hash.new do |hash, key|
  raise "You tried to access using #{key.inspect} when the only keys we have are #{hash.keys.inspect}"
end

If the hash already exists, you can add on exception-raising behaviour:

my_hash = Hash[[[1,2]]]
my_hash.default_proc = proc do |hash, key|
  raise "You tried to access using #{key.inspect} when the only keys we have are #{hash.keys.inspect}"
end

Normally, you don't have to worry about typo safety with constants. If you misspell a constant name, it'll usually raise an exception.

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
  • It seems you're advocating emulating enums with **hashes**, without saying so explicitly. It might be a good idea to edit your answer to say so. (I also currently have a need for something like enums in Ruby, and my first approach to solving it is by using hashes: `FOO_VALUES = {missing: 0, something: 1, something_else: 2, ...}`. This defines the key symbols `missing`, `something`, etc., and also makes them comparable via the associated values.) – Teemu Leisti Sep 10 '13 at 09:06
  • I mean, without saying so at the very start of the answer. – Teemu Leisti Sep 10 '13 at 09:33
6

Another solution is using OpenStruct. Its pretty straight forward and clean.

https://ruby-doc.org/stdlib-2.3.1/libdoc/ostruct/rdoc/OpenStruct.html

Example:

# bar.rb
require 'ostruct' # not needed when using Rails

# by patching Array you have a simple way of creating a ENUM-style
class Array
   def to_enum(base=0)
      OpenStruct.new(map.with_index(base).to_h)
   end
end

class Bar

    MY_ENUM = OpenStruct.new(ONE: 1, TWO: 2, THREE: 3)
    MY_ENUM2 = %w[ONE TWO THREE].to_enum

    def use_enum (value)
        case value
        when MY_ENUM.ONE
            puts "Hello, this is ENUM 1"
        when MY_ENUM.TWO
            puts "Hello, this is ENUM 2"
        when MY_ENUM.THREE
            puts "Hello, this is ENUM 3"
        else
            puts "#{value} not found in ENUM"
        end
    end

end

# usage
foo = Bar.new    
foo.use_enum 1
foo.use_enum 2
foo.use_enum 9


# put this code in a file 'bar.rb', start IRB and type: load 'bar.rb'
Roger
  • 7,535
  • 5
  • 41
  • 63
4

Recently we released a gem that implements Enums in Ruby. In my post you will find the answers on your questions. Also I described there why our implementation is better than existing ones (actually there are many implementations of this feature in Ruby yet as gems).

ka8725
  • 2,788
  • 1
  • 24
  • 39
4

Someone went ahead and wrote a ruby gem called Renum. It claims to get the closest Java/C# like behavior. Personally I'm still learning Ruby, and I was a little shocked when I wanted to make a specific class contain a static enum, possibly a hash, that it wasn't exactly easily found via google.

dlamblin
  • 43,965
  • 20
  • 101
  • 140
  • I have never needed an enum in Ruby. Symbols and constants are idiomatic and solve the same problems, don't they? – Chuck Mar 04 '09 at 20:53
  • Probably Chuck; but googling for an enum in ruby won't get you that far. It will show you results for people's best attempt at a direct equivalent. Which makes me wonder, maybe there's something nice about having the concept wrapped together. – dlamblin Mar 05 '09 at 01:33
  • @Chuck Symbols and constants don't enforce, e.g., that a value has to be one of a small set of values. – David Moles Mar 20 '15 at 21:44
4

It all depends how you use Java or C# enums. How you use it will dictate the solution you'll choose in Ruby.

Try the native Set type, for instance:

>> enum = Set['a', 'b', 'c']
=> #<Set: {"a", "b", "c"}>
>> enum.member? "b"
=> true
>> enum.member? "d"
=> false
>> enum.add? "b"
=> nil
>> enum.add? "d"
=> #<Set: {"a", "b", "c", "d"}>
mislav
  • 14,919
  • 8
  • 47
  • 63
2
module Status
  BAD  = 13
  GOOD = 24

  def self.to_str(status)
    for sym in self.constants
      if self.const_get(sym) == status
        return sym.to_s
      end
    end
  end

end


mystatus = Status::GOOD

puts Status::to_str(mystatus)

Output:

GOOD
Hossein
  • 390
  • 7
  • 14
2

This seems a bit superfluous, but this is a methodology that I have used a few times, especially where I am integrating with xml or some such.

#model
class Profession
  def self.pro_enum
    {:BAKER => 0, 
     :MANAGER => 1, 
     :FIREMAN => 2, 
     :DEV => 3, 
     :VAL => ["BAKER", "MANAGER", "FIREMAN", "DEV"]
    }
  end
end

Profession.pro_enum[:DEV]      #=>3
Profession.pro_enum[:VAL][1]   #=>MANAGER

This gives me the rigor of a c# enum and it is tied to the model.

daniero
  • 335
  • 1
  • 8
  • 11
jjk
  • 536
  • 7
  • 15
  • I wouldn't advise this approach because it relies on you manually setting the values and ensuring you get the order right in `:VAL`. It would be better to start with an array and construct the hash using `.map.with_index` – DaveMongoose Sep 19 '19 at 15:25
  • 1
    The exact point is tying yourself to a value that is dictated by third parties. It's not about extensibility per se, but about having to deal with extraneous constraints that impact computability within your process boundaries. – jjk Sep 19 '19 at 16:15
  • 1
    Fair point! In that case it definitely makes sense to specify the values, but I'd be inclined to do the reverse lookup with `.key` or `.invert` rather than a `:VAL` key ( https://stackoverflow.com/a/10989394/2208016 ) – DaveMongoose Sep 23 '19 at 13:23
  • Yeah, that is (back at you) a fair point. My ruby was inelegant and unwieldy. Would def use `key` or `invert` – jjk Nov 14 '19 at 15:38
2

Symbols is the ruby way. However, sometimes one need to talk to some C code or something or Java that expose some enum for various things.


#server_roles.rb
module EnumLike

  def EnumLike.server_role
    server_Symb=[ :SERVER_CLOUD, :SERVER_DESKTOP, :SERVER_WORKSTATION]
    server_Enum=Hash.new
    i=0
    server_Symb.each{ |e| server_Enum[e]=i; i +=1}
    return server_Symb,server_Enum
  end

end

This can then be used like this


require 'server_roles'

sSymb, sEnum =EnumLike.server_role()

foreignvec[sEnum[:SERVER_WORKSTATION]]=8

This is can of course be made abstract and you can roll our own Enum class

Jonke
  • 6,525
  • 2
  • 25
  • 40
  • Are you capitalizing the second word in variables (eg `server_Symb`) for a particular reason? Unless there's a particular reason, it's idiomatic for variables to be `snake_case_with_all_lower_case`, and for symbols to be `:lower_case`. – Andrew Grimm Mar 16 '11 at 23:03
  • 1
    @Andrew; this example were taken from a real world thing and the network protocol documentation used xxx_Yyy, so the code in several languages used the same concept so one could follow changes of specification. – Jonke Mar 17 '11 at 19:55
  • 1
    Code golfing: `server_Symb.each_with_index { |e,i| server_Enum[e] = i}`. No need for `i = 0`. – Andrew Grimm Mar 17 '11 at 21:48
2

I have implemented enums like that

module EnumType

  def self.find_by_id id
    if id.instance_of? String
      id = id.to_i
    end 
    values.each do |type|
      if id == type.id
        return type
      end
    end
    nil
  end

  def self.values
    [@ENUM_1, @ENUM_2] 
  end

  class Enum
    attr_reader :id, :label

    def initialize id, label
      @id = id
      @label = label
    end
  end

  @ENUM_1 = Enum.new(1, "first")
  @ENUM_2 = Enum.new(2, "second")

end

then its easy to do operations

EnumType.ENUM_1.label

...

enum = EnumType.find_by_id 1

...

valueArray = EnumType.values
Masuschi
  • 21
  • 1
1

Sometimes all I need is to be able to fetch enum's value and identify its name similar to java world.

module Enum
     def get_value(str)
       const_get(str)
     end
     def get_name(sym)
       sym.to_s.upcase
     end
 end

 class Fruits
   include Enum
   APPLE = "Delicious"
   MANGO = "Sweet"
 end

 Fruits.get_value('APPLE') #'Delicious'
 Fruits.get_value('MANGO') # 'Sweet'

 Fruits.get_name(:apple) # 'APPLE'
 Fruits.get_name(:mango) # 'MANGO'

This to me serves the purpose of enum and keeps it very extensible too. You can add more methods to the Enum class and viola get them for free in all the defined enums. for example. get_all_names and stuff like that.

dark_src
  • 486
  • 5
  • 11
1

Try the inum. https://github.com/alfa-jpn/inum

class Color < Inum::Base
  define :RED
  define :GREEN
  define :BLUE
end
Color::RED 
Color.parse('blue') # => Color::BLUE
Color.parse(2)      # => Color::GREEN

see more https://github.com/alfa-jpn/inum#usage

horun
  • 11
  • 1
1

Most people use symbols (that's the :foo_bar syntax). They're sort of unique opaque values. Symbols don't belong to any enum-style type so they're not really a faithful representation of C's enum type but this is pretty much as good as it gets.

Jan Krüger
  • 17,870
  • 3
  • 59
  • 51
0

Another way to mimic an enum with consistent equality handling (shamelessly adopted from Dave Thomas). Allows open enums (much like symbols) and closed (predefined) enums.

class Enum
  def self.new(values = nil)
    enum = Class.new do
      unless values
        def self.const_missing(name)
          const_set(name, new(name))
        end
      end

      def initialize(name)
        @enum_name = name
      end

      def to_s
        "#{self.class}::#@enum_name"
      end
    end

    if values
      enum.instance_eval do
        values.each { |e| const_set(e, enum.new(e)) }
      end
    end

    enum
  end
end

Genre = Enum.new %w(Gothic Metal) # creates closed enum
Architecture = Enum.new           # creates open enum

Genre::Gothic == Genre::Gothic        # => true
Genre::Gothic != Architecture::Gothic # => true
0

Another approach is to use a Ruby class with a hash containing names and values as described in the following RubyFleebie blog post. This allows you to convert easily between values and constants (especially if you add a class method to lookup the name for a given value).

Philippe Monnet
  • 1,152
  • 1
  • 9
  • 13
0

I think the best way to implement enumeration like types is with symbols since the pretty much behave as integer (when it comes to performace, object_id is used to make comparisons ); you don't need to worry about indexing and they look really neat in your code xD

goreorto
  • 302
  • 2
  • 9
0
irb(main):016:0> num=[1,2,3,4]
irb(main):017:0> alph=['a','b','c','d']
irb(main):018:0> l_enum=alph.to_enum
irb(main):019:0> s_enum=num.to_enum
irb(main):020:0> loop do
irb(main):021:1* puts "#{s_enum.next} - #{l_enum.next}"
irb(main):022:1> end

Output:

1 - a
2 - b
3 - c
4 - d

Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
Anu
  • 9
  • 1