3

任意のデータのセルをラップしているクラスがあります。一種のフィルター。セルはバックエンドデータストアに存在します。しかし、それは可能な限り透明でなければなりません。

簡単なアクセサーの作成は非常に簡単です。

def foo
  # fetch backend cell value and return it
end
def foo=(val)
  # store val in backend cell
end

私がトリッキーだと思っているのは、データがラップされていない場合に通常はデータに影響を与えるメソッドを傍受して追跡することです。たとえば、データが配列の場合、その場obj.foo << 17で配列に要素を追加します。バックエンドに保存されているデータでその動作を維持したい(つまり、保存された値に要素も追加される)。私はおそらく助けになると思いました:obj.foo << 17method_missing

def method_missing(meth, *args)
  methsym = meth.to_sym
  curval = self.get
  lastval = curval.clone
  opresult = curval.__send__(methsym, *args)
  if (curval != lastval)
    self.set(curval)
  end
  return opresult
end

しかし、リーダーアクセサーと組み合わせることで、操作の制御は私を超えて移動しました。これは、返されるものがそれ自体ではないためです。つまり、バックエンドデータが配列の場合、そのコピーを返します。変更されて返送されることのないコピーです。

これは可能ですか?もしそうなら、どうすればそれを行うことができますか?(それはおそらく痛々しいほど明白であり、私は疲れているのでそれを見逃しているだけです-または多分そうではありません。:-)

ありがとう!

[編集]

別の言い方 #method_missingをすれば、未知のメソッドの呼び出しプロセスにフックすることができます。同様に呼び出しプロセスにフックする方法を探していますが、既知および未知のすべてのメソッドについてです。

ありがとう!

4

2 に答える 2

3

クラスから返された各オブジェクトを、バックエンドを認識しているメタオブジェクト内にラップする必要があり、必要に応じて更新できます。

あなたの例では、挿入、削除などを処理できる配列ラッパーオブジェクトを返す必要があります。

- - 編集 - -

多くのラッパークラスを作成する代わりに、特に特別な処理が必要なメソッドを簡単に識別できる場合は、返されたオブジェクトに「シングルトンメソッド」を追加できる場合があります。

module BackEndIF
  alias :old_send :__send__
  def __send__ method, *args
    if MethodsThatNeedSpecialHandling.include?(method)
       doSpecialHandling()
    else
      old_send(method,args)
    end
  end
end

#in your class:
def foo
   data = Backend.fetch(query)
   data.extend(BackEndIF)
   return data
end

返されるオブジェクトには問題のメソッドがあるため、メソッドの欠落に基づくものは機能しないと思います。(つまり、配列には演算子<<がありますが、欠落していません)

method_missingまたは、アウトラインを作成したようなもので何かを行うことができるかもしれません。次のような単一のmeta_objectを作成します。

class DBobject
   def initialize(value, db_reference)
      @value = value
      @ref = db_reference
    end
   def method_missing(meth, *args)
     old_val = @value
     result = @value.__send__(meth, *args)
     DatabaseUpdate(@ref, @value) if (@value != old_val)
     return result   
   end
end

次にfoo、を返しますDBObject.new(objectFromDB, referenceToDB)

于 2011-08-29T21:19:54.893 に答える
0

Delegatorモジュールから借りてこれを解決しました。(以下のコードは動作が保証されていません。私はいくつかの詳細を手作業で編集しました。しかし、それは要点を提供するはずです。)

  • フェッチ(リーダーアクセサー)で、変更されたメソッドで返される値に注釈を付けます。

    def enwrap(target)
      #
      # Shamelessly cadged from delegator.rb
      #
      eigenklass = eval('class << target ; self ; end')
      preserved = ::Kernel.public_instance_methods(false)
      preserved -= [ 'to_s', 'to_a', 'inspect', '==', '=~', '===' ]
      swbd = {}
      target.instance_variable_set(:@_method_map, swbd)
      target.instance_variable_set(:@_datatype, target.class)
      for t in self.class.ancestors
        preserved |= t.public_instance_methods(false)
        preserved |= t.private_instance_methods(false)
        preserved |= t.protected_instance_methods(false)
      end
      preserved << 'singleton_method_added'
      target.methods.each do |method|
        next if (preserved.include?(method))
        swbd[method] = target.method(method.to_sym)
        target.instance_eval(<<-EOS)
          def #{method}(*args, &block)
            iniself = self.clone
            result = @_method_map['#{method}'].call(*args, &block)
            if (self != iniself)
              #
              # Store the changed entity
              #
              newklass = self.class
              iniklass = iniself.instance_variable_get(:@_datatype)
              unless (self.kind_of?(iniklass))
                begin
                  raise RuntimeError('Class mismatch')
                rescue RuntimeError
                  if ($@)
                    $@.delete_if { |s|
                      %r"\A#{Regexp.quote(__FILE__)}:\d+:in `" =~ s
                    }
                  end
                  raise
                end
              end
              # update back end here
            end
            return result
          end
        EOS
      end
    end                         # End of def enwrap
    
  • ストア(ライターアクセサー)で、追加したシングルトンメソッドを削除します。

    def unwrap(target)
      remap = target.instance_variable_get(:@_method_map)
      return nil unless (remap.kind_of?(Hash))
      remap.keys.each do |method|
        begin
          eval("class << target ; remove_method(:#{method}) ; end")
        rescue
        end
      end
      target.instance_variable_set(:@_method_map, nil)
      target.instance_variable_set(:@_datatype, nil)
    end                        # End of def unwrap
    

したがって、値が要求されると、返される前に「wrapper」メソッドが追加され、バックエンドに何かが格納される前にシングルトンが削除されます。値を変更する操作は、副作用としてバックエンドも更新します。

現在実装されているこの手法には、いくつかの不幸な副作用があります。ラップされた変数を持つクラスがでインスタンス化され、変数の1つが:を介してbackendアクセスされると想定します。 ivar_foo

backend.ivar_foo
=> nil
backend.ivar_foo = [1, 2, 3]
=> [1,2,3]
bar = backend.ivar_foo
=> [1,2,3]
bar << 4
=> [1,2,3,4]
backend.ivar_foo = 'string'
=> "string"
bar
=> [1,2,3,4]
backend.ivar_foo
=> "string"
bar.pop
=> 4
bar
=> [1,2,3]
backend.ivar_foo
=> [1,2,3]

しかし、それは今のところ私にとっての問題というよりも不思議なことです。:-)

ヘルプと提案をありがとう!

于 2011-09-06T17:39:19.733 に答える