加载Marshal时的注意事项

提供了将Ruby对象写入文件(或字符串)并读取回来的功能的模块。Ruby 2.1.0参考手册| Marshal 模块。

那是Marshal。
将对象写入文件时,可以通过Marshal.dump将散列哈希串行化,并将其保存在数据存储区域,如Redis或Mongo中。
(一般情况下,更常见的做法是使用json。)

hoge = {
  foo: 'bar'
}
p str = Marshal.dump(hoge) # => "\x04\b{\x06:\bfooI\"\bbar\x06:\x06ET"
# strをデータストアに入れる
p Marshal.load(str) # => {:foo=>"bar"}

所以,最近我沉迷于以下的模式。

require 'redis'
require 'date'

conf = {
  # ここにはRedisの設定が記述してあります
}
redis = Redis.new(conf)
date = Date.new(1999, 2, 24)
redis.set('key', Marshal.dump(date))
require 'redis'

conf = {
  # set.rbと同様のRedisの設定が記述してあります
  # 本来はちゃんと共通のmodule化してます。
}
redis = Redis.new(conf)
Marshal.load(redis.get('key'))
# => TypeError: instance of Date needs to have method `marshal_load'

哎呀。

Marshal.dump是一个用于将Ruby对象序列化为字符串的方法。

    • 名前のついてないオブジェクト

 

    システムがオブジェクトを保持するもの

手册中指出,如果尝试写入等内容,会导致TypeError错误。

Marshal.dump(Dir.new('/')) # Dirが「システムがオブジェクトを保持するもの」に該当している
# => TypeError: no _dump_data is defined for class Dir

我在Marshal.load的时候发生了问题,这是怎么回事呢?我困惑了一会儿。

所以,总结一下就是

require 'redis'
require 'date' # <= これ

conf = {
  # set.rbと同様のRedisの設定が記述してあります
}
redis = Redis.new(conf)
Marshal.load(redis.get('key'))
# => #<Date: 1993-02-24 ((2449043j,0s,0n),+0s,2299161j)>

在进行Marshal.load时,需要在相同的环境中才能恢复目标对象的状态。(不确定这样的表达方式是否能传达到你的意思…)

如果是其他物体的话,这个错误相对来说就比较容易理解。

require 'redis'
require 'bigdecimal'

conf = {
  # ここにはRedisの設定が記述してあります
}
redis = Redis.new(conf)
decimal = BigDecimal.new('0.1234')
redis.set('key', Marshal.dump(decimal))
require 'redis'

conf = {
  # decimal_set.rbと同様のRedisの設定が記述してあります
}
redis = Redis.new(conf)
Marshal.load(redis.get('key'))
# => ArgumentError: undefined class/module BigDecimal

如果没有引入`bigdecimal`的话,可能会得到这样的结果:”哦,我忘记引入`bigdecimal`了”,但是对于`Date`对象来说,并不需要执行引入操作,它已经存在了。

p Date # => Date

这个事情导致了不必要的混乱状态。

我不太清楚这个东西是什么,但是我注意到即使进行了Date.methods操作,也没有出现date库的方法,这可能意味着需要require ‘date’来重新打开Date类吧,我只是猜测。我猜想如果在Date.freeze之后进行require ‘date’操作,可能会出现RuntimeError: can’t modify frozen class的错误。

因此,在执行Marshal.load时,请确保环境匹配,这就是提示。

广告
将在 10 秒后关闭
bannerAds