2010-11-15 4 views
18

Disculpas si la pregunta es un poco imprecisa, pero describiré a continuación mi problema.Almacenamiento adecuado de modelos de "enumeración" en Rails

Soy la creación de algunos modelos en un proyecto de carriles, y una cosa que he notado que estoy corriendo en más de un par de veces que se trata de atributos que cumplan los siguientes criterios:

  • Ellos se puede establecer en uno de un pequeño conjunto predefinido de valores
  • Esos valores deben tener tanto un nombre como un identificador (ya sea un identificador numérico, código, lo que sea)
  • Los valores solo cambiarán como resultado de una buena cantidad de cambio de código.

Por ejemplo, uno de mis modelos debe tener un campo status que se puede establecer en uno de: Definición, Ejecutado o Completado. Necesito mostrar esas palabras específicas dentro de la interfaz, pero no quiero almacenar las cadenas en el DB en caso de que necesite cambiarlas en el futuro (o internacionalizar, o lo que sea)

La opción obvia es para definir modelos para cada uno de estos modelos, pero parece presentar una gran cantidad de gastos indirectos en el mantenimiento de los modelos, lo que garantiza que escriba las migraciones entre entornos, etc. para cada uno de estos, lo que parece una gran sobrecarga.

La otra opción es almacenarlo como un entero, y activar una clase de tipo "enumeración" que almacena la traducción de esos valores - esto probablemente funcionaría bien, pero me preocupa perder asociaciones y otras cosas útiles que obtengo de los modelos de ActiveRecord.

¿Algún consejo sobre la mejor manera de manejar esta situación?

Respuesta

13

Echa un vistazo a la gema de rubí en la que he estado trabajando llamado classy_enum. Estoy bastante seguro de que hace exactamente lo que estás buscando. El README tiene algún uso de ejemplo, pero la premisa es que le permite definir múltiples miembros enum como clases que pueden tener diferentes propiedades.

+0

Esta es una gran joya ... ¡gracias! – elsurudo

+0

@elsurudo gracias! Ha recorrido un largo camino desde la primera vez que publiqué esto y me encantaría recibir comentarios si terminas usándolo. –

+1

@Beerlington Finalmente encontré la manera correcta de hacer la pregunta para poder encontrar tu gema; es una solución elegante, simple y poderosa a un problema que he resuelto y resuelto en numerosos proyectos de Rails. ¡Lo recomiendo para otros! –

1

¿Qué hay de ponerlo en un módulo y mezclándolo con modelos:

module StatusCodes 
    DEFINING = 1 
    EXECUTING = 2 
    COMPLETED = 3 

    def status 
    return "" unless self[:status] # handle nil 
    const_lookup = self[:status] - 1 # index to module constants 
    StatusCodes.constants[const_lookup].to_s.downcase.camelcase # note: needs Ruby 1.9 
    end 
end 

class MyModel < ActiveRecord::Base 
    include StatusCodes 
end 

Ahora agregue una columna entera status al modelo y se puede asignar este modo:

m = MyModel.new(:status=>StatusCodes::DEFINING) 

y recuperar una cadena:

m.status # "Defining" 
0

junto con las otras opciones excelentes, se puede usar esto si usted elige su última opción se indica:

http://github.com/jasondew/coded_options

(acostumbrados a trabajar con Jason, y se utilizó un predecesor de esto en varias aplicaciones carriles , especialmente si el usuario siempre selecciona la opción de una etiqueta de selección)

12

definir un varchar o ENUM en la base de datos y validar el campo en el modelo:

validates_inclusion_of :status, :in => %w(Defining Executed Completed) 

Rails lo tratará como un campo de cadena, pero aún valida cuáles son los valores.

Si realmente necesita para abstraer el texto del campo de estado, que sólo podría guardarlo como un entero:

class Foo < ActiveRecord::Base 
    STATUS_DESCRIPTIONS = %w(Defining Executed Completed) 

    def status 
    STATUS_DESCRIPTIONS[ read_attribute(:status) ] 
    end 
end 

Si se pone más complicado que eso, usted debe tratar @ joya de Beerlington.

+0

El problema de esta manera es que acaba de obtener un montón de cadenas que no le dicen nada sobre lo que significa cada estado. Aún debe agregar lógica a la aplicación para manejar el comportamiento de cada estado. En ese punto, también podrías usar una gema. –

+1

@Beerlington Sí, eso depende de lo complicada que sea la lógica comercial. No tenemos suficiente información para juzgar eso, por lo que sugiero el enfoque más simple posible. Para escenarios más complicados, creo que tu gema funcionaría muy bien. –

+0

Beerlington tiene razón en que mi situación es probable que se vuelva un poco más compleja, pero aún así vale la pena votar, ya que es una solución perfectamente buena que debería considerarse si la complejidad no está presente. –

2

Estaba buscando una solución similar cuando me encontré con el enumerize gem. Me gusta su DSL limpia y simple.

Si sus estados contienen una gran cantidad de conocimientos específicos del estado, entonces el gen de máquina de estado sugerido por scaney podría ser una buena idea. La otra opción es usar el viejo y viejo state pattern con el state_pattern gem.

0

Para completar, Rails tiene una clase Enum desde 4.1.

Cuestiones relacionadas