2009-10-03 8 views
17

Ich kenne ActiveRecord :: Dirty und die verwandten Methoden, aber ich sehe kein Mittel, mit dem ich ein Ereignis mit Attributänderung abonnieren kann. Etwas wie:Rückruf für geänderte ActiveRecord Attribute?

class Person < ActiveRecord::Base 
    def attribute_changed(attribute_name, old_value, new_value) 
    end 

    #or 

    attribute_changed do |attribute_name, old_value, new_value| 
    end 
end 

Gibt es einen Rails-Standard oder ein Plugin dafür? Ich fühle, dass es irgendwo da sein muss und ich vermisse es einfach.

Antwort

18

cwninjas Antwort sollte den Trick machen, aber es gibt ein bisschen mehr dazu.

Zunächst erfolgt die Verarbeitung des Basisattributs mit der Methode write_attribute, sodass Sie darauf zugreifen sollten.

Rails hat auch eine eingebaute Callback-Struktur, die schön sein könnte, obwohl es nicht erlaubt, Argumente zu übergeben, die ein bisschen ärgerlich ist.

Verwenden von benutzerdefinierten Rückrufe Sie könnte es wie folgt tun:

class Person < ActiveRecord::Base 

    def write_attribute(attr_name, value) 
    attribute_changed(attr_name, read_attribute(attr_name), value) 
    super 
    end 

    private 

    def attribute_changed(attr, old_val, new_val) 
     logger.info "Attribute Changed: #{attr} from #{old_val} to #{new_val}" 
    end 

end 

Wenn Sie versuchen wollte Rails Rückrufe verwenden (besonders nützlich, wenn Sie mehrere Rückrufe und/oder Unterklassen haben könnte), die Sie so etwas tun könnte :

class Person < ActiveRecord::Base 
    define_callbacks :attribute_changed 

    attribute_changed :notify_of_attribute_change 

    def write_attribute(attr_name, value) 
    returning(super) do 
     @last_changed_attr = attr_name 
     run_callbacks(:attribute_changed) 
    end 
    end 

    private 

    def notify_of_attribute_change 
     attr = @last_changed_attr 
     old_val, new_val = send("#{attr}_change") 
     logger.info "Attribute Changed: #{attr} from #{old_val} to #{new_val}" 
    end 

end 
+0

Peter, das ist schön, werde mich retten eine TON von "write_attribute" Ich habe überall auf meinem Code verstreut. Vielen Dank! – BushyMark

+0

Scheint, das ist nur für Rails 2. – lulalala

+0

Kam zu diesem über Google ohne Datum zu überprüfen. Dieser neuere Durchlauf hat getan, was ich brauchte: [Wie man Änderungen eines Modells in einem 'after_callbacks' verfolgt (http://ruby-journal.com/how-to-track-changes-with-after-callbacks-in-) rails-3-or-newer /) – Jay

5

Versuch:

def attribute_changed(attribute_name, old_value, new_value) 
end 

def attribute=(attribute_name, value) 
    returning(super) do 
    attribute_changed(attribute_name, attribute_was(attribute_name), attribute(attribute_name)) 
    end 
end 

Genau dies jetzt erfunden, aber es sollte funktionieren.

+0

Danke, dass Sie mich auf den richtigen Weg gebracht haben. – Mario

3

Sie immer die private Methode gibt changed_attributes und überprüfen Sie die Tasten zugreifen konnte ein before_save und mit ihm zu tun mit, was werden Sie.

14

Für Rails 3:

class MyModel < ActiveRecord::Base 
    after_update :my_listener, :if => :my_attribute_changed? 

    def my_listener 
    puts "Attribute 'my_attribute' has changed" 
    end 
end 

kommentiert in: https://stackoverflow.com/a/1709956/316700

Ich habe keine offizielle Dokumentation darüber finden.

+2

Ich glaube, er möchte, dass eine Methode aufgerufen wird, sobald ein Attribut im Speicher geändert wird, bevor save aufgerufen wird, dh 'my_object.some_attr =" foo "' würde einen Callback generieren. – MikeJ

+0

'after_update' ist veraltet seit Rails 2.3 (http://apidock.com/rails/ActiveRecord/Callbacks/after_update) – schmijos

2

Der einfachste Weg, den ich gefunden:

after_save :my_method, :if => Proc.new{ self.my_attribute_changed? } 

def my_method 
    ... do stuff... 
end 
+1

after_save: my_method,: if =>: my_attribute_changed? –

+0

nett! es ist noch besser :) – pesta

8

Für Rails 4

def attribute=(value) 
    super(value) 
    your_callback(self.attribute) 
end 

Wenn Sie den Wert des Attributs überschreiben möchten Sie write_attribute(:attribute, your_callback(self.attribute)) und nicht attribute= oder Sie Schleife über verwenden sollten es ist, bis Sie einen Stapel zu tief Ausnahme bekommen.

Verwandte Themen