summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS.md1
-rw-r--r--lib/ostruct.rb27
-rw-r--r--spec/ruby/library/yaml/dump_spec.rb4
-rw-r--r--test/ostruct/test_ostruct.rb21
4 files changed, 52 insertions, 1 deletions
diff --git a/NEWS.md b/NEWS.md
index 4078f19358..24abb1d08c 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -275,6 +275,7 @@ Outstanding ones only.
* Builtin methods can now be overridden safely. [[Bug #15409]]
* Implementation uses only methods ending with `!`.
* Ractor compatible.
+ * Improved support for YAML [[Bug #8382]]
* Use officially discouraged. Read "Caveats" section.
* Reline
diff --git a/lib/ostruct.rb b/lib/ostruct.rb
index d2e93b1463..4ecffb094e 100644
--- a/lib/ostruct.rb
+++ b/lib/ostruct.rb
@@ -398,6 +398,33 @@ class OpenStruct
@table.hash
end
+ #
+ # Provides marshalling support for use by the YAML library.
+ #
+ 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
+ end
+ end
+
+ #
+ # Provides marshalling support for use by the YAML library.
+ #
+ 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
+ end
+ update_to_values!(h)
+ end
+
# Make all public methods (builtin or our own) accessible with `!`:
instance_methods.each do |method|
new_name = "#{method}!"
diff --git a/spec/ruby/library/yaml/dump_spec.rb b/spec/ruby/library/yaml/dump_spec.rb
index 5af794b7f8..9e884b0fd7 100644
--- a/spec/ruby/library/yaml/dump_spec.rb
+++ b/spec/ruby/library/yaml/dump_spec.rb
@@ -37,7 +37,9 @@ describe "YAML.dump" do
it "dumps an OpenStruct" do
require "ostruct"
os = OpenStruct.new("age" => 20, "name" => "John")
- YAML.dump(os).should match_yaml("--- !ruby/object:OpenStruct\ntable:\n :age: 20\n :name: John\n")
+ os2 = YAML.load(YAML.dump(os))
+ os2.age.should == 20
+ os2.name.should == "John"
end
it "dumps a File without any state" do
diff --git a/test/ostruct/test_ostruct.rb b/test/ostruct/test_ostruct.rb
index 5de6f278d0..7dd1ac626b 100644
--- a/test/ostruct/test_ostruct.rb
+++ b/test/ostruct/test_ostruct.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'test/unit'
require 'ostruct'
+require 'yaml'
class TC_OpenStruct < Test::Unit::TestCase
def test_initialize
@@ -309,4 +310,24 @@ class TC_OpenStruct < Test::Unit::TestCase
end.take
assert obj1.object_id == obj2.object_id
end if defined?(Ractor)
+
+ def test_legacy_yaml
+ s = "--- !ruby/object:OpenStruct\ntable:\n :foo: 42\n"
+ o = YAML.load(s)
+ assert_equal(42, o.foo)
+
+ o = OpenStruct.new(table: {foo: 42})
+ assert_equal({foo: 42}, YAML.load(YAML.dump(o)).table)
+ end
+
+ 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"
+ os1 = OpenStruct.new(h)
+ os2 = YAML.load(os1.to_yaml)
+ assert_equal yaml, os1.to_yaml
+ assert_equal os1, os2
+ assert_equal true, os1.eql?(os2)
+ assert_equal 300.42, os2.pension
+ end
end