Frage

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

War es hilfreich?

Lösung

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

Die Symbole sind geeignet, wenn Sie die Lesbarkeit verbessern möchten, ohne Code mit Literalzeichenfolgen Littering.

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

Konstanten sind geeignet, wenn Sie einen zugrunde liegenden Wert haben, der wichtig ist. deklarieren nur ein Modul Ihre Konstanten zu halten und dann die Konstanten innerhalb dieser erklären.

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

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

Andere Tipps

Ich bin überrascht, dass niemand so etwas wie die folgende (geerntet von dem 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

, die verwendet werden können, etwa 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

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 spielt auch in Datenbank-Szenarien oder wenn mit C-Stil-Konstanten / Aufzählungen zu tun (wie es der Fall ist, wenn mit FFI , die RAPI umfangreiche Verwendung von) macht.

Auch Sie müssen nicht über Fehler verursachen stille Ausfälle Sorgen, wie würden Sie eine Hash-Typ-Lösung bei der Verwendung.

Die meisten idiomatische Weg, dies zu tun ist, Symbole zu verwenden. Zum Beispiel statt:

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)

Das ist ein bisschen mehr offene als Aufzählungen, aber sie paßt gut mit dem Ruby-Geist.

Symbole auch sehr gut ab. Vergleicht man zwei Symbole für die Gleichstellung, zum Beispiel, ist viel schneller als zwei Strings verglichen werden.

Ich verwende den folgenden Ansatz:

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

Ich mag es für die folgenden Vorteile:

  1. Es Gruppen Werte visuell als Ganzes
  2. Es hat einige Kompilierung-Zeit überprüft (im Gegensatz zu nur unter Verwendung von Symbolen)
  3. Ich kann leicht die Liste aller möglichen Werte zugreifen: nur MY_ENUM
  4. Ich kann leicht unterschiedliche Werte zugreifen: MY_VALUE_1
  5. Es kann Werte von jeder Art hat, nicht nur Symbol

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

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

Rails verfügt jetzt standardmäßig über Aufzählungen, ohne dass Gems einbezogen werden müssen.

Dies ist Java- und C++-Aufzählungen sehr ähnlich (und bietet mehr Funktionen).

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

Das ist mein Ansatz für Aufzählungen in Ruby. Ich wollte kurz und süß, nicht unbedingt das die 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

Schauen Sie sich die rubin Enum Juwel, https://github.com/dblock/ruby-enum .

class Gender
  include Enum

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

Gender.all
Gender::MALE

Ich weiß, dass es eine lange Zeit her ist, seit der Mann diese Frage gestellt, aber ich hatte die gleiche Frage und dieser Beitrag nicht geben Sie mir die Antwort. Ich wollte einen einfachen Weg, um zu sehen, was die Zahl steht für einen einfachen Preisvergleich und die meisten aller Active Unterstützung für die Suche die Spalte, die die Enumeration verwendet wird.

Ich habe nichts gefunden, so habe ich eine tolle Implementierung namens yinum , die alles erlaubt I war auf der Suche nach. Hergestellt Tonne Spezifikationen, also bin ich ziemlich sicher, dass es sicher ist.

Einige Beispiel-Funktionen:

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

Wenn Sie besorgt über Tippfehler mit Symbolen, stellen Sie sicher, dass Ihr Code eine Ausnahme auslöst, wenn Sie einen Wert mit einem nicht vorhandenen Schlüssel zugreifen. Sie können dies tun, indem fetch statt [] mit:

my_value = my_hash.fetch(:key)

oder indem die Hash eine Ausnahme von Standard erhöhen, wenn Sie einen nicht vorhandenen Schlüssel liefern:

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 auf Ausnahme Bungsverhalten 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 nicht mit Konstanten über Typo Sicherheit sorgen. Wenn Sie einen konstanten Namen falsch schreiben, wird es in der Regel eine Ausnahme ausgelöst.

Vielleicht ist der beste leichte Ansatz wäre,

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

Auf diese Weise Werte Namen haben assoziiert sind, wie in Java / C #:

MyConstants::ABC
=> MyConstants::ABC

alle Werte zu erhalten, können Sie tun,

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

Wenn Sie eine ENUM des Ordinalwertes möchten, können Sie tun

MyConstants.constants.index :GHI
=> 2

Jemand ging voran und schrieb ein Ruby Gem genannt Renum . Sie behauptet, die nächsten Java / C # Verhalten zu bekommen. Persönlich bin ich immer noch Ruby-Lernen, und ich war ein wenig schockiert, als ich eine bestimmte Klasse enthält eine statische Enum, möglicherweise ein Hash machen wollte, dass es nicht gerade leicht war über Google gefunden.

Es hängt alles davon, wie Sie verwenden Java oder C # Aufzählungen. Wie Sie es verwenden, wird die Lösung diktieren Sie in Ruby wählen werden.

Versuchen Sie, den nativen Set Typen, 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"}>

Vor kurzem veröffentlichten wir ein gem dass Arbeitsgeräte Aufzählungen in Ruby . In meinem Post Sie die Antworten auf Ihre Fragen. Auch beschreiben ich es, warum unsere Implementierung ist besser als bestehende (tatsächlich gibt es viele Implementierungen dieser Funktion in Ruby noch als Edelstein).

Symbole ist der Rubin Weg. Aber manchmal braucht man bis zu einem gewissen C-Code oder etwas oder Java, die einige Enum aussetzen für verschiedene Dinge zu sprechen.


#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 wie folgt verwendet werden


require 'server_roles'

sSymb, sEnum =EnumLike.server_role()

foreignvec[sEnum[:SERVER_WORKSTATION]]=8

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

Ich habe implementiert Aufzählungen wie das

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 seine einfache Operationen zu tun

EnumType.ENUM_1.label

...

enum = EnumType.find_by_id 1

...

valueArray = EnumType.values

Dies scheint ein wenig überflüssig, aber das ist eine Methode, die ich einige Male verwendet haben, vor allem, wenn ich mit XML oder so bin zu integrieren.

#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

Das gibt mir die Disziplin eines c # Enum, und es wird an das Modell gebunden.

Eine andere Lösung ist OpenStruct verwenden. Es ist ziemlich einfach 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'

Die meisten Leute benutzen Symbole (das ist die :foo_bar Syntax). Sie sind eine Art einzigartiger undurchsichtiger Wert. Symbole gehören nicht zu einem Enum-Style, so dass sie nicht wirklich eine getreue Darstellung von C der Enum-Typ sind, aber das ist ziemlich genau so gut wie es geht.

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

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

Manchmal brauche ich nicht in der Lage seinen Aufzählungswert zu holen und seinen Namen in Java-Welt ähnlich zu identifizieren.

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'

Das ist mir dient der Enum und hält es auch sehr dehnbar. Sie können mehrere Methoden zum Enum-Klasse hinzufügen und Viola sie in allen definierten Aufzählungen kostenlos. zum Beispiel. get_all_names und solche Sachen.

Eine weitere Möglichkeit, eine Enumeration mit gleichbleibender Gleichheit Handling (schamlos von Dave Thomas angenommen) zu imitieren. Erlaubt offene Aufzählungen (ähnlich wie Symbole) und geschlossene (vordefinierte) Aufzählungen.

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

Versuchen Sie, die 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

Schnell und schmutzig, 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, wie Sie eine Enum verwenden würde:

method_that_needs_options(FeelsLikeAnEnum.Option_1)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top