2012-09-18 15 views
5

tengo una clase rieles modelo con un atributo Hash serializado, así:ActiveRecord convierte teclas de símbolos de cuerdas al serializar mi hash de

class Action 
    serialize :metadata, Hash 
    # . . . 
end 

Esa columna se almacena en una columna text con una codificación YAML. El problema es que cuando paso un valor metadata al método create!, las claves hash se convierten de símbolos a cadenas, pero esa conversión no ocurre otras veces. Por ejemplo:

$ rails console 
> a = Action.create!(:metadata => {:foo => "bar"}) 
> a.metadata 
=> {"foo"=>"bar"} 
> a.metadata[:fizz] = "buzz" 
> a.metadata 
=> {"foo"=>"bar", :fizz=>"buzz"} 

Ahora al guardar el modelo, la base de datos va a tener este valor de texto:

--- 
foo: bar 
:fizz: buzz 

Alguna sugerencia de cómo solucionar este problema?

(Esto es con rieles 3.0.16.)

+0

¿presenta la misma comportamiento con 'serialize: metadata, HashWithIndifferentAccess'? – gregates

+1

@gregates: Lo intenté, y significa que puedo consultar el hash con cadenas o símbolos y obtener el valor, pero hay dos problemas: 'Action.create! (: Metadata => {: foo =>: barra}) 'plantea una excepción porque ya no puedo pasar Hash sin formato, y' Action.find (an_old_id) 'genera una excepción SerializationTypeMismatch porque Rails no sabe cómo convertir el YAML en un HashWithIndifferentAccess. –

Respuesta

3

va a responder por mí mismo, con una variante de la sugerencia de @ Mori:

class Action < ActiveRecord::Base 
    def metadata=(v) 
    self[:metadata] = v.try(:symbolize_keys!) 
    end 
end 

Por lo que yo puedo decir, este trabaja alrededor del problema sin romper la interfaz en otro lado. Supongo que si quería una clave para ser una cadena, este método me causaría una desagradable sorpresa, pero ese no es el caso, por lo que estoy más dispuesto a aceptarlo que las sorpresas planteadas por otras soluciones propuestas.

Por cierto, si alguien puede proponer una solución que no requiere este tipo de solución en absoluto, lo aceptaré felizmente como la respuesta correcta en su lugar.

2
class Action < ActiveRecord::Base 
    def metadata 
    self[:metadata].try :symbolize_keys 
    end 
end 
+1

Esta es una buena sugerencia, pero ahora si digo 'a.metadata [: foo] = 'bar'', el cambio se pierde porque estoy asignando una copia del hash, no el hash al que hace referencia el modelo. –

+0

. . . Pero, * does * funciona si en cambio digo 'self [: metadata] .try: symbolize_keys!'. Sin embargo, eso se siente un poco mal. –

0

Terminé yendo por otra ruta. Me aseguro de que tengo una espalda HashWithIndifferentAccess cuando accedo el atributo:

def metadata 
    return self[:metadata] if self[:metadata].is_a?(HashWithIndifferentAccess) 
    self[:metadata] = HashWithIndifferentAccess.new(self[:metadata]) 
end 

Una ventaja añadida a esto es si metadata no está configurado consigo un hash vacío espalda en lugar de nil

Cuestiones relacionadas