it-swarm-eu.dev

Enums in Ruby

Was ist der beste Weg, um das enum-Idiom in Ruby zu implementieren? Ich suche nach etwas, das ich (fast) wie das Java/C # -Enum verwenden kann.

290
auramo

Zwei Wege. Symbole (:foo-Notation) oder Konstanten (FOO-Notation).

Symbole sind sinnvoll, wenn Sie die Lesbarkeit verbessern möchten, ohne Code mit Literalzeichenfolgen zu verschwenden.

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

Konstanten sind angemessen, wenn Sie einen zugrunde liegenden Wert haben, der wichtig ist. Deklarieren Sie einfach ein Modul, das Ihre Konstanten enthält, und deklarieren Sie dann die Konstanten darin.

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

flags = Foo::BAR | Foo::BAZ # flags = 3
289
mlibby

Die idiomatischste Art, dies zu tun, ist die Verwendung von Symbolen. Zum Beispiel anstelle von:

enum {
  FOO,
  BAR,
  BAZ
}

myFunc(FOO);

... Sie können nur Symbole verwenden:

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

Dies ist etwas offener als Enums, passt aber gut zum Ruby-Geist.

Symbole funktionieren auch sehr gut. Der Vergleich zweier Symbole auf Gleichheit ist beispielsweise viel schneller als der Vergleich zweier Zeichenfolgen.

52
emk

Ich bin überrascht, dass niemand so etwas angeboten hat (aus dem RAPI gem gemagert):

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

Was kann so verwendet werden:

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

Beispiel:

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

Dies funktioniert gut in Datenbankszenarien oder beim Umgang mit Konstanten/Enumerationen im C-Stil (wie bei der Verwendung von FFI , von der RAPI ausgiebig Gebrauch macht).

Sie müssen sich auch keine Gedanken über Tippfehler machen, die stille Fehler verursachen, wie dies bei einer Hash-Lösung der Fall wäre.

52
Charles

Ich verwende den folgenden Ansatz:

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

Ich mag es für folgende Vorteile:

  1. Werte gruppieren sich visuell zu einem Ganzen
  2. Es führt einige Überprüfungen der Kompilierungszeit durch (im Gegensatz zur Verwendung von Symbolen)
  3. Ich kann leicht auf die Liste aller möglichen Werte zugreifen: einfach MY_ENUM
  4. Ich kann leicht auf verschiedene Werte zugreifen: MY_VALUE_1
  5. Es kann Werte eines beliebigen Typs haben, nicht nur Symbol

Symbole können besser sein, da Sie den Namen der äußeren Klasse nicht schreiben müssen, wenn Sie ihn in einer anderen Klasse verwenden (MyClass::MY_VALUE_1).

30
Alexey

Wenn Sie Rails 4.2 oder höher verwenden, können Sie Rails-Enums verwenden.

Rails verfügt jetzt standardmäßig über eine Aufzählung, ohne dass Edelsteine ​​hinzugefügt werden müssen.

Dies ist sehr ähnlich (und mehr mit Funktionen) zu Java, C++ - Enums.

Zitiert aus 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
17
vedant

Dies ist meine Herangehensweise an Aufzählungen in Ruby. Ich war kurz und bündig, nicht unbedingt das C-like. Irgendwelche Gedanken?

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

Ich weiß, es ist lange her, seit der Typ diese Frage gestellt hat, aber ich hatte die gleiche Frage und dieser Beitrag gab mir keine Antwort. Ich wollte einen einfachen Weg, um herauszufinden, was die Zahl darstellt, einen einfachen Vergleich und vor allem die Unterstützung von ActiveRecord für die Suche anhand der Spalte, die das Enum darstellt.

Ich habe nichts gefunden, also habe ich eine tolle Implementierung namens yinum gemacht, die alles erlaubt, wonach ich gesucht habe. Wir haben eine Menge Spezifikationen, also bin ich ziemlich sicher, dass es sicher ist.

Einige Beispielfunktionen:

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

Schauen Sie sich den Ruby-Enum-Edelstein an, https://github.com/dblock/Ruby-enum .

class Gender
  include Enum

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

Gender.all
Gender::MALE
7
dB.

Wenn Sie sich wegen Tippfehlern mit Symbolen Sorgen machen, stellen Sie sicher, dass Ihr Code eine Ausnahme auslöst, wenn Sie mit einem nicht vorhandenen Schlüssel auf einen Wert zugreifen. Sie können dies tun, indem Sie fetch anstelle von [] verwenden:

my_value = my_hash.fetch(:key)

oder indem Sie den Hash standardmäßig veranlassen, eine Exception auszulösen, wenn Sie einen nicht vorhandenen Schlüssel angeben:

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

Wenn der Hash bereits vorhanden ist, können Sie ein Verhalten zur Ausnahmebedingung hinzufügen:

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

Normalerweise müssen Sie sich bei Konstanten nicht um die Sicherheit von Tippfehlern sorgen. Wenn Sie einen konstanten Namen falsch schreiben, wird in der Regel eine Ausnahme ausgelöst.

5
Andrew Grimm

Jemand ging voran und schrieb einen Ruby-Edelstein namens Renum . Es behauptet, das nächstliegende Java/C # -ähnliche Verhalten zu erhalten. Persönlich lerne ich immer noch Ruby, und ich war ein wenig geschockt, als ich wollte, dass eine bestimmte Klasse eine statische Aufzählung enthält, möglicherweise einen Hash, der über Google nicht so leicht gefunden werden konnte.

4
dlamblin

Vielleicht wäre der beste leichtgewichtige Ansatz

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

Auf diese Weise haben Werte wie in Java/C # zugeordnete Namen:

MyConstants::ABC
=> MyConstants::ABC

Um alle Werte zu erhalten, können Sie dies tun

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

Wenn Sie den Ordinalwert einer Aufzählung wünschen, können Sie dies tun

MyConstants.constants.index :GHI
=> 2
4
Daniel Lubarov

Kürzlich haben wir ein gem veröffentlicht, das Enums in Ruby implementiert. In meinem post finden Sie die Antworten auf Ihre Fragen. Ich habe dort auch beschrieben, warum unsere Implementierung besser ist als bestehende (tatsächlich gibt es viele Implementierungen dieser Funktion in Ruby noch als Edelsteine). 

3
ka8725

Dies scheint ein wenig überflüssig zu sein, aber dies ist eine Methodik, die ich ein paar Mal verwendet habe, vor allem wenn ich XML integriert habe oder so etwas.

#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

Dies gibt mir die Strenge eines C # -Enums und ist an das Modell gebunden.

2
jjk

Symbole sind der Ruby-Weg. Manchmal muss man jedoch mit etwas C-Code oder etwas oder Java sprechen, das einiges Enum für verschiedene Dinge aufdeckt.


#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

Dies kann dann so verwendet werden


require 'server_roles'

sSymb, sEnum =EnumLike.server_role()

foreignvec[sEnum[:SERVER_WORKSTATION]]=8

Dies kann natürlich abstrakt gemacht werden und Sie können unsere eigene Enum-Klasse rollen 

2
Jonke

Ich habe solche Enums implementiert 

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

dann ist es einfach, Operationen durchzuführen 

EnumType.ENUM_1.label

...

enum = EnumType.find_by_id 1

...

valueArray = EnumType.values
2
Masuschi

Es hängt alles davon ab, wie Sie Java- oder C # -Enummen verwenden. Wie Sie es verwenden, bestimmt die Lösung, die Sie in Ruby wählen.

Versuchen Sie den nativen Set-Typ, zum Beispiel:

>> 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"}>
2
mislav

Eine andere Lösung verwendet OpenStruct. Es ist ziemlich geradlinig und sauber.

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

Beispiel:

# 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'
2
Roger

Die meisten Leute verwenden Symbole (das ist die :foo_bar-Syntax). Sie sind eine Art einzigartige undurchsichtige Werte. Symbole gehören nicht zu einem Enum-Typ, daher sind sie keine wirklich getreue Darstellung des C-Enum-Typs, aber das ist so gut, wie es nur geht.

1
Jan Krüger

Manchmal brauche ich nur, den Wert von enum abrufen und seinen Namen ähnlich der Java-Welt identifizieren zu können.

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'

Dies dient mir zum Zweck der Enumeration und hält es auch sehr dehnbar. Sie können der Enum-Klasse weitere Methoden hinzufügen und Viola in allen definierten Enums kostenlos zur Verfügung stellen. zum Beispiel. get_all_names und solche Sachen.

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

Ausgabe:

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

1
Anu
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)

Ausgabe:

GOOD
1
Hossein

Schnell und dreckig, fühlt sich an wie C #:

class FeelsLikeAnEnum
  def self.Option_1() :option_1 end
  def self.Option_2() :option_2 end
  def self.Option_3() :option_3 end
end

Verwenden Sie es so, als würden Sie eine Aufzählung verwenden:

method_that_needs_options(FeelsLikeAnEnum.Option_1)
0
David Foley

Ich denke, der beste Weg, um Aufzählungstypen zu implementieren, ist mit Symbolen zu arbeiten, da sich diese ziemlich ganzzahlig verhalten (wenn Performace verwendet wird, wird object_id für Vergleiche verwendet). Sie müssen sich keine Gedanken über die Indizierung machen, und sie sehen in Ihrem Code xD wirklich ordentlich aus

0
goreorto

Ein anderer Ansatz ist die Verwendung einer Ruby-Klasse mit einem Hash, der Namen und Werte enthält, wie im folgenden RubyFleebie-Blogbeitrag beschrieben. Dadurch können Sie problemlos zwischen Werten und Konstanten konvertieren (insbesondere, wenn Sie eine Klassenmethode hinzufügen, um den Namen eines bestimmten Werts nachzuschlagen).

0
Philippe Monnet

Eine weitere Möglichkeit, ein Enum mit gleichbleibender Gleichheitsbehandlung nachzuahmen (schamlos von Dave Thomas übernommen). Erlaubt offene Enums (ähnlich wie Symbole) und geschlossene (vordefinierte) 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

Versuchen Sie das 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

mehr sehen https://github.com/alfa-jpn/inum#usage

0
horun