2011-11-16 4 views
15

Actualmente tengo un modelo Asistir que tendrá una columna de estado, y esta columna de estado solo tendrá algunos valores. STATUS_OPTIONS = {: yes,: no,: maybe}¿Cómo especifico y valido una enumeración en rieles?

1) No estoy seguro de cómo puedo validar esto antes de que un usuario inserte un Attend? Básicamente una enumeración en Java, pero ¿cómo podría hacer esto en los rieles?

+0

mu y respuestas de Mike son buenos - véase también http://stackoverflow.com/q/265725/887124, lo que da algunas respuestas ligeramente diferentes a una pregunta similar. –

+0

Sí, en realidad uso algo más similar a la solución que proporcionó Bob: [gist] (https://gist.github.com/1372650) – mnelson

Respuesta

26

crear una matriz de acceso global de las opciones que desee y, a continuación validar la valor de su columna de estado:

class Attend < ActiveRecord::Base 

    STATUS_OPTIONS = %w(yes no maybe) 

    validates :status, :inclusion => {:in => STATUS_OPTIONS} 

end 

a continuación, puede acceder a los posibles estados a través de Attend::STATUS_OPTIONS

+5

Pre Rails 4.1 esto está bien. En Rails 4.1+ usan las enumeraciones incorporadas: http://edgeguides.rubyonrails.org/4_1_release_notes.html#active-record-enums – mnelson

+1

Usar las enumeraciones "nativas" en los raíles 4.1 tiene los mismos inconvenientes de utilizar enums realmente nativos en postgresql. Debes ocuparte del pedido real, no puedes modificar fácilmente la enumeración una vez creada, etc. Además, con las enumeraciones de rieles "nativas", se trata de una característica rota, porque en un lado las escribes y las lees como aguijones, pero en al otro lado necesitas consultarlos como números. Al menos con las enumeraciones de postgresql, todo lo que esté fuera de la base de datos verá cadenas. – rewritten

+0

Después de luchar durante una hora tratando de conseguir trabajo, esta solución es mucho más interesante y no tiene inconvenientes, no puedo decir lo mismo de Enum. –

4

Se podría utilizar una columna de cadena para el estado y luego el :inclusion option for validates para asegurarse de que sólo recibe lo que estás esperando:

class Attend < ActiveRecord::Base 
    validates :size, :inclusion => { :in => %w{yes no maybe} } 
    #... 
end 
45

Ahora que Rails 4.1 incluye enumeraciones se puede hacer lo siguiente:

class Attend < ActiveRecord::Base 
    enum size: [:yes, :no, :maybe] 
    # also can use the %i() syntax for an array of symbols: 
    # %i(yes no maybe) 
    validates :size, inclusion: { in: sizes.keys } 
end 

que luego le proporciona con un alcance (es decir: Attend.yes, Attend.no, Attend.maybe para cada un método corrector para ver si cierto estado se establece (es decir: #yes?, #no?, #maybe?), junto con los métodos de establecimiento de atributos (es decir, #yes!, #no!, #maybe!).

Rails Docs on enums

+4

Según lo que estoy leyendo, los rieles de la implementación actual de las enumeraciones son para valores internos y no para exponer a los usuarios como la pregunta (lo que creo que es una lástima). Estoy obteniendo esto del siguiente hilo del 14 de febrero. Https://github.com/rails/rails/issues/13971 –

+2

Me encanta. Nueva mejor práctica, imo. –

2

Lo que hemos empezado a hacer es definir nuestros artículos de enumeración dentro de una matriz y luego usar esa matriz para especificar la enumeración, estudio de factibilidad, y el uso de los valores dentro de la aplicación.

STATUS_OPTIONS = [:yes, :no, :maybe] 
enum status_option: STATUS_OPTIONS 
validates :status_option, inclusion: { in: STATUS_OPTIONS.map {|t| t.to_s } } 

De esta manera también puede usar STATUS_OPTIONS más adelante, como para crear listas desplegables. Si desea exponer sus valores a los que el usuario siempre se puede asignar como esto:

STATUS_OPTIONS.map {|s| s.to_s.titleize } 
0

Para las enumeraciones en ActiveModels puede utilizar esta joya Enumerize

8

Así es como puedo aplicar en mis Carriles 4 proyecto.

class Attend < ActiveRecord::Base 
    enum size: [:yes, :no, :maybe] 
    validates :size, inclusion: { in: Attend.sizes.keys } 
end 

Attend.sizes le proporciona la asignación.

Attend.sizes # {"yes" => 0, "no" => 1, "maybe" => 2} 

See more in Rails doc

+6

Esencialmente el mismo comentario que [este] (http://stackoverflow.com/questions/8146965/how-do-i-specify-and-validate-an-enum-in-rails/8147002#comment42094863_26365696), pero esto no funcionará, porque los atributos enum lanzarán una excepción InvalidArgument en los valores no válidos, antes de que se llame a la validación. Más sobre eso aquí: https://github.com/rails/rails/issues/13971 –

+0

@Koen. es correcto, probé esta solución y terminé con el 'ArgumentError' independientemente. –

0

Después de algunos mirando, no pude encontrar una sola línea en el modelo para ayudar a que esto suceda. Por ahora, Rails proporciona Enumeraciones, pero no una forma integral de validar valores no válidos.

Por lo tanto, opté por una solución compuesta: para agregar una validación en el controlador, antes de configurar el strong_params, y luego al comparar con el modelo.

Así, en el modelo, voy a crear un atributo y una validación personalizada:

asistir.rb

enum :status => { your set of values } 
attr_accessor :invalid_status 

validate :valid_status 
#... 
private 
    def valid_status 
     if self.invalid_status == true 
      errors.add(:status, "is not valid") 
     end 
    end 

Además, voy a hacer una verificación contra los parámetros de entrada no válida y enviar el resultado (si es necesario) para el modelo, por lo que se añadirá un error al objeto, por lo que es válido

attends_controller.rb

private 
    def attend_params 
     #modify strong_params to include the additional check 
     if params[:attend][:status].in?(Attend.statuses.keys << nil) # to also allow nil input 
      # Leave this as it was before the check 
      params.require(:attend).permit(....) 
     else 
      params[:attend][:invalid_status] = true 
      # remove the 'status' attribute to avoid the exception and 
      # inject the attribute to the params to force invalid instance 
      params.require(:attend).permit(...., :invalid_status) 
     end 
    end 
Cuestiones relacionadas