2012-05-05 7 views
142

Estoy usando Ruby on Rails 3.2.2 y me gustaría saber si la siguiente es una forma "correcta"/"correcta"/"segura" de anular un método setter para un atributo de mi clase.¿Cuál es la manera correcta de anular un método setter en Ruby on Rails?

attr_accessible :attribute_name 

def attribute_name=(value) 
    ... # Some custom operation. 

    self[:attribute_name] = value 
end 

El código anterior parece funcionar como se esperaba. Sin embargo, Me gustaría saber si, usando el código anterior, en el futuro tendré problemas o, al menos, qué problemas "debería esperar"/"podría pasar" con Ruby on Rails. Si esa no es la manera correcta de anular un método setter, ¿cuál es la forma correcta?


Nota: Si utilizo el código

attr_accessible :attribute_name 

def attribute_name=(value) 
    ... # Some custom operation. 

    self.attribute_name = value 
end 

me sale el siguiente error:

SystemStackError (stack level too deep): 
    actionpack (3.2.2) lib/action_dispatch/middleware/reloader.rb:70 
+4

Me encanta la terminología aplicada ' "adecuada"/"correcta"/"seguro"'. Cuando le das 3 formas, realmente garantiza que no haya interpretaciones erróneas. ¡Buen trabajo! – Jay

+5

@Jay - "Fineliness italianisms"; -) – Backo

+1

Para que quede claro, el "nivel de pila demasiado profundo" se refiere al hecho de que es una llamada recursiva ... se llama a sí misma. – Nippysaurus

Respuesta

237

============= =============================================== ============ Actualización: 19 de julio de 2017

Ahora el Rails documentation También se sugiere utilizar super así:

class Model < ActiveRecord::Base 

    def attribute_name=(value) 
    # custom actions 
    ### 
    super(value) 
    end 

end 

========================== ==============================================

respuesta original

Si desea anular los métodos setter para las columnas de una tabla mientras se accede a través de modelos, esta es la manera de hacerlo.

class Model < ActiveRecord::Base 
    attr_accessible :attribute_name 

    def attribute_name=(value) 
    # custom actions 
    ### 
    write_attribute(:attribute_name, value) 
    # this is same as self[:attribute_name] = value 
    end 

end 

Ver Overriding default accessors en la documentación de los carriles.

Por lo tanto, su primer método es la forma correcta de anular los colocadores de columna en Modelos de Ruby on Rails. Rails ya proporcionan estos accesos para acceder a las columnas de la tabla como atributos del modelo. Esto es lo que llamamos mapeo ORM de ActiveRecord.

También tenga en cuenta que el attr_accessible en la parte superior del modelo no tiene nada que ver con los accesorios. Tiene una functionlity completamente diferente (ver this question)

Pero en Ruby puro, si ha definido descriptores de acceso para una clase y quiere anular el organismo, lo que tiene que hacer uso de variable de instancia como esta:

class Person 
    attr_accessor :name 
end 

class NewPerson < Person 
    def name=(value) 
    # do something 
    @name = value 
    end 
end 

Esto será más fácil de entender una vez que sepa lo que attr_accessor hace. El código attr_accessor :name es equivalente a estos dos métodos (get y set)

def name # getter 
    @name 
end 

def name=(value) # setter 
    @name = value 
end 

También su segundo método falla porque va a provocar un bucle infinito como la que está llamando el mismo método attribute_name= dentro de ese método.

+8

Para Rails 4 simplemente salte 'attr_accessible' ya que ya no está allí, y debería funcionar – zigomir

+10

¿Por qué no llamar a' super'? –

+1

Tenía la impresión de que, dado que los accesores y los escritores se crean dinámicamente, 'super' podría no funcionar. Pero, parece que no es el caso. Lo comprobé y me funciona. Además, esta [pregunta] (http://stackoverflow.com/questions/373731/override-activerecord-attribute-methods) pregunta lo mismo – rubyprince

3

he encontrado que (al menos para las colecciones de relación ActiveRecord) el siguiente patrón funciona:

has_many :specialties 

def specialty_ids=(values) 
    super values.uniq.first(3) 
end 

(Este toma las 3 primeras entradas no duplicados en la matriz pasada.)

+0

muchas gracias;) – JustBasti

38

Usa el super palabra clave:

def attribute_name=(value) 
    super(value.some_custom_encode) 
end 

a la inversa, para anular el lector:

def attribute_name 
    super.some_custom_decode 
end 
+1

Mejor respuesta que la IMO aceptada ya que mantiene la llamada al método limitada al mismo nombre. Esto conserva el comportamiento reemplazado heredado a nombre_atributo = –

+0

La anulación del método getter se ha vuelto peligrosa en Rails 4.2 debido a este cambio: https://github.com/rails/rails/commit/787e22bb491bd8c36db1e9734261c4ce02c5c5fd Anteriormente, los helper de formularios llamaban al valor de subtítulo del campo y no llame a su getter personalizado. Ahora llaman a su método y, por lo tanto, producirán resultados confusos en sus formularios, dependiendo de cómo esté anulando el valor. –

15

En los carriles 4

Digamos que usted tiene edad atributo en la tabla de

def age=(dob) 
    now = Time.now.utc.to_date 
    age = now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1) 
    super(age) #must add this otherwise you need to add this thing and place the value which you want to save. 
    end 

Nota: para los recién llegados en los carriles 4 que no es necesario especificar attr_accessible en el modelo. En su lugar, tiene que poner en una lista blanca sus atributos en el nivel del controlador usando el método.

0

Usando attr_writer sobrescribir colocador attr_writer: nombre_atributo

def attribute_name=(value) 
    # manipulate value 
    # then send result to the default setter 
    super(result) 
    end 
Cuestiones relacionadas