stdlib/marshal_objects.rb

tests/_examples/stdlib/marshal_objects.rb in the metorex repository.

# Marshal writes objects by their class and instance variables, and refuses
# what it could not read back.
Point = Struct.new(:x, :y)
Size = Data.define(:width, :height)
module Labeled; end

class Account
  def initialize
    @owner = "ann".b
  end
end

class Snapshot
  def marshal_dump = [1, 2]
end

class Packed
  def _dump(_limit) = "packed".b
end

p(Marshal.dump(Account.new))
p(Marshal.dump(Account.new.extend(Labeled)))
p(Marshal.dump(Point.new(1, 2)))
p(Marshal.dump(Size.new(width: 3, height: 4)))
p(Marshal.dump(Account))
p(Marshal.dump(Labeled))
p(Marshal.dump(Snapshot.new))
p(Marshal.dump(Packed.new))

refused = [
  -> { Marshal.dump(Class.new.new) },
  -> { Marshal.dump(Object.new.singleton_class) },
  -> { Marshal.dump(proc { }) },
  -> { Marshal.dump([[[]]], 1) },
  -> { Marshal.dump("text", Object.new) },
]
refused.each do |attempt|
  begin
    attempt.call
  rescue TypeError, ArgumentError => error
    p([error.class, error.message.sub(/0x\h+/, "0x...")])
  end
end

written = []
target = Object.new
target.define_singleton_method(:write) { |bytes| written << bytes }
target.define_singleton_method(:binmode) { written << :binmode }
Marshal.dump(:note, target)
p(written)

Output:

"\x04\bo:\fAccount\x06:\v@owner\"\bann"
"\x04\be:\fLabeledo:\fAccount\x06:\v@owner\"\bann"
"\x04\bS:\nPoint\a:\x06xi\x06:\x06yi\a"
"\x04\bS:\tSize\a:\nwidthi\b:\vheighti\t"
"\x04\bc\fAccount"
"\x04\bm\fLabeled"
"\x04\bU:\rSnapshot[\ai\x06i\a"
"\x04\bu:\vPacked\vpacked"
[TypeError, "can't dump anonymous class #<Class:0x...>"]
[TypeError, "singleton class can't be dumped"]
[TypeError, "no _dump_data is defined for class Proc"]
[ArgumentError, "exceed depth limit"]
[TypeError, "instance of IO needed"]
[:binmode, "\x04\b:\tnote"]