Qual è il modo giusto per sovrascrivere un metodo setter in Ruby on Rails?


184

Sto usando Ruby on Rails 3.2.2 e vorrei sapere se quanto segue è un modo "corretto" / "corretto" / "sicuro" per sovrascrivere un metodo setter per un attributo my class.

attr_accessible :attribute_name

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

  self[:attribute_name] = value
end

Il codice sopra sembra funzionare come previsto. Tuttavia, vorrei sapere se, utilizzando il codice sopra, in futuro avrò problemi o, almeno, quali problemi "dovrei aspettarmi" / "potrebbe accadere" con Ruby on Rails . Se questo non è il modo giusto per sovrascrivere un metodo setter, qual è il modo giusto?


Nota : se uso il codice

attr_accessible :attribute_name

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

  self.attribute_name = value
end

Ottengo il seguente errore:

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

4
Adoro la terminologia applicata "" corretta "/" corretta "/" sicura "'. Quando gli dai 3 modi, ti assicura davvero che non ci siano errori di interpretazione. Buon lavoro!
Jay,

5
@Jay - "Finezza italianismi"; -)
Backo

2
Giusto per essere chiari, il "livello di stack troppo profondo" si riferisce al fatto che è una chiamata ricorsiva ... la sua stessa chiamata.
Nippysaurus,

Risposte:


295

================================================== ========================= Aggiornamento: 19 luglio 2017

Ora la documentazione di Rails suggerisce anche di usare in superquesto modo:

class Model < ActiveRecord::Base

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

end

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

Risposta originale

Se si desidera sovrascrivere i metodi setter per le colonne di una tabella durante l'accesso attraverso i modelli, questo è il modo per farlo.

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

Vedere Sostituzione degli accessori predefiniti nella documentazione di Rails.

Quindi, il tuo primo metodo è il modo corretto di sovrascrivere i setter di colonne in Models of Ruby on Rails. Questi accessor sono già forniti da Rails per accedere alle colonne della tabella come attributi del modello. Questo è ciò che chiamiamo mappatura ORM di ActiveRecord.

Inoltre, tieni presente che la attr_accessibleparte superiore del modello non ha nulla a che fare con gli accessori. Ha una funzionalità completamente diversa (vedi questa domanda )

Ma in puro Ruby, se hai definito gli accessori per una classe e vuoi sovrascrivere il setter, devi usare la variabile di istanza in questo modo:

class Person
  attr_accessor :name
end

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

Questo sarà più facile da capire una volta che sai cosa attr_accessorfa. Il codice attr_accessor :nameè equivalente a questi due metodi (getter e setter)

def name # getter
  @name
end

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

Anche il tuo secondo metodo fallisce perché causerà un ciclo infinito mentre stai chiamando lo stesso metodo attribute_name=all'interno di quel metodo.


9
Per Rails 4 basta saltare attr_accessiblepoiché non è più lì e dovrebbe funzionare
zigomir

11
Perché non chiamare super?
Nathan Lilienthal,

1
Ho avuto l'impressione che, poiché gli accessori e gli scrittori sono stati creati dinamicamente, superpotrebbe non funzionare. Ma sembra che non sia così. L'ho appena controllato e funziona per me. Inoltre, questa domanda fa lo stesso
rubyprince,

4
C'è un enorme gotcha con write_attribute. Le conversioni verranno ignorate. Tieni presente che write_attributesalterà le conversioni del fuso orario con le date, che saranno quasi sempre indesiderate.
Tim Scott,

2
super funzionerà pure, tuttavia ci sono alcuni motivi per cui potresti non volerci. Ad esempio nella gemma mongoid è presente un bug in cui non è possibile eseguire il push sull'array se si supera il metodo getter. È un bug a causa del modo in cui gestisce l'array in memoria. Anche @name restituirà anche il valore impostato piuttosto che chiamare il metodo che sovrascrivi. Tuttavia, nella soluzione precedente entrambi funzioneranno bene.
newdark-it

44

Usa la superparola chiave:

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

Al contrario, per ignorare il lettore:

def attribute_name
  super.some_custom_decode
end

1
Risposta migliore dell'IMO accettato poiché mantiene la chiamata del metodo limitata allo stesso nome. Ciò preserva il comportamento ignorato ereditato da nome_attributo =
Andrew Schwartz

La modifica del metodo getter è diventata pericolosa in Rails 4.2 a causa di questa modifica: github.com/rails/rails/commit/… In precedenza gli helper di modulo chiamavano il valore non tipizzato del campo e non chiamavano il tuo getter personalizzato. Ora chiamano il tuo metodo e quindi produrranno risultati confusi nei tuoi moduli a seconda di come sovrascrivi il valore.
Brendon Muir il

16

In rotaie 4

supponiamo che tu abbia l' attributo age nella tua tabella

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: per i nuovi arrivati ​​nelle rotaie 4 non è necessario specificare attr_accessible nel modello. Invece devi inserire nella lista bianca i tuoi attributi a livello di controller usando il metodo di permesso .


3

Ho scoperto che (almeno per le raccolte di relazioni ActiveRecord) funziona il seguente modello:

has_many :specialties

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

(In questo modo vengono prese le prime 3 voci non duplicate nella matrice passata.)


0

Utilizzo attr_writerper sovrascrivere setter attr_writer: nome_attributo

  def attribute_name=(value)
    # manipulate value
    # then send result to the default setter
    super(result)
  end
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.