スーパークラスの一連のメソッドをいくつかの静的コードまたはアスペクト内にラップする必要が頻繁にあると想像してみてください。たとえば、更新通知を配列メソッドにラップして、監視可能な配列を取得します。
私の最初の考えは、クラスレベルのメソッドを次のように使用することです。
class MyArray < Array
extend ObserverHelper # defines :wrap_method
wrap_notify(:[]=) do |index_or_range, *args|
[self[index_or_range], super(index_or_range, *args)]
# much nicer it would be to define
# removed = self[index_or_range]
# added = super(index_or_range, *args)
# but this makes it even more complicated (suggestions welcome!)
end
...
end
ここまでは順調ですね。:wrap_method を今すぐ実装するには? 私の最も有望なアプローチはここまで到達しました。スーパーのために動作しません:
require 'observer'
module ObserverHelper
include Observable
def wrap_notify(meth, &block)
define_method meth,->(*args) {
removed, added = instance_exec(*args, &block)
changed
notify_observers(removed, added)
added
}
end
end
エラー:
配列サブクラスで直接定義されている場合: メソッドの外部で呼び出されるスーパー
モジュールで定義され、配列に含まれている場合: 「複数のクラスに定義されているシングルトン メソッドからのスーパーはサポートされていません。これは 1.9.3 以降で修正されます」またはメソッドに適切な戻り値がある場合は「セグメンテーション違反」)
しばらく検索しましたが、クロージャーのバインディングを変更するための推奨される解決策が見つかりませんでした。
Rails は文字列を評価することでこれらのことを行いますが、このようにしたいかどうかはまだわかりません。パフォーマンスはまだ重要ではありませんが、重要になるでしょう。したがって、さまざまな可能性とパフォーマンスの問題についてのフィードバックが必要です。
最後に、さらに 2 つの詳細なソリューションを実行しました。最初に、スーパーもセルフもなしでインスタンスを渡します。
wrap_observer(:[]=) do |instance, index_or_range, *args|
[instance[index_or_range], instance.send(meth, index_or_range, *args)]
end
もう 1 つは、wrap_observer を instance_method として使用します。
def []=(index_or_range, *args)
wrap_observer do
[self[index_or_range], super(index_or_range, *args)]
end
end
私はそれが望ましいと考えています。
DSL の解決策は、ラッパーなしでメソッドを定義し、それを追加する定義されたメソッドを反復処理することです。
意図した DSL でこのパフォーマンスと保守性を解決する方法は他にありますか?