seeing_is_believing 3.0.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ class String
2
+ def pretty_print(pp)
3
+ pp.text inspect.gsub(/\\n(?!")/, '\n" +'+"\n"+'"')
4
+ end
5
+ end
@@ -56,15 +56,7 @@ class SeeingIsBelieving
56
56
  rescue EncodingError
57
57
  str = str.force_encoding(Encoding::UTF_8)
58
58
  end
59
- return str.scrub('�') if str.respond_to? :scrub
60
- # basically reimplement scrub, b/c it's not implemented on 1.9.3
61
- str.each_char.inject("") do |new_str, char|
62
- if char.valid_encoding?
63
- new_str << char
64
- else
65
- new_str << '�'
66
- end
67
- end
59
+ str.scrub '�'
68
60
  end
69
61
 
70
62
  def initialize(streams)
@@ -74,39 +66,40 @@ class SeeingIsBelieving
74
66
  event_stream = streams.fetch :events
75
67
  stdout_stream = streams.fetch :stdout
76
68
  stderr_stream = streams.fetch :stderr
77
-
78
- Thread.new do
79
- begin
80
- stdout_stream.each_line { |line| queue << Events::Stdout.new(value: line) }
81
- queue << Events::StdoutClosed.new(side: :producer)
82
- rescue IOError
83
- queue << Events::StdoutClosed.new(side: :consumer)
84
- ensure
85
- queue << lambda { finish_criteria.stdout_thread_finished! }
86
- end
87
- end
88
-
89
- Thread.new do
90
- begin
91
- stderr_stream.each_line { |line| queue << Events::Stderr.new(value: line) }
92
- queue << Events::StderrClosed.new(side: :producer)
93
- rescue IOError
94
- queue << Events::StderrClosed.new(side: :consumer)
95
- ensure
96
- queue << lambda { finish_criteria.stderr_thread_finished! }
97
- end
98
- end
99
-
100
- Thread.new do
101
- begin
102
- event_stream.each_line { |line| queue << line }
103
- queue << Events::EventStreamClosed.new(side: :producer)
104
- rescue IOError
105
- queue << Events::EventStreamClosed.new(side: :consumer)
106
- ensure
107
- queue << lambda { finish_criteria.event_thread_finished! }
108
- end
109
- end
69
+ self.threads = [
70
+ Thread.new do
71
+ begin
72
+ stdout_stream.each_line { |line| queue << Events::Stdout.new(value: line) }
73
+ queue << Events::StdoutClosed.new(side: :producer)
74
+ rescue IOError
75
+ queue << Events::StdoutClosed.new(side: :consumer)
76
+ ensure
77
+ queue << lambda { finish_criteria.stdout_thread_finished! }
78
+ end
79
+ end,
80
+
81
+ Thread.new do
82
+ begin
83
+ stderr_stream.each_line { |line| queue << Events::Stderr.new(value: line) }
84
+ queue << Events::StderrClosed.new(side: :producer)
85
+ rescue IOError
86
+ queue << Events::StderrClosed.new(side: :consumer)
87
+ ensure
88
+ queue << lambda { finish_criteria.stderr_thread_finished! }
89
+ end
90
+ end,
91
+
92
+ Thread.new do
93
+ begin
94
+ event_stream.each_line { |line| queue << line }
95
+ queue << Events::EventStreamClosed.new(side: :producer)
96
+ rescue IOError
97
+ queue << Events::EventStreamClosed.new(side: :consumer)
98
+ ensure
99
+ queue << lambda { finish_criteria.event_thread_finished! }
100
+ end
101
+ end,
102
+ ]
110
103
  end
111
104
 
112
105
  def call(n=1)
@@ -135,10 +128,13 @@ class SeeingIsBelieving
135
128
  }
136
129
  end
137
130
 
131
+ def join
132
+ threads.each(&:join)
133
+ end
138
134
 
139
135
  private
140
136
 
141
- attr_accessor :queue, :finish_criteria
137
+ attr_accessor :queue, :finish_criteria, :threads
142
138
 
143
139
  def next_event
144
140
  raise NoMoreEvents if @finished
@@ -1,29 +1,27 @@
1
- require 'seeing_is_believing/event_stream/events'
2
1
  require 'seeing_is_believing/safe'
3
- require 'thread'
2
+ require 'seeing_is_believing/event_stream/events'
3
+ require 'thread' # <-- do we still need this?
4
+
5
+ using SeeingIsBelieving::Safe
4
6
 
5
7
  class SeeingIsBelieving
6
8
  module EventStream
7
9
  class Producer
8
10
  module NullQueue
9
11
  extend self
10
- def <<(*) end
11
- def shift() end
12
- # TODO: this one doesn't have clear, but we can call that on the real one.
13
- # find a way to test this situation?
12
+ Queue.instance_methods.each do |name|
13
+ define_method(name) { |*| }
14
+ end
14
15
  end
15
16
 
16
17
  attr_accessor :max_line_captures, :filename
17
18
 
18
19
  def initialize(resultstream)
19
- resultstream = Safe::Stream[resultstream]
20
20
  self.filename = nil
21
21
  self.max_line_captures = Float::INFINITY
22
22
  self.recorded_results = []
23
- self.queue = Safe::Queue[Queue.new]
24
- self.producer_thread = Safe::Thread[
25
- build_producer_thread(resultstream)
26
- ]
23
+ self.queue = Queue.new
24
+ self.producer_thread = build_producer_thread(resultstream)
27
25
  end
28
26
 
29
27
  attr_reader :version
@@ -45,7 +43,7 @@ class SeeingIsBelieving
45
43
  StackErrors = [SystemStackError]
46
44
  StackErrors << Java::JavaLang::StackOverflowError if defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java'
47
45
  def record_result(type, line_number, value)
48
- counts = recorded_results[line_number] ||= Safe::Hash.new(0)
46
+ counts = recorded_results[line_number] ||= Hash.new(0)
49
47
  count = counts[type]
50
48
  recorded_results[line_number][type] = count.next
51
49
  if count < max_line_captures
@@ -64,33 +62,30 @@ class SeeingIsBelieving
64
62
  rescue Exception
65
63
  inspected = "#<no inspect available>"
66
64
  end
67
- queue << "result #{line_number} #{type} #{to_string_token inspected}"
65
+ queue << "result #{line_number.to_s} #{type.to_s} #{to_string_token inspected}"
68
66
  elsif count == max_line_captures
69
- queue << "maxed_result #{line_number} #{type}"
67
+ queue << "maxed_result #{line_number.to_s} #{type.to_s}"
70
68
  end
71
69
  value
72
70
  end
73
71
 
74
72
  # records the exception, returns the exitstatus for that exception
75
73
  def record_exception(line_number, exception)
76
- return exception.status if SystemExit === exception
77
- exception = Safe::Exception[exception]
74
+ return exception.status if SystemExit === exception # TODO === is not in the list
78
75
  if !line_number && filename
79
- begin line_number = exception.backtrace.grep(/#{filename}/).first[/:\d+/][1..-1].to_i
76
+ begin line_number = exception.backtrace.grep(/#{filename.to_s}/).first[/:\d+/][1..-1].to_i
80
77
  rescue NoMethodError
81
78
  end
82
79
  end
83
80
  line_number ||= -1
84
- queue << Safe::Array[[
81
+ queue << [
85
82
  "exception",
86
- Safe::Fixnum[line_number],
83
+ line_number.to_s,
87
84
  to_string_token(exception.class.name),
88
85
  to_string_token(exception.message),
89
- Safe::Fixnum[
90
- Safe::Array[exception.backtrace].size
91
- ],
92
- *Safe::Array[exception.backtrace].map { |line| to_string_token line }
93
- ]].join(" ")
86
+ exception.backtrace.size.to_s,
87
+ *exception.backtrace.map { |line| to_string_token line }
88
+ ].join(" ")
94
89
  1 # exit status
95
90
  end
96
91
 
@@ -108,7 +103,7 @@ class SeeingIsBelieving
108
103
  end
109
104
 
110
105
  def finish!
111
- queue << :break # note that consumer will continue reading until stream is closed
106
+ queue << :break # note that consumer will continue reading until stream is closed, which is not the responsibility of the producer
112
107
  producer_thread.join
113
108
  end
114
109
 
@@ -118,17 +113,17 @@ class SeeingIsBelieving
118
113
 
119
114
  # for a consideration of many different ways of doing this, see 5633064
120
115
  def to_string_token(string)
121
- Safe::Array[[Safe::Marshal.dump(Safe::String[string].to_s)]].pack('m0')
116
+ [Marshal.dump(string.to_s)].pack('m0')
122
117
  end
123
118
 
124
119
  def build_producer_thread(resultstream)
125
120
  ::Thread.new {
126
- Safe::Thread.current.abort_on_exception = true
121
+ Thread.current.abort_on_exception = true
127
122
  begin
128
123
  resultstream.sync = true
129
124
  loop do
130
125
  to_publish = queue.shift
131
- break if Safe::Symbol[:break] == to_publish
126
+ break if :break == to_publish
132
127
  resultstream << (to_publish << "\n")
133
128
  end
134
129
  rescue IOError, Errno::EPIPE
@@ -147,9 +142,7 @@ class SeeingIsBelieving
147
142
  loop { break if queue.shift == :fork }
148
143
 
149
144
  # recreate the thread since forking in Ruby kills threads
150
- @producer_thread = Safe::Thread[
151
- build_producer_thread(resultstream)
152
- ]
145
+ @producer_thread = build_producer_thread(resultstream)
153
146
  end
154
147
 
155
148
  end
@@ -1,43 +1,97 @@
1
+ # require this before anything else, b/c it expects the world to be sane when it is loaded
1
2
  class SeeingIsBelieving
2
3
  module Safe
3
- def self.build(klass, *method_names)
4
- options = {}
5
- options = method_names.pop if method_names.last.kind_of? ::Hash
6
-
7
- Class.new do
8
- class << self
9
- alias [] new
10
- end
11
-
12
- define_method :initialize do |instance|
13
- @_instance = instance
14
- end
15
-
16
- methods = method_names.map { |name| [name, klass.instance_method(name)] }
17
- methods.each do |name, method|
18
- define_method(name) do |*args, &block|
19
- method.bind(@_instance).call(*args, &block)
20
- end
21
- end
22
-
23
- singleton_methods = options.fetch(:class, []).map { |name| [name, klass.method(name)] }
24
- singleton_methods.each do |name, method|
25
- define_singleton_method name do |*args, &block|
26
- method.call(*args, &block)
27
- end
28
- end
29
- end
30
- end
31
-
32
- Queue = build ::Queue, :<<, :shift, :clear
33
- Stream = build ::IO, :sync=, :<<, :flush, :close
34
- Symbol = build ::Symbol, :==, class: [:define_method]
35
- String = build ::String, :to_s
36
- Fixnum = build ::Fixnum, :to_s
37
- Array = build ::Array, :pack, :map, :size, :join
38
- Hash = build ::Hash, :[], :[]=, class: [:new]
39
- Marshal = build ::Marshal, class: [:dump]
40
- Exception = build ::Exception, :message, :backtrace, :class, class: [:define_method]
41
- Thread = build ::Thread, :join, class: [:current]
4
+ refine Class do
5
+ alias === ===
6
+ end
7
+
8
+ refine Queue do
9
+ alias << <<
10
+ alias shift shift
11
+ alias clear clear
12
+ end
13
+
14
+ refine IO do
15
+ alias sync= sync=
16
+ alias << <<
17
+ alias flush flush
18
+ alias close close
19
+ end
20
+
21
+ refine Symbol do
22
+ alias == ==
23
+ alias to_s to_s
24
+ alias inspect inspect
25
+ end
26
+
27
+ refine Symbol.singleton_class do
28
+ alias define_method define_method
29
+ alias class_eval class_eval
30
+ end
31
+
32
+ refine String do
33
+ alias == ==
34
+ alias to_s to_s
35
+ alias to_str to_str
36
+ end
37
+
38
+ refine Fixnum do
39
+ alias to_s to_s
40
+ alias next next
41
+ alias < <
42
+ end
43
+
44
+ refine Array do
45
+ alias pack pack
46
+ alias map map
47
+ alias size size
48
+ alias join join
49
+ alias [] []
50
+ alias []= []=
51
+ end
52
+
53
+ refine Hash do
54
+ alias [] []
55
+ alias []= []=
56
+ end
57
+
58
+ refine Hash.singleton_class do
59
+ alias new new
60
+ end
61
+
62
+ refine Marshal.singleton_class do
63
+ alias dump dump
64
+ end
65
+
66
+ refine Exception do
67
+ alias message message
68
+ alias backtrace backtrace
69
+ alias class class
70
+ end
71
+
72
+ refine Exception.singleton_class do
73
+ alias define_method define_method
74
+ alias class_eval class_eval
75
+ end
76
+
77
+ refine Thread do
78
+ alias join join
79
+ end
80
+
81
+ refine Thread.singleton_class do
82
+ alias current current
83
+ end
84
+
85
+ refine Method do
86
+ alias call call
87
+ end
88
+
89
+ refine Proc do
90
+ alias call call
91
+ end
92
+
93
+ refine Object do
94
+ alias block_given? block_given?
95
+ end
42
96
  end
43
97
  end
@@ -1,7 +1,9 @@
1
- require_relative 'version'
2
1
  require_relative 'safe'
2
+ require_relative 'version'
3
3
  require_relative 'event_stream/producer'
4
4
 
5
+ using SeeingIsBelieving::Safe
6
+
5
7
  sib_vars = Marshal.load ENV["SIB_VARIABLES.MARSHAL.B64"].unpack('m0').first
6
8
  event_stream = IO.open sib_vars.fetch(:event_stream_fd), "w"
7
9
  $SiB = SeeingIsBelieving::EventStream::Producer.new(event_stream)
@@ -12,12 +14,11 @@ $SiB.record_num_lines sib_vars.fetch(:num_lines)
12
14
  $SiB.record_max_line_captures sib_vars.fetch(:max_line_captures)
13
15
 
14
16
  STDOUT.sync = true
15
- stdout = SeeingIsBelieving::Safe::Stream[STDOUT]
16
- stderr = SeeingIsBelieving::Safe::Stream[STDERR]
17
+ stdout, stderr = STDOUT, STDERR
17
18
 
18
19
  finish = lambda do
19
20
  $SiB.finish!
20
- SeeingIsBelieving::Safe::Stream[event_stream].close
21
+ event_stream.close
21
22
  stdout.flush
22
23
  stderr.flush
23
24
  end
@@ -56,16 +57,15 @@ Kernel.define_singleton_method :fork, &fork_defn
56
57
  Process.define_singleton_method :fork, &fork_defn
57
58
 
58
59
 
59
- # Some things need to just be recorded and readded as they are called from Ruby C code
60
+ # Some things need to be recorded and readded as they are called from Ruby C code and it blows up in really difficult to dianose ways -.-
60
61
  symbol_to_s = Symbol.instance_method(:to_s)
61
62
  exception_message = Exception.instance_method(:message)
62
- # exception_backtrace = Exception.instance_method(:backtrace)
63
+ exception_backtrace = Exception.instance_method(:backtrace)
63
64
 
64
65
  at_exit do
65
- # SeeingIsBelieving::Safe::Exception.define_method :backtrace, exception_backtrace
66
- SeeingIsBelieving::Safe::Exception.define_method :message, exception_message
67
- SeeingIsBelieving::Safe::Symbol.define_method :to_s, symbol_to_s
68
-
66
+ Exception.class_eval { define_method :message, exception_message }
67
+ Exception.class_eval { define_method :backtrace, exception_backtrace }
68
+ Symbol.class_eval { define_method :to_s, symbol_to_s }
69
69
  exitstatus = ($! ? $SiB.record_exception(nil, $!) : 0)
70
70
  finish.call
71
71
  real_exit_bang.call(exitstatus) # clears exceptions so they don't print to stderr and change the processes actual exit status (we recorded what it should be)
@@ -1,3 +1,3 @@
1
1
  class SeeingIsBelieving
2
- VERSION = '3.0.1'
2
+ VERSION = '3.1.0'
3
3
  end
@@ -93,6 +93,12 @@ class SeeingIsBelieving
93
93
  .each { |child| wrap_recursive child }
94
94
  end
95
95
 
96
+ def add_interpolations_in(ast)
97
+ ast.children
98
+ .select { |child| child.type == :begin }
99
+ .each { |child| add_children child }
100
+ end
101
+
96
102
  def wrap_recursive(ast)
97
103
  return wrappings unless ast.kind_of? ::AST::Node
98
104
  case ast.type
@@ -128,7 +134,22 @@ class SeeingIsBelieving
128
134
  when :array
129
135
  add_to_wrappings ast
130
136
  the_begin = ast.location.begin
131
- add_children ast if the_begin && the_begin.source !~ /\A%/
137
+ if !the_begin
138
+ # uhhhh, idk, nothing seems to hit this branch (at least in its spec)
139
+ # maybe this is 'a = 1,2,3'? (I'd try it out, but have to call the folks)
140
+ elsif the_begin.source !~ /\A%/
141
+ # normal array
142
+ add_children ast
143
+ elsif the_begin.source =~ /\A%/
144
+ # array of literals
145
+
146
+ ast.children.each do |child|
147
+ t = child.type
148
+ if t == :dsym || t == :dstr
149
+ add_interpolations_in child
150
+ end
151
+ end
152
+ end
132
153
  when :block
133
154
  add_to_wrappings ast
134
155
 
@@ -205,11 +226,9 @@ class SeeingIsBelieving
205
226
  when :str
206
227
  add_to_wrappings ast
207
228
 
208
- when :dstr, :regexp
229
+ when :regexp, :dstr, :xstr, :dsym
209
230
  add_to_wrappings ast
210
- ast.children
211
- .select { |child| child.type == :begin }
212
- .each { |child| add_children child }
231
+ add_interpolations_in ast
213
232
 
214
233
  when :hash
215
234
  # method arguments might not have braces around them