require 'json' class Counter def initialize @count = 0 end def increment @count += 1 end def clear @count = 0 end def count @count end end class Stream def initialize(out, buf_size = 10, counter = nil, root = self) counter = Counter.new if counter.nil? @buf_size = buf_size @out = out @counter = counter @root = root @data = {} @substreams = {} @first = true end def start '[' end def start! @out.write(start) end def flush if @root == self r = to_json clear @out.write(r) @out.flush return r end end def stop ']' end def stop! flush @out.write(stop) end def clear @counter.clear @data = {} @substreams.values.each do | substream | substream.clear end end def output(stream_name, obj) if not @data.keys.include? stream_name @data[stream_name] = [] end @data[stream_name].push(obj) @counter.increment if @counter.count >= @buf_size @root.flush end end def new_stream(name) s = Stream.new(@out, @buf_size, @counter, @root) @substreams[name] = s s end def empty? empty = @data == {} @substreams.values.each do |substream| empty = empty and substream.empty? end empty end def add_comma_if_not_first(s) if @first @first = false else s.push(',') end end def to_json s = [] if not @data.empty? add_comma_if_not_first(s) s.push(@data.to_json) end @substreams.each do |key, stream| if not stream.empty? add_comma_if_not_first(s) s.push("{#{key}:") s.push(stream.start) s.push(stream.to_json) s.push(stream.stop) s.push('}') end end s.join '' end end def main s = Stream.new $stdout, 4 s.start! 7.times do |i| proc = {:pid => i + 1, :name => "init"} s.output(:things, proc) end q = s.new_stream 'q' q.output(:test, 'potato') 10.times do |i| proc = {:pid => i + 1, :name => "init"} s.output(:processes, proc) end q.output(:test, "salad") q.output(:test, "rocks") s.flush s.stop! end main