Skip to content

Commit

Permalink
Add --stream flag to print the event stream as it is seen
Browse files Browse the repository at this point in the history
* You may now provide an `event_handler` to toplevel
* `SeeingIsBelieving`, it is expected to respond to `#call` and `#return_value`
  `SeeingIsBelieving.call` will return the handler's `return_value`.
  This is to avoid breaking the toplevel interface,
  as the default handler aggregates the events into a result, and returns that,
  allowing the return value to still be a Result, as before.
* Events have an `#event_name` method, which returns their class's `event_name`
* Handlers no longer do this silly `#to_proc` thing, they only need a `#call` method.
* `EmitJsonEventsHandler` records `exitstatus` and `has_exception?` to
* facilitate the needs of `Binary` without having to stack an `UpdateResultHandler` in there.
  That would be mildly annoying, and potentially expensive (one reason you might stream
  is to avoid building up a big in-memory object)
* The recorded result is no longer part of the debugging output
  as there is not currently an interface to provide the debugger to an event stream.
  • Loading branch information
JoshCheek committed Jan 16, 2015
1 parent ed68953 commit 6893b81
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 33 deletions.
30 changes: 28 additions & 2 deletions features/flags.feature
Original file line number Diff line number Diff line change
Expand Up @@ -436,8 +436,7 @@ Feature: Using flags
And the exit status is 0
And stderr includes "REWRITTEN PROGRAM:"
And stderr includes "$SiB"
And stderr includes "RESULT:"
And stderr includes "@results="
And stderr includes "EVENTS:"
And stderr includes "OUTPUT:"
And stderr includes:
"""
Expand Down Expand Up @@ -481,3 +480,30 @@ Feature: Using flags
}
"""

Scenario: --stream prints events from the event stream as they are seen
Given the file "record_event_stream.rb" "3.times { |i| p i }"
When I run "seeing_is_believing record_event_stream.rb --stream"
Then stderr is empty
And the exit status is 0
And stdout is:
"""
{"event":"stdout","value":"0\n"}
{"event":"stdout","value":"1\n"}
{"event":"stdout","value":"2\n"}
{"event":"ruby_version","value":"2.1.1"}
{"event":"sib_version","value":"3.0.0.beta.4"}
{"event":"filename","value":"record_event_stream.rb"}
{"event":"max_line_captures","value":Infinity}
{"event":"num_lines","value":1}
{"event":"line_result","type":"inspect","line_number":1,"inspected":"3"}
{"event":"event_stream_closed","side":"producer"}
{"event":"stdout_closed","side":"producer"}
{"event":"stderr_closed","side":"producer"}
{"event":"exitstatus","value":0}
{"event":"finished"}
"""

Scenario: --stream respects the exit status
When I run "seeing_is_believing -ie 'exit 12' --stream"
Then stderr is empty
And the exit status is 12
14 changes: 7 additions & 7 deletions lib/seeing_is_believing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

class SeeingIsBelieving
class Options < HashStruct
predicate(:event_handler) { EventStream::UpdateResultHandler.new Result.new }
attribute(:filename) { nil }
attribute(:encoding) { nil }
attribute(:stdin) { "" }
Expand Down Expand Up @@ -40,11 +41,12 @@ def call
options.filename,
options.max_line_captures

options.debugger.context("REWRITTEN PROGRAM") { new_program }
event_handler = options.event_handler
if options.debugger.enabled?
options.debugger.context("REWRITTEN PROGRAM") { new_program }
event_handler = EventStream::DebuggingHandler.new options.debugger, event_handler
end

result = Result.new
event_handler = EventStream::UpdateResultHandler.new(result)
event_handler = EventStream::DebuggingHandler.new(options.debugger, event_handler)
EvaluateByMovingFiles.call \
new_program,
options.filename,
Expand All @@ -55,9 +57,7 @@ def call
encoding: options.encoding,
timeout_seconds: options.timeout_seconds

options.debugger.context("RESULT") { result.inspect }

result
event_handler.return_value
}
end
end
8 changes: 4 additions & 4 deletions lib/seeing_is_believing/binary.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module Binary
NONDISPLAYABLE_ERROR_STATUS = 2 # e.g. SiB was invoked incorrectly

def self.call(argv, stdin, stdout, stderr)
config = Config.new.parse_args(argv, stderr).finalize(stdin, File)
config = Config.new.parse_args(argv, stderr).finalize(stdin, stdout, File) # TODO: move debugger initialization to finalize, so that parsing only takes argv
engine = Engine.new config

if config.print_help?
Expand Down Expand Up @@ -48,9 +48,9 @@ def self.call(argv, stdin, stdout, stderr)
require 'json'
stdout.puts JSON.dump(engine.results.as_json)
return SUCCESS_STATUS
end

if config.debug?
elsif config.print_event_stream?
# no op, the event stream handler has been printing it all along
elsif config.debug?
config.debugger.context("OUTPUT") { engine.annotated_body }
else
stdout.print engine.annotated_body
Expand Down
35 changes: 24 additions & 11 deletions lib/seeing_is_believing/binary/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def to_s
predicate(:print_version) { false }
predicate(:print_cleaned) { false }
predicate(:print_help) { false }
predicate(:print_event_stream) { false }
predicate(:result_as_json) { false }
predicate(:inherit_exitstatus) { false }
predicate(:debug) { false }
Expand Down Expand Up @@ -101,6 +102,9 @@ def parse_args(args, debug_stream)
when '-j', '--json'
self.result_as_json = true

when '--stream'
self.print_event_stream = true

when '-h', '--help'
self.print_help = true
self.help_screen = Binary.help_screen(markers)
Expand Down Expand Up @@ -170,14 +174,6 @@ def parse_args(args, debug_stream)
end
end

when /\A-K(.+)/
self.lib_options.encoding = $1

when '-K', '--encoding'
next_arg.call arg, "an encoding" do |encoding|
self.lib_options.encoding = encoding
end

when '--shebang'
executable = args.shift
if executable
Expand All @@ -187,6 +183,14 @@ def parse_args(args, debug_stream)
saw_deprecated.call "SiB now uses the Ruby it was invoked with", arg
end

when /\A-K(.+)/
self.lib_options.encoding = $1

when '-K', '--encoding'
next_arg.call arg, "an encoding" do |encoding|
self.lib_options.encoding = encoding
end

when /^(-.|--.*)$/
add_error("#{arg} is not an option, see the help screen (-h) for a list of options")

Expand All @@ -199,11 +203,14 @@ def parse_args(args, debug_stream)
end

filenames.size > 1 &&
add_error("Can only have one filename but found #{filenames.map(&:inspect).join ', '}")
add_error("can only have one filename but found #{filenames.map(&:inspect).join ', '}")

result_as_json && annotator == AnnotateMarkedLines &&
result_as_json? && annotator == AnnotateMarkedLines &&
add_error("SiB does not currently support output with both json and xmpfilter... maybe v4 :)")

print_event_stream? && (result_as_json? || annotator == AnnotateMarkedLines) &&
add_error("can only have one output format, --stream is not compatible with --json, -x, and --xmpfilter-style")

self.filename = filenames.first
self.lib_options.filename = as || filename
self.lib_options.rewrite_code = annotator.expression_wrapper(markers)
Expand All @@ -213,7 +220,7 @@ def parse_args(args, debug_stream)
self
end

def finalize(stdin, file_class)
def finalize(stdin, stdout, file_class)
if filename && body
add_error("Cannot give a program body and a filename to get the program body from.")
elsif filename && file_class.exist?(filename)
Expand All @@ -228,6 +235,12 @@ def finalize(stdin, file_class)
else
self.body = stdin.read
end

if print_event_stream?
require 'seeing_is_believing/event_stream/emit_json_events_handler'
lib_options.event_handler = EventStream::EmitJsonEventsHandler.new(stdout)
end

self
end

Expand Down
4 changes: 4 additions & 0 deletions lib/seeing_is_believing/event_stream/debugging_handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ def ==(other)
other.kind_of?(self.class) && other.handler == handler && other.debugger == debugger
end

def return_value
@handler.return_value
end

protected

attr_reader :debugger, :handler
Expand Down
36 changes: 32 additions & 4 deletions lib/seeing_is_believing/event_stream/emit_json_events_handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,46 @@ class EmitJsonEventsHandler
attr_reader :stream

def initialize(stream)
@flush = true if stream.respond_to? :flush
@stream = stream
@flush = true if stream.respond_to? :flush
@stream = stream
@has_exception = false
@exitstatus = :not_yet_seen
end

def call(event)
write_event event
record_outcome event
end

def return_value
self
end

def has_exception?
true
end

def exitstatus
@exitstatus
end

def ==(other)
other.kind_of?(self.class) && other.stream == stream
end

private

def write_event(event)
@stream << JSON.dump(event.as_json)
@stream << "\n"
@stream.flush if @flush
end

def ==(other)
other.kind_of?(self.class) && other.stream == stream
def record_outcome(event)
case event
when Events::Exception then @has_exception = true
when Events::Exitstatus then @exitstatus = event.value
end
end
end
end
Expand Down
4 changes: 4 additions & 0 deletions lib/seeing_is_believing/event_stream/events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ def self.event_name
raise NotImplementedError, "Subclass should have defined this!"
end

def event_name
self.class.event_name
end

def as_json
{event: self.class.event_name}.merge(to_h)
end
Expand Down
12 changes: 8 additions & 4 deletions lib/seeing_is_believing/event_stream/update_result_handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ def initialize(result)
@result = result
end

def ==(other)
other.kind_of?(self.class) # this is dumb, it's b/c Result doesn't correctly implement ==
end

def call(event)
case event
when LineResult then result.record_result(event.type, event.line_number, event.inspected)
Expand All @@ -37,6 +33,14 @@ def call(event)
else raise "Unknown event: #{event.inspect}"
end
end

def ==(other)
other.kind_of?(self.class) # this is dumb, it's b/c Result doesn't correctly implement ==
end

def return_value
result
end
end
end
end
35 changes: 34 additions & 1 deletion spec/binary/config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -511,10 +511,31 @@ def assert_deprecated(flag, *args)
end
end

describe 'print_event_stream?' do
it 'print_event_stream? is false by default' do
expect(parse([]).print_event_stream?).to eq false
end
it 'print_event_stream? can be turned on with --stream' do
expect(parse(['--stream']).print_event_stream?).to eq true
end
it 'adds an error if --stream is used with --json' do
expect(parse(['--stream'])).to_not have_error '--stream'
expect(parse(['--stream', '--json'])).to have_error '--stream'
expect(parse(['--json', '--stream'])).to have_error '--stream'
end
it 'adds an error if --stream is used with -x or --xmpfilter-style' do
expect(parse(['--stream'])).to_not have_error '--stream'
expect(parse(['--stream', '-x'])).to have_error '--stream'
expect(parse(['-x', '--stream'])).to have_error '--stream'
expect(parse(['--xmpfilter-style', '--stream'])).to have_error '--stream'
end
end


describe '.finalize' do
let(:stdin_data) { 'stdin data' }
let(:stdin) { object_double $stdin, read: stdin_data }
let(:stdout) { object_double $stdout }

let(:file_class) { class_double File }
let(:nonexisting_filename) { 'badfilename' }
Expand All @@ -528,7 +549,7 @@ def assert_deprecated(flag, *args)
end

def call(attrs={})
described_class.new(attrs).finalize(stdin, file_class)
described_class.new(attrs).finalize(stdin, stdout, file_class)
end

describe 'additional errors' do
Expand Down Expand Up @@ -602,5 +623,17 @@ def call(attrs={})
expect(call(filename: nonexisting_filename).lib_options.stdin).to eq default
end
end

describe 'lib_options.event_handler' do
it 'is an UpdateResultHandler when print_event_stream? is false' do
expect(call(print_event_stream: false).lib_options.event_handler)
.to be_an_instance_of SeeingIsBelieving::EventStream::UpdateResultHandler
end
it 'is an EmitJsonEventsHandler to stdout when print_event_stream? is true' do
handler = call(print_event_stream: true).lib_options.event_handler
expect(handler).to be_an_instance_of SeeingIsBelieving::EventStream::EmitJsonEventsHandler
expect(handler.stream).to eq stdout
end
end
end
end

0 comments on commit 6893b81

Please sign in to comment.