문제
Ruby에서 enum 관용구를 구현하는 가장 좋은 방법은 무엇입니까?Java/C# 열거형과 같이 (거의) 사용할 수 있는 것을 찾고 있습니다.
해결책
두 가지 방법.기호(:foo
표기법) 또는 상수(FOO
표기법).
리터럴 문자열로 코드를 어지럽히지 않고 가독성을 높이려는 경우 기호가 적합합니다.
postal_code[:minnesota] = "MN"
postal_code[:new_york] = "NY"
중요한 기본 값이 있는 경우 상수가 적합합니다.상수를 담을 모듈을 선언한 다음 그 안에 상수를 선언하면 됩니다.
module Foo
BAR = 1
BAZ = 2
BIZ = 4
end
flags = Foo::BAR | Foo::BAZ # flags = 3
다른 팁
나는 아무도 다음과 같은 것을 제안하지 않았다는 사실에 놀랐습니다. 라피 보석):
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
다음과 같이 사용할 수 있습니다.
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
예:
>> 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
이는 데이터베이스 시나리오에서 또는 C 스타일 상수/열거형을 처리할 때(사용하는 경우와 마찬가지로) 잘 작동합니다. FFI, RAPI가 광범위하게 사용함).
또한 해시 유형 솔루션을 사용할 때처럼 오타로 인해 자동 오류가 발생하는 것에 대해 걱정할 필요가 없습니다.
이를 수행하는 가장 관용적인 방법은 기호를 사용하는 것입니다.예를 들면 다음과 같습니다.
enum {
FOO,
BAR,
BAZ
}
myFunc(FOO);
...기호만 사용할 수 있습니다.
# 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)
이는 열거형보다 좀 더 개방적이지만 Ruby 정신에 잘 맞습니다.
기호도 매우 잘 수행됩니다.예를 들어 두 기호의 동등성을 비교하는 것은 두 문자열을 비교하는 것보다 훨씬 빠릅니다.
나는 다음 접근 방식을 사용합니다.
class MyClass
MY_ENUM = [MY_VALUE_1 = 'value1', MY_VALUE_2 = 'value2']
end
나는 다음과 같은 장점 때문에 그것을 좋아합니다:
- 가치를 시각적으로 하나의 전체로 그룹화합니다.
- 일부 컴파일 시간 검사를 수행합니다(기호만 사용하는 것과는 대조적).
- 가능한 모든 값 목록에 쉽게 액세스할 수 있습니다.단지
MY_ENUM
- 고유한 값에 쉽게 액세스할 수 있습니다.
MY_VALUE_1
- Symbol뿐만 아니라 모든 유형의 값을 가질 수 있습니다.
다른 클래스에서 사용하는 경우 외부 클래스의 이름을 쓸 필요가 없기 때문에 기호가 더 나을 수 있습니다(MyClass::MY_VALUE_1
)
Rails 4.2 이상을 사용하는 경우 Rails 열거형을 사용할 수 있습니다.
Rails에는 이제 보석을 포함할 필요 없이 기본적으로 열거형이 있습니다.
이는 Java, C++ 열거형과 매우 유사하며 기능도 더 많습니다.
에서 인용 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
이것이 Ruby의 열거형에 대한 나의 접근 방식입니다.나는 짧고 달콤하게 가고 싶었지만 반드시 가장 C와 유사한 것은 아닙니다.이견있는 사람?
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
Ruby-enum gem을 확인해 보세요. https://github.com/dblock/ruby-enum.
class Gender
include Enum
Gender.define :MALE, "male"
Gender.define :FEMALE, "female"
end
Gender.all
Gender::MALE
그 사람이 이 질문을 게시한 지 오랜 시간이 흘렀다는 것을 알고 있지만, 저도 같은 질문을 갖고 있었는데 이 게시물에서는 답변을 얻을 수 없었습니다.나는 숫자가 무엇을 나타내는지 쉽게 확인할 수 있는 방법, 쉬운 비교, 그리고 무엇보다도 열거형을 나타내는 열을 사용한 조회에 대한 ActiveRecord 지원을 원했습니다.
아무것도 찾지 못했기 때문에 다음과 같은 멋진 구현을 만들었습니다. 이눔 내가 찾고 있던 모든 것을 허용했습니다.엄청난 사양을 만들어서 안전하다고 확신합니다.
몇 가지 예시 기능은 다음과 같습니다.
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
기호 오타가 걱정된다면 존재하지 않는 키를 사용하여 값에 액세스할 때 코드에서 예외가 발생하는지 확인하세요.다음을 사용하여 이 작업을 수행할 수 있습니다. fetch
오히려 []
:
my_value = my_hash.fetch(:key)
또는 존재하지 않는 키를 제공하는 경우 기본적으로 해시에서 예외가 발생하도록 만듭니다.
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
해시가 이미 존재하는 경우 예외 발생 동작을 추가할 수 있습니다.
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
일반적으로 상수의 오타 안전에 대해 걱정할 필요가 없습니다.상수 이름의 철자를 잘못 입력하면 일반적으로 예외가 발생합니다.
아마도 가장 가벼운 접근 방식은 다음과 같습니다.
module MyConstants
ABC = Class.new
DEF = Class.new
GHI = Class.new
end
이렇게 하면 Java/C#에서와 같이 값에 연관된 이름이 있습니다.
MyConstants::ABC
=> MyConstants::ABC
모든 값을 얻으려면 다음을 수행하십시오.
MyConstants.constants
=> [:ABC, :DEF, :GHI]
열거형의 서수 값을 원하면 다음을 수행할 수 있습니다.
MyConstants.constants.index :GHI
=> 2
누군가가 앞서서 다음과 같은 루비 보석을 작성했습니다. 레넘.이는 동작과 유사한 Java/C#을 얻는다고 주장합니다.개인적으로 저는 아직 Ruby를 배우고 있는데 특정 클래스에 정적 열거형(해시일 수도 있음)을 포함하려고 했을 때 Google을 통해 쉽게 찾을 수 없다는 사실에 약간 충격을 받았습니다.
그것은 모두 Java 또는 C# 열거형을 사용하는 방법에 따라 다릅니다.이를 사용하는 방법에 따라 Ruby에서 선택할 솔루션이 결정됩니다.
네이티브를 사용해 보세요 Set
예를 들어 다음과 같이 입력하세요.
>> 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"}>
기호는 루비 방식입니다.그러나 때로는 다양한 항목에 대한 일부 열거형을 노출하는 일부 C 코드나 무언가 또는 Java와 통신해야 할 때도 있습니다.
#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
그러면 다음과 같이 사용할 수 있습니다.
require 'server_roles'
sSymb, sEnum =EnumLike.server_role()
foreignvec[sEnum[:SERVER_WORKSTATION]]=8
물론 이것은 추상적으로 만들 수 있으며 자체 Enum 클래스를 롤링할 수도 있습니다.
나는 그런 열거 형을 구현했습니다
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
그러면 작업하기가 쉽습니다
EnumType.ENUM_1.label
...
enum = EnumType.find_by_id 1
...
valueArray = EnumType.values
이것은 약간 불필요한 것처럼 보이지만 이것은 특히 XML 등을 통합할 때 몇 번 사용한 방법론입니다.
#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
이는 C# 열거형의 엄격함을 제공하며 모델에 연결됩니다.
또 다른 솔루션은 OpenStruct를 사용하는 것입니다.꽤 간단하고 깨끗합니다.
https://ruby-doc.org/stdlib-2.3.1/libdoc/ostruct/rdoc/OpenStruct.html
예:
# 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'
대부분의 사람들은 기호를 사용합니다. :foo_bar
통사론).그것들은 일종의 고유하고 불투명한 값입니다.기호는 열거형 스타일 유형에 속하지 않으므로 실제로 C의 열거형 유형을 충실하게 표현하지는 않지만 이 정도는 가능한 한 좋습니다.
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
산출:
1 - 에이
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)
산출:
GOOD
때때로 나에게 필요한 것은 enum의 값을 가져오고 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'
이것은 나에게 enum의 목적을 제공하고 확장성을 유지합니다.Enum 클래스에 더 많은 메서드를 추가하면 정의된 모든 열거형에서 해당 메서드를 무료로 얻을 수 있습니다.예를 들어.get_all_names 등.
또 다른 접근 방식은 다음에 설명된 대로 이름과 값이 포함된 해시와 함께 Ruby 클래스를 사용하는 것입니다. RubyFleebie 블로그 게시물.이를 통해 값과 상수 사이를 쉽게 변환할 수 있습니다(특히 주어진 값의 이름을 조회하기 위해 클래스 메서드를 추가하는 경우).
유형과 같은 열거형을 구현하는 가장 좋은 방법은 기호를 사용하는 것입니다. 왜냐하면 거의 정수처럼 동작하기 때문입니다(성능에 관해서는 object_id를 사용하여 비교합니다).인덱싱에 대해 걱정할 필요가 없으며 코드에서 정말 깔끔하게 보입니다 xD
일관된 동등 처리로 열거형을 모방하는 또 다른 방법입니다(Dave Thomas가 뻔뻔하게 채택함).개방형 열거형(기호와 유사) 및 폐쇄형(미리 정의된) 열거형을 허용합니다.
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
이넘을 사용해 보세요.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
빠르고 지저분하며 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
Enum을 사용하는 것처럼 사용하십시오.
method_that_needs_options(FeelsLikeAnEnum.Option_1)