อะไรคือวิธีที่ดีที่สุดในการใช้สำนวน enum ใน Ruby ฉันกำลังมองหาสิ่งที่ฉันสามารถใช้ (เกือบ) เช่น Java / C # enums
อะไรคือวิธีที่ดีที่สุดในการใช้สำนวน enum ใน Ruby ฉันกำลังมองหาสิ่งที่ฉันสามารถใช้ (เกือบ) เช่น Java / C # enums
คำตอบ:
สองทาง. สัญลักษณ์ ( :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
:minnesota.to_s
เมื่อบันทึกลงในฐานข้อมูลเพื่อบันทึกสัญลักษณ์สตริงรุ่น ผมเชื่อว่า Rails มีวิธีการช่วยเหลือในการจัดการกับสิ่งนี้
ฉันประหลาดใจที่ไม่มีใครเสนอสิ่งต่อไปนี้ (เก็บเกี่ยวจาก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
ซึ่งสามารถใช้เช่น:
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 / enums (เช่นในกรณีที่ใช้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)
นี่เป็นปลายเปิดที่มากกว่า enums เล็กน้อย แต่มันเข้ากันได้ดีกับวิญญาณทับทิม
สัญลักษณ์ยังทำงานได้ดีมาก ยกตัวอย่างเช่นการเปรียบเทียบสองสัญลักษณ์เพื่อความเท่าเทียมกันนั้นเร็วกว่าการเปรียบเทียบสองสาย
ฉันใช้วิธีการต่อไปนี้:
class MyClass
MY_ENUM = [MY_VALUE_1 = 'value1', MY_VALUE_2 = 'value2']
end
ฉันชอบมันสำหรับข้อดีดังต่อไปนี้:
MY_ENUM
MY_VALUE_1
สัญลักษณ์อาจดีกว่าเพราะคุณไม่ต้องเขียนชื่อคลาสภายนอกถ้าคุณใช้มันในคลาสอื่น ( MyClass::MY_VALUE_1
)
หากคุณใช้ Rails 4.2 หรือสูงกว่าคุณสามารถใช้ Rails enums ได้
Rails จะมีค่าเริ่มต้นโดยไม่จำเป็นต้องใส่อัญมณีใด ๆ
นี้คล้ายกันมาก (และอื่น ๆ พร้อมคุณสมบัติ) กับ Java, C ++ enums
อ้างถึงจาก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
Conversation
คลาสได้หลายอินสแตนซ์- ฉันเชื่อว่าต้องอนุญาตเพียง 1 อินสแตนซ์เท่านั้น
นี่เป็นแนวทางของฉันในการสมัครสมาชิกรูบี ฉันกำลังจะสั้นและหวานไม่จำเป็นต้องเป็นแบบ 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
บางทีวิธีการที่มีน้ำหนักเบาที่สุดอาจเป็นไปได้
module MyConstants
ABC = Class.new
DEF = Class.new
GHI = Class.new
end
วิธีนี้ค่ามีชื่อที่เกี่ยวข้องเช่นใน Java / C #:
MyConstants::ABC
=> MyConstants::ABC
ในการรับค่าทั้งหมดคุณสามารถทำได้
MyConstants.constants
=> [:ABC, :DEF, :GHI]
หากคุณต้องการค่าลำดับของ enum คุณสามารถทำได้
MyConstants.constants.index :GHI
=> 2
class ABC; end
ฉันรู้ว่าเป็นเวลานานแล้วที่คนโพสต์คำถามนี้ แต่ฉันมีคำถามเดียวกันและโพสต์นี้ไม่ได้ให้คำตอบกับฉัน ฉันต้องการวิธีง่ายๆในการดูว่าตัวเลขแสดงถึงการเปรียบเทียบง่ายและส่วนใหญ่ของการสนับสนุน ActiveRecord ทั้งหมดสำหรับการค้นหาโดยใช้คอลัมน์ที่เป็นตัวแทนของ enum
ฉันไม่พบอะไรเลยดังนั้นฉันจึงใช้งานได้อย่างยอดเยี่ยมที่เรียกว่าyinumซึ่งอนุญาตทุกอย่างที่ฉันกำลังมองหา ทำสเปคจำนวนมากดังนั้นฉันค่อนข้างมั่นใจว่าปลอดภัย
คุณสมบัติตัวอย่างบางส่วน:
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
โดยปกติคุณไม่ต้องกังวลเกี่ยวกับความปลอดภัยของการพิมพ์ผิดกับค่าคงที่ หากคุณสะกดชื่อคงที่มันมักจะทำให้เกิดข้อยกเว้น
FOO_VALUES = {missing: 0, something: 1, something_else: 2, ...}
กำหนดนี้สัญลักษณ์ที่สำคัญ. missing
, something
ฯลฯ และยังทำให้พวกเขาเปรียบได้ผ่านทางค่าที่เกี่ยวข้อง.)
มีคนเดินไปข้างหน้าและเขียนอัญมณีทับทิมชื่อเรนัม มันอ้างว่าได้รับพฤติกรรมคล้าย Java / C # ที่ใกล้เคียงที่สุด โดยส่วนตัวแล้วฉันยังคงเรียนรู้ทับทิมและฉันก็รู้สึกตกใจเล็กน้อยเมื่อฉันต้องการให้ชั้นเรียนเฉพาะเจาะจงมีคงที่เป็น enum ซึ่งอาจเป็นแฮชที่ไม่พบได้ง่ายผ่าน Google
ทุกอย่างขึ้นอยู่กับว่าคุณใช้ Java หรือ C # enums วิธีที่คุณใช้จะกำหนดวิธีแก้ปัญหาที่คุณเลือกใน 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"}>
Set[:a, :b, :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'
สัญลักษณ์เป็นวิธีทับทิม อย่างไรก็ตามบางครั้งเราจำเป็นต้องพูดคุยกับรหัส C หรืออะไรบางอย่างหรือ Java ที่เปิดเผย enum บางอย่างสำหรับสิ่งต่าง ๆ
#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 ของเราเองได้
server_Symb
) ด้วยเหตุผลเฉพาะหรือไม่? เว้นแต่จะมีเหตุผลโดยเฉพาะอย่างยิ่งมันเป็นสำนวนสำหรับตัวแปรที่จะเป็นและจะเป็นสัญลักษณ์snake_case_with_all_lower_case
:lower_case
server_Symb.each_with_index { |e,i| server_Enum[e] = i}
กอล์ฟรหัสสินค้า: i = 0
ไม่จำเป็นต้อง
ฉันได้ดำเนินการ 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
จากนั้นการดำเนินการที่ทำได้ง่าย
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
สิ่งนี้ให้ความแม่นยำของ ac # enum และเชื่อมโยงกับโมเดล
:VAL
ฉันจะไม่แนะนำวิธีนี้เพราะมันขึ้นอยู่กับการตั้งค่าด้วยตนเองคุณค่าและสร้างความมั่นใจให้คุณได้รับการสั่งซื้อที่เหมาะสมใน มันจะเป็นการดีกว่าถ้าคุณเริ่มต้นด้วยอาร์เรย์และสร้างแฮชโดยใช้.map.with_index
.key
หรือ.invert
มากกว่า:VAL
คีย์ ( stackoverflow.com/a/10989394/2208016 )
key
หรือinvert
คนส่วนใหญ่ใช้สัญลักษณ์ (นั่นคือ:foo_bar
ไวยากรณ์) มันเป็นค่าทึบแสงที่ไม่เหมือนใคร สัญลักษณ์ไม่ได้เป็นของประเภท enum ใด ๆ ดังนั้นพวกเขาจึงไม่ได้เป็นตัวแทนที่ซื่อสัตย์ของประเภท enum ของ 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 - a
2 - b
3 - c
4 - d
to_enum
ให้ enumera torในขณะที่enum
ใน C # / Java sense เป็น enumera tion
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 และระบุชื่อของมันคล้ายกับโลกของจาวา
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 และวิโอล่าหาได้ฟรีในทุก enums ที่กำหนดไว้ ตัวอย่างเช่น. get_all_names และสิ่งต่าง ๆ เช่นนั้น
อีกวิธีหนึ่งคือการใช้ระดับทับทิมกับกัญชาที่มีชื่อและค่าตามที่อธิบายไว้ในต่อไปนี้โพสต์บล็อก RubyFleebie สิ่งนี้ช่วยให้คุณสามารถแปลงระหว่างค่าและค่าคงที่ได้อย่างง่ายดาย (โดยเฉพาะถ้าคุณเพิ่มเมธอดคลาสเพื่อค้นหาชื่อสำหรับค่าที่กำหนด)
ฉันคิดว่าวิธีที่ดีที่สุดในการใช้การแจงนับเช่นประเภทคือมีสัญลักษณ์เนื่องจากพฤติกรรมค่อนข้างเป็นจำนวนเต็ม (เมื่อพูดถึง performace, object_id ใช้เพื่อทำการเปรียบเทียบ); คุณไม่จำเป็นต้องกังวลเกี่ยวกับการจัดทำดัชนีและพวกเขาดูเรียบร้อยในรหัส xD ของคุณ
อีกวิธีในการเลียนแบบ enum ที่มีการจัดการความเท่าเทียมกันอย่างสม่ำเสมอ อนุญาตให้ enums เปิด (เหมือนสัญลักษณ์) และปิด (กำหนดไว้ล่วงหน้า) 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
ลอง 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
ดูhttps://github.com/alfa-jpn/inum#usageเพิ่มเติม