Jak zaimplementować Enums w Ruby?
Jaki jest najlepszy sposób implementacji idiomu enum w Rubim? Szukam czegoś, czego mógłbym użyć (prawie) jak Java / C # enums.
25 answers
Na dwa sposoby. Symbole (:foo
notacja) lub stałe (FOO
notacja).
Symbole są odpowiednie, gdy chcesz poprawić czytelność bez zaśmiecania kodu literalnymi ciągami znaków.
postal_code[:minnesota] = "MN"
postal_code[:new_york] = "NY"
Stałe są odpowiednie, gdy masz podstawową wartość, która jest ważna. Po prostu zadeklaruj moduł, który przechowuje Twoje stałe, a następnie zadeklaruj stałe w nim.
module Foo
BAR = 1
BAZ = 2
BIZ = 4
end
flags = Foo::BAR | Foo::BAZ # flags = 3
Dodano 2021-01-17
Jeśli przekazujesz wartość enum (na przykład przechowujesz ją w bazie danych) i musisz być w stanie przetłumaczyć wartość z powrotem na symbol, istnieje mashup obu podejść
COMMODITY_TYPE = {
currency: 1,
investment: 2,
}
def commodity_type_string(value)
COMMODITY_TYPE.key(value)
end
COMMODITY_TYPE[:currency]
To podejście zainspirowane odpowiedzią andrew-Grimma https://stackoverflow.com/a/5332950/13468
Polecam również przeczytać resztę odpowiedzi tutaj, ponieważ istnieje wiele sposobów, aby rozwiązać ten problem i to naprawdę sprowadza się do tego, co jest o enum innego języka, na którym ci zależy
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-17 15:16:00
Dziwię się, że nikt nie zaoferował czegoś takiego (pobranego z 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
Które mogą być używane tak:
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
Przykład:
>> 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
To dobrze sprawdza się w scenariuszach bazodanowych, lub gdy radzimy sobie ze stałymi/enumami w stylu C (jak to ma miejsce w przypadku używania FFI , z których RAPI szeroko korzysta).
Ponadto, nie musisz się martwić o literówki powodujące ciche błędy, tak jak przy użyciu rozwiązania typu hash.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-29 21:19:58
Najbardziej idiomatycznym sposobem na to jest użycie symboli. Na przykład zamiast:
enum {
FOO,
BAR,
BAZ
}
myFunc(FOO);
...można po prostu używać symboli:
# 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)
Jest to nieco bardziej otwarte niż enum, ale dobrze pasuje do Ducha rubinowego.
Symbole również działają bardzo dobrze. Porównywanie dwóch symboli dla równości, na przykład, jest znacznie szybsze niż porównywanie dwóch ciągów.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 19:06:04
Stosuję następujące podejście:
class MyClass
MY_ENUM = [MY_VALUE_1 = 'value1', MY_VALUE_2 = 'value2']
end
Podoba mi się za następujące zalety:
- grupuje wartości wizualnie jako jedną całość W przeciwieństwie do symboli, program sprawdza czas kompilacji.]}
- mogę łatwo uzyskać dostęp do listy wszystkich możliwych wartości: just
MY_ENUM
- mogę łatwo uzyskać dostęp do różnych wartości:
MY_VALUE_1
- może mieć wartości dowolnego typu, a nie tylko Symbol
Symbole mogą być lepsze, bo nie musisz pisać nazwa zewnętrznej klasy, jeśli używasz jej w innej klasie (MyClass::MY_VALUE_1
)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-14 09:45:06
Jeśli używasz Rails 4.2 lub nowszego, możesz użyć rails enums.
Rails ma teraz domyślnie enums bez potrzeby dołączania żadnych klejnotów.
Jest to bardzo podobne (i Więcej z funkcjami) do JAVA, C++ enums.
Cytat z 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-24 11:23:26
Wiem, że minęło sporo czasu odkąd facet napisał to pytanie, ale ja miałem to samo pytanie i ten post nie dał mi odpowiedzi. Chciałem w łatwy sposób zobaczyć, co reprezentuje liczba, łatwe porównanie, a przede wszystkim wsparcie ActiveRecord dla lookup przy użyciu kolumny reprezentującej enum.
Nic nie znalazłem, więc zrobiłem niesamowitą implementację o nazwie yinum , która pozwalała na wszystko, czego szukałem. Zrobiłem mnóstwo specyfikacji, więc jestem prawie pewien, że to bezpiecznie.
Niektóre funkcje przykładowe:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-03 07:35:34
To jest moje podejście do enum w Ruby. Szedłem na krótko i słodko, niekoniecznie na najbardziej "C". Jakieś pomysły?
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-12 15:48:35
Zobacz klejnot rubinowy, https://github.com/dblock/ruby-enum .
class Gender
include Enum
Gender.define :MALE, "male"
Gender.define :FEMALE, "female"
end
Gender.all
Gender::MALE
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-28 02:03:20
Być może najlepszym lekkim podejściem byłoby
module MyConstants
ABC = Class.new
DEF = Class.new
GHI = Class.new
end
W ten sposób wartości mają powiązane nazwy, jak w Javie / C#:
MyConstants::ABC
=> MyConstants::ABC
Aby uzyskać wszystkie wartości, możesz wykonać
MyConstants.constants
=> [:ABC, :DEF, :GHI]
Jeśli chcesz mieć wartość porządkową enum, możesz zrobić
MyConstants.constants.index :GHI
=> 2
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-19 21:50:52
Jeśli obawiasz się literówek z symbolami, upewnij się, że kod wyświetla wyjątek, gdy uzyskujesz dostęp do wartości za pomocą nieistniejącego klucza. Możesz to zrobić używając fetch
zamiast []
:
my_value = my_hash.fetch(:key)
Lub poprzez wywołanie skrótu domyślnie wywołuje wyjątek, jeśli podasz nieistniejący klucz:
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
Jeśli hash już istnieje, możesz dodać zachowanie podnoszące wyjątki:
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
Normalnie nie musisz się martwić o bezpieczeństwo literówek ze stałymi. Jeśli źle przeliczysz stałą nazwa, Zwykle wywoła wyjątek.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-16 23:14:48
Wszystko zależy jak używasz Javy lub C# enums. Sposób użycia będzie dyktował rozwiązanie, które wybierzesz w Rubim.
Spróbuj natywnego typu Set
, na przykład:
>> 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"}>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 20:35:24
Ktoś poszedł naprzód i napisał rubinowy klejnot o nazwie Renum . Twierdzi, że zachowanie podobne do Javy/C# jest najbliższe. Osobiście wciąż uczę się Rubiego i byłem trochę zszokowany, gdy chciałem, aby konkretna Klasa zawierała statyczne enum, prawdopodobnie hash, którego nie było łatwo znaleźć przez google.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-04 20:47:49
Niedawno wydaliśmy gem , który implementuje Enums w Rubim . W moim poście znajdziesz odpowiedzi na swoje pytania. Opisałem tam również dlaczego nasza implementacja jest lepsza od istniejących (w rzeczywistości jest wiele implementacji tej funkcji w Ruby jeszcze jako perełki).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-28 17:53:47
Innym rozwiązaniem jest użycie OpenStruct. Jest całkiem prosto i czysto.
Https://ruby-doc.org/stdlib-2.3.1/libdoc/ostruct/rdoc/OpenStruct.html
Przykład:
# 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'
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-14 09:17:55
Symbole to sposób ruby. Czasami jednak trzeba porozmawiać z jakimś kodem C lub czymś w rodzaju Javy, które ujawniają pewne enum dla różnych rzeczy.
#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
To może być użyte w ten sposób
require 'server_roles'
sSymb, sEnum =EnumLike.server_role()
foreignvec[sEnum[:SERVER_WORKSTATION]]=8
To może być oczywiście abstrakcyjne i można rolować naszą własną klasę Enum
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-02 20:47:41
Zaimplementowałem takie enums
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
Następnie jego łatwe do wykonania operacje
EnumType.ENUM_1.label
...
enum = EnumType.find_by_id 1
...
valueArray = EnumType.values
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-28 13:38:05
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)
Wyjście:
GOOD
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-11 12:29:01
Wydaje się to trochę zbędne, ale jest to metodologia, której używałem kilka razy, szczególnie tam, gdzie integruję się z xml lub czymś takim.
#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
To daje mi rygor C # enum i jest związany z modelem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-05 17:51:27
Większość ludzi używa symboli(to jest składnia :foo_bar
). To coś w rodzaju unikalnych nieprzezroczystych wartości. Symbole nie należą do żadnego typu w stylu enum, więc nie są wierną reprezentacją typu enum C, ale jest to prawie tak dobre, jak to tylko możliwe.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 19:04:45
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
Wyjście:
1-a
2-B
3-c
4-D
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-06 16:09:05
Czasami wszystko czego potrzebuję to być w stanie pobrać wartość enum i zidentyfikować jej nazwę podobną do 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'
Dla mnie to służy celowi enum i sprawia, że jest on również bardzo rozszerzalny. Możesz dodać więcej metod do klasy Enum i otrzymać je za darmo we wszystkich zdefiniowanych enumach. na przykład. get_all_names i takie tam.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-21 21:54:19
Spróbuj 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
Zobacz więcej https://github.com/alfa-jpn/inum#usage
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-11 04:47:02
Innym podejściem jest użycie klasy Ruby z Hashem zawierającym nazwy i wartości, jak opisano w następującym po rubyfleebie blog post . Pozwala to na łatwą konwersję pomiędzy wartościami i stałymi (zwłaszcza jeśli dodasz metodę klasy, aby wyszukać nazwę dla danej wartości).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-29 18:07:32
Myślę, że najlepszym sposobem na zaimplementowanie wyliczeń takich jak typy jest użycie symboli, ponieważ w zasadzie zachowują się jak liczba całkowita (jeśli chodzi o performace, object_id jest używany do porównań ); nie musisz się martwić o indeksowanie i wyglądają naprawdę schludnie w kodzie xD
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-03 23:57:49
Inny sposób naśladowania enum ze spójną obsługą równości (bezwstydnie przyjęty przez Dave ' a Thomasa). Pozwala na otwarte liczby (podobnie jak Symbole) i zamknięte (predefiniowane).
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-30 01:44:43