2017-05-18 3 views
1

Ich möchte etwas tun, wenn sich der Inhalt eines Hashes ändert. I Unter eingestuft Hash und überwog die Methode []= wie folgt aus:Hook zum Ausführen, wenn sich der Inhalt eines Hashes ändert

def []= (key,val) 
    super(key,val) 
    puts 'do something' 
end 

Aber es funktioniert nicht, wenn ich merge nennen. Was ist der richtige Weg? Muss ich jede Methode überschreiben, die den Hash aktualisiert?

+1

Wenn Sie 'merge' aufrufen, nennen Sie' merge', nicht '[] ='. –

+0

@AndreyDeineko so möchte ich eine Methode aufrufen, wenn der Inhalt des Hash ändert ob durch '[] =' oder 'merge' – Flora

+0

_Sidenote: _ Unterklasse' Hash' für das ist eine ** sehr schlechte Idee **, da Ruby Hashes sind hoch optimiert und dieser Ansatz ruiniert einfach alles, Ruby-Code vor banalen alten guten 'C'-Aufrufen. – mudasobwa

Antwort

0

Denken Sie daran, den Zugriff Wert zurückzukehren, und überschreiben Sie die merge Methode zu:

def []= (key,val) 
    out = super(key,val) 
    puts 'do something' 
    out 
end 

def merge(hash) 
    out = super 
    puts 'do something' 
    out 
end 

Bitte beachten Sie, eine andere Lösung, da moneky-Patching Klassen Kern eine schreckliche Praxis ist, was zu undefinierten Verhalten führen kann (wie Ihre merge Methode versagt).

Try Vererbung statt:

class MyHash < Hash 
    def []= (key,val) 
    out = super(key,val) 
    puts 'do something' 
    out 
    end 

    def merge(hash) 
    out = super(hash) 
    puts 'do something' 
    out 
    end 
end 

# Usage 
hash = MyHash.new 
hash['a'] = 42 # prints 'do something' 

Für eine elegantere Lösung, können Sie die hooks gem überprüfen möchten. (. Rettung, Reporting und eine weitere Kopie der Aktualisierung)

+0

Nur um darauf hinzuweisen, diese Lösung ist noch lange nicht am Überwachen, wenn sich irgendein Wert ändert! ... Was ist 'Hash # compact!', 'Hash # delete',' Hash # delete_if', 'Hash # keep_if!' , 'Hash # reject!', 'Hash # select!', 'Hash # speichern',' Hash # transform_values! ',' Hash # update', (und wahrscheinlich mehr, was ich verpasst habe ??) –

+0

Danke sehr viel – Flora

+0

Ich würde auch darauf hinweisen, dass das Überschreiben von 'Hash # merge' (im Gegensatz zu' Hash # merge! ') möglicherweise unnötig ist - da diese Methode einen neuen Hash erstellt, den bestehenden nicht mutiert. –

2

Da Sie nicht über die Leistung kümmern, könnten Sie die gefrorenen Hash-wickeln und zu eingewickelt eingefroren Instanz alle Methoden delegieren Hier gehen Sie:

class MyHash < BasicObject 
    def initialize(hash = {}) 
    hash! hash 
    end 

    def hash!(hash) 
    @hash = hash.freeze 
    end 

    def method_missing(m, *args, &λ) 
    hash.send(m, *args, &λ) 
    rescue => e 
    raise e unless e.message.ends_with?('frozen Hash') 

    # modification attempt was made 
    puts 'do something' 
    hash!(@hash.dup.send(m, *args, &λ) 
    end 
end 

Ungeprüfte, aber du hast die Idee.

+0

OP bittet darum, einige Aktionen auszuführen, wenn sich der Hash ändert, nicht wenn _method fehlt_; Ihr Code wird "etwas" für jede ausprobierte Methode ausführen, auch wenn die Methode selbst den Hash nicht ändert (d. H. '[]' -Operator). Sie können eine Liste von Methoden erstellen, die den internen Status des Hash ändern und dies innerhalb von 'method_missing' überprüfen. Ich denke, es ist besser, die gewünschten Methoden einfach zu überschreiben. – Wikiti

+0

@Wikiti Wenn die Methode den Hash nicht ändert, wird die 'rettung'-Klausel erfolgreich umgangen, da der' freeze'-Vertrag nicht verletzt wird. – mudasobwa

+0

Interessant, das ist ein cleverer Weg, um Änderungen zu verhindern. Es wird jedoch immer noch den Code 'tu etwas' ausführen, selbst wenn die Methode nicht existiert. Sie sollten wahrscheinlich prüfen, ob '@hash' auf die fehlende Methode reagiert, bevor Sie versuchen, sie zu' senden'. – Wikiti

Verwandte Themen