2009-09-22 8 views
3

Tout d'abord, ce n'est pas un double de Enums in Ruby :)avancée énumérations Java comme dans Ruby

La réponse acceptée de cette question suggère cela comme une bonne façon de représenter les énumérations dans Ruby:

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

En Java, il est possible d'associer plusieurs valeurs et méthodes à une valeur enum. Je veux faire la même chose ou quelque chose de similaire dans Ruby.

Quel serait le plus Ruby comme façon de représenter ce enum Java:

public enum Enum 

    VALUE_1("Value 1"), 
    VALUE_2("Value 2"), 
    VALUE_3("Value 3"); 

    Enum(String value) { 
     this.value = value; 
    } 
    public String getValue() { 
     return value; 
    } 
    private String value; 
} 

EDIT:

Je veux aussi garder les caractéristiques implicites de Java énumérations:

  • ... récupérer la valeur ordinale
  • ... appeler des méthodes sur les valeurs enum (ou quelque chose équivalent)

Exemples:

Enum.VALUE_1.getValue(); // "Value 1" 
Enum.VALUE_2.name();  // "VALUE_2" 
Enum.VALUE_3.ordinal(); // 2 
+0

Vous devez supprimer le modificateur 'public' sur le constructeur pour le compiler. –

+0

Fait, merci. –

Répondre

12
class MyEnum 
    attr_accessor :value 
    def initialize(value) 
    @value = value 
    end 

    VALUE1 = new("Value 1") 
    VALUE2 = new("Value 2") 

    class << self 
    private :new 
    end 
end 

MyEnum::VALUE2 # Enum with value "Value 2" 
MyEnum.new # Error 

Une solution plus élaborée qui vous permet de définir des "classes enum" arbitraires et vous donne également ordinal():

def enum(*values, &class_body) 
    Class.new(Class.new(&class_body)) do 
    attr_reader :ordinal 

    def initialize(ordinal, *args, &blk) 
     super(*args, &blk) 
     @ordinal = ordinal 
    end 

    values.each_with_index do |(name, *parameters), i| 
     const_set(name, new(i, *parameters)) 
    end 

    class <<self 
     private :new 
    end 
    end 
end 

# Usage: 
MyEnum = enum([:VALUE1, "Value 1"], [:VALUE2, "Value 2"]) do 
    attr_reader :str 
    def initialize(str) 
    @str = str 
    end 
end 

MyEnum::VALUE1.str #=> "Value 1" 
MyEnum::VALUE2.ordinal #=> 1 
+0

Je pense qu'il est approprié d'enlever attr_accessor: valeur, non? – khelll

+2

Selon le comportement souhaité, il peut être judicieux de le remplacer par 'attr_reader'. Cependant, je ne vois pas pourquoi on voudrait l'enlever complètement parce que je m'attends à ce que l'on veuille au moins pouvoir lire la valeur, sinon il n'y aurait aucune raison d'avoir une valeur en premier lieu. – sepp2k

+1

Absolument vrai, au moins il devrait être capable de faire: MyEnum :: VALUE2.value, ou nous pouvons surcharger to_s pour retourner la valeur, qui serait plus concise;) – khelll

3

Vous peut toujours créer un système similaire à la version Java:

module Foo 
    class Value 
    attr_reader :value 

    def initialize(value) 
     # Save a frozen, immutable copy 
     @value = value.dup.freeze 
    end 

    # Patch in methods to make it appear more friendly and string-like 
    alias_method :to_s, :value 
    alias_method :inspect, :value 
    end 

    # Define constants 
    BAR = Value.new('bar') 
    BAZ = Value.new('baz') 
    BIZ = Value.new('biz') 
end 

puts Foo::BAR 
# => bar 
+0

+1 pour aliasing to_s – khelll

0

Ceci est mon code ... suggéré

EnumModule.rb

module EnumModule 
    CONVERT_PROC = Proc.new do 
    @values = constants.collect{|c| const_get(c)}.freeze 

    @values.each_with_index do |value, idx| 
     the_symbol = constants.find{|c| const_get(c) == value} 
     sig = class << value ; self end 
     sig.send :define_method, :name, proc{the_symbol} 
     sig.send :define_method, :ordinal, proc{idx} 

     if value.is_a? Hash 
     value.each do |k, v| 
      sig.send :define_method, k, (v.is_a?(Proc) ? v : proc{v}) 
     end 
     end 
     value.freeze 
    end 

    class << self 
     alias :value_of :const_get 
    end 

    module_function 
    def each 
     @values.each { |v| yield v } 
    end 
    def values 
     @values 
    end 
    extend Enumerable 

    freeze 
    end 

    def self.extended extending_obj 
    extending_obj.module_eval &CONVERT_PROC 
    end 
end 

SampleEnum.rb

require 'EnumModule' 

module SampleEnum 
    VALUE_1 = { 
    to_s: 'Value_1_str', 
    get_value: proc{'Value 1'} 
    } 

    VALUE_2 = { 
    to_s: 'Value_2_str', 
    get_value: proc{'Value 2'} 
    } 

    extend EnumModule 
end 

#defined method 
p SampleEnum::VALUE_1.get_value #=> "Value 1" 
p SampleEnum::VALUE_2.get_value #=> "Value 2" 

p SampleEnum::VALUE_1.to_s  #=> "Value_1_str" 

#name (returns the symbol of the constant) 
p SampleEnum::VALUE_1.name #=> :VALUE_1 
p SampleEnum::VALUE_2.name #=> :VALUE_2 

#ordinal 
p SampleEnum::VALUE_1.ordinal #=> 0 
p SampleEnum::VALUE_2.ordinal #=> 1 

#emulates Java Enum's valueOf(is an alias of const_get) 
p SampleEnum.value_of('VALUE_1').get_value #=> "Value 1" 
p SampleEnum.value_of(:VALUE_1).get_value #=> "Value 1" 
p SampleEnum.const_get('VALUE_1').get_value #=> "Value 1" 

#emulates Java Enum's values 
SampleEnum.values.each do |m| 
    p m.ordinal, m.name, m.get_value, m.to_s 
end 

#an Enumerable 
p SampleEnum.map{|m| m.get_value} #=> ["Value 1","Value 2"] 

En étendant la EnumModule, Hash constantes contenu (clé & value) deviennent des méthodes Singleton.

Questions connexes