2009-10-03 9 views
17

Jestem świadomy ActiveRecord :: Dirty i związanych z nimi metod, ale nie widzę środków, za pomocą których można subskrybować zdarzenia zmienione atrybut. Coś jak:Oddzwonienie do zmienionych atrybutów ActiveRecord?

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 

Czy istnieje standard Rails lub wtyczka do tego? Czuję, że musi gdzieś tam być, a ja po prostu tęsknię.

Odpowiedz

18

Odpowiedź cwninja powinna załatwić sprawę, ale jest w tym trochę więcej.

Po pierwsze, obsługa atrybutów bazowych jest wykonywana za pomocą metody write_attribute, więc powinieneś naciskać na nią.

Szyny również mają wbudowaną strukturę wywołań zwrotnych, która mogłaby być przyjemna w użyciu, chociaż nie pozwala na przekazywanie argumentów, co jest trochę irytujące.

pomocą Custom callbacks można zrobić to tak:

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 

Jeśli chciał spróbować użyć Rails wywołania zwrotne (szczególnie przydatne, jeśli można mieć wiele zwrotnych i/lub podklasy) można zrobić coś takiego :

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, to jest piękne, zamierzam zaoszczędzić mi TON "write_attribute", które zasypałem całym moim kodem. Dzięki! – BushyMark

+0

Wydaje się, że dotyczy to tylko Rails 2. – lulalala

+0

Doszło do tego przez google bez sprawdzania daty. Ta nowsza instrukcja zrobiła to, czego potrzebowałem: [Jak śledzić zmiany modelu w 'after_callbacks'] (http://ruby-journal.com/how-to-track-changes-with-after-callbacks-in- rails-3-or-newer /) – Jay

5

try:

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 

Wystarczy wymyślił to teraz, ale to powinno działać.

+0

Dzięki za ustawienie mnie na właściwej drodze. – Mario

3

można zawsze dostęp do prywatnej metody changed_attributes i sprawdzić za pomocą klawiszy tam before_save i zrobić z nim, co zechcesz.

14

dla szyn 3:

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

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

skomentowane w: https://stackoverflow.com/a/1709956/316700

nie znajdę oficjalną dokumentację na ten temat.

+2

Wierzę, że chce wywołać metodę, gdy tylko atrybut zostanie zmieniony w pamięci, zanim zostanie wywołany save, tj. 'my_object.some_attr =" foo "' wygeneruje wywołanie zwrotne. – MikeJ

+0

'after_update' jest przestarzałe od czasu 2.3 (http://apidock.com/rails/ActiveRecord/Callbacks/after_update) – schmijos

2

Najłatwiej że znalazłem:

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

fajnie! jest jeszcze lepiej :) – pesta

8

dla szyn 4

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

Jeśli chcesz zastąpić wartość atrybutu należy użyć write_attribute(:attribute, your_callback(self.attribute)) i nie attribute= lub będziesz pętli nad dopóki nie uzyskasz zbyt głębokiego stosu.