Bagaimana cara mengimplementasikan Enums di Ruby?

324

Apa cara terbaik untuk menerapkan idiom enum di Ruby? Saya mencari sesuatu yang bisa saya gunakan (hampir) seperti Java / C # enums.

auramo
sumber
7
@auramo, pertanyaan bagus, dan pilihan bagus untuk jawaban terbaik. Suka atau benci, Anda tidak mendapatkan keamanan jenis dan (setidaknya di Ruby) tidak ada keamanan ketik. Saya sangat senang ketika saya menemukan enum di C # dan kemudian di Jawa (pilih nilai, tetapi dari ini!), Ruby tidak memberikan cara nyata untuk melakukan itu dalam hal apa pun.
Dan Rosenstark
2
Masalah dengan pertanyaan ini adalah bahwa Java dan C # enum adalah hal yang sangat berbeda. Anggota Java enum adalah instance objek dan singleton. Java enum dapat memiliki konstruktor. Sebaliknya, C # enum didasarkan pada nilai-nilai Primitive. Perilaku apa yang dicari si penanya? Meskipun kemungkinan kasus yang diinginkan adalah kasus C #, Java disebutkan secara eksplisit, bukan C atau C ++, jadi ada beberapa keraguan. Adapun untuk menyarankan bahwa tidak ada cara untuk menjadi 'aman' di Ruby, itu secara transparan salah, tetapi Anda harus menerapkan sesuatu yang lebih canggih.
user1164178

Jawaban:

319

Dua arah. Simbol ( :foonotasi) atau konstanta ( FOOnotasi).

Simbol sesuai ketika Anda ingin meningkatkan keterbacaan tanpa mengotori kode dengan string literal.

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

Konstanta sesuai ketika Anda memiliki nilai dasar yang penting. Hanya mendeklarasikan modul untuk memegang konstanta Anda dan kemudian mendeklarasikan konstanta di dalamnya.

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

flags = Foo::BAR | Foo::BAZ # flags = 3
mlibby
sumber
2
Bagaimana jika enum ini terlalu disimpan ke database? Akankah notasi simbol berfungsi? Saya ragu ...
Phuyơng Nguyễn
Saya akan menggunakan pendekatan konstanta jika saya menyimpan ke database. Tentu saja Anda harus melakukan semacam pencarian ketika menarik data kembali dari DB. Anda juga bisa menggunakan sesuatu seperti :minnesota.to_ssaat menyimpan ke database untuk menyimpan versi string simbol. Rails, saya percaya, memiliki beberapa metode penolong untuk menangani beberapa hal ini.
mlibby
7
Bukankah sebuah modul akan lebih baik untuk dikelompokkan konstanta - karena Anda tidak akan membuat contoh itu?
thomthom
3
Hanya komentar. Ruby agak kesal tentang penamaan tetapi tidak terlalu jelas sampai mereka tersandung. Nama-nama enum harus semua huruf besar dan huruf pertama dari nama modul harus dikapitalisasi untuk ruby ​​untuk mengetahui bahwa modul adalah modul konstanta.
Rokujolady
3
Tidak sepenuhnya benar. Huruf pertama dari konstanta harus ditulis dengan huruf besar, tetapi tidak semua huruf harus. Ini adalah masalah preferensi konvensi. Sebagai contoh, semua nama modul dan nama kelas sebenarnya adalah konstanta juga.
Michael Brown
59

Saya terkejut bahwa tidak ada yang menawarkan sesuatu seperti berikut (diambil dari permata 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

Yang bisa digunakan seperti ini:

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

Contoh:

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

Ini berfungsi baik dalam skenario basis data, atau ketika berhadapan dengan konstanta gaya C / enum (seperti halnya ketika menggunakan FFI , yang membuat penggunaan ekstensif oleh RAPI).

Selain itu, Anda tidak perlu khawatir tentang kesalahan ketik yang menyebabkan kegagalan diam, seperti halnya Anda menggunakan solusi tipe-hash.

Charles
sumber
1
Itu cara yang bagus untuk menyelesaikan masalah khusus itu, tetapi alasan tidak ada yang menyarankan itu mungkin ada hubungannya dengan fakta bahwa itu tidak banyak seperti C # / Java enum.
mlibby
1
Ini agak tidak lengkap, tetapi berfungsi sebagai petunjuk bagus tentang bagaimana Anda dapat mengimplementasikan solusi dengan pendekatan dinamis. Ini memiliki beberapa kemiripan dengan C # enum dengan set FlagsAttribute, tetapi seperti simbol / solusi berbasis konstan di atas, itu adalah salah satu jawaban dari banyak. Masalahnya adalah pertanyaan asli, yang kacau di maksudnya (C # dan Java tidak dapat dipertukarkan). Ada banyak cara untuk merinci objek di Ruby; memilih yang tepat tergantung pada masalah yang sedang dipecahkan. Fitur-fitur replikasi yang tidak Anda perlukan adalah salah arah. Jawaban yang benar harus bergantung pada konteksnya.
user1164178
52

Cara paling idiomatis untuk melakukan ini adalah dengan menggunakan simbol. Misalnya, alih-alih:

enum {
  FOO,
  BAR,
  BAZ
}

myFunc(FOO);

... Anda bisa menggunakan simbol:

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

Ini sedikit lebih terbuka daripada enum, tetapi cocok dengan semangat Ruby.

Simbol juga berkinerja sangat baik. Membandingkan dua simbol untuk kesetaraan, misalnya, jauh lebih cepat daripada membandingkan dua string.

emk
sumber
107
Jadi semangat Ruby adalah: "Typos will compile"
mxcl
82
Kerangka kerja Ruby populer sangat bergantung pada pemrograman runtime, dan melakukan terlalu banyak pengecekan waktu muat akan menghilangkan sebagian besar daya ekspresif Ruby. Untuk menghindari masalah, kebanyakan programmer Ruby mempraktikkan desain yang digerakkan oleh tes, yang tidak hanya akan menemukan kesalahan ketik tetapi juga kesalahan logika.
emk
10
@yar: Nah, desain bahasa adalah serangkaian pertukaran, dan fitur bahasa berinteraksi. Jika Anda ingin bahasa yang baik, sangat dinamis, ikuti Ruby, tulis unit test Anda terlebih dahulu, dan ikuti semangat bahasa tersebut. :-) Jika bukan itu yang Anda cari, ada lusinan bahasa bagus lainnya di luar sana, yang masing-masing menghasilkan pengorbanan yang berbeda.
emk
10
@ em, saya setuju, tapi masalah pribadi saya adalah saya merasa cukup nyaman di Ruby, tapi saya tidak merasa nyaman refactoring di Ruby. Dan sekarang setelah saya mulai menulis unit test (akhirnya), saya menyadari bahwa itu bukan obat mujarab: tebakan saya adalah 1) bahwa kode Ruby tidak mendapatkan refactored secara masif yang sering, dalam praktek dan 2) Ruby bukan akhir -of-the-line dalam hal bahasa dinamis, justru karena sulit untuk refactor secara otomatis. Lihat pertanyaan saya 2317579 yang diambil alih, anehnya, oleh orang-orang Smalltalk.
Dan Rosenstark
4
Ya, tetapi menggunakan string itu tidak akan menjadi semangat bahasa C #, itu hanya praktik yang buruk.
Ed S.
38

Saya menggunakan pendekatan berikut:

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

Saya suka untuk keuntungan berikut:

  1. Ini mengelompokkan nilai-nilai secara visual sebagai satu kesatuan
  2. Ia melakukan pengecekan waktu kompilasi (berbeda dengan hanya menggunakan simbol)
  3. Saya dapat dengan mudah mengakses daftar semua nilai yang mungkin: adil MY_ENUM
  4. Saya dapat dengan mudah mengakses nilai yang berbeda: MY_VALUE_1
  5. Itu dapat memiliki nilai jenis apa pun, bukan hanya Simbol

Simbol mungkin lebih baik karena Anda tidak perlu menulis nama kelas luar, jika Anda menggunakannya di kelas lain ( MyClass::MY_VALUE_1)

Alexey
sumber
4
Saya pikir ini adalah jawaban terbaik. Fungsionalitas, sintaksis, dan overhead kode minimal paling mendekati Java / C #. Anda juga dapat menyarangkan definisi lebih dalam dari satu level dan masih memulihkan semua nilai dengan MyClass :: MY_ENUM.flatten. Sebagai catatan saya akan menggunakan nama-nama besar di sini seperti standar untuk konstanta di Ruby. MyClass :: MyEnum mungkin keliru untuk referensi ke subkelas.
Janosch
@Janosch, saya sudah memperbarui nama. terima kasih untuk saran
Alexey
Saya masih sedikit bingung, dan tautannya 410'd (tidak, bukan 404). Bisakah Anda memberi contoh bagaimana enum ini akan digunakan?
Shelvacu
17

Jika Anda menggunakan Rails 4.2 atau lebih tinggi, Anda dapat menggunakan Rails enum.

Rails sekarang memiliki enum secara default tanpa perlu menyertakan permata apa pun.

Ini sangat mirip (dan lebih banyak dengan fitur) dengan Java, C ++ enums.

Dikutip dari 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
sumber
7
Seperti yang Anda katakan - tidak berguna jika OP tidak menggunakan Rails (atau lebih tepatnya objek bukan tipe ActiveRecord). Menjelaskan downvote saya saja.
Ger
2
Ini bukan enums di Ruby, ini adalah antarmuka ActiveRecord ke Enums di database Anda. Bukan solusi yang dapat digeneralisasi yang dapat diterapkan dalam setiap use case lainnya.
Adam Lassek
Saya telah aleady menyebutkan bahwa dalam jawaban saya.
vedant
Ini adalah jawaban terbaik IFF menggunakan Rails.
theUtherSide
Saya tidak suka karena itu harus disimpan dalam database Rails (untuk bekerja) dan karena memungkinkan untuk membuat banyak instance dari Conversationkelas - saya percaya itu harus memungkinkan hanya 1 instance.
prograils
8

Ini adalah pendekatan saya untuk enums di Ruby. Saya ingin pendek dan manis, belum tentu yang paling seperti C. Adakah pikiran?

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

Mungkin pendekatan ringan terbaik adalah

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

Nilai cara ini memiliki nama terkait, seperti dalam Java / C #:

MyConstants::ABC
=> MyConstants::ABC

Untuk mendapatkan semua nilai, Anda bisa melakukannya

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

Jika Anda ingin nilai ordinal enum, Anda bisa melakukannya

MyConstants.constants.index :GHI
=> 2
Daniel Lubarov
sumber
1
IMHO ini sangat dekat mereplikasi penggunaan dan tujuan (tipe keselamatan) dari Jawa, juga, sebagai pilihan, konstanta dapat didefinisikan seperti ini:class ABC; end
wik
8

Saya tahu sudah lama sejak orang itu memposting pertanyaan ini, tetapi saya memiliki pertanyaan yang sama dan posting ini tidak memberi saya jawabannya. Saya ingin cara mudah untuk melihat apa yang mewakili angka, perbandingan mudah, dan sebagian besar dari semua dukungan ActiveRecord untuk pencarian menggunakan kolom yang mewakili enum.

Saya tidak menemukan apa pun, jadi saya membuat implementasi yang luar biasa yang disebut yinum yang memungkinkan semua yang saya cari. Membuat banyak spesifikasi, jadi saya cukup yakin itu aman.

Beberapa contoh fitur:

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

Jika Anda khawatir tentang kesalahan ketik dengan simbol, pastikan kode Anda menimbulkan pengecualian saat Anda mengakses nilai dengan kunci yang tidak ada. Anda dapat melakukan ini dengan menggunakan fetchdaripada []:

my_value = my_hash.fetch(:key)

atau dengan membuat hash meningkatkan pengecualian secara default jika Anda memberikan kunci yang tidak ada:

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

Jika hash sudah ada, Anda bisa menambahkan perilaku peningkatan pengecualian:

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

Biasanya, Anda tidak perlu khawatir tentang keamanan kesalahan ketik dengan konstanta. Jika Anda salah mengeja nama konstan, biasanya akan muncul pengecualian.

Andrew Grimm
sumber
Tampaknya Anda menganjurkan meniru enum dengan hash , tanpa mengatakannya secara eksplisit. Mungkin ada baiknya mengedit jawaban Anda untuk mengatakannya. (Saya juga saat ini memiliki kebutuhan untuk sesuatu seperti enum di Ruby, dan pendekatan pertama saya untuk memecahkan itu adalah dengan menggunakan hash: FOO_VALUES = {missing: 0, something: 1, something_else: 2, ...}ini mendefinisikan simbol kunci. missing, something, Dll, dan juga membuat mereka sebanding melalui nilai-nilai terkait.)
Teemu Leisti
Maksudku, tanpa mengatakannya di awal jawaban.
Teemu Leisti
4

Seseorang pergi ke depan dan menulis permata ruby ​​bernama Renum . Ia mengklaim untuk mendapatkan perilaku Java / C # terdekat. Secara pribadi saya masih belajar Ruby, dan saya sedikit terkejut ketika saya ingin membuat kelas tertentu berisi enum statis, mungkin hash, bahwa itu tidak mudah ditemukan melalui google.

dlamblin
sumber
Saya tidak pernah membutuhkan enum di Ruby. Simbol dan konstanta itu idiom dan menyelesaikan masalah yang sama, bukan?
Chuck
Mungkin Chuck; tetapi googling untuk enum di ruby ​​tidak akan membuat Anda sejauh itu. Ini akan menunjukkan hasil untuk upaya terbaik orang di setara langsung. Yang membuat saya bertanya-tanya, mungkin ada sesuatu yang menyenangkan tentang konsep yang disatukan.
dlamblin
@Chuck Simbol dan konstanta tidak memberlakukan, misalnya, bahwa nilai harus menjadi salah satu dari sejumlah kecil nilai.
David Moles
3

Itu semua tergantung bagaimana Anda menggunakan Java atau C # enums. Cara Anda menggunakannya akan menentukan solusi yang akan Anda pilih di Ruby.

Coba Setjenis asli , misalnya:

>> 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
sumber
9
Mengapa tidak menggunakan simbol Set[:a, :b, :c]?
Dan Rosenstark
2
Latihan yang jauh lebih baik untuk menggunakan simbol di sini, IMO.
Collin Graves
3

Baru-baru ini kami merilis permata yang mengimplementasikan Enums di Ruby . Dalam posting saya, Anda akan menemukan jawaban atas pertanyaan Anda. Juga saya jelaskan di sana mengapa implementasi kami lebih baik daripada yang sudah ada (sebenarnya ada banyak implementasi fitur ini di Ruby sebagai permata).

ka8725
sumber
Ini memungkinkan peningkatan nilai diri, tanpa menyatakannya secara eksplisit. +1
dimid
3

Solusi lain adalah menggunakan OpenStruct. Cukup lurus ke depan dan bersih.

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

Contoh:

# 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
sumber
2

Simbol adalah cara batu delima. Namun, terkadang seseorang perlu berbicara dengan beberapa kode C atau sesuatu atau Java yang mengekspos beberapa enum untuk berbagai hal.


#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

Ini kemudian dapat digunakan seperti ini


require 'server_roles'

sSymb, sEnum =EnumLike.server_role()

foreignvec[sEnum[:SERVER_WORKSTATION]]=8

Ini tentu saja dapat dibuat abstrak dan Anda dapat memutar kelas Enum kami sendiri

Jonke
sumber
Apakah Anda menggunakan server_Symbhuruf kedua dalam variabel (mis. ) Karena alasan tertentu? Kecuali jika ada alasan tertentu, itu menjadi idiomatis untuk variabel snake_case_with_all_lower_case, dan simbol :lower_case.
Andrew Grimm
1
@Andrew; contoh ini diambil dari dunia nyata dan dokumentasi protokol jaringan menggunakan xxx_Yyy, jadi kode dalam beberapa bahasa menggunakan konsep yang sama sehingga orang dapat mengikuti perubahan spesifikasi.
Jonke
1
Kode golf: server_Symb.each_with_index { |e,i| server_Enum[e] = i}. Tidak perlu i = 0.
Andrew Grimm
2

Saya telah menerapkan enum seperti itu

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

maka mudah untuk melakukan operasi

EnumType.ENUM_1.label

...

enum = EnumType.find_by_id 1

...

valueArray = EnumType.values
Masuschi
sumber
2

Ini sepertinya agak berlebihan, tetapi ini adalah metodologi yang telah saya gunakan beberapa kali, terutama di mana saya mengintegrasikan dengan xml atau semacamnya.

#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

Ini memberi saya ketelitian ac # enum dan itu terikat dengan model.

jjk
sumber
Saya tidak akan menyarankan pendekatan ini karena ini bergantung pada Anda secara manual mengatur nilai-nilai dan memastikan Anda mendapatkan pesanan dengan benar :VAL. Akan lebih baik untuk memulai dengan sebuah array dan membangun hash menggunakan.map.with_index
DaveMongoose
1
Poin yang tepat adalah mengikat diri Anda pada nilai yang ditentukan oleh pihak ketiga. Ini bukan tentang ekstensibilitas per se, tetapi tentang harus berurusan dengan kendala asing yang memengaruhi komputabilitas dalam batas proses Anda.
jjk
Titik adil! Dalam hal ini jelas masuk akal untuk menentukan nilai-nilai, tetapi saya akan cenderung untuk melakukan pencarian terbalik dengan .keyatau .invertdaripada :VALkunci ( stackoverflow.com/a/10989394/2208016 )
DaveMongoose
Ya, itu (kembali padamu) poin yang adil. Rubi saya tidak bagus dan berat. Akan menggunakan def keyatauinvert
jjk
1

Kebanyakan orang menggunakan simbol (itulah :foo_barsintaks). Mereka semacam nilai-nilai buram yang unik. Simbol bukan milik tipe enum apa pun sehingga simbol tersebut tidak benar-benar mewakili tipe enum C, tetapi ini cukup bagus.

Jan Krüger
sumber
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

Keluaran:

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

Anu
sumber
to_enummemberi Anda enumera tor , sedangkan enumdalam C # / Java sense adalah 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)

Keluaran:

GOOD
Hossein
sumber
1

Terkadang yang saya butuhkan adalah bisa mengambil nilai enum dan mengidentifikasi namanya mirip dengan dunia java.

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'

Ini bagi saya melayani tujuan enum dan menjaganya agar tetap dapat diperluas. Anda dapat menambahkan lebih banyak metode ke kelas Enum dan mendapatkan biola secara gratis di semua enum yang ditentukan. sebagai contoh. get_all_names dan semacamnya.

dark_src
sumber
0

Pendekatan lain adalah dengan menggunakan kelas Ruby dengan hash yang berisi nama dan nilai seperti yang dijelaskan dalam posting blog RubyFleebie berikut . Ini memungkinkan Anda untuk mengkonversi dengan mudah antara nilai dan konstanta (terutama jika Anda menambahkan metode kelas untuk mencari nama untuk nilai yang diberikan).

Philippe Monnet
sumber
0

Saya pikir cara terbaik untuk mengimplementasikan enumerasi seperti jenis adalah dengan simbol karena cukup banyak berperilaku sebagai integer (ketika datang ke performace, object_id digunakan untuk membuat perbandingan); Anda tidak perlu khawatir tentang pengindeksan dan mereka terlihat sangat rapi dalam kode xD Anda

goreorto
sumber
0

Cara lain untuk meniru enum dengan penanganan kesetaraan yang konsisten (tanpa malu-malu diadopsi dari Dave Thomas). Mengizinkan enum terbuka (seperti simbol) dan enum tertutup (sudah ditentukan sebelumnya).

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
sumber