* 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:
tenderlove 2010-05-20 04:03:47 +00:00
Родитель f0b0dd2912
Коммит 5256d10265
9 изменённых файлов: 151 добавлений и 11 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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
Просмотреть файл

@ -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