Pergunta

Qual é a melhor forma de implementar a enumeração de expressão em Ruby?Eu estou procurando algo que eu possa usar (quase) como o Java/C# enums.

Foi útil?

Solução

Duas maneiras.Símbolos (:foo notação) ou constantes (FOO notação).

Símbolos são apropriadas quando você quer aumentar a legibilidade, sem desarrumar código com seqüências de caracteres literais.

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

Constantes são apropriadas quando você tem um valor subjacente que é importante.Basta declarar um módulo para realizar o seu constantes e, em seguida, declarar constantes dentro.

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

flags = Foo::BAR | Foo::BAZ # flags = 3

Outras dicas

Eu estou surpreso que ninguém ofereceu algo como o seguinte (extraída do 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

O que pode ser usado assim:

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

Exemplo:

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

Este joga bem na base de dados de cenários, ou quando se lida com estilo C constantes/enumerações (como é o caso, quando usar FFI, que RAPI faz uso extensivo de).

Além disso, você não terá de se preocupar com erros de digitação causando silêncio falhas, como seria com a utilização de um hash tipo de solução.

Mais idiomáticas maneira de fazer isso é usar símbolos.Por exemplo, em vez de:

enum {
  FOO,
  BAR,
  BAZ
}

myFunc(FOO);

...você pode apenas usar símbolos:

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

Este é um pouco mais aberto do que as enumerações, mas ele se encaixa bem com o Ruby espírito.

Símbolos também executa muito bem.Comparando os dois símbolos para a igualdade, por exemplo, é muito mais rápido do que comparar duas cadeias de caracteres.

Eu uso a seguinte abordagem:

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

Eu gosto para as seguintes vantagens:

  1. Ele agrupa os valores visualmente como um todo
  2. Faz algum compilação de tempo de verificação (em contraste com apenas usando símbolos)
  3. Que eu possa acessar facilmente a lista de todos os possíveis valores:apenas MY_ENUM
  4. Eu posso facilmente o acesso de valores distintos: MY_VALUE_1
  5. Ele pode ter valores de qualquer tipo, e não apenas Símbolo

Símbolos podem ser melhor fazer que você não tem que escrever o nome da classe externa, se você estiver usando ele em outra classe (MyClass::MY_VALUE_1)

Se você estiver usando Rails 4.2 ou superior você pode usar Rails enums.

Rails agora tem enumerações por padrão, sem a necessidade de incluir qualquer gemas.

Isto é muito semelhante (e com mais recursos) para Java, C++ enums.

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

Esta é a minha abordagem para enums em Ruby.Eu estava indo para o curto e doce, não necessariamente a mais C-like.Quaisquer pensamentos?

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

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

Eu sei que tem sido um longo tempo desde que o cara postou esta pergunta, mas eu tinha a mesma pergunta e este post não me deu a resposta.Eu queria uma maneira fácil de ver que o número representa, fácil de comparação, e, acima de tudo, o ActiveRecord suporte para pesquisa usando a coluna que representa o enum.

Eu não encontrei nada, então eu fiz uma incrível implementação chamado yinum o que permitiu que tudo que eu estava procurando.Feito tonelada de especificações, então eu tenho certeza que é seguro.

Alguns exemplos de funcionalidades:

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

Se você está preocupado com erros de ortografia, com símbolos, certifique-se de que o seu código gera uma exceção quando você acessar um valor com um inexistente chave.Você pode fazer isso usando fetch em vez de []:

my_value = my_hash.fetch(:key)

fazer o hash de levantar uma exceção, por padrão, se você fornecer um inexistente chave:

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

Se o hash já existe, você pode adicionar na exceção de sensibilização comportamento:

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

Normalmente, você não terá de se preocupar com erro de digitação segurança constantes.Se você digitar incorretamente o nome de uma constante, que vai geralmente de levantar uma exceção.

Talvez a melhor abordagem simples poderia ser

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

Desta forma, os valores têm nomes associados, como em Java/C#:

MyConstants::ABC
=> MyConstants::ABC

Para obter todos os valores, você pode fazer

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

Se você quer um enum valor ordinal, você pode fazer

MyConstants.constants.index :GHI
=> 2

Alguém foi à frente e escreveu um ruby gem chamada Renum.Ele afirma que para chegar o mais próximo de Java/C# como o comportamento.Pessoalmente, eu ainda estou aprendendo Ruby, e eu estava um pouco chocado quando eu queria fazer uma classe específica conter um static enum, possivelmente, de um hash, que não era exatamente facilmente encontrado via google.

Tudo depende de como você usar Java ou C# enums.Como você usar vai ditar a solução que você vai escolher em Ruby.

Tente nativo Set tipo, como por exemplo:

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

Recentemente, lançamos um gem que implementa Enums em Ruby.No meu post você vai encontrar as respostas sobre suas dúvidas.Também descrevi lá porque a nossa implementação é melhor do que os já existentes (na verdade, existem muitas implementações do presente recurso em Ruby ainda como pedras).

Símbolos é o ruby forma.No entanto, às vezes uma necessidade de falar com algumas código C ou alguma coisa, ou de Java que expor algumas enum para várias coisas.


#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

Este pode ser utilizado como presente


require 'server_roles'

sSymb, sEnum =EnumLike.server_role()

foreignvec[sEnum[:SERVER_WORKSTATION]]=8

Isso é claro que pode ser feito abstrato e você pode rolar a nossa própria classe Enum

Eu tenho implementado enumerações como que

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

em seguida, é fácil de fazer operações

EnumType.ENUM_1.label

...

enum = EnumType.find_by_id 1

...

valueArray = EnumType.values

Isto parece um pouco redundante, mas esta é uma metodologia que eu tenho usado algumas vezes, especialmente quando estou a integração com xml, ou algum tipo.

#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

Isso me dá o rigor de um c# enum e é ligado ao modelo.

Outra solução é utilizar OpenStruct.É bastante simples e limpa.

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

Exemplo:

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

A maioria das pessoas usa símbolos (que é o :foo_bar sintaxe).Eles são uma espécie de única opaco valores.Símbolos que não pertencem a nenhum enum tipo de estilo para que eles realmente não são uma representação fiel de C do tipo enum, mas este é tão bom quanto ele ganha.

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

Saída:

1 - um
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)

Saída:

GOOD

Às vezes tudo que eu preciso é ser capaz de buscar enum valor e identificar seu nome semelhante para o mundo 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'

Isso me serve o propósito de enum e mantém muito extensível também.Você pode adicionar mais métodos para que a classe Enum e viola obtê-los gratuitamente em todos os definidas as enumerações.por exemplo.get_all_names e coisas assim.

Outra abordagem é usar uma classe Ruby com um hash contendo os nomes e valores, conforme descrito no seguinte RubyFleebie post de blog.Isso permite converter facilmente entre os valores e as constantes (especialmente se você adicionar um método de classe para pesquisa o nome para um determinado valor).

Eu acho que a melhor forma de implementar a enumeração como tipos é com símbolos desde o muito bonito de se comportar como inteiro (quando se trata de desempenho, object_id é usado para fazer comparações );você não necessita preocupar-se sobre a indexação e olhar realmente elegante em seu código xD

Outra maneira de imitar um enum com consistente igualdade de tratamento (descaradamente adotado a partir de Dave Thomas).Permite abrir as enumerações (muito parecido com símbolos) e fechados (pré-definido) 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

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

veja mais https://github.com/alfa-jpn/inum#usage

Rápido e sujo, sente-se como 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

Use-o como você gostaria de usar um Enum:

method_that_needs_options(FeelsLikeAnEnum.Option_1)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top