From b0a934b3398467c83c137db5112f00aad1c5c221 Mon Sep 17 00:00:00 2001 From: Aaron Ogata Date: Tue, 20 Dec 2022 07:30:29 -0800 Subject: [PATCH] fix incorrect deserialization of some legacy formats ``` In at least some cases, the legacy format includes an additional field that needs to be considered. # ruby 2.6.10p210 (2022-04-12 revision 67958) [universal.x86_64-darwin22] require "ostruct" require "yaml" os = OpenStruct.new os.a = 'b' YAML.dump(os) # => "--- !ruby/object:OpenStruct\ntable:\n :a: b\nmodifiable: true\n" ``` --- lib/ostruct.rb | 17 +++++++++-------- test/ostruct/test_ostruct.rb | 9 +++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/lib/ostruct.rb b/lib/ostruct.rb index a08561d..b8fa74e 100644 --- a/lib/ostruct.rb +++ b/lib/ostruct.rb @@ -435,9 +435,10 @@ def encode_with(coder) # :nodoc: @table.each_pair do |key, value| coder[key.to_s] = value end - if @table.size == 1 && @table.key?(:table) # support for legacy format - # in the very unlikely case of a single entry called 'table' - coder['legacy_support!'] = true # add a bogus second entry + + # ensure data isn't corrupted on reload if it looks like the legacy format + if @table.size == 1 && @table.key?(:table) || @table.size == 2 && [:modifiable, :table].all? { |x| @table.key?(x) } + coder['legacy_support!'] = true # add a bogus entry to change the size end end @@ -446,12 +447,12 @@ def encode_with(coder) # :nodoc: # def init_with(coder) # :nodoc: h = coder.map - if h.size == 1 # support for legacy format - key, val = h.first - if key == 'table' - h = val - end + + # support for legacy format + if h.size == 1 && h.key?('table') || h.size == 2 && %w[modifiable table].all? { |x| h.key?(x) } + h = h['table'] end + update_to_values!(h) end diff --git a/test/ostruct/test_ostruct.rb b/test/ostruct/test_ostruct.rb index 256db7a..225b83a 100644 --- a/test/ostruct/test_ostruct.rb +++ b/test/ostruct/test_ostruct.rb @@ -390,6 +390,15 @@ def test_legacy_yaml assert_equal({foo: 42}, YAML.safe_load(YAML.dump(o), permitted_classes: [Symbol, OpenStruct]).table) end if RUBY_VERSION >= '2.6' + def test_legacy_yaml_2 + s = "--- !ruby/object:OpenStruct\ntable:\n :foo: 42\nmodifiable: true\n" + o = YAML.safe_load(s, permitted_classes: [Symbol, OpenStruct]) + assert_equal(42, o.foo) + + o = OpenStruct.new(table: {foo: 42}) + assert_equal({foo: 42}, YAML.safe_load(YAML.dump(o), permitted_classes: [Symbol, OpenStruct]).table) + end if RUBY_VERSION >= '2.6' + def test_yaml h = {name: "John Smith", age: 70, pension: 300.42} yaml = "--- !ruby/object:OpenStruct\nname: John Smith\nage: 70\npension: 300.42\n"