Comment implémenter Enums dans Ruby?

324

Quelle est la meilleure façon d'implémenter l'idiome enum dans Ruby? Je cherche quelque chose que je peux utiliser (presque) comme les énumérations Java / C #.

auramo
la source
7
@auramo, bonne question et excellent choix pour la meilleure réponse. Aimez-le ou détestez-le, vous n'obtenez aucune sécurité de type et (au moins dans Ruby) aucune sécurité de faute de frappe. J'ai été ravi lorsque j'ai découvert des énumérations en C # et plus tard en Java (choisissez une valeur, mais à partir de celles-ci!), Ruby ne fournit en aucun cas un véritable moyen de le faire.
Dan Rosenstark
2
Le problème avec cette question est que les énumérations Java et C # sont des choses radicalement différentes. Un membre de l'énumération Java est une instance d'objet et un singleton. Une énumération Java peut avoir un constructeur. En revanche, les énumérations C # sont basées sur des valeurs primitives. Quel comportement le questionneur recherche-t-il? Bien qu'il soit probable que le cas C # soit recherché, Java est explicitement mentionné, plutôt que C ou C ++, il y a donc un doute. Quant à suggérer qu'il n'y a aucun moyen d'être «sûr» dans Ruby, c'est complètement faux, mais vous devez implémenter quelque chose de plus sophistiqué.
user1164178

Réponses:

319

Deux manières. Symboles ( :foonotation) ou constantes ( FOOnotation).

Les symboles sont appropriés lorsque vous souhaitez améliorer la lisibilité sans encombrer le code avec des chaînes littérales.

postal_code[:minnesota] = "MN"
postal_code[:new_york] = "NY"

Les constantes sont appropriées lorsque vous avez une valeur sous-jacente qui est importante. Déclarez simplement un module pour contenir vos constantes, puis déclarez les constantes à l'intérieur.

module Foo
  BAR = 1
  BAZ = 2
  BIZ = 4
end

flags = Foo::BAR | Foo::BAZ # flags = 3
mlibby
la source
2
Que faire si ces énumérations sont également stockées dans la base de données? La notation des symboles fonctionne-t-elle? Je doute ...
Phương Nguyễn
J'utiliserais l'approche des constantes si je sauvegardais dans une base de données. Bien sûr, vous devez alors faire une sorte de recherche lorsque vous retirez les données de la base de données. Vous pouvez également utiliser quelque chose comme :minnesota.to_slors de l'enregistrement dans une base de données pour enregistrer la version chaîne du symbole. Rails, je crois, a des méthodes d'aide pour gérer certains de ces problèmes.
mlibby
7
Un module ne serait-il pas préférable de regrouper des constantes - car vous n'en ferez aucune instance?
thomthom
3
Juste un commentaire. Ruby est un peu pénible à propos des conventions de dénomination, mais pas vraiment évidentes à leur sujet jusqu'à ce que vous les survoliez. Les noms des énumérations doivent être en majuscules et la première lettre du nom du module doit être en majuscule pour que ruby ​​sache que le module est un module de constantes.
Rokujolady
3
Pas tout à fait vrai. La première lettre de la constante doit être en majuscule, mais pas toutes les lettres. C'est une question de préférence conventionnelle. Par exemple, tous les noms de module et les noms de classe sont également des constantes.
Michael Brown
59

Je suis surpris que personne n'ait offert quelque chose comme ce qui suit (récolté à partir de la gemme RAPI ):

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

Qui peut être utilisé comme ceci:

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

Exemple:

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

Cela fonctionne bien dans les scénarios de base de données, ou lorsqu'il s'agit de constantes / énumérations de style C (comme c'est le cas lors de l'utilisation de FFI , que RAPI utilise largement).

De plus, vous n'avez pas à vous soucier des fautes de frappe provoquant des échecs silencieux, comme vous le feriez avec une solution de type hachage.

Charles
la source
1
C'est un excellent moyen de résoudre ce problème particulier, mais la raison pour laquelle personne n'a suggéré que cela a probablement à voir avec le fait qu'il ne ressemble pas beaucoup aux énumérations C # / Java.
mlibby
1
Ceci est un peu incomplet, mais sert de bon indice sur la façon dont vous pouvez implémenter des solutions avec une approche dynamique. Cela ressemble à une énumération C # avec l'ensemble FlagsAttribute, mais comme les solutions basées sur les symboles / constantes ci-dessus, c'est une réponse parmi tant d'autres. Le problème est la question d'origine, qui est confuse dans son intention (C # et Java ne sont pas interchangeables). Il existe de nombreuses façons de détailler des objets dans Ruby; sélectionner le bon dépend du problème à résoudre. Répliquer servilement les fonctionnalités dont vous n'avez pas besoin est erroné. La bonne réponse doit dépendre du contexte.
user1164178
52

La façon la plus idiomatique de le faire est d'utiliser des symboles. Par exemple, au lieu de:

enum {
  FOO,
  BAR,
  BAZ
}

myFunc(FOO);

... vous pouvez simplement utiliser des symboles:

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

C'est un peu plus ouvert que les énumérations, mais cela correspond bien à l'esprit Ruby.

Les symboles fonctionnent également très bien. La comparaison de deux symboles pour l'égalité, par exemple, est beaucoup plus rapide que la comparaison de deux chaînes.

emk
la source
107
Donc l'esprit Ruby est: "Typos compilera"
mxcl
82
Les frameworks Ruby populaires reposent fortement sur la métaprogrammation d'exécution, et effectuer trop de vérification du temps de chargement enlèverait la plupart de la puissance expressive de Ruby. Pour éviter les problèmes, la plupart des programmeurs Ruby pratiquent la conception pilotée par les tests, qui détectera non seulement les fautes de frappe, mais également les erreurs logiques.
emk
10
@yar: Eh bien, la conception du langage est une série de compromis et les fonctionnalités du langage interagissent. Si vous voulez une bonne langue très dynamique, allez avec Ruby, écrivez d'abord vos tests unitaires et allez avec l'esprit de la langue. :-) Si ce n'est pas ce que vous recherchez, il existe des dizaines d'autres excellentes langues, chacune faisant des compromis différents.
emk
10
@emk, je suis d'accord, mais mon problème personnel est que je me sens assez à l'aise dans Ruby, mais je ne me sens pas à l'aise dans la refactorisation dans Ruby. Et maintenant que j'ai commencé à écrire des tests unitaires (enfin), je me rends compte qu'ils ne sont pas une panacée: je suppose que 1) que le code Ruby n'est pas massivement refactorisé souvent, en pratique et 2) Ruby n'est pas la fin -de la ligne en termes de langages dynamiques, précisément parce qu'il est difficile de refactoriser automatiquement. Voir ma question 2317579 qui a été étrangement reprise par les gens de Smalltalk.
Dan Rosenstark
4
Oui, mais utiliser ces chaînes ne serait pas dans l'esprit du langage C #, c'est simplement une mauvaise pratique.
Ed S.
38

J'utilise l'approche suivante:

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

Je l'aime pour les avantages suivants:

  1. Il regroupe les valeurs visuellement comme un tout
  2. Il effectue une vérification au moment de la compilation (contrairement à l'utilisation de symboles uniquement)
  3. Je peux facilement accéder à la liste de toutes les valeurs possibles: juste MY_ENUM
  4. Je peux facilement accéder à des valeurs distinctes: MY_VALUE_1
  5. Il peut avoir des valeurs de tout type, pas seulement de Symbol

Les symboles peuvent être meilleurs car vous n'avez pas à écrire le nom de la classe externe, si vous l'utilisez dans une autre classe ( MyClass::MY_VALUE_1)

Alexey
la source
4
Je pense que c'est la meilleure réponse. La fonctionnalité, la syntaxe et la surcharge minimale de code se rapprochent le plus de Java / C #. Vous pouvez également imbriquer les définitions encore plus profondément qu'un niveau et toujours récupérer toutes les valeurs avec MyClass :: MY_ENUM.flatten. En guise de remarque, j'utiliserais ici des noms en majuscules, comme c'est la norme pour les constantes en Ruby. MyClass :: MyEnum peut être confondu avec une référence à une sous-classe.
Janosch
@Janosch, j'ai mis à jour les noms. merci pour la suggestion
Alexey
Je suis toujours un peu confus, et le lien 410'd (non, pas 404). Pourriez-vous donner des exemples sur la façon dont cette énumération serait utilisée?
Shelvacu
17

Si vous utilisez Rails 4.2 ou supérieur, vous pouvez utiliser les énumérations Rails.

Rails a maintenant des énumérations par défaut sans avoir besoin d'inclure de gemmes.

Ceci est très similaire (et plus avec des fonctionnalités) aux énumérations Java, C ++.

Cité sur 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
la source
7
Comme vous l'avez dit - pas utile si l'OP n'utilise pas Rails (ou plus précisément l'objet n'est pas de type ActiveRecord). Juste expliquer mon downvote est tout.
Ger
2
Ce ne sont pas des énumérations dans Ruby, c'est une interface ActiveRecord avec Enums dans votre base de données. Pas une solution généralisable qui peut être appliquée dans tout autre cas d'utilisation.
Adam Lassek
J'ai déjà mentionné cela dans ma réponse.
vedant
C'est la meilleure réponse IFF en utilisant Rails.
theUtherSide
Je ne l'aime pas car il doit être stocké dans une base de données Rails (pour fonctionner) et parce qu'il permet de créer de nombreuses instances de la Conversationclasse - je crois qu'il ne doit autoriser qu'une seule instance.
prograils
8

C'est mon approche des énumérations en Ruby. J'allais pour court et doux, pas nécessairement le plus C-like. Des pensées?

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
=> 3
johnnypez
la source
8

La meilleure approche légère serait peut-être

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

De cette façon, les valeurs ont des noms associés, comme dans Java / C #:

MyConstants::ABC
=> MyConstants::ABC

Pour obtenir toutes les valeurs, vous pouvez faire

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

Si vous voulez une valeur ordinale d'une énumération, vous pouvez le faire

MyConstants.constants.index :GHI
=> 2
Daniel Lubarov
la source
1
À mon humble avis, cela reproduit de très près l'utilisation et le but (sécurité du type) de Java, également, de préférence, les constantes peuvent être définies comme suit:class ABC; end
wik
8

Je sais que cela fait longtemps que le gars n'a pas posté cette question, mais j'avais la même question et ce post ne m'a pas donné la réponse. Je voulais un moyen facile de voir ce que le nombre représente, une comparaison facile et surtout la prise en charge d'ActiveRecord pour la recherche en utilisant la colonne représentant l'énumération.

Je n'ai rien trouvé, j'ai donc fait une implémentation géniale appelée yinum qui a permis tout ce que je cherchais. Fait des tonnes de spécifications, donc je suis sûr que c'est sûr.

Quelques exemples de fonctionnalités:

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
la source
5

Si vous vous inquiétez des fautes de frappe avec des symboles, assurez-vous que votre code lève une exception lorsque vous accédez à une valeur avec une clé inexistante. Vous pouvez le faire en utilisant fetchplutôt que []:

my_value = my_hash.fetch(:key)

ou en faisant le hachage lever une exception par défaut si vous fournissez une clé inexistante:

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

Si le hachage existe déjà, vous pouvez ajouter un comportement de levée d'exception:

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

Normalement, vous n'avez pas à vous soucier de la sécurité des fautes de frappe avec les constantes. Si vous mal orthographiez un nom constant, cela déclenchera généralement une exception.

Andrew Grimm
la source
Il semble que vous préconisiez d'émuler des énumérations avec des hachages , sans le dire explicitement. Ce serait une bonne idée de modifier votre réponse pour le dire. (Je aussi ai actuellement un besoin de quelque chose comme énumérations en Ruby, et ma première approche pour résoudre c'est à l'aide hash: FOO_VALUES = {missing: 0, something: 1, something_else: 2, ...}Ceci définit les symboles clés. missing, somethingEtc., et rend également comparables par les valeurs associées.)
Teemu Leisti
Je veux dire, sans le dire au tout début de la réponse.
Teemu Leisti
4

Quelqu'un est allé de l'avant et a écrit un joyau de rubis appelé Renum . Il prétend obtenir le comportement similaire à Java / C # le plus proche. Personnellement, j'apprends encore Ruby, et j'ai été un peu choqué quand j'ai voulu faire qu'une classe spécifique contienne une énumération statique, peut-être un hachage, qu'elle ne soit pas exactement facilement trouvée via google.

dlamblin
la source
Je n'ai jamais eu besoin d'une énumération en Ruby. Les symboles et les constantes sont idiomatiques et résolvent les mêmes problèmes, non?
Chuck
Probablement Chuck; mais googler pour une énumération en rubis ne vous mènera pas si loin. Il vous montrera les résultats de la meilleure tentative des gens pour un équivalent direct. Ce qui me fait me demander, peut-être qu'il y a quelque chose de bien à regrouper le concept.
dlamblin
@Chuck Les symboles et les constantes n'imposent pas, par exemple, qu'une valeur doit faire partie d'un petit ensemble de valeurs.
David Moles
3

Tout dépend de la façon dont vous utilisez les énumérations Java ou C #. La façon dont vous l'utiliserez dictera la solution que vous choisirez dans Ruby.

Essayez le Settype natif , par exemple:

>> 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
la source
9
Pourquoi ne pas utiliser des symboles Set[:a, :b, :c]?
Dan Rosenstark
2
Bien meilleure pratique pour utiliser des symboles ici, OMI.
Collin Graves
3

Récemment, nous avons publié une gemme qui implémente Enums in Ruby . Dans mon article, vous trouverez les réponses à vos questions. J'y ai également expliqué pourquoi notre implémentation est meilleure que celles existantes (en fait, il existe de nombreuses implémentations de cette fonctionnalité dans Ruby encore comme des gemmes).

ka8725
la source
Il permet des valeurs auto-incrémentées, sans les indiquer explicitement. +1
dimid
3

Une autre solution utilise OpenStruct. C'est assez simple et propre.

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

Exemple:

# 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
la source
2

Les symboles sont la voie rubis. Cependant, il faut parfois parler à du code C ou à quelque chose ou à Java qui exposent une énumération pour diverses choses.


#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

Cela peut ensuite être utilisé comme ceci


require 'server_roles'

sSymb, sEnum =EnumLike.server_role()

foreignvec[sEnum[:SERVER_WORKSTATION]]=8

Ceci peut bien sûr être rendu abstrait et vous pouvez lancer notre propre classe Enum

Jonke
la source
Mettez-vous en majuscule le deuxième mot dans des variables (par exemple server_Symb) pour une raison particulière? À moins qu'il n'y ait une raison particulière, il est idiomatique que les variables soient snake_case_with_all_lower_caseet que les symboles soient :lower_case.
Andrew Grimm
1
@Andrew; cet exemple a été tiré d'un monde réel et la documentation du protocole réseau a utilisé xxx_Yyy, donc le code dans plusieurs langues a utilisé le même concept afin que l'on puisse suivre les changements de spécification.
Jonke
1
Code golf: server_Symb.each_with_index { |e,i| server_Enum[e] = i}. Pas besoin de i = 0.
Andrew Grimm
2

J'ai implémenté des énumérations comme ça

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

puis ses opérations faciles à faire

EnumType.ENUM_1.label

...

enum = EnumType.find_by_id 1

...

valueArray = EnumType.values
Masuschi
la source
2

Cela semble un peu superflu, mais c'est une méthodologie que j'ai utilisée à quelques reprises, en particulier lorsque j'intègre avec xml ou quelque chose du genre.

#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

Cela me donne la rigueur d'un ac # enum et c'est lié au modèle.

jjk
la source
Je ne conseillerais pas cette approche, car elle repose sur vous en définissant manuellement les valeurs et en vous assurant d'obtenir la bonne commande :VAL. Il serait préférable de commencer avec un tableau et de construire le hachage en utilisant.map.with_index
DaveMongoose
1
Le point exact est de vous lier à une valeur dictée par des tiers. Il ne s'agit pas d'extensibilité en soi, mais de devoir faire face à des contraintes externes qui ont un impact sur la calculabilité dans les limites de votre processus.
jjk
Bon point! Dans ce cas, il est certainement logique de spécifier les valeurs, mais je serais enclin à faire la recherche inversée avec .keyou .invertplutôt qu'avec une :VALclé ( stackoverflow.com/a/10989394/2208016 )
DaveMongoose
Ouais, c'est (de retour à vous) un bon point. Mon rubis était inélégant et peu maniable. Voudrait utiliser keyouinvert
jjk
1

La plupart des gens utilisent des symboles (c'est la :foo_barsyntaxe). Ce sont en quelque sorte des valeurs opaques uniques. Les symboles n'appartiennent à aucun type de style enum, ils ne sont donc pas vraiment une représentation fidèle du type enum de C, mais c'est à peu près aussi bon que possible.

Jan Krüger
la source
1
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

Production:

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

Anu
la source
to_enumvous donne un enumera teur , alors que enumdans le C # / Java sens est une enumera tion
DaveMongoose
1
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)

Production:

GOOD
Hossein
la source
1

Parfois, tout ce dont j'ai besoin est de pouvoir récupérer la valeur d'énumération et identifier son nom de manière similaire à 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'

Pour moi, cela sert le but de l'énumération et le maintient très extensible aussi. Vous pouvez ajouter plus de méthodes à la classe Enum et l'alto les obtenir gratuitement dans toutes les énumérations définies. par exemple. get_all_names et des trucs comme ça.

dark_src
la source
0

Une autre approche consiste à utiliser une classe Ruby avec un hachage contenant des noms et des valeurs comme décrit dans le billet de blog RubyFleebie suivant . Cela vous permet de convertir facilement entre des valeurs et des constantes (surtout si vous ajoutez une méthode de classe pour rechercher le nom d'une valeur donnée).

Philippe Monnet
la source
0

Je pense que la meilleure façon d'implémenter l'énumération comme les types est avec des symboles car ils se comportent à peu près comme des entiers (quand il s'agit de performace, object_id est utilisé pour faire des comparaisons); vous n'avez pas à vous soucier de l'indexation et ils ont l'air vraiment bien dans votre code xD

goreorto
la source
0

Une autre façon d'imiter une énumération avec une gestion cohérente de l'égalité (adoptée sans vergogne par Dave Thomas). Permet des énumérations ouvertes (un peu comme les symboles) et des énumérations fermées (prédéfinies).

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
Daniel Doubleday
la source