2012-01-27 16 views
22

pensé que sería bueno para rellenar un campo de estado en una tabla de ActiveRecord utilizando constantes. Sin embargo, cuando se trata de verificar si este estado tiene un estado particular, estoy teniendo problemas.Cómo almacenar y comparar: símbolos en un ActiveRecord (Ruby on Rails)

Si hago lo siguiente,

e = Mytable.new 
e.status = :cancelled 
e.save 

entonces reencontrar el registro y tratar de comparar mi estado para el símbolo, la comprobación falla. Tengo algo de salida de la consola para mostrar esto.

irb(main):060:0> e.status.eql?("cancelled") 
=> true 
irb(main):061:0> e.status.eql?(:cancelled) 
=> false 
irb(main):062:0> e.status == :cancelled 
=> false 
irb(main):063:0> e.status == "cancelled" 
=> true 
irb(main):064:0> e.status == :cancelled.to_s 
=> true 

¿Existe una forma mejor de mantener un estado en un registro? ¿Hay una manera de probar si un valor de campo actual es igual al: Símbolo sin necesidad de convertir el símbolo: en una cadena? Estoy pensando que puede haber un operador del que no estoy al tanto.

+1

ecoologic tiene una buena solución para usted, pero yo recomendaría tal vez dirigir lejos de esto y tal vez hacer una clase con constantes en el mismo. Que puede hacer cosas como 'e.status = Status :: CANCELLED' y qué no. E internamente eso podría ser una cadena y no importa. Usted todavía está utilizando constantes, y va a error si esa constante no existe, y es más limpio de esa manera. – MrDanA

+0

¿por qué no anula el captador de su columna? – apneadiving

+0

Enmendé mi respuesta antes de leer estos dos comentarios, pero me gustaría decir que me encanta la solución @MrDanA, ¡debe escribir una respuesta y la votaré! – ecoologic

Respuesta

7

A petición de ecoologic, aquí está mi comentario como respuesta:

ecoologic tiene una buena solución para usted, pero yo recomendaría dirección lejos de esto y hacer una clase con constantes en el mismo. Que puede hacer cosas como e.status = Estados :: CANCELADO. E internamente eso podría ser una cadena y no importa. Usted todavía está utilizando constantes, y va a error si esa constante no existe, y es más limpio de esa manera.

+0

Como una cuestión de buenas prácticas, ¿dónde colocarías esta clase? En su propio archivo en lib, o ayudantes? – seanyboy

+0

He colocado la clase de estado en el archivo de modelo. Está funcionando bien. También agregué un self.method a la clase para convertir el estado a un color. Entonces eso mantiene todo eso en un solo lugar. – seanyboy

+1

Normalmente los pongo en su propio archivo en lib, sí. A continuación, puede cargar automáticamente los archivos (en lugar de tener que 'requerirlos ') editando el archivo de configuración de la aplicación. – MrDanA

7

Si recuerdo bien los símbolos en ActiveRecord se almacenan en formato yaml, se debe realizar algún tipo de conversión porque no existe un símbolo en db relacional (que conozco, al menos). Cuando se lee que es entonces una cadena que no coincidirá con su símbolo y ni siquiera la cadena del símbolo, de hecho, debe ser algo como:

:x # => "--- :x\n" 

Creo que este plugin puede resolver su problema, pero no he Lo usé honestamente. https://github.com/zargony/activerecord_symbolize

* * EDITAR

dejo lo anterior porque recuerdo que era la situación que tenía y se puede corregir si estoy equivocado, sin embargo, estoy tratando este momento y el almacenado valor (rieles) 3.1.3 es una simple cadena con el valor del símbolo, por lo que el siguiente debería ser suficiente.

class Example < ActiveRecord::Base 

    def aaa 
    super.to_sym 
    end 

    def aaa=(value) 
    super(value.to_sym) 
    aaa 
    end 

end 

Por supuesto, esto forzará el valor a ser siempre un símbolo

** Edición después de AGES ** Creo que está bien en esta situación, ya que es evidente que en el PP es una cadena y la lógica es simple, pero no recomendamos en absoluto anulando métodos de atributos db añadir una lógica más compleja.

+1

también tenga en cuenta que usar: to_sym en la entrada del usuario es una mala idea: http://www.tricksonrails.com/2010/06/avoid-memory-leaks-in-ruby-rails-code-and-protect-against -denial-of-service/ – oseiskar

+0

yeap, vale la pena mencionar, es una mala práctica – ecoologic

1

De de programación Ruby 1.9, en relación con el operador == en la clase de símbolos (p 729).:

Returns true only if sym and obj are symbols with the same object_id. 

Lo que ha almacenado en la base de datos tendrá siempre object_id diferente que el object_id fijo de el símbolo (un puntero a un literal de cadena, en este caso).

5

También se puede sobrescribir el método reader:

def status 
    read_attribute(:status).to_sym 
end 
8

Esto es un poco tarde, pero puede ayudar a alguien más.

Si tiene clases con diferentes estados, usted podría considerar un enfoque utilizando constantes a lo largo de los alcances de esta manera:

class Account < ActiveRecord::Base 
    #------------------------------------------------------------------------------- 
    # Configuration 
    #------------------------------------------------------------------------------- 

    # STATUS is used to denote what state the account is in. 
    STATUS = { :active => 1, :suspended => 2, :closed => 3 } 

    # Scopes 
    scope :active, where(:status => Account::STATUS[:active]) 
    scope :suspended, where(:status => Account::STATUS[:suspended]) 
    scope :closed, where(:status => Account::STATUS[:closed]) 
    ... 
end 

entonces usted puede encontrar fácilmente los registros basándose en el estado, así:

# get all active accounts 
active_accounts = Consumer.active.all 
# get 50 suspended accounts 
suspended_accounts = Consumer.suspended.limit(50) 
# get accounts that are closed and [some search criteria] 
closed_accounts = Consumer.closed.where([some search criteria]) 

¡Espero que esto ayude a otra persona!

EDITAR: Si está más interesado en el uso de gemas, la gema simple_enum parece una excelente alternativa.

12

Con Rails 4.1.0, es probable que desee utilizar las encripciones de Active Record.

citar el official release notes:

class Conversation < ActiveRecord::Base 
  enum status: [ :active, :archived ] 
end 
  
conversation.archived! 
conversation.active? # => false 
conversation.status  # => "archived" 
  
Conversation.archived # => Relation for all archived Conversations 
  
Conversation.statuses # => { "active" => 0, "archived" => 1 } 
6

A partir de Rails 4.1, Active Record ahora es compatible con las enumeraciones

De los release notes:

2,5 enumeraciones Active Record

declarar una atributo enum donde los valores se correlacionan con enteros en el base de datos, pero se puede consultar por su nombre.

class Conversation < ActiveRecord::Base 
    enum status: [ :active, :archived ] 
end 

conversation.archived! 
conversation.active? # => false 
conversation.status # => "archived" 

Conversation.archived # => Relation for all archived Conversations 

Conversation.statuses # => { "active" => 0, "archived" => 1 } 

documentación adicional aquí: http://api.rubyonrails.org/v4.1.0/classes/ActiveRecord/Enum.html