mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* 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 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@27912 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
f0b0dd2912
commit
5256d10265
9 changed files with 151 additions and 11 deletions
|
@ -1,3 +1,12 @@
|
|||
Thu May 20 12:59:49 2010 Aaron Patterson <aaron@tenderlovemaking.com>
|
||||
|
||||
* 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@tenderlovemaking.com>
|
||||
|
||||
* ext/psych/emitter.c: output strings are automatically transcoded
|
||||
|
|
|
@ -97,6 +97,8 @@ module Psych
|
|||
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.
|
||||
|
|
|
@ -211,5 +211,11 @@ module Psych
|
|||
# Called when the YAML stream ends
|
||||
def end_stream
|
||||
end
|
||||
|
||||
###
|
||||
# Is this handler a streaming handler?
|
||||
def streaming?
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ module Psych
|
|||
super(version, tag_directives, true)
|
||||
end
|
||||
|
||||
def end_document implicit_end
|
||||
def end_document implicit_end = !streaming?
|
||||
super(true)
|
||||
end
|
||||
|
||||
|
|
53
ext/psych/lib/psych/stream.rb
Normal file
53
ext/psych/lib/psych/stream.rb
Normal file
|
@ -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
|
|
@ -57,7 +57,7 @@ module Psych
|
|||
# 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -10,11 +10,9 @@ module Psych
|
|||
class YAMLTree < Psych::Visitors::Visitor
|
||||
def initialize options = {}, emitter = Psych::TreeBuilder.new
|
||||
super()
|
||||
@emitter = emitter
|
||||
@st = {}
|
||||
@ss = ScalarScanner.new
|
||||
|
||||
@emitter.start_stream Psych::Nodes::Stream::UTF8
|
||||
@emitter = emitter
|
||||
@st = {}
|
||||
@ss = ScalarScanner.new
|
||||
|
||||
@dispatch_cache = Hash.new do |h,klass|
|
||||
method = "visit_#{(klass.name || '').split('::').join('_')}"
|
||||
|
@ -27,15 +25,29 @@ module Psych
|
|||
end
|
||||
end
|
||||
|
||||
def tree
|
||||
@emitter.end_stream
|
||||
def start encoding = Nodes::Stream::UTF8
|
||||
@emitter.start_stream(encoding).tap do
|
||||
@started = true
|
||||
end
|
||||
end
|
||||
|
||||
def << object
|
||||
def finish
|
||||
@emitter.end_stream.tap do
|
||||
@finished = true
|
||||
end
|
||||
end
|
||||
|
||||
def tree
|
||||
finish unless finished?
|
||||
end
|
||||
|
||||
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
|
||||
|
|
49
test/psych/test_stream.rb
Normal file
49
test/psych/test_stream.rb
Normal file
|
@ -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
|
Loading…
Reference in a new issue