2012-05-05 12 views
142

Sto usando Ruby on Rails 3.2.2 e vorrei sapere se il seguente è un modo "corretto"/"corretto"/"sicuro" per sovrascrivere un metodo setter per un mio attributo di classe.Qual è il modo giusto per sostituire un metodo setter in Ruby on Rails?

attr_accessible :attribute_name 

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

    self[:attribute_name] = value 
end 

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


Nota: Se io 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

amo la terminologia applicata ' "corretta"/"corretta"/"certo"'. Quando gli dai 3 modi, assicura davvero che non ci siano equivoci. Buon lavoro! – Jay

+5

@ Jay - "Fine italianismi"; -) – Backo

+1

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

risposta

237

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

Ora il Rails documentation propone inoltre di utilizzare super come questo:

class Model < ActiveRecord::Base 

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

end 

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

risposta originale

Se si desidera ignorare i metodi setter per le colonne di una tabella durante l'accesso attraverso 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 Overriding default accessors nella documentazione di Rails.

Quindi, il tuo primo metodo è il modo corretto per sovrascrivere i setter di colonna in Modelli di 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 ActiveRecord.

Inoltre, tenere presente che lo attr_accessible nella parte superiore del modello non ha nulla a che fare con gli accessor. Ha un functionlity completamente differente (vedi this question)

Ma in puro Ruby, se è stato definito di accesso per una classe e si vuole ignorare il setter, si deve fare uso di 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 quando saprai cosa fa attr_accessor. 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 secondo metodo non riesce perché causerà un ciclo infinito, come si sta chiamando lo stesso metodo attribute_name= dentro quel metodo.

+8

per rotaie 4 saltare 'attr_accessible' dato che non è più lì, e dovrebbe funzionare – zigomir

+10

Perché non chiamare' super'? –

+1

Ho avuto l'impressione che dal momento che gli accessor e i writer sono creati dinamicamente, 'super' potrebbe non funzionare. Ma sembra che non sia il caso. L'ho appena controllato e funziona per me. Inoltre, questo [domanda] (http://stackoverflow.com/questions/373731/override-activerecord-attribute-methods) chiedere la stessa – rubyprince

3

ho scoperto che (almeno per le collezioni di relazione ActiveRecord) funziona il seguente schema:

has_many :specialties 

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

(Questo prende le prime 3 voci non duplicati nella matrice passata.)

+0

grazie mille;) – JustBasti

38

Utilizzare il super parola 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 di quella accettata IMO poiché mantiene la chiamata al metodo limitato allo stesso nome. Ciò mantiene il comportamento sovrascritto ereditato in attribute_name = –

+0

L'override del metodo getter è diventato pericoloso in Rails 4.2 a causa di questa modifica: https://github.com/rails/rails/commit/787e22bb491bd8c36db1e9734261c4ce02c5c5fd Gli helper in precedenza modulo avrebbero chiamato il valore untypecast del campo e non chiamare il tuo getter personalizzato. Ora chiamano il tuo metodo, e così generano risultati confusi nelle tue forme a seconda di come stai sovrascrivendo il valore. –

15

In rotaie 4

Diciamo che avete età attributo nella 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 ​​in rotaie 4 non c'è bisogno di specificare attr_accessible nel modello. Invece devi white-list i tuoi attributi a livello di controller usando il metodo per il metodo.

0

Utilizzando attr_writer sovrascrivere setter attr_writer: nome_attributo

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