目录 搜索
Ruby用户指南 3、开始 4、简单的例子 5、字符串 6、正则表达式 7、数组 8、回到那些简单的例子 9、流程控制 10、迭代器 11、面向对象思维 12、方法 13、类 14、继承 15、重载方法 16、访问控制 17、单态方法 18、模块 19、过程对象 20、变量 21、全局变量 22、实变量 23、局部变量 24、类常量 25、异常处理:rescue 26、异常处理:ensure 27、存取器 28、对象的初始化 29、杂项 RGSS入门教程 1、什么是RGSS 2、开始:最简单的脚本 3、数据类型:数字 4、数据类型:常量与变量 5、数据类型:字符串 6、控制语句:条件分歧语句 7、控制语句:循环 8、函数 9、对象与类 10、显示图片 11、数组 12、哈希表(关联数组) 13、类 14、数据库 15、游戏对象 16、精灵的管理 17、窗口的管理 18、活动指令 19、场景类 Programming Ruby的翻译 Programming Ruby: The Pragmatic Programmer's Guide 前言 Roadmap Ruby.new 类,对象和变量 容器Containers,块Blocks和迭代Iterators 标准类型 深入方法 表达式Expressions 异常,捕捉和抛出(已经开始,by jellen) 模块 基本输入输出 线程和进程 当遭遇挫折 Ruby和它的世界 Ruby和Web开发 Ruby Tk Ruby 和微软的 Windows 扩展Ruby Ruby语言 (by jellen) 类和对象 (by jellen) Ruby安全 反射Reflection 内建类和方法 标准库 OO设计 网络和Web库 Windows支持 内嵌文档 交互式Ruby Shell 支持 Ruby参考手册 Ruby首页 卷首语 Ruby的启动 环境变量 对象 执行 结束时的相关处理 线程 安全模型 正则表达式 字句构造 程序 变量和常数 字面值 操作符表达式 控制结构 方法调用 类/方法的定义 内部函数 内部变量 内部常数 内部类/模块/异常类 附加库 Ruby变更记录 ruby 1.6 特性 ruby 1.7 特性 Ruby术语集 Ruby的运行平台 pack模板字符串 sprintf格式 Marshal格式 Ruby FAQ Ruby的陷阱
文字

Marshal格式

2002-04-04 草稿....

  • 以4.8(对应于1.8)版的格式为蓝本

    # 截至2003-05-02为止的格式版本如下所示
    p Marshal.Dump(Object.new).unpack("cc").join(".")
        => ruby 1.6.0 (2000-09-19) [i586-linux]
           "4.4"
        => ruby 1.6.1 (2000-09-27) [i586-linux]
           "4.4"
        => ruby 1.6.2 (2000-12-25) [i586-linux]
           "4.5"
        => ruby 1.6.3 (2001-03-19) [i586-linux]
           "4.5"
        => ruby 1.6.4 (2001-06-04) [i586-linux]
           "4.5"
        => ruby 1.6.5 (2001-09-19) [i586-linux]
           "4.6"
        => ruby 1.6.6 (2001-12-26) [i586-linux]
           "4.6"
        => ruby 1.6.7 (2002-03-01) [i586-linux]
           "4.6"
        => ruby 1.6.7 (2002-09-06) [i586-linux]
           "4.6"
        => ruby 1.7.3 (2002-09-06) [i586-linux]
           "4.7"
        => ruby 1.7.3 (2002-09-20) [i586-linux]
           "4.8"
        => ruby 1.8.0 (2003-08-03) [i586-linux]
           "4.8"
    
  • 本文兼顾了以前的版本,同时也指出了兼容性问题
  • 还提到了Ruby的Marshal中的BUG(?)
nil
true
false

分别是'0', 'T', 'F'

p Marshal.Dump(nil).unpack("x2 a*")
# => ["0"]

此时,即使设置了实例变量也无法Dump。

class NilClass
  attr_accessor :foo
end
nil.foo = 1
p nil.foo                           # => 1
p Marshal.Dump(nil).unpack("x2 a*") # => ["0"]
Fixnum

在'i'之后是表示Fixnum的数据结构。

以数值n为例,在表示数值部分的形式中(不仅限于Fixnum,在其它地方也是如此),保存着

形式 1:

n == 0:       0
0 < n < 123:  n + 5
-124 < n < 0: n - 5

这样的数值(1 byte)。之所以加减5,是为了有别于下面的形式。

例:

p Marshal.Dump(-1).unpack("x2 a*") # => "i\372"
p Marshal.Dump(0).unpack("x2 a*")  # => "i\000"
p Marshal.Dump(1).unpack("x2 a*")  # => "i\006"
p Marshal.Dump(2).unpack("x2 a*")  # => "i\a"   ("i\007")

若数值N超出形式1的范围时,则有下面的形式。

形式 2:

| len | n1 | n2 | n3 | n4 |
 <-1-> <-     len       ->
 byte        bytes

len的值是-4 ~ -1, 1 ~ 4。这表示符号和后续的数据存在于n1 ~ n|len|。

# 举个更好的例子...
def foo(len, n1, n2 = 0, n3 = 0, n4 = 0)

    case len
    when -3;           n4 = 255
    when -2;      n3 = n4 = 255
    when -1; n2 = n3 = n4 = 255
    end

    n = (0xffffff00 | n1) &
        (0xffff00ff | n2 * 0x100) &
        (0xff00ffff | n3 * 0x10000) &
        (0x00ffffff | n4 * 0x1000000)
    # p "%x" % n
    n = -((n ^ 0xffff_ffff) + 1) if len < 0
    n
end

p Marshal.Dump(-125).unpack("x2 acC*") # => ["i", -1, 131]
p foo(-1, 131)
p Marshal.Dump(-255).unpack("x2 acC*") # => ["i", -1, 1]
p foo(-1, 1)
p Marshal.Dump(-256).unpack("x2 acC*") # => ["i", -1, 0]
p foo(-1, 0)
p Marshal.Dump(-257).unpack("x2 acC*") # => ["i", -2, 255, 254]
p foo(-2, 255, 254)
p Marshal.Dump(124).unpack("x2 acC*") # => ["i", 1, 124]
p foo(1, 124)
p Marshal.Dump(256).unpack("x2 acC*") # => ["i", 2, 0, 1]
p foo(2, 0, 1)

即使设定了实例变量,也无法Dump。

class Fixnum
  attr_accessor :foo
end
99.foo = 1
p 99.foo                           # => 1
p 999.foo                          # => nil
p Marshal.Dump(99).unpack("x2 ac") # => ["i", 104]
instance of the user class

'C': String, Regexp, Array, Hash 的子类的实例变量

| 'C' | 类名(Symbol)的 Dump | 父类的实例的 Dump |

例 1:

class Foo < String # (or Regexp, Array, Hash)
end
p Marshal.Dump(Foo.new("foo")).unpack("x2 a a c a3 aca*")
# => ["C", ":", 8, "Foo", "\"", 8, "foo"]
                          ^^^ (or '/', '[', '{')

例 2: 有实例变量(请参考instance variable)

class Foo < String # (or Regexp, Array, Hash)
  def initialize(obj)
    @foo = obj
    super(obj)
  end
end
p Marshal.Dump(Foo.new("foo")).unpack("x2 a a a c a3 aca3 caca4 aca*")
# => ["I", "C", ":", 8, "Foo", "\"", 8, "foo", 6, ":", 9, "@foo", "\"", 8, "foo"]

除此以外,将变为'o'。这是因为内部结构有所差异所致(请参考Object)

例:

class Foo
end
p Marshal.Dump(Foo.new).unpack("x2 a a c a*")
# => ["o", ":", 8, "Foo\000"]

'u'

若定义了_Dump、_load的话,就是'u'。因为无法Dump实例变量,所以必须使用_Dump/_load进行处理。

| 'u' | 类名(Symbol)的 Dump | _Dump 的结果的长度(Fixnum形式) |
| _Dump 的返回值 |

例:

class Foo
  def self._load
  end
  def _Dump(obj)
    "hogehoge"
  end
end
p Marshal.Dump(Foo.new).unpack("x2 a aca3 c a*")
# => ["u", ":", 8, "Foo", 13, "hogehoge"]

'U' ruby 1.8 特性

若定义了marshal_Dump、marshal_load的话,就是'U'。因为无法Dump实例变量,所以必须使用marshal_Dump/marshal_load来处理。

| 'U' | 类名(Symbol)的 Dump | marshal_Dump 方法的返回值的 Dump |

例:

class Foo
  def marshal_Dump
    "hogehoge"
  end
  def marshal_load(obj)
  end
end
p Marshal.Dump(Foo.new).unpack("x2 a aca3 a c a*")

# => ["U", ":", 8, "Foo", "\"", 13, "hogehoge"]
Object

'o'

| 'o' | 类名(Symbol)的 Dump | 实例变量的数量(Fixnum形式) |
| 实例变量名(Symbol) 的Dump(1) | 值(1) |
          :
          :
| 实例变量名(Symbol) 的Dump(n) | 值(n) |

例 1:

p Marshal.Dump(Object.new).unpack("x2 a a c a*")
# => ["o", ":", 11, "Object\000"]

例 2: 有实例变量

class Foo
  def initialize
    @foo = "foo"
    @bar = "bar"
  end
end
p Marshal.Dump(Foo.new).unpack("x2 a a c a3 c aca4 aca3 aca4 aca3")
# => ["o", ":", 8, "Foo", 7,
      ":", 9, "@bar", "\"", 8, "bar",
      ":", 9, "@foo", "\"", 8, "foo"]
Float

'f'

| 'f' | 数串的长度(Fixnum形式) | "%.16g" 的字符串 |

例:

p Marshal.Dump(Math::PI).unpack("x2 a c a*")
# => ["f", 22, "3.141592653589793"]

p Marshal.Dump(0.0/0).unpack("x2 a c a*")  # => ["f", 8, "nan"]
p Marshal.Dump(1.0/0).unpack("x2 a c a*")  # => ["f", 8, "inf"]
p Marshal.Dump(-1.0/0).unpack("x2 a c a*") # => ["f", 9, "-inf"]
p Marshal.Dump(-0.0).unpack("x2 a c a*")   # => ["f", 9, "-0"]
Bignum

'l'

| 'l' | '+'/'-' | short的个数(Fixnum形式) | ... |

例:

p Marshal.Dump(2**32).unpack("x2 a a c a*")
# => ["l", "+", 8, "\000\000\000\000\001\000"]

# => ["l", "+", 8, "\000\000\001\000"]  <- BUG: ruby version 1.6.3
String

'"'

| '"' | 长度(Fixnum形式) | 字符串 |

例:

p Marshal.Dump("hogehoge").unpack("x2 a c a*")
# => ["\"", 13, "hogehoge"]
Regexp

'/'

| '/' | 长度(Fixnum形式) | source字符串 | 选项 |

选项是 options的结果+汉字代码的flag值。

例:

p Marshal.Dump(/(hoge)*/).unpack("x2 a c a7 c")
# => ["/", 12, "(hoge)*", 0]

p Marshal.Dump(/hogehoge/m).unpack("x2 a c a8 c")
# => ["/", 13, "hogehoge", 4]

p Marshal.Dump(/hogehoge/e).unpack("x2 a c a8 c")

# => ["/", 13, "hogehoge", 32]
Array

'['

| '[' | 元素数(Fixnum形式) | 元素的 Dump | ... |

例:

p Marshal.Dump(["hogehoge", /hogehoge/]).unpack("x2 a c aca8 aca*")
# => ["[", 7, "\"", 13, "hogehoge", "/", 13, "hogehoge\000"]
Hash

'{'

| '{' | 元素数(Fixnum形式) | 键的 Dump | 值的 Dump | ... |

例:

p Marshal.Dump({"hogehoge", /hogehoge/}).unpack("x2 a c aca8 aca*")
# => ["{", 6, "\"", 13, "hogehoge", "/", 13, "hogehoge\000"]
Hash with default value ( not Proc )

'}'

| '}' | 元素数(Fixnum形式) | 键的 Dump | 值的 Dump | ... | 默认值 |

例:

h = Hash.new(true)
h["foo"] = "bar"
p Marshal.Dump(h).unpack("x2 a c aca3 aca*")
# => ["}", 6, "\"", 8, "foo", "\"", 8, "barT"]

若某Hash的默认对象是Proc的话,则无法Dump该Hash

h = Hash.new { }
Marshal.Dump(h)
=> -:2:in `Dump': cannot Dump hash with default proc (TypeError)
Struct

'S': 结构体类的实例的Dump

| 'S' | 类名(Symbol) 的 Dump | 成员数量(Fixnum形式) |
| 成员名(Symbol) 的 Dump | 值 | ... |

例:

Struct.new("XXX", :foo, :bar)
p Marshal.Dump(Struct::XXX.new).unpack("x2 a ac a11 c aca3a aca3a")
# => ["S", ":", 16, "Struct::XXX", 7,
      ":", 8, "foo", "0",
      ":", 8, "bar", "0"]
Class/Module (old format)

'M'

| 'M' | 长度(Fixnum形式) | 模块/类名 |

例: 因为已经无法dump这种形式,所以使用load进行说明。

class Mod
end
p Marshal.load([4,7, 'M', 3+5, 'Mod'].pack("ccaca*"))
# => Mod
Class/Module

'c', 'm'

| 'c'/'m' | 类名的长度(Fixnum 形式) | 类名 |

例:

class Foo
end
p Marshal.Dump(Foo).unpack("x2 a c a*") # => ["c", 8, "Foo"]

例 2: 无法dump类/模块的实例变量

module Bar
  @bar = 1
end
p Bar.instance_eval { @bar }
Marshal.Dump(Bar, open("/tmp/foo", "w"))
# => 1

module Bar
end
p bar = Marshal.load(open("/tmp/foo"))
p bar.instance_eval { @bar }
# => nil

例 3: 无法dump类变量

module Baz
  @@baz = 1
  def self.baz
    @@baz
  end
end
p Baz.baz
Marshal.Dump(Baz, open("/tmp/foo", "w"))
# => 1

module Baz
  def self.baz
    @@baz
  end
end
p baz = Marshal.load(open("/tmp/foo"))
baz.baz
# => Baz
     -:3:in `baz': uninitialized class variable @@baz in Baz (NameError)
             from -:7
Symbol

':'

| ':' | 符号名的长度(Fixnum形式) | 符号名 |

例:

p Marshal.Dump(:foo).unpack("x2 a c a*")
# => [":", 8, "foo"]
Symbol (link)

';'

| ';' | 表明Symbol实际状态的号码(Fixnum形式) |

在相应符号名已被dump/load时使用。该号码是内部管理的号码。(在dump/load时,会生成哈希表以便对Symbol进行管理。它表示记录位置)

例:

p Marshal.Dump([:foo, :foo]).unpack("x2 ac aca3 aC*")
# => ["[", 7, ":", 8, "foo", ";", 0]

p Marshal.Dump([:foo, :foo, :bar, :bar]).
    unpack("x2 ac aca3 aC aca3 aC*")
# => ["[", 9, ":", 8, "foo", ";", 0, ":", 8, "bar", ";", 6]
instance variable

'I': Object, Class, Module 的实例以外的对象

| 'I' | 对象的 Dump | 实例变量的数量(Fixnum形式) |
| 实例变量名(Symbol) 的Dump(1) | 值(1) |
          :
          :
| 实例变量名(Symbol) 的Dump(n) | 值(n) |

因为Object的实例中包含实例变量,所以会采用其他的形式进行Dump(请参考Object)。该形式只针对Array 或 String 的实例。

例:

obj = String.new
obj.instance_eval { @foo = "bar" }
p Marshal.Dump(obj).unpack("x2 a ac c a c a4 aca*")
# => ["I", "\"", 0, 6, ":", 9, "@foo", "\"", 8, "bar"]

类或模块(Class/Module的实例)不会dump实例变量的信息。(请参考Class/Module)

link

'@'

| '@' | 表明对象实际状态的号码(Fixnum形式 |

在相应对象已被dump/load时使用。该号码是内部管理的号码。(在dump/load时,会生成哈希表以便对对象进行管理。它表示记录位置)

例:

obj = Object.new
p Marshal.Dump([obj, obj]).unpack("x2 ac aaca6c aca*")
# => ["[", 7, "o", ":", 11, "Object", 0, "@", 6, ""]

ary = []
ary.push ary
p Marshal.Dump(ary).unpack("x2 acac")

# => ["[", 6, "@", 0]

Marshal 的BUG

在ruby version 1.6中发现了下列BUG。括号()中列出的是正确的运作方式(1.7的运作方式)。

<= 1.6.7
  • 类的clone中的实例是可以Dump的,但却不能加载(因为是无名类的对象,所以无法Dump)
  • 当某对象通过include/extend无名Module而定义了特殊方法后,仍可以Dump/加载该对象(若某对象include了无名模块的话,则不能Dump该对象)
1.6.6, 1.6.7
  • 拥有实例变量的Array和String是可以Dump的,但却不能加载(既能Dump,又能加载)
<= 1.6.5
  • 类的clone中的实例是可以Dump的,但却不能正常加载。否则就会生成奇怪的对象(?)
  • 特殊类被dump成为普通类了(特殊类是无法Dump的)
  • 无名类是可以Dump的,但却不能加载(无名类是无法Dump的)
<= 1.6.4
  • 模块可以Dump却不能加载(可以加载)
  • 无名模块可以Dump却不能加载(无名模块是不能Dump的)
<= 1.6.3
  • dump Float时,其保存精度偏低
<= 1.6.2
  • dump时,无法保存正则表达式中/m, /x 选项的状态
1.6.2, 1.6.3
  • 在1.6.2, 1.6.3中,Bignum可以Dump却不能加载。按理说其他版本也会有这个BUG,但因为没有测试脚本,所以无法证实。
<= 1.6.1
  • dump时无法保存Range中的特定标识,该标识表明该范围中是否包含终点

下面就是测试脚本(请参考[RAA:RubyUnit])

# test for Marshal for ruby version 1.6
require 'rubyunit'

$version_dependent_behavior = true
# for test_userClass, test_userModule
module UserModule
  def foo
  end
end
class UserClass
  def foo
  end
end

class TestMarshal < RUNIT::TestCase

  def assert_no_Dumpable(obj)
    ex = assert_exception(TypeError) {
      begin
        # Marshal.Dump will cause TypeError or ArgumentError
        Marshal.Dump obj
      rescue ArgumentError
        case $!.message
        when /can't Dump anonymous/,
             /cannot Dump hash with default proc/
          raise TypeError
        else
          raise "unknown error"
        end
      end
    }
  end
  def assert_Dumpable_but_not_equal(obj)
    obj2 = Marshal.load(Marshal.Dump(obj))
    assert(obj != obj2)
    assert_equals(obj.type, obj2.type)
  end
  def assert_Dumpable_and_equal(obj)
    obj2 = Marshal.load(Marshal.Dump(obj))
    assert_equals(obj, obj2)
    assert_equals(obj.type, obj2.type)

    # check values of instance variable
    ivars = obj.instance_variables
    ivars2 = obj2.instance_variables
    assert_equals(ivars, ivars2)
    while ivars.size != 0
      assert_equals(obj.instance_eval(ivars.shift),
                    obj2.instance_eval(ivars2.shift))
    end
  end

  def test_Object
    assert_Dumpable_but_not_equal Object.new
  end

  # object with singleton method
  def test_Object_with_singleton_method
    obj = Object.new
    # On ruby version 1.6.0 - 1.6.2, cause parse error (nested method)
    class <<obj
      def foo
      end
    end

    # object has singleton method can't be Dumped
    assert_no_Dumpable obj
  end

  # object with singleton method (with named module)
  def test_Object_with_singleton_method2
    obj = Object.new
    # On ruby version 1.6.0 - 1.6.2, cause parse error (nested method)
    class <<obj
      include UserModule
    end

    # On ruby version 1.6.0 - 1.6.7, no consider the singleton
    # method with Mix-in.
    # On ruby version 1.7, Dumpable object which is extended by
    # named module.
    assert_Dumpable_but_not_equal obj
  end

  # object with singleton method (with anonymous module)
  def test_Object_with_singleton_method3
    obj = Object.new
    # On ruby version 1.6.0 - 1.6.2, cause parse error (nested method)
    class <<obj
      include Module.new
    end

    if $version_dependent_behavior and RUBY_VERSION <= "1.6.7"
      # On ruby version 1.6.0 - 1.6.7, no consider the singleton method with Mix-in.
      assert_Dumpable_but_not_equal obj
    else
      # object has singleton method (with anonymous module) can't be Dumped
      assert_no_Dumpable obj
    end
  end

  # singleton class
  def test_singletonClass
    obj = Object.new
    # On ruby version 1.6.0 - 1.6.2, cause parse error (nested method)
    singleton_class = class <<obj
                        def foo
                        end
                        self
                      end

    # singleton class can't be Dumped
    # On ruby version 1.6.0 - 1.6.5, singleton class be able to Dumped
    # as normal class.
    if $version_dependent_behavior and RUBY_VERSION <= "1.6.5"
      assert_equals(Object, Marshal.load(Marshal.Dump(singleton_class)))
    else
      assert_no_Dumpable singleton_class
    end
  end

  def test_Array
    assert_Dumpable_and_equal [1,"foo", :foo]
  end

  def test_Array_with_instance_variable
    ary = [1,"foo", :foo]
    ary.instance_eval{ @var = 1 }

    if $version_dependent_behavior and %w(1.6.6 1.6.7).member?(RUBY_VERSION)
      # On ruby version 1.6.6 - 1.6.7, Array(or String ...) has instance
      # variable is able to be Dumped, but can't load it.
      Dump = Marshal.Dump(ary)
      ex = assert_exception(ArgumentError) {
        Marshal.load(Dump)
      }
    else
      assert_Dumpable_and_equal ary
    end
  end

  def test_Binding
    assert_no_Dumpable binding
  end

  def test_Continuation
    assert_no_Dumpable callcc {|c| c}
  end

  def test_Data
    # assert_fail("")
  end

  def test_Exception
    assert_Dumpable_but_not_equal Exception.new("hoge")
  end

  def test_Dir
    assert_no_Dumpable Dir.open("/")
  end

  def test_FalseClass
    assert_Dumpable_and_equal false
  end

  def test_File__Stat
    assert_no_Dumpable File.stat("/")
  end

  def test_Hash
    assert_Dumpable_and_equal(1=>"1",2=>"2")

    # 1.7 feature.
    if $version_dependent_behavior and RUBY_VERSION >= '1.7.0'
      # On ruby version 1.7, hash with default Proc cannot be Dumped.
      # see [ruby-dev:15417]
      assert_no_Dumpable(Hash.new { })
    end
  end

  def test_IO
    assert_no_Dumpable IO.new(0)
  end

  def test_File
    assert_no_Dumpable File.open("/")
  end

  def test_MatchData
    assert_no_Dumpable(/foo/ =~ "foo" && $~)
  end

  def test_Method
    assert_no_Dumpable Object.method(:method)
  end

  def test_UnboundMethod
    assert_no_Dumpable Object.instance_method(:id)
  end

  def test_Module
    # On ruby version 1.6.0 - 1.6.4, loaded module is not a module.
    if $version_dependent_behavior and RUBY_VERSION <= '1.6.4'
      Dump = Marshal.Dump Enumerable
      ex = assert_exception(TypeError) {
        Marshal.load Dump
      }
      assert_matches(ex.message, /is not a module/)
    else
      assert_Dumpable_and_equal Enumerable
    end
  end

  def test_userModule
    # On ruby version 1.6.0 - 1.6.4, loaded module is not a module.
    if $version_dependent_behavior and RUBY_VERSION <= '1.6.4'
      # same as test_Module
    else
      # Note: this module must be defineed for Marshal.load.
      assert_Dumpable_and_equal(UserModule)
    end
  end

  def test_anonymousModule
    # On ruby version 1.6.0 - 1.6.4, anonymous class is able to be Dumped,
    # but loaded object is not identical.
    if $version_dependent_behavior and RUBY_VERSION <= '1.6.4'
      Dump = Marshal.Dump(Module.new)
      ex = assert_exception(ArgumentError) {
        Marshal.load Dump
      }
      assert_matches(ex.message, /can\'t retrieve anonymous class/)
    else
      assert_no_Dumpable Module.new
    end
  end

  def test_Class
    assert_Dumpable_and_equal Class
  end

  def test_userClass
    # Note: this class must be defineed for Marshal.load.
    assert_Dumpable_and_equal(UserClass)
  end
  def test_anonymousClass
    # On ruby version 1.6.0 - 1.6.5, anonymous class able to be Dumped,
    # but can't load it.
    if $version_dependent_behavior and RUBY_VERSION <= '1.6.5'
      Dump = Marshal.Dump(Class.new)
      ex = assert_exception(ArgumentError) {
        Marshal.load(Dump)
      }
      assert_matches(ex.message, /can\'t retrieve anonymous class/)
    else
      assert_no_Dumpable Class.new
    end
  end

  def test_clonedClass
    # On ruby version 1.6.0 - 1.6.7, instance of cloned class is able to
    # Dumped, but loaded object is not identical.
    # see [ruby-dev:14961]
    if $version_dependent_behavior
      if RUBY_VERSION <= '1.6.5'
        obj = String.clone.new("foo")
        Dump = Marshal.Dump(obj)
        obj2 = Marshal.load Dump
        assert(obj == obj2)
        assert(obj.type != obj2.type)
        assert(obj.type.inspect == obj2.type.inspect)
      elsif RUBY_VERSION <= '1.6.7'
        Dump = Marshal.Dump(String.clone.new("foo"))
        assert_exception(ArgumentError) {
          Marshal.load Dump
        }
      else
        assert_no_Dumpable String.clone.new("foo")
      end
    else
      # anonymous class can't be Dumped
      assert_no_Dumpable String.clone.new("foo")
    end
  end

  def test_Numeric
    # assert_fail("")
  end

  def test_Integer
    # assert_fail("")
  end

  def test_Fixnum
    assert_Dumpable_and_equal 100
  end

  def test_Bignum
    # derived from Rubicon
    assert_Dumpable_and_equal 123456789012345678901234567890
    assert_Dumpable_and_equal -123**99
    if $version_dependent_behavior and %w(1.6.2 1.6.3).member?(RUBY_VERSION)
      Dump = Marshal.Dump 2**32
      ex = assert_exception(ArgumentError) {
        Marshal.load(Dump)
      }
      assert_matches(ex.message, /marshal data too short/)
    else
      assert_Dumpable_and_equal 2**32
    end
  end

  def test_Float
    assert_Dumpable_and_equal 1.41421356

    # On ruby version 1.6.4, Dumped format changed from "%.12g" to "%.16g"
    if $version_dependent_behavior and RUBY_VERSION <= '1.6.3'
      assert_Dumpable_but_not_equal Math::PI
    else
      assert_Dumpable_and_equal Math::PI
    end
  end

  def test_Proc
    assert_no_Dumpable proc { }
  end

  def test_Process__Status
    assert_Dumpable_and_equal system("true") && $?
  end

  def test_Range
    # Range#== is changed from 1.6.2
    # On ruby version 1.6.0 - 1.6.1, Range.new(1,2) != Range.new(1,2)
    # assert_Dumpable_and_equal 1..2
    # assert_Dumpable_and_equal 1...2

    obj = Marshal.load(Marshal.Dump 1..2)
    assert_equals(1, obj.begin)
    assert_equals(2, obj.end)
    assert_equals(false, obj.exclude_end?)

    obj = Marshal.load(Marshal.Dump 1...2)
    assert_equals(1, obj.begin)
    assert_equals(2, obj.end)

    # On ruby version 1.6.0 - 1.6.1, the attribute exclude_end? is not saved.
    if $version_dependent_behavior and RUBY_VERSION <= '1.6.1'
      assert_equals(false, obj.exclude_end?)
    else
      assert_equals(true, obj.exclude_end?)
    end
  end

  def test_Regexp
    # this test is no consider the /foo/p
    assert_Dumpable_and_equal /foo/
    assert_Dumpable_and_equal /foo/i
    assert_Dumpable_and_equal /foo/m
    assert_Dumpable_and_equal /foo/x
    assert_Dumpable_and_equal /foo/e
    assert_Dumpable_and_equal /foo/s
    assert_Dumpable_and_equal /foo/u

    # On ruby version 1.6.0 - 1.6.2, Regexp#== is ignore the option.
    for obj in [/foo/, /foo/i, /foo/m, /foo/x, /foo/e, /foo/s, /foo/u]
      obj2 = Marshal.load(Marshal.Dump obj)

      if $version_dependent_behavior and RUBY_VERSION <= '1.6.2' and
          %w(/foo/m /foo/x).member?(obj.inspect)
        # On ruby version 1.6.0 - 1.6.2,
        # //m options is not saved.
        assert_equals('/foo/', obj2.inspect)
      else
        assert_equals(obj.inspect, obj2.inspect)
      end
    end
  end

  def test_String
    assert_Dumpable_and_equal "foo"
  end

  def test_Struct
    assert_Dumpable_and_equal Struct.new("Foo", :foo, :bar)

    Object.const_set('Foo', Struct.new(:foo, :bar))
    assert_Dumpable_and_equal Foo
  end

  def test_aStruct
    assert_Dumpable_and_equal Struct.new("Bar", :foo, :bar).new("foo", "bar")

    # see [ruby-dev:14961]
  end

  def test_Symbol
    assert_Dumpable_and_equal :foo
  end

  def test_Thread
    assert_no_Dumpable Thread.new { sleep }
  end

  def test_ThreadGroup
    assert_no_Dumpable ThreadGroup::Default
  end

  def test_Time
    assert_Dumpable_and_equal Time.now
    assert_Dumpable_and_equal Time.now.gmtime

    # time zone is not saved.
    assert_equals(false, Marshal.load(Marshal.Dump(Time.now)).utc?)
    assert_equals(false, Marshal.load(Marshal.Dump(Time.now.gmtime)).utc?)
  end

  def test_TrueClass
    assert_Dumpable_and_equal true
  end

  def test_NilClass
    assert_Dumpable_and_equal nil
  end
end


上一篇: 下一篇: