summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/psych/emitter.c56
-rw-r--r--lib/psych.rb7
-rw-r--r--lib/psych/nodes/document.rb4
-rw-r--r--lib/psych/nodes/node.rb1
-rw-r--r--lib/psych/ruby.rb13
-rw-r--r--lib/psych/tree_builder.rb13
-rw-r--r--lib/psych/visitors.rb1
-rw-r--r--lib/psych/visitors/emitter.rb12
-rw-r--r--lib/psych/visitors/yast_builder.rb45
-rw-r--r--test/visitors/test_emitter.rb46
-rw-r--r--test/visitors/test_yast_builder.rb43
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