在Ruby中实现枚举用法的最佳方法是什么?我正在寻找(几乎)可以使用的东西,例如Java / C#枚举。
在Ruby中实现枚举用法的最佳方法是什么?我正在寻找(几乎)可以使用的东西,例如Java / C#枚举。
Answers:
两种方式。符号(: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样式常量/枚举(如使用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
符号可能更好,因为如果您在另一个类(MyClass::MY_VALUE_1
)中使用它,则不必写外部类的名称。
如果您使用的是Rails 4.2或更高版本,则可以使用Rails枚举。
Rails现在默认情况下具有枚举,无需包含任何gem。
这与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
Conversation
该类的许多实例-我认为它只能允许一个实例。
这是我在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
也许最好的轻量级方法是
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
class ABC; end
我知道这个人发布这个问题已经有很长时间了,但是我也有同样的问题,而这个发布并没有给我答案。我想要一种简单的方法来查看数字表示的内容,进行简单的比较,以及使用表示枚举的列对查询进行的所有ActiveRecord支持。
我什么都没找到,所以我做了一个叫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
等,也使他们通过关联的值相媲美。)
有人继续写了一个名为Renum的红宝石。它声称获得最接近的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"}>
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或某些暴露某些事物的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类
server_Symb
出于特定原因将变量中的第二个单词大写(例如)?除非有特殊原因,否则变量为snake_case_with_all_lower_case
,符号为是惯用法:lower_case
。
server_Symb.each_with_index { |e,i| server_Enum[e] = i}
。不需要i = 0
。
我已经实现了这样的枚举
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#枚举的严谨性,并且与模型紧密相关。
:VAL
。最好从数组开始,然后使用.map.with_index
key
还是invert
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的感觉是一个enumera 重刑
有时,我所需要的只是能够获取枚举的值并识别其名称,类似于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'
对我来说,这符合枚举的目的,并且使其也非常可扩展。您可以向Enum类添加更多方法,中提琴可以在所有定义的枚举中免费获取它们。例如。get_all_names之类的东西。
另一种方法是将Ruby类与包含名称和值的哈希一起使用,如以下RubyFleebie博客文章中所述。这使您可以轻松地在值和常量之间进行转换(尤其是如果您添加类方法以查找给定值的名称)。
模仿具有一致的相等性处理的枚举的另一种方法(从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
尝试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