* 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
@ -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>
|
Thu May 20 02:12:20 2010 Aaron Patterson <aaron@tenderlovemaking.com>
|
||||||
|
|
||||||
* ext/psych/emitter.c: output strings are automatically transcoded
|
* ext/psych/emitter.c: output strings are automatically transcoded
|
||||||
|
@ -97,6 +97,8 @@ module Psych
|
|||||||
class Exception < RuntimeError
|
class Exception < RuntimeError
|
||||||
end
|
end
|
||||||
|
|
||||||
|
autoload :Stream, 'psych/stream'
|
||||||
|
|
||||||
###
|
###
|
||||||
# Load +yaml+ in to a Ruby data structure. If multiple documents are
|
# Load +yaml+ in to a Ruby data structure. If multiple documents are
|
||||||
# provided, the object contained in the first document will be returned.
|
# provided, the object contained in the first document will be returned.
|
||||||
|
@ -211,5 +211,11 @@ module Psych
|
|||||||
# Called when the YAML stream ends
|
# Called when the YAML stream ends
|
||||||
def end_stream
|
def end_stream
|
||||||
end
|
end
|
||||||
|
|
||||||
|
###
|
||||||
|
# Is this handler a streaming handler?
|
||||||
|
def streaming?
|
||||||
|
false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -8,7 +8,7 @@ module Psych
|
|||||||
super(version, tag_directives, true)
|
super(version, tag_directives, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
def end_document implicit_end
|
def end_document implicit_end = !streaming?
|
||||||
super(true)
|
super(true)
|
||||||
end
|
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.
|
# and +implicit+ styling.
|
||||||
#
|
#
|
||||||
# See Psych::Handler#start_document
|
# See Psych::Handler#start_document
|
||||||
def end_document implicit_end
|
def end_document implicit_end = !streaming?
|
||||||
@last.implicit_end = implicit_end
|
@last.implicit_end = implicit_end
|
||||||
pop
|
pop
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,15 @@
|
|||||||
module Psych
|
module Psych
|
||||||
module Visitors
|
module Visitors
|
||||||
class Visitor
|
class Visitor
|
||||||
|
attr_reader :started, :finished
|
||||||
|
alias :finished? :finished
|
||||||
|
alias :started? :started
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@started = false
|
||||||
|
@finished = false
|
||||||
|
end
|
||||||
|
|
||||||
def accept target
|
def accept target
|
||||||
case target
|
case target
|
||||||
when Psych::Nodes::Scalar then visit_Psych_Nodes_Scalar target
|
when Psych::Nodes::Scalar then visit_Psych_Nodes_Scalar target
|
||||||
|
@ -10,11 +10,9 @@ module Psych
|
|||||||
class YAMLTree < Psych::Visitors::Visitor
|
class YAMLTree < Psych::Visitors::Visitor
|
||||||
def initialize options = {}, emitter = Psych::TreeBuilder.new
|
def initialize options = {}, emitter = Psych::TreeBuilder.new
|
||||||
super()
|
super()
|
||||||
@emitter = emitter
|
@emitter = emitter
|
||||||
@st = {}
|
@st = {}
|
||||||
@ss = ScalarScanner.new
|
@ss = ScalarScanner.new
|
||||||
|
|
||||||
@emitter.start_stream Psych::Nodes::Stream::UTF8
|
|
||||||
|
|
||||||
@dispatch_cache = Hash.new do |h,klass|
|
@dispatch_cache = Hash.new do |h,klass|
|
||||||
method = "visit_#{(klass.name || '').split('::').join('_')}"
|
method = "visit_#{(klass.name || '').split('::').join('_')}"
|
||||||
@ -27,15 +25,29 @@ module Psych
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def tree
|
def start encoding = Nodes::Stream::UTF8
|
||||||
@emitter.end_stream
|
@emitter.start_stream(encoding).tap do
|
||||||
|
@started = true
|
||||||
|
end
|
||||||
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
|
@emitter.start_document [], [], false
|
||||||
accept object
|
accept object
|
||||||
@emitter.end_document true
|
@emitter.end_document
|
||||||
end
|
end
|
||||||
|
alias :<< :push
|
||||||
|
|
||||||
def accept target
|
def accept target
|
||||||
# return any aliases we find
|
# 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…
x
Reference in New Issue
Block a user