Los nuevos datos no persisten en la columna Rails Array en Postgres

Tengo un modelo de usuario con una columna de amigos de tipo texto. Esta migración se ejecutó para usar la función de matriz con postgres:

add_column :users, :friends, :text, array: true 

El modelo de usuario tiene este método:

 def add_friend(target) #target would be a value like "1234" self.friends = [] if self.friends == nil update_attributes friends: self.friends.push(target) end 

La siguiente especificación pasa hasta que agregue user.reload después de llamar a #add_friend :

 it "adds a friend to the list of friends" do user = create(:user, friends: ["123","456"]) stranger = create(:user, uid: "789") user.add_friend(stranger.uid) user.reload #turns the spec red user.friends.should include("789") user.friends.should include("123") end 

Esto sucede en el desarrollo también. La instancia del modelo se actualiza y tiene el nuevo uid en la matriz, pero una vez que se vuelve a cargar o volver a cargar al usuario en una acción diferente, vuelve a ser lo que era antes de add_friend método add_friend .

Usando Rails 4.0.0.rc2 y pg 0.15.1

¿Qué podría ser esto?

Sospecho que ActiveRecord no está notando que la matriz de tus friends ha cambiado porque, bueno, la referencia de matriz subyacente no cambia cuando:

 self.friends.push(target) 

Eso alterará el contenido de la matriz, pero la matriz en sí seguirá siendo la misma matriz. Sé que este problema surge con la gem postgres_ext en Rails3 y dado este problema :

El atributo de cadena no está marcado como sucio, cuando cambia con < <

Esperaría que Rails4 se comporte de la misma manera.

La solución sería crear una nueva matriz en lugar de tratar de modificar la matriz in situ:

 update_attributes friends: self.friends + [ target ] 

Hay muchas formas de crear una nueva matriz al agregar un elemento a una matriz existente, use la que le guste.

Parece que el problema podría ser el uso de push , que modifica la matriz en su lugar.

No puedo encontrar una fuente atm más primaria, pero esta publicación dice:

Una cosa importante a tener en cuenta cuando se interactúa con una matriz (u otros valores mutables) en un modelo. ActiveRecord actualmente no rastrea cambios “destructivos” o en su lugar. Estos incluyen arrastre de matriz y poping, advance objetos DateTime. Si desea utilizar una actualización “destructiva”, debe llamar a _will_change! para que ActiveRecord sepa que cambiaste ese valor.

Si desea utilizar el tipo de matriz Postgresql, deberá cumplir con su formato. Desde Postgresql documentos, el formato de entrada es

 '{10000, 10000, 10000, 10000}' 

que no es lo que devolverá friends.to_s . En ruby:

 [1,2,3].to_s => "[1,2,3]" 

Es decir, corchetes en lugar de llaves. Tendrás que hacer la conversión tú mismo.

Sin embargo, prefiero confiar en serializar ActiveRecord (ver serializar ). La base de datos no necesita saber que el valor es realmente una matriz, que es el modelo de su dominio que se filtra en su base de datos. Deje que Rails haga lo suyo y encapsule esa información; ya sabe cómo serializar / deserializar el valor.

Nota: Esta respuesta es aplicable a Rails 3, no a 4. Saldré de aquí en caso de que ayude a alguien en el futuro.