¿Cuál es la forma correcta de anular un método setter en Ruby on Rails?

Estoy usando Ruby on Rails 3.2.2 y me gustaría saber si la siguiente es una manera “correcta” / “correcta” / “segura” de anular un método setter para un atributo de mi clase.

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 uso el código

 attr_accessible :attribute_name def attribute_name=(value) ... # Some custom operation. self.attribute_name = value end 

Obtuve el siguiente error:

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

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

Ahora la documentación de Rails también sugiere usar super como esta:

 class Model < ActiveRecord::Base def attribute_name=(value) # custom actions ### super(value) end end 

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

Respuesta original

Si desea sobrescribir los métodos de configuración para las columnas de una tabla mientras se accede a través de los 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 

Consulte Anulación de los accesos predeterminados en la documentación de Rails.

Por lo tanto, su primer método es la forma correcta de anular los establecedores de columnas 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 attr_accessible en la parte superior del modelo no tiene nada que ver con los attr_accessible acceso. Tiene una función completamente diferente (ver esta pregunta )

Pero en Ruby puro, si ha definido los descriptores de acceso para una clase y desea anular el setter, debe hacer uso de una 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 (getter y setter)

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

También su segundo método falla porque causará un bucle infinito ya que está llamando al mismo método attribute_name= dentro de ese método.

Use la palabra clave super :

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

Por el contrario, para anular al lector:

 def attribute_name super.some_custom_decode end 

En Rails 4

Digamos que tienes un atributo de edad en tu mesa

 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 las nuevas esquinas en Rails 4 no es necesario especificar attr_accessible en el modelo. En su lugar, debe poner en una lista blanca sus atributos en el nivel de controlador utilizando el método de permiso .

Descubrí que (al menos para las colecciones de relaciones ActiveRecord) funciona el siguiente patrón:

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

(Esto toma las primeras 3 entradas no duplicadas en el conjunto pasado).

Usar attr_writer para sobrescribir setter attr_writer: attribute_name

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