summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/psych/ruby.rb3
-rw-r--r--lib/psych/visitors/to_ruby.rb14
-rw-r--r--lib/psych/visitors/yast_builder.rb11
-rw-r--r--test/visitors/test_to_ruby.rb36
-rw-r--r--test/visitors/test_yast_builder.rb12
5 files changed, 74 insertions, 2 deletions
diff --git a/lib/psych/ruby.rb b/lib/psych/ruby.rb
index f226c84..f564565 100644
--- a/lib/psych/ruby.rb
+++ b/lib/psych/ruby.rb
@@ -4,8 +4,7 @@ require 'date'
[
Object, String, Class, Hash, Array, NilClass, Float, FalseClass, TrueClass,
- Range, Complex, Rational, Date, Time, Regexp, Exception
- # Struct
+ Range, Complex, Rational, Date, Time, Regexp, Exception, Struct
].each do |klass|
klass.send(:remove_method, :to_yaml) rescue NameError
end
diff --git a/lib/psych/visitors/to_ruby.rb b/lib/psych/visitors/to_ruby.rb
index d00f72a..1d5bd4b 100644
--- a/lib/psych/visitors/to_ruby.rb
+++ b/lib/psych/visitors/to_ruby.rb
@@ -77,6 +77,20 @@ module Psych
def visit_Psych_Nodes_Mapping o
case o.tag
+ when /!ruby\/struct(:.*)?$/
+ klassname = $1
+ h = Hash[*o.children.map { |c| accept c }].to_a
+
+ if klassname && klassname.length > 1
+ s = klassname.sub(/^:/,'').split('::').inject(Object) { |k,o|
+ k.const_get(o)
+ }.allocate
+ h.each { |k,v| s.send("#{k}=", v) }
+ s
+ else
+ Struct.new(*h.map { |k,v| k.to_sym }).new(*h.map { |k,v| v })
+ end
+
when '!ruby/range'
h = Hash[*o.children.map { |c| accept c }]
Range.new(h['begin'], h['end'], h['excl'])
diff --git a/lib/psych/visitors/yast_builder.rb b/lib/psych/visitors/yast_builder.rb
index 914a891..6100264 100644
--- a/lib/psych/visitors/yast_builder.rb
+++ b/lib/psych/visitors/yast_builder.rb
@@ -21,6 +21,17 @@ module Psych
raise TypeError, "Can't dump #{target.class}"
end
+ def visit_Struct o
+ tag = ['!ruby/struct', o.class.name].compact.join(':')
+
+ @stack.push append Nodes::Mapping.new(nil, tag, false)
+ o.members.each do |member|
+ accept member
+ accept o[member]
+ end
+ @stack.pop
+ end
+
def visit_Exception o
@stack.push append Nodes::Mapping.new(nil, '!ruby/exception', false)
['message', o.message].each do |m|
diff --git a/test/visitors/test_to_ruby.rb b/test/visitors/test_to_ruby.rb
index e8b7a6e..9c37690 100644
--- a/test/visitors/test_to_ruby.rb
+++ b/test/visitors/test_to_ruby.rb
@@ -11,6 +11,42 @@ module Psych
@visitor = ToRuby.new
end
+ A = Struct.new(:foo)
+
+ def test_struct
+ s = A.new('bar')
+
+ mapping = Nodes::Mapping.new nil, "!ruby/struct:#{s.class}"
+ mapping.children << Nodes::Scalar.new('foo')
+ mapping.children << Nodes::Scalar.new('bar')
+
+ ruby = mapping.to_ruby
+
+ assert_equal s.class, ruby.class
+ assert_equal s.foo, ruby.foo
+ assert_equal s, ruby
+ end
+
+ def test_anon_struct_legacy
+ s = Struct.new(:foo).new('bar')
+
+ mapping = Nodes::Mapping.new nil, '!ruby/struct:'
+ mapping.children << Nodes::Scalar.new('foo')
+ mapping.children << Nodes::Scalar.new('bar')
+
+ assert_equal s.foo, mapping.to_ruby.foo
+ end
+
+ def test_anon_struct
+ s = Struct.new(:foo).new('bar')
+
+ mapping = Nodes::Mapping.new nil, '!ruby/struct'
+ mapping.children << Nodes::Scalar.new('foo')
+ mapping.children << Nodes::Scalar.new('bar')
+
+ assert_equal s.foo, mapping.to_ruby.foo
+ end
+
def test_exception
exc = Exception.new 'hello'
diff --git a/test/visitors/test_yast_builder.rb b/test/visitors/test_yast_builder.rb
index d35ff3d..4730e7d 100644
--- a/test/visitors/test_yast_builder.rb
+++ b/test/visitors/test_yast_builder.rb
@@ -8,6 +8,18 @@ module Psych
@v = Visitors::YASTBuilder.new
end
+ A = Struct.new(:foo)
+
+ def test_struct
+ assert_round_trip A.new('bar')
+ end
+
+ def test_struct_anon
+ s = Struct.new(:foo).new('bar')
+ obj = Psych.load(Psych.dump(s))
+ assert_equal s.foo, obj.foo
+ end
+
def test_exception
ex = Exception.new 'foo'
loaded = Psych.load(Psych.dump(ex))