[前][次][番号順一覧][スレッド一覧]

ruby-changes:15967

From: tenderlove <ko1@a...>
Date: Thu, 20 May 2010 13:05:05 +0900 (JST)
Subject: [ruby-changes:15967] Ruby:r27912 (trunk): * ext/psych/lib/psych/stream.rb: adding YAML streaming API for

tenderlove	2010-05-20 13:03:47 +0900 (Thu, 20 May 2010)

  New Revision: 27912

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=27912

  Log:
    * ext/psych/lib/psych/stream.rb: adding YAML streaming API for
      infinite length streams.
    * ext/psych/lib/psych.rb: refactoring for streaming API
    * ext/psych/lib/psych/{handler, stream, tree_builder}.rb: ditto

  Added files:
    trunk/ext/psych/lib/psych/stream.rb
    trunk/test/psych/test_stream.rb
  Modified files:
    trunk/ChangeLog
    trunk/ext/psych/lib/psych/handler.rb
    trunk/ext/psych/lib/psych/json/tree_builder.rb
    trunk/ext/psych/lib/psych/tree_builder.rb
    trunk/ext/psych/lib/psych/visitors/visitor.rb
    trunk/ext/psych/lib/psych/visitors/yaml_tree.rb
    trunk/ext/psych/lib/psych.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 27911)
+++ ChangeLog	(revision 27912)
@@ -1,3 +1,12 @@
+Thu May 20 12:59:49 2010  Aaron Patterson <aaron@t...>
+
+	* ext/psych/lib/psych/stream.rb: adding YAML streaming API for
+	  infinite length streams.
+
+	* ext/psych/lib/psych.rb: refactoring for streaming API
+
+	* ext/psych/lib/psych/{handler, stream, tree_builder}.rb: ditto
+
 Thu May 20 02:12:20 2010  Aaron Patterson <aaron@t...>
 
 	* ext/psych/emitter.c: output strings are automatically transcoded
Index: ext/psych/lib/psych/handler.rb
===================================================================
--- ext/psych/lib/psych/handler.rb	(revision 27911)
+++ ext/psych/lib/psych/handler.rb	(revision 27912)
@@ -211,5 +211,11 @@
     # Called when the YAML stream ends
     def end_stream
     end
+
+    ###
+    # Is this handler a streaming handler?
+    def streaming?
+      false
+    end
   end
 end
Index: ext/psych/lib/psych/visitors/visitor.rb
===================================================================
--- ext/psych/lib/psych/visitors/visitor.rb	(revision 27911)
+++ ext/psych/lib/psych/visitors/visitor.rb	(revision 27912)
@@ -1,6 +1,15 @@
 module Psych
   module Visitors
     class Visitor
+      attr_reader :started, :finished
+      alias :finished? :finished
+      alias :started? :started
+
+      def initialize
+        @started  = false
+        @finished = false
+      end
+
       def accept target
         case target
         when Psych::Nodes::Scalar   then visit_Psych_Nodes_Scalar target
Index: ext/psych/lib/psych/visitors/yaml_tree.rb
===================================================================
--- ext/psych/lib/psych/visitors/yaml_tree.rb	(revision 27911)
+++ ext/psych/lib/psych/visitors/yaml_tree.rb	(revision 27912)
@@ -10,12 +10,10 @@
     class YAMLTree < Psych::Visitors::Visitor
       def initialize options = {}, emitter = Psych::TreeBuilder.new
         super()
-        @emitter = emitter
-        @st      = {}
-        @ss      = ScalarScanner.new
+        @emitter  = emitter
+        @st       = {}
+        @ss       = ScalarScanner.new
 
-        @emitter.start_stream Psych::Nodes::Stream::UTF8
-
         @dispatch_cache = Hash.new do |h,klass|
           method = "visit_#{(klass.name || '').split('::').join('_')}"
 
@@ -27,15 +25,29 @@
         end
       end
 
+      def start encoding = Nodes::Stream::UTF8
+        @emitter.start_stream(encoding).tap do
+          @started = true
+        end
+      end
+
+      def finish
+        @emitter.end_stream.tap do
+          @finished = true
+        end
+      end
+
       def tree
-        @emitter.end_stream
+        finish unless finished?
       end
 
-      def << object
+      def push object
+        start unless started?
         @emitter.start_document [], [], false
         accept object
-        @emitter.end_document true
+        @emitter.end_document
       end
+      alias :<< :push
 
       def accept target
         # return any aliases we find
Index: ext/psych/lib/psych/tree_builder.rb
===================================================================
--- ext/psych/lib/psych/tree_builder.rb	(revision 27911)
+++ ext/psych/lib/psych/tree_builder.rb	(revision 27912)
@@ -57,7 +57,7 @@
     # and +implicit+ styling.
     #
     # See Psych::Handler#start_document
-    def end_document implicit_end
+    def end_document implicit_end = !streaming?
       @last.implicit_end = implicit_end
       pop
     end
Index: ext/psych/lib/psych/json/tree_builder.rb
===================================================================
--- ext/psych/lib/psych/json/tree_builder.rb	(revision 27911)
+++ ext/psych/lib/psych/json/tree_builder.rb	(revision 27912)
@@ -8,7 +8,7 @@
         super(version, tag_directives, true)
       end
 
-      def end_document implicit_end
+      def end_document implicit_end = !streaming?
         super(true)
       end
 
Index: ext/psych/lib/psych/stream.rb
===================================================================
--- ext/psych/lib/psych/stream.rb	(revision 0)
+++ ext/psych/lib/psych/stream.rb	(revision 27912)
@@ -0,0 +1,53 @@
+module Psych
+  ###
+  # Psych::Stream is a streaming YAML emitter.  It will not buffer your YAML,
+  # but send it straight to an IO.
+  #
+  # Here is an example use:
+  #
+  #   stream = Psych::Stream.new($stdout)
+  #   stream.start
+  #   stream.push({:foo => 'bar'})
+  #   stream.finish
+  #
+  # YAML will be immediately emitted to $stdout with no buffering.
+  #
+  # Psych::Stream#start will take a block and ensure that Psych::Stream#finish
+  # is called, so you can do this form:
+  #
+  #   stream = Psych::Stream.new($stdout)
+  #   stream.start do |em|
+  #     em.push(:foo => 'bar')
+  #   end
+  #
+  class Stream < Psych::Visitors::YAMLTree
+    class Emitter < Psych::Emitter # :nodoc:
+      def end_document implicit_end = !streaming?
+        super
+      end
+
+      def streaming?
+        true
+      end
+    end
+
+    ###
+    # Create a new streaming emitter.  Emitter will print to +io+.  See
+    # Psych::Stream for an example.
+    def initialize io
+      super({}, Emitter.new(io))
+    end
+
+    ###
+    # Start streaming using +encoding+
+    def start encoding = Nodes::Stream::UTF8
+      super.tap { yield self if block_given?  }
+    ensure
+      finish if block_given?
+    end
+
+    private
+    def register target, obj
+    end
+  end
+end
Index: ext/psych/lib/psych.rb
===================================================================
--- ext/psych/lib/psych.rb	(revision 27911)
+++ ext/psych/lib/psych.rb	(revision 27912)
@@ -97,6 +97,8 @@
   class Exception < RuntimeError
   end
 
+  autoload :Stream, 'psych/stream'
+
   ###
   # Load +yaml+ in to a Ruby data structure.  If multiple documents are
   # provided, the object contained in the first document will be returned.
Index: test/psych/test_stream.rb
===================================================================
--- test/psych/test_stream.rb	(revision 0)
+++ test/psych/test_stream.rb	(revision 27912)
@@ -0,0 +1,49 @@
+require_relative 'helper'
+
+module Psych
+  class TestStream < TestCase
+    def test_explicit_documents
+      io     = StringIO.new
+      stream = Psych::Stream.new(io)
+      stream.start
+      stream.push({ 'foo' => 'bar' })
+
+      assert !stream.finished?, 'stream not finished'
+      stream.finish
+      assert stream.finished?, 'stream finished'
+
+      assert_match(/^---/, io.string)
+      assert_match(/\.\.\.$/, io.string)
+    end
+
+    def test_start_takes_block
+      io     = StringIO.new
+      stream = Psych::Stream.new(io)
+      stream.start do |emitter|
+        emitter.push({ 'foo' => 'bar' })
+      end
+
+      assert stream.finished?, 'stream finished'
+      assert_match(/^---/, io.string)
+      assert_match(/\.\.\.$/, io.string)
+    end
+
+    def test_no_backreferences
+      io     = StringIO.new
+      stream = Psych::Stream.new(io)
+      stream.start do |emitter|
+        x = { 'foo' => 'bar' }
+        emitter.push x
+        emitter.push x
+      end
+
+      assert stream.finished?, 'stream finished'
+      assert_match(/^---/, io.string)
+      assert_match(/\.\.\.$/, io.string)
+      assert_equal 2, io.string.scan('---').length
+      assert_equal 2, io.string.scan('...').length
+      assert_equal 2, io.string.scan('foo').length
+      assert_equal 2, io.string.scan('bar').length
+    end
+  end
+end

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]