diff options
-rw-r--r-- | ext/psych/emitter.c | 56 | ||||
-rw-r--r-- | lib/psych.rb | 7 | ||||
-rw-r--r-- | lib/psych/nodes/document.rb | 4 | ||||
-rw-r--r-- | lib/psych/nodes/node.rb | 1 | ||||
-rw-r--r-- | lib/psych/ruby.rb | 13 | ||||
-rw-r--r-- | lib/psych/tree_builder.rb | 13 | ||||
-rw-r--r-- | lib/psych/visitors.rb | 1 | ||||
-rw-r--r-- | lib/psych/visitors/emitter.rb | 12 | ||||
-rw-r--r-- | lib/psych/visitors/yast_builder.rb | 45 | ||||
-rw-r--r-- | test/visitors/test_emitter.rb | 46 | ||||
-rw-r--r-- | test/visitors/test_yast_builder.rb | 43 |
11 files changed, 237 insertions, 4 deletions
diff --git a/ext/psych/emitter.c b/ext/psych/emitter.c index 7b8a43a..126f174 100644 --- a/ext/psych/emitter.c +++ b/ext/psych/emitter.c @@ -173,6 +173,59 @@ static VALUE end_sequence(VALUE self) return self; } +static VALUE start_mapping( + VALUE self, + VALUE anchor, + VALUE tag, + VALUE implicit, + VALUE style +) { + yaml_emitter_t * emitter; + Data_Get_Struct(self, yaml_emitter_t, emitter); + + yaml_event_t event; + yaml_mapping_start_event_initialize( + &event, + (yaml_char_t *)(Qnil == anchor ? NULL : StringValuePtr(anchor)), + (yaml_char_t *)(Qnil == anchor ? NULL : StringValuePtr(tag)), + Qtrue == implicit ? 1 : 0, + (yaml_sequence_style_t)NUM2INT(style) + ); + + emit(emitter, &event); + + return self; +} + +static VALUE end_mapping(VALUE self) +{ + yaml_emitter_t * emitter; + Data_Get_Struct(self, yaml_emitter_t, emitter); + + yaml_event_t event; + yaml_mapping_end_event_initialize(&event); + + emit(emitter, &event); + + return self; +} + +static VALUE alias(VALUE self, VALUE anchor) +{ + yaml_emitter_t * emitter; + Data_Get_Struct(self, yaml_emitter_t, emitter); + + yaml_event_t event; + yaml_alias_event_initialize( + &event, + (yaml_char_t *)(Qnil == anchor ? NULL : StringValuePtr(anchor)) + ); + + emit(emitter, &event); + + return self; +} + void Init_psych_emitter() { VALUE psych = rb_define_module("Psych"); @@ -189,4 +242,7 @@ void Init_psych_emitter() rb_define_method(cPsychEmitter, "scalar", scalar, 6); rb_define_method(cPsychEmitter, "start_sequence", start_sequence, 4); rb_define_method(cPsychEmitter, "end_sequence", end_sequence, 0); + rb_define_method(cPsychEmitter, "start_mapping", start_mapping, 4); + rb_define_method(cPsychEmitter, "end_mapping", end_mapping, 0); + rb_define_method(cPsychEmitter, "alias", alias, 1); } diff --git a/lib/psych.rb b/lib/psych.rb index bbb8662..3ebc1fa 100644 --- a/lib/psych.rb +++ b/lib/psych.rb @@ -13,6 +13,7 @@ require 'psych/visitors' require 'psych/handler' require 'psych/tree_builder' require 'psych/parser' +require 'psych/ruby' require 'psych/psych' module Psych @@ -22,8 +23,12 @@ module Psych ### # Load +yaml+ in to a Ruby data structure def self.load yaml + parse(yaml).to_ruby + end + + def self.parse yaml parser = Psych::Parser.new(TreeBuilder.new) parser.parse yaml - parser.handler.root.children.first.to_ruby + parser.handler.root.children.first.children.first end end diff --git a/lib/psych/nodes/document.rb b/lib/psych/nodes/document.rb index be786c6..4f46644 100644 --- a/lib/psych/nodes/document.rb +++ b/lib/psych/nodes/document.rb @@ -10,11 +10,15 @@ module Psych # Was this document implicitly created? attr_accessor :implicit + # Is the end of the document implicit? + attr_accessor :implicit_end + def initialize version = [], tag_directives = [], implicit = false super() @version = version @tag_directives = tag_directives @implicit = implicit + @implicit_end = true end ### diff --git a/lib/psych/nodes/node.rb b/lib/psych/nodes/node.rb index a63fc60..6aaba15 100644 --- a/lib/psych/nodes/node.rb +++ b/lib/psych/nodes/node.rb @@ -17,6 +17,7 @@ module Psych def to_ruby Visitors::ToRuby.new.accept self end + alias :transform :to_ruby def to_yaml io = StringIO.new diff --git a/lib/psych/ruby.rb b/lib/psych/ruby.rb new file mode 100644 index 0000000..6802391 --- /dev/null +++ b/lib/psych/ruby.rb @@ -0,0 +1,13 @@ +[Object, String, Class, Hash].each do |klass| + klass.send(:remove_method, :to_yaml) +end + +class Object + include Psych::Visitable + + def to_yaml options = {} + visitor = Psych::Visitors::YASTBuilder.new options + visitor.accept self + visitor.tree.to_yaml + end +end diff --git a/lib/psych/tree_builder.rb b/lib/psych/tree_builder.rb index efbeaf9..089ee96 100644 --- a/lib/psych/tree_builder.rb +++ b/lib/psych/tree_builder.rb @@ -17,7 +17,6 @@ module Psych end %w{ - Document Sequence Mapping }.each do |node| @@ -28,12 +27,22 @@ module Psych @stack.push n end - def end_#{node.downcase}(*args) + def end_#{node.downcase} @stack.pop end } end + def start_document(*args) + n = Nodes::Document.new(*args) + @stack.last.children << n + @stack.push n + end + + def end_document implicit_end + @stack.pop.implicit_end = implicit_end + end + def start_stream encoding @stack.push Nodes::Stream.new(encoding) end diff --git a/lib/psych/visitors.rb b/lib/psych/visitors.rb index df58e2e..0708e94 100644 --- a/lib/psych/visitors.rb +++ b/lib/psych/visitors.rb @@ -1,3 +1,4 @@ require 'psych/visitors/visitor' require 'psych/visitors/to_ruby' require 'psych/visitors/emitter' +require 'psych/visitors/yast_builder' diff --git a/lib/psych/visitors/emitter.rb b/lib/psych/visitors/emitter.rb index a0ff9d9..3e33095 100644 --- a/lib/psych/visitors/emitter.rb +++ b/lib/psych/visitors/emitter.rb @@ -14,7 +14,7 @@ module Psych visitor_for(Nodes::Document) do |o| @handler.start_document o.version, o.tag_directives, o.implicit o.children.each { |c| c.accept self } - @handler.end_document o.implicit + @handler.end_document o.implicit_end end visitor_for(Nodes::Scalar) do |o| @@ -26,6 +26,16 @@ module Psych o.children.each { |c| c.accept self } @handler.end_sequence end + + visitor_for(Nodes::Mapping) do |o| + @handler.start_mapping o.anchor, o.tag, o.implicit, o.style + o.children.each { |c| c.accept self } + @handler.end_mapping + end + + visitor_for(Nodes::Alias) do |o| + @handler.alias o.anchor + end end end end diff --git a/lib/psych/visitors/yast_builder.rb b/lib/psych/visitors/yast_builder.rb new file mode 100644 index 0000000..00f533f --- /dev/null +++ b/lib/psych/visitors/yast_builder.rb @@ -0,0 +1,45 @@ +module Psych + module Visitors + class YASTBuilder < Psych::Visitors::Visitor + attr_reader :tree + + def initialize options = {} + super() + @tree = Nodes::Stream.new + @tree.children << Nodes::Document.new + @stack = @tree.children.dup + end + + def accept target + target.class.ancestors.each do |klass| + method_name = :"visit_#{target.class.name.split('::').join('_')}" + if respond_to?(method_name) + return send(method_name, target) + end + end + raise TypeError, "Can't dump #{target.class}" + end + + visitor_for(::String) do |o| + @stack.last.children << Nodes::Scalar.new(o) + end + + visitor_for(::Class) do |o| + raise TypeError, "can't dump anonymous class #{o.class}" + end + + visitor_for(::Hash) do |o| + map = Nodes::Mapping.new + @stack.last.children << map + @stack.push map + + o.each do |k,v| + k.accept self + v.accept self + end + + @stack.pop + end + end + end +end diff --git a/test/visitors/test_emitter.rb b/test/visitors/test_emitter.rb index b7b9110..e79a7b5 100644 --- a/test/visitors/test_emitter.rb +++ b/test/visitors/test_emitter.rb @@ -29,6 +29,22 @@ module Psych assert_equal @io.string, s.to_yaml end + def test_document_implicit_end + s = Nodes::Stream.new + doc = Nodes::Document.new + mapping = Nodes::Mapping.new + mapping.children << Nodes::Scalar.new('key') + mapping.children << Nodes::Scalar.new('value') + doc.children << mapping + s.children << doc + + @visitor.accept s + + assert_match(/key: value/, @io.string) + assert_equal @io.string, s.to_yaml + assert(/\.\.\./ !~ s.to_yaml) + end + def test_scalar s = Nodes::Stream.new doc = Nodes::Document.new @@ -58,6 +74,36 @@ module Psych assert_match(/- hello/, @io.string) assert_equal @io.string, s.to_yaml end + + def test_mapping + s = Nodes::Stream.new + doc = Nodes::Document.new + mapping = Nodes::Mapping.new + mapping.children << Nodes::Scalar.new('key') + mapping.children << Nodes::Scalar.new('value') + doc.children << mapping + s.children << doc + + @visitor.accept s + + assert_match(/key: value/, @io.string) + assert_equal @io.string, s.to_yaml + end + + def test_alias + s = Nodes::Stream.new + doc = Nodes::Document.new + mapping = Nodes::Mapping.new + mapping.children << Nodes::Scalar.new('key', 'A') + mapping.children << Nodes::Alias.new('A') + doc.children << mapping + s.children << doc + + @visitor.accept s + + assert_match(/&A key: \*A/, @io.string) + assert_equal @io.string, s.to_yaml + end end end end diff --git a/test/visitors/test_yast_builder.rb b/test/visitors/test_yast_builder.rb new file mode 100644 index 0000000..027764a --- /dev/null +++ b/test/visitors/test_yast_builder.rb @@ -0,0 +1,43 @@ +require 'minitest/autorun' +require 'psych' + +module Psych + module Visitors + class TestYASTBuilder < MiniTest::Unit::TestCase + def setup + @v = Visitors::YASTBuilder.new + end + def test_scalar + @v.accept 'foo' + + assert_equal 'foo', Psych.load(@v.tree.to_yaml) + assert_equal 'foo', Psych.load('foo'.to_yaml) + end + + def test_binary + string = [0, 123,22, 44, 9, 32, 34, 39].pack('C*') + assert_equal string, Psych.load(string.to_yaml) + end + + def test_anon_class + assert_raises(TypeError) do + @v.accept Class.new + end + + assert_raises(TypeError) do + Class.new.to_yaml + end + end + + def test_hash + assert_round_trip('a' => 'b') + end + + def assert_round_trip obj + @v.accept(obj) + assert_equal(obj, Psych.load(@v.tree.to_yaml)) + assert_equal(obj, Psych.load(obj.to_yaml)) + end + end + end +end |