hitode909の日記

趣味はマリンスポーツですの日記です

特異メソッドを定義したオブジェクトをMarshal.dumpしようとして困ってる

特異メソッドを追加したHashのインスタンスをMarshalでdump,loadしようとしているが,うまくいかなくて,困ってる.

Marshal.dumpでHashのインスタンスをdumpできる.

def set_foo(obj)
  class << obj
    def foo
      "foo"
    end
  end
  obj
end

p Marshal.dump({ :a => 'aaaaa'}) #=> "\004\b{\006:\006a\"\naaaaa"


特異メソッドを定義してみる.

def set_foo(obj)
  class << obj
    def foo
      "foo"
    end
  end
  obj
end

puts set_foo({ :a => 'aaaaa'}).foo # => foo

できた.


特異メソッドを定義したオブジェクトはdumpできないので,これをdumpしようとすると,例外が起きる.

def set_foo(obj)
  class << obj
    def foo
      "foo"
    end
  end
  obj
end

p Marshal.dump(set_foo({ :a => 'aaaaa'})) #=> `dump': singleton can't be dumped (TypeError)


特異メソッドで,marshal_dumpを定義するとdumpできるようになるが,なにもせずにdumpした場合と異なる結果になってしまう.

def set_foo(obj)
  class << obj
    def foo
      "foo"
    end

    def marshal_dump
      dup
    end
  end
  obj
end

p Marshal.dump(set_foo({ :a => 'aaaaa'})) #=> "\004\bU:\tHash{\006:\006a\"\naaaaa"

先頭に,:\tHash という文字列が加わってる.


このdumpしたのをMarshal.loadしようとすると,Hashクラスのmarshal_loadを呼ぼうとするが,定義してないので,動かない.

def set_foo(obj)
  class << obj
    def foo
      "foo"
    end

    def marshal_dump
      dup
    end
  end
  obj
end

p Marshal.load(Marshal.dump(set_foo({ :a => 'aaaaa'}))) #=> instance of Hash needs to have method `marshal_load' (TypeError)


Hashクラスにmarshal_loadを定義すると,loadできるようになるが,こんなところを書き換えてると,思わぬところで副作用がありそう.

class Hash
  def marshal_load(obj)
    self.update(obj)
  end
end

def set_foo(obj)
  class << obj
    def foo
      "foo"
    end

    def marshal_dump
      dup
    end
  end
  obj
end

p Marshal.load(Marshal.dump(set_foo({ :a => 'aaaaa'}))) #=> {:a=>"aaaaa"}


gem memcache-clientがmemcachedにオブジェクトを保存するときに,Marshal.dumpでシリアライズしていて,このように特異メソッドを定義しているオブジェクトをキャッシュすることができない.困った.