Methode temporär umdefinieren

21/04/2009 - 01:44 von Michael Schuerig | Report spam
Ich habe eine Rails-Erweiterung geforkt und weiterentwickelt, die in
ActiveRecord Datenbank-Views unterstützt[*]. Das führt u.a. dazu, dass
die Methode tables nicht nur echte Tabellen, sondern auch Views zurück
gibt. Das ist allgemein so korrekt, in einem speziellen Fall aber nicht,
nàmlich dann, wenn Datenbank-Constraints für die Dauer eines Blocks
aufgehoben werden sollen. Solche Constraints gibt es nur für Tabellen,
nicht für Views.

In der originalen Implementierung verwendet die Methode
disable_referential_integrity natürlich die originale tables-Methode und
alles ist gut. Mit der geànderten tables-Methode gibt es Probleme, weil
sie zusàtzlich Views zurück gibt. Es gibt außerdem eine Methode
base_tables, die nur die echten Tabellen liefert.

Ich will erreichen, dass von tables, die innerhalb von
disable_referential_integrity vorkommen, in Aufrufe von base_tables
umgewandelt werden. Gewissermaßen brauche ich einen cflow pointcut +
around advice -- wenn das nicht eine andere Sprache wàre.

Meine erste und beste Idee sieht so aus

def disable_referential_integrity_with_views_excluded(&block)
self.class.send(:alias_method, :tables, :base_tables)
disable_referential_integrity_without_views_excluded(&block)
ensure
self.class.send(:alias_method, :tables, :tables_with_views_included)
end

Das ist einerseits der Rails-typische Tanz mit alias_method_chain.
Andererseits eine noch verwegenere Verschiebung von Aliasen. Der Code
ist natürlich nicht Thread-sicher, aber das ist zum Glück im konkreten
Fall auch nicht wichtig.

Wie geht das in dem gegebenen Umfeld eleganter?

Michael

[*] http://github.com/mschuerig/rails_sql_views

Michael Schuerig
mailto:michael@schuerig.de
http://www.schuerig.de/michael/
 

Lesen sie die antworten

#1 Robert Klemme
21/04/2009 - 18:46 | Warnen spam
On 21.04.2009 01:44, Michael Schuerig wrote:
Ich habe eine Rails-Erweiterung geforkt und weiterentwickelt, die in
ActiveRecord Datenbank-Views unterstützt[*]. Das führt u.a. dazu, dass
die Methode tables nicht nur echte Tabellen, sondern auch Views zurück
gibt. Das ist allgemein so korrekt, in einem speziellen Fall aber nicht,
nàmlich dann, wenn Datenbank-Constraints für die Dauer eines Blocks
aufgehoben werden sollen. Solche Constraints gibt es nur für Tabellen,
nicht für Views.

In der originalen Implementierung verwendet die Methode
disable_referential_integrity natürlich die originale tables-Methode und
alles ist gut. Mit der geànderten tables-Methode gibt es Probleme, weil
sie zusàtzlich Views zurück gibt. Es gibt außerdem eine Methode
base_tables, die nur die echten Tabellen liefert.

Ich will erreichen, dass von tables, die innerhalb von
disable_referential_integrity vorkommen, in Aufrufe von base_tables
umgewandelt werden. Gewissermaßen brauche ich einen cflow pointcut +
around advice -- wenn das nicht eine andere Sprache wàre.

Meine erste und beste Idee sieht so aus

def disable_referential_integrity_with_views_excluded(&block)
self.class.send(:alias_method, :tables, :base_tables)
disable_referential_integrity_without_views_excluded(&block)
ensure
self.class.send(:alias_method, :tables, :tables_with_views_included)
end

Das ist einerseits der Rails-typische Tanz mit alias_method_chain.
Andererseits eine noch verwegenere Verschiebung von Aliasen. Der Code
ist natürlich nicht Thread-sicher, aber das ist zum Glück im konkreten
Fall auch nicht wichtig.

Wie geht das in dem gegebenen Umfeld eleganter?



Möglicherweise über Delegation. Das funktioniert natürlich nur, wenn
self an der betreffenden Stelle nicht schon bekannt ist, sondern erst
übergeben wird. Dann würdest Du einen Wrapper (SimpleDelegator)
reinreichen, der sich entsprechend anders verhàlt.

Eine noch dreckigere Methode wàre, das Verhalten von tables mittels
einem ThreadLocal zu àndern,

def tables
if Thread.current[:only_views]
base_tables
else
tables_with_views_included
end
end

def disable_referential_integrity_with_views_excluded
old = Thread.current[:only_views]
Thread.current[:only_views] = true
begin
yield
ensure
Thread.current[:only_views] = old
end
end

Vorteil ist immerhin, dass es Thread safe ist.

Ciao

robert

Ähnliche fragen