¿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 lo siguiente es una forma "adecuada"/"correcta"/"segura" de sobrescribir un método setter para un atributo my class.

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 suceder" con Ruby on Rails. Si esa no es la forma correcta de anular un método setter, ¿cuál es la correcta ¿camino?


Nota : Si utilizo el código

attr_accessible :attribute_name

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

  self.attribute_name = value
end

Obtengo el siguiente error:

SystemStackError (stack level too deep):
  actionpack (3.2.2) lib/action_dispatch/middleware/reloader.rb:70
Author: Backo, 2012-05-05

5 answers

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

Ahora la documentación de Rails también sugiere usar 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 accede a través de modelos, esta es la forma haciéndolo.

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 Sobreescribir los accesos predeterminados en la documentación de Rails.

Por lo tanto, su primer método es la forma correcta de anular los setters de columna en Modelos de Ruby on Rails. Estos accesores ya son proporcionados por Rails para acceder a las columnas de la tabla como atributos del modelo. Esto es lo que llamamos ActiveRecord mapping mapping.

También tenga en cuenta que la attr_accessible en la parte superior del modelo no tiene nada que ver con los accesorios. Tiene un funcionalidad completamente diferente (ver esta pregunta)

Pero en pure Ruby, si tienes definidos los accesores para una clase y quieres sobrescribir el setter, tienes que hacer uso de la 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 sepas lo que hace attr_accessor. 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 mientras llama al mismo método attribute_name= dentro de ese método.

 253
Author: rubyprince,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-19 07:28:07

Usa la palabra clave super:

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

Por el contrario, para anular el lector:

def attribute_name
  super.some_custom_decode
end
 41
Author: Robert Kajic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-05 16:09:31

En carriles 4

Digamos que tienes atributo age en tu tabla

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 rails 4 no es necesario especificar attr_accessible en el modelo. En su lugar, tiene que hacer una lista blanca de sus atributos a nivel de controlador utilizando el método permit.

 15
Author: Taimoor Changaiz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-10 11:50:30

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

has_many :specialties

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

(Esto agarra las primeras 3 entradas no duplicadas en el array pasado.)

 3
Author: Robin Daugherty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-25 01:26:38

Usando attr_writer para sobrescribir el setter attr_writer: attribute_name

  def attribute_name=(value)
    # manipulate value
    # then send result to the default setter
    super(result)
  end
 0
Author: bananaappletw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-22 15:23:20