usage rails how enumeraciones enum ruby enums

rails - Enums en ruby



rails enum as hash (25)

A veces, todo lo que necesito es poder obtener el valor de enum e identificar su nombre similar a 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''

Esto me sirve para el propósito de enumeración y lo mantiene muy extensible también. Puede agregar más métodos a la clase Enum y viola obtenerlos gratis en todas las enumeraciones definidas. por ejemplo. get_all_names y cosas por el estilo.

¿Cuál es la mejor manera de implementar el lenguaje de enumeración en Ruby? Estoy buscando algo que pueda usar (casi) como las enumeraciones de Java / C #.


Alguien se adelantó y escribió una gema rubí llamada Renum . Pretende obtener el comportamiento más parecido a Java / C #. Personalmente todavía estoy aprendiendo a Ruby, y me sorprendió un poco cuando quise que una clase específica contuviera una enumeración estática, posiblemente un hash, que no se encontrara fácilmente a través de Google.


Creo que la mejor manera de implementar la enumeración como los tipos es con símbolos ya que prácticamente se comportan como enteros (cuando se trata de performace, object_id se usa para hacer comparaciones); no necesita preocuparse por la indexación y se ven realmente ordenados en su código xD


Echa un vistazo a la gema ruby-enum, https://github.com/dblock/ruby-enum .

class Gender include Enum Gender.define :MALE, "male" Gender.define :FEMALE, "female" end Gender.all Gender::MALE


Este es mi enfoque para enums en Ruby. Me quedaba corto y dulce, no necesariamente el más parecido a C. ¿Alguna idea?

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


Esto parece un poco superfluo, pero esta es una metodología que he usado varias veces, especialmente cuando me estoy integrando con xml o algo así.

#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

Esto me da el rigor del ac # enum y está vinculado al modelo.


He implementado enums así

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

entonces es fácil hacer operaciones

EnumType.ENUM_1.label

...

enum = EnumType.find_by_id 1

...

valueArray = EnumType.values


La forma más idiomática de hacer esto es usar símbolos. Por ejemplo, en lugar de:

enum { FOO, BAR, BAZ } myFunc(FOO);

... puedes 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)

Esto es un poco más abierto que enums, pero encaja bien con el espíritu Ruby.

Los símbolos también funcionan muy bien. La comparación de la igualdad de dos símbolos, por ejemplo, es mucho más rápida que la comparación de dos cadenas.


La mayoría de la gente usa símbolos (esa es la sintaxis de :foo_bar ). Son una especie de valores opacos únicos. Los símbolos no pertenecen a ningún tipo de estilo de enumeración, por lo que en realidad no son una representación fiel del tipo de enumeración de C, pero esto es bastante bueno.


Los símbolos son el camino del rubí. Sin embargo, a veces es necesario hablar con algún código C o algo o Java que exponga algo de enumeración para varias cosas.

#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

Esto puede ser usado como este

require ''server_roles'' sSymb, sEnum =EnumLike.server_role() foreignvec[sEnum[:SERVER_WORKSTATION]]=8

Esto, por supuesto, se puede hacer abstracto y puedes lanzar nuestra propia clase Enum


Me sorprende que nadie haya ofrecido algo como lo siguiente (extraído de la gema RAPI ):

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

Que se puede utilizar como tal:

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

Ejemplo:

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

Esto funciona bien en escenarios de base de datos, o cuando se trata de constantes / enums de estilo C (como es el caso cuando se usa FFI , que RAPI hace uso extensivo de).

Además, no tiene que preocuparse por los errores tipográficos que causan fallas silenciosas, como lo haría con el uso de una solución de tipo hash.


Otra forma de imitar una enumeración con un manejo de igualdad consistente (adoptado descaradamente de Dave Thomas). Permite enums abiertos (como símbolos) y enums cerrados (predefinidos).

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


Otra solución es utilizar OpenStruct. Es bastante sencillo y limpio.

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

Ejemplo:

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


Otro enfoque es usar una clase de Ruby con un hash que contenga nombres y valores como se describe en la siguiente publicación del blog de RubyFleebie . Esto le permite convertir fácilmente entre valores y constantes (especialmente si agrega un método de clase para buscar el nombre para un valor dado).



Quizás el mejor enfoque ligero sería

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

De esta manera, los valores tienen nombres asociados, como en Java / C #:

MyConstants::ABC => MyConstants::ABC

Para obtener todos los valores, puedes hacerlo.

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

Si quieres el valor ordinal de una enumeración, puedes hacerlo

MyConstants.constants.index :GHI => 2


Recientemente lanzamos una gem que implementa Enums en Ruby . En mi post encontrarás las respuestas a tus preguntas. También describí allí por qué nuestra implementación es mejor que las existentes (en realidad, hay muchas implementaciones de esta característica en Ruby como gemas).


Sé que ha pasado mucho tiempo desde que el chico publicó esta pregunta, pero tuve la misma pregunta y esta publicación no me dio la respuesta. Quería una forma fácil de ver qué representa el número, una comparación fácil y, sobre todo, el soporte de ActiveRecord para la búsqueda mediante la columna que representa la enumeración.

No encontré nada, así que hice una implementación impresionante llamada yinum que permitía todo lo que estaba buscando. Hecho toneladas de especificaciones, así que estoy bastante seguro de que es seguro.

Algunas características de ejemplo:

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


Si está utilizando Rails 4.2 o superior, puede usar las enumeraciones de Rails.

Rails ahora tiene enumeraciones por defecto sin la necesidad de incluir gemas.

Esto es muy similar (y más con características) a 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


Si le preocupan los errores tipográficos con símbolos, asegúrese de que su código genere una excepción cuando acceda a un valor con una clave que no existe. Puedes hacer esto usando fetch lugar de [] :

my_value = my_hash.fetch(:key)

o haciendo que el hash genere una excepción de forma predeterminada si proporciona una clave que no existe:

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

Si el hash ya existe, puede agregar el comportamiento de aumento de excepciones:

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, no tiene que preocuparse por la seguridad tipográfica con constantes. Si escribe incorrectamente un nombre constante, generalmente generará una excepción.


Todo depende de cómo uses las listas de Java o C #. La forma en que lo uses determinará la solución que elegirás en Ruby.

Pruebe el tipo de Set nativo, por ejemplo:

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


Yo uso el siguiente enfoque:

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

Me gusta por las siguientes ventajas:

  1. Agrupa los valores visualmente como un todo.
  2. Realiza algunas verificaciones en tiempo de compilación (en contraste con solo usar símbolos)
  3. Puedo acceder fácilmente a la lista de todos los valores posibles: solo MY_ENUM
  4. Puedo acceder fácilmente a valores distintos: MY_VALUE_1
  5. Puede tener valores de cualquier tipo, no solo de Símbolo

Los símbolos pueden ser mejores porque no tiene que escribir el nombre de la clase externa, si lo está usando en otra clase ( MyClass::MY_VALUE_1 )


Dos caminos. Símbolos ( :foo notación :foo ) o constantes (notación FOO ).

Los símbolos son apropiados cuando se desea mejorar la legibilidad sin ensuciar el código con cadenas literales.

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

Las constantes son apropiadas cuando tienes un valor subyacente que es importante. Simplemente declare un módulo para mantener sus constantes y luego declare las constantes dentro de eso.

module Foo BAR = 1 BAZ = 2 BIZ = 4 end flags = Foo::BAR | Foo::BAZ # flags = 3


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

Salida:

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

Salida:

GOOD