andyw8-seeing_is_believing 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/test.yml +60 -0
  3. data/.gitignore +19 -0
  4. data/.rspec +2 -0
  5. data/Gemfile +2 -0
  6. data/README.md +70 -0
  7. data/Rakefile +88 -0
  8. data/appveyor.yml +32 -0
  9. data/bin/seeing_is_believing +7 -0
  10. data/docs/example.gif +0 -0
  11. data/docs/frog-brown.png +0 -0
  12. data/docs/sib-streaming.gif +0 -0
  13. data/features/deprecated-flags.feature +91 -0
  14. data/features/errors.feature +155 -0
  15. data/features/examples.feature +423 -0
  16. data/features/flags.feature +852 -0
  17. data/features/regression.feature +898 -0
  18. data/features/support/env.rb +102 -0
  19. data/features/xmpfilter-style.feature +471 -0
  20. data/lib/seeing_is_believing/binary/align_chunk.rb +47 -0
  21. data/lib/seeing_is_believing/binary/align_file.rb +24 -0
  22. data/lib/seeing_is_believing/binary/align_line.rb +25 -0
  23. data/lib/seeing_is_believing/binary/annotate_end_of_file.rb +56 -0
  24. data/lib/seeing_is_believing/binary/annotate_every_line.rb +52 -0
  25. data/lib/seeing_is_believing/binary/annotate_marked_lines.rb +179 -0
  26. data/lib/seeing_is_believing/binary/comment_lines.rb +36 -0
  27. data/lib/seeing_is_believing/binary/commentable_lines.rb +126 -0
  28. data/lib/seeing_is_believing/binary/config.rb +455 -0
  29. data/lib/seeing_is_believing/binary/data_structures.rb +58 -0
  30. data/lib/seeing_is_believing/binary/engine.rb +161 -0
  31. data/lib/seeing_is_believing/binary/format_comment.rb +79 -0
  32. data/lib/seeing_is_believing/binary/interline_align.rb +57 -0
  33. data/lib/seeing_is_believing/binary/remove_annotations.rb +113 -0
  34. data/lib/seeing_is_believing/binary/rewrite_comments.rb +62 -0
  35. data/lib/seeing_is_believing/binary.rb +73 -0
  36. data/lib/seeing_is_believing/code.rb +139 -0
  37. data/lib/seeing_is_believing/compatibility.rb +28 -0
  38. data/lib/seeing_is_believing/debugger.rb +32 -0
  39. data/lib/seeing_is_believing/error.rb +17 -0
  40. data/lib/seeing_is_believing/evaluate_by_moving_files.rb +195 -0
  41. data/lib/seeing_is_believing/event_stream/consumer.rb +221 -0
  42. data/lib/seeing_is_believing/event_stream/events.rb +193 -0
  43. data/lib/seeing_is_believing/event_stream/handlers/debug.rb +61 -0
  44. data/lib/seeing_is_believing/event_stream/handlers/record_exit_events.rb +26 -0
  45. data/lib/seeing_is_believing/event_stream/handlers/stream_json_events.rb +23 -0
  46. data/lib/seeing_is_believing/event_stream/handlers/update_result.rb +41 -0
  47. data/lib/seeing_is_believing/event_stream/producer.rb +178 -0
  48. data/lib/seeing_is_believing/hard_core_ensure.rb +58 -0
  49. data/lib/seeing_is_believing/hash_struct.rb +206 -0
  50. data/lib/seeing_is_believing/result.rb +89 -0
  51. data/lib/seeing_is_believing/safe.rb +112 -0
  52. data/lib/seeing_is_believing/swap_files.rb +90 -0
  53. data/lib/seeing_is_believing/the_matrix.rb +97 -0
  54. data/lib/seeing_is_believing/version.rb +3 -0
  55. data/lib/seeing_is_believing/wrap_expressions.rb +265 -0
  56. data/lib/seeing_is_believing/wrap_expressions_with_inspect.rb +19 -0
  57. data/lib/seeing_is_believing.rb +69 -0
  58. data/seeing_is_believing.gemspec +84 -0
  59. data/spec/binary/alignment_specs.rb +27 -0
  60. data/spec/binary/comment_lines_spec.rb +852 -0
  61. data/spec/binary/config_spec.rb +831 -0
  62. data/spec/binary/engine_spec.rb +114 -0
  63. data/spec/binary/format_comment_spec.rb +210 -0
  64. data/spec/binary/marker_spec.rb +71 -0
  65. data/spec/binary/remove_annotations_spec.rb +342 -0
  66. data/spec/binary/rewrite_comments_spec.rb +106 -0
  67. data/spec/code_spec.rb +233 -0
  68. data/spec/debugger_spec.rb +45 -0
  69. data/spec/evaluate_by_moving_files_spec.rb +204 -0
  70. data/spec/event_stream_spec.rb +762 -0
  71. data/spec/hard_core_ensure_spec.rb +120 -0
  72. data/spec/hash_struct_spec.rb +514 -0
  73. data/spec/seeing_is_believing_spec.rb +1094 -0
  74. data/spec/sib_spec_helpers/version.rb +17 -0
  75. data/spec/spec_helper.rb +26 -0
  76. data/spec/spec_helper_spec.rb +16 -0
  77. data/spec/wrap_expressions_spec.rb +1013 -0
  78. metadata +340 -0
@@ -0,0 +1,23 @@
1
+ require 'json'
2
+ class SeeingIsBelieving
3
+ module EventStream
4
+ module Handlers
5
+ class StreamJsonEvents
6
+ attr_reader :stream
7
+
8
+ def initialize(stream)
9
+ @flush = true if stream.respond_to? :flush
10
+ @stream = stream
11
+ @has_exception = false
12
+ @exitstatus = :not_yet_seen
13
+ end
14
+
15
+ def call(event)
16
+ @stream << JSON.dump(event.as_json)
17
+ @stream << "\n"
18
+ @stream.flush if @flush
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,41 @@
1
+ require 'seeing_is_believing/event_stream/events'
2
+ class SeeingIsBelieving
3
+ module EventStream
4
+ module Handlers
5
+ class UpdateResult
6
+ include EventStream::Events
7
+
8
+ attr_reader :result
9
+
10
+ def initialize(result)
11
+ @result = result
12
+ end
13
+
14
+ def call(event)
15
+ case event
16
+ when LineResult then result.record_result(event.type, event.line_number, event.inspected)
17
+ when ResultsTruncated then result.record_result(event.type, event.line_number, '...') # <-- is this really what I want?
18
+ when Exception then result.record_exception event.line_number, event.class_name, event.message, event.backtrace
19
+ when Stdout then result.stdout << event.value
20
+ when Stderr then result.stderr << event.value
21
+ when MaxLineCaptures then result.max_line_captures = event.value
22
+ when Exitstatus then result.exitstatus = event.value
23
+ when NumLines then result.num_lines = event.value
24
+ when SiBVersion then result.sib_version = event.value
25
+ when RubyVersion then result.ruby_version = event.value
26
+ when Filename then result.filename = event.value
27
+ when Timeout then result.timeout_seconds = event.seconds
28
+ when Exec,
29
+ Finished,
30
+ StdoutClosed,
31
+ StderrClosed,
32
+ EventStreamClosed,
33
+ FileLoaded
34
+ # no op
35
+ else raise "Unknown event: #{event.inspect}"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,178 @@
1
+ require 'seeing_is_believing/safe'
2
+ require 'seeing_is_believing/event_stream/events'
3
+ require 'thread' # <-- do we still need this?
4
+
5
+ using SeeingIsBelieving::Safe
6
+
7
+ class SeeingIsBelieving
8
+ module EventStream
9
+ class Producer
10
+
11
+ # Guarding against hostile users (e.g. me) that do ridiculous things like blowing away these constants
12
+ old_w, $-w = $-w, nil # Ruby warns about accessing deprecated constants
13
+ Object.constants.each do |name|
14
+ next if name == :SortedSet # Removed in 3.0, but apparently the constant still exists, it just explodes if you reference it
15
+ const_set name, Object.const_get(name)
16
+ end
17
+ $-w = old_w
18
+
19
+ ErrnoEPIPE = Errno::EPIPE # not actually tested, but we can see it is referenced below
20
+
21
+ module NullQueue
22
+ extend self
23
+ Queue.instance_methods(false).each do |name|
24
+ define_method(name) { |*| }
25
+ end
26
+ end
27
+
28
+ attr_accessor :max_line_captures, :filename
29
+
30
+ def initialize(resultstream)
31
+ self.filename = nil
32
+ self.max_line_captures = Float::INFINITY
33
+ self.recorded_results = []
34
+ self.queue = Queue.new
35
+ self.producer_thread = build_producer_thread(resultstream)
36
+ end
37
+
38
+ attr_reader :version
39
+ alias ver version
40
+ def record_sib_version(sib_version)
41
+ @version = sib_version
42
+ queue << "sib_version #{to_string_token sib_version}"
43
+ end
44
+
45
+ def record_ruby_version(ruby_version)
46
+ queue << "ruby_version #{to_string_token ruby_version}"
47
+ end
48
+
49
+ def record_max_line_captures(max_line_captures)
50
+ self.max_line_captures = max_line_captures
51
+ queue << "max_line_captures #{max_line_captures}"
52
+ end
53
+
54
+ def file_loaded
55
+ queue << "file_loaded"
56
+ end
57
+
58
+ StackErrors = [SystemStackError]
59
+ StackErrors << Java::JavaLang::StackOverflowError if defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java'
60
+ def record_result(type, line_number, value)
61
+ counts = recorded_results[line_number] ||= Hash.new(0)
62
+ count = counts[type]
63
+ recorded_results[line_number][type] = count.next
64
+ if count < max_line_captures
65
+ begin
66
+ if block_given?
67
+ inspected = yield(value)
68
+ else
69
+ inspected = value.inspect
70
+ end
71
+ unless String === inspected
72
+ inspected = inspected.to_str
73
+ raise unless String === inspected
74
+ end
75
+ rescue *StackErrors
76
+ # this is necessary because SystemStackError won't show the backtrace of the method we tried to call
77
+ # which means there won't be anything showing the user where this came from
78
+ # so we need to re-raise the error to get a backtrace that shows where we came from
79
+ # otherwise it looks like the bug is in SiB and not the user's program, see https://github.com/JoshCheek/seeing_is_believing/issues/37
80
+ raise SystemStackError, "Calling inspect blew the stack (is it recursive w/o a base case?)"
81
+ rescue Exception
82
+ begin
83
+ inspected = Kernel.instance_method(:inspect).bind(value).call
84
+ rescue Exception
85
+ inspected = "#<no inspect available>"
86
+ end
87
+ end
88
+ queue << "result #{line_number.to_s} #{type.to_s} #{to_string_token inspected}"
89
+ elsif count == max_line_captures
90
+ queue << "maxed_result #{line_number.to_s} #{type.to_s}"
91
+ end
92
+ value
93
+ end
94
+
95
+ # records the exception, returns the exitstatus for that exception
96
+ def record_exception(line_number, exception)
97
+ return exception.status if SystemExit === exception # TODO === is not in the list
98
+ unless line_number
99
+ if filename
100
+ begin line_number = exception.backtrace.grep(/#{filename.to_s}/).first[/:\d+/][1..-1].to_i
101
+ rescue NoMethodError
102
+ end
103
+ end
104
+ end
105
+ line_number ||= -1
106
+ queue << [
107
+ "exception",
108
+ line_number.to_s,
109
+ to_string_token(exception.class.name),
110
+ to_string_token(exception.message),
111
+ exception.backtrace.size.to_s,
112
+ *exception.backtrace.map { |line| to_string_token line }
113
+ ].join(" ")
114
+ 1 # exit status
115
+ end
116
+
117
+ def record_filename(filename)
118
+ self.filename = filename
119
+ queue << "filename #{to_string_token filename}"
120
+ end
121
+
122
+ def record_exec(args)
123
+ queue << "exec #{to_string_token args.inspect}"
124
+ end
125
+
126
+ def record_num_lines(num_lines)
127
+ queue << "num_lines #{num_lines}"
128
+ end
129
+
130
+ def finish!
131
+ queue << :break # note that consumer will continue reading until stream is closed, which is not the responsibility of the producer
132
+ producer_thread.join
133
+ end
134
+
135
+ private
136
+
137
+ attr_accessor :resultstream, :queue, :producer_thread, :recorded_results
138
+
139
+ # for a consideration of many different ways of doing this, see 5633064
140
+ def to_string_token(string)
141
+ [Marshal.dump(string.to_s)].pack('m0')
142
+ rescue TypeError => err
143
+ raise unless err.message =~ /singleton can't be dumped/
144
+ to_string_token string.to_s.dup
145
+ end
146
+
147
+ def build_producer_thread(resultstream)
148
+ ::Thread.new {
149
+ Thread.current.abort_on_exception = true
150
+ begin
151
+ resultstream.sync = true
152
+ loop do
153
+ to_publish = queue.shift
154
+ break if :break == to_publish
155
+ resultstream << (to_publish << "\n")
156
+ end
157
+ rescue IOError, Errno::EPIPE
158
+ queue.clear
159
+ ensure
160
+ self.queue = NullQueue
161
+ resultstream.flush rescue nil
162
+ end
163
+ }
164
+ end
165
+
166
+ def forking_occurred_and_you_are_the_child(resultstream)
167
+ # clear the queue b/c we don't want to report the same lines 2x,
168
+ # parent process can report them
169
+ queue << :fork
170
+ loop { break if queue.shift == :fork }
171
+
172
+ # recreate the thread since forking in Ruby kills threads
173
+ @producer_thread = build_producer_thread(resultstream)
174
+ end
175
+
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,58 @@
1
+ class SeeingIsBelieving
2
+ class HardCoreEnsure
3
+ def self.call(options)
4
+ new(options).call
5
+ end
6
+
7
+ def initialize(options)
8
+ self.options = options
9
+ validate_options
10
+ end
11
+
12
+ def call
13
+ trap_sigint
14
+ invoke_code
15
+ ensure
16
+ invoke_ensure
17
+ end
18
+
19
+ private
20
+
21
+ attr_accessor :options, :ensure_invoked, :old_handler
22
+
23
+ def trap_sigint
24
+ self.old_handler = trap 'INT' do
25
+ invoke_ensure
26
+ Process.kill 'INT', $$
27
+ end
28
+ trap 'INT', old_handler if ignore_interrupt? old_handler
29
+ end
30
+
31
+ def invoke_code
32
+ options[:code].call
33
+ end
34
+
35
+ def invoke_ensure
36
+ return if ensure_invoked
37
+ self.ensure_invoked = true
38
+ trap 'INT', old_handler
39
+ options[:ensure].call
40
+ end
41
+
42
+ def validate_options
43
+ raise ArgumentError, "Must pass the :code key" unless options.key? :code
44
+ raise ArgumentError, "Must pass the :ensure key" unless options.key? :ensure
45
+ unknown_keys = options.keys - [:code, :ensure]
46
+ if options.size == 3
47
+ raise ArgumentError, "Unknown key: #{unknown_keys.first.inspect}"
48
+ elsif options.size > 3
49
+ raise ArgumentError, "Unknown keys: #{unknown_keys.map(&:inspect).join(', ')}"
50
+ end
51
+ end
52
+
53
+ def ignore_interrupt?(interrupt_handler)
54
+ # any handler that ignores gets normalized to IGNORE
55
+ interrupt_handler == 'IGNORE'
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,206 @@
1
+ class SeeingIsBelieving
2
+ HashStruct = Class.new
3
+
4
+ class << HashStruct
5
+ NoDefault = Module.new
6
+
7
+ def init_blocks
8
+ @init_blocks ||= {}
9
+ end
10
+
11
+ def attribute(name, value=NoDefault, &init_block)
12
+ init_blocks.key?(name) && raise(ArgumentError, "#{name} was already defined")
13
+ name.kind_of?(Symbol) || raise(ArgumentError, "#{name.inspect} should have been a symbol")
14
+
15
+ init_block ||= lambda do |hash_struct|
16
+ if value == NoDefault
17
+ raise ArgumentError, "Must provide a value for #{name.inspect}"
18
+ else
19
+ value
20
+ end
21
+ end
22
+ init_blocks[name] = init_block
23
+ define_method(name) { self[name] }
24
+ define_method(:"#{name}=") { |val| self[name] = val }
25
+
26
+ self
27
+ end
28
+
29
+ def attributes(*names_or_pairs)
30
+ names_or_pairs.each do |norp|
31
+ case norp
32
+ when Symbol then attribute(norp)
33
+ else norp.each { |name, default| attribute name, default }
34
+ end
35
+ end
36
+ self
37
+ end
38
+
39
+ def predicate(name, *rest, &b)
40
+ attribute name, *rest, &b
41
+ define_method(:"#{name}?") { !!self[name] }
42
+ self
43
+ end
44
+
45
+ def predicates(*names_or_pairs)
46
+ names_or_pairs.each do |name_or_pairs|
47
+ name = pairs = name_or_pairs
48
+ name_or_pairs.kind_of?(Symbol) ?
49
+ predicate(name) :
50
+ pairs.each { |name, default| predicate name, default }
51
+ end
52
+ self
53
+ end
54
+
55
+ def anon(&block)
56
+ Class.new self, &block
57
+ end
58
+
59
+ def for(*attributes_args, &block)
60
+ anon(&block).attributes(*attributes_args)
61
+ end
62
+
63
+ def for?(*predicate_args, &block)
64
+ anon(&block).predicates(*predicate_args)
65
+ end
66
+ end
67
+
68
+ class HashStruct
69
+ def self.inspect
70
+ name || "HashStruct.anon"
71
+ end
72
+
73
+ # This could support dynamic attributes very easily
74
+ # ie they are calculated, but appear as a value (e.g. in to_hash)
75
+ # not sure how to deal with the fact that they could be assigned, though
76
+ class Attr
77
+ def initialize(instance, value=nil, &block)
78
+ @instance = instance
79
+ @block = block if block
80
+ @value = value unless block
81
+ end
82
+ def value
83
+ return @value if defined? @value
84
+ @value = @block.call(@instance)
85
+ end
86
+ end
87
+
88
+ # The aggressivenes of this is kind of annoying when you're trying to build up a large hash of values
89
+ # maybe new vs new! one validates arg presence,
90
+ # maybe a separate #validate! method for that?
91
+ def initialize(initial_values={}, &initializer)
92
+ initial_values.respond_to?(:each) ||
93
+ raise(ArgumentError, "#{self.class.inspect} expects to be initialized with a hash-like object, but got #{initial_values.inspect}")
94
+ @attributes = self
95
+ .class
96
+ .ancestors
97
+ .take_while { |ancestor| ancestor != HashStruct }
98
+ .map(&:init_blocks)
99
+ .reverse
100
+ .inject({}, :merge)
101
+ .each_with_object({}) { |(name, block), attrs| attrs[name] = Attr.new(self, &block) }
102
+ initial_values.each { |key, value| self[key] = value }
103
+ initializer.call self if initializer
104
+ each { } # access each key to see if it blows up
105
+ end
106
+
107
+ include Enumerable
108
+ def each(&block)
109
+ return to_enum :each unless block
110
+ @attributes.keys.each do |name|
111
+ block.call(name, self[name])
112
+ end
113
+ end
114
+
115
+ def [](key)
116
+ @attributes[internalize! key].value
117
+ end
118
+
119
+ def []=(key, value)
120
+ @attributes[internalize! key] = Attr.new(self, value)
121
+ end
122
+
123
+ def fetch(key, ignored=nil)
124
+ self[key]
125
+ end
126
+
127
+ def to_hash
128
+ Hash[to_a]
129
+ end
130
+ alias to_h to_hash
131
+
132
+ def merge(overrides)
133
+ self.class.new(to_h.merge overrides)
134
+ end
135
+
136
+ def keys
137
+ to_a.map(&:first)
138
+ end
139
+
140
+ def values
141
+ to_a.map(&:last)
142
+ end
143
+
144
+ def inspect
145
+ classname = self.class.name ? "HashStruct #{self.class.name}" : self.class.inspect
146
+ inspected_attrs = map { |k, v| "#{k}: #{v.inspect}" }.join(", ")
147
+ "#<#{classname}: {#{inspected_attrs}}>"
148
+ end
149
+
150
+ def pretty_print(pp)
151
+ pp.text self.class.name || 'HashStruct.anon { ... }'
152
+ pp.text '.new('
153
+ pp.group 2 do
154
+ pp.breakable '' # place inside so that if we break, we are indented
155
+ last_key = keys.last
156
+ each do |key, value|
157
+ # text-space-value, or text-neline-indent-value
158
+ pp.text "#{key}:"
159
+ pp.group 2 do
160
+ pp.breakable " "
161
+ pp.pp value
162
+ end
163
+ # all lines end in a comma, and can have a newline, except the last
164
+ pp.comma_breakable unless key == last_key
165
+ end
166
+ end
167
+ pp.breakable ''
168
+ pp.text ')'
169
+ end
170
+
171
+ def key?(key)
172
+ key.respond_to?(:to_sym) && @attributes.key?(key.to_sym)
173
+ end
174
+ alias has_key? key?
175
+ alias include? key? # b/c Hash does this
176
+ alias member? key? # b/c Hash does this
177
+
178
+ def ==(other)
179
+ if equal? other
180
+ true
181
+ elsif other.kind_of? Hash
182
+ to_h == other
183
+ elsif other.respond_to?(:to_h)
184
+ to_h == other.to_h
185
+ else
186
+ false
187
+ end
188
+ end
189
+ alias eql? ==
190
+
191
+ # this might be pretty expensive
192
+ def hash
193
+ to_h.hash
194
+ end
195
+
196
+ private
197
+
198
+ def internalize!(key)
199
+ internal = key.to_sym
200
+ @attributes.key?(internal) || raise(KeyError)
201
+ internal
202
+ rescue NoMethodError, KeyError
203
+ raise KeyError, "#{key.inspect} is not an attribute, should be in #{@attributes.keys.inspect}"
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,89 @@
1
+ class SeeingIsBelieving
2
+ class Result
3
+ include Enumerable
4
+ RecordedException = Struct.new :line_number, :class_name, :message, :backtrace
5
+
6
+ attr_accessor :stdout, :stderr, :exitstatus, :max_line_captures, :exceptions, :num_lines, :sib_version, :ruby_version, :filename, :timeout_seconds
7
+
8
+ def has_exception?
9
+ exceptions.any?
10
+ end
11
+
12
+ def initialize
13
+ self.stdout = ''
14
+ self.stderr = ''
15
+ self.exceptions = []
16
+ end
17
+
18
+ def has_stdout?
19
+ stdout && !stdout.empty?
20
+ end
21
+
22
+ def has_stderr?
23
+ stderr && !stderr.empty?
24
+ end
25
+
26
+ def timeout?
27
+ !!timeout_seconds
28
+ end
29
+
30
+ def record_result(type, line_number, value)
31
+ results_for(line_number, type) << value
32
+ value
33
+ end
34
+
35
+ def record_exception(line_number, exception_class, exception_message, exception_backtrace)
36
+ self.exceptions << RecordedException.new(line_number, exception_class, exception_message, exception_backtrace)
37
+ end
38
+
39
+ def exception
40
+ exceptions.first
41
+ end
42
+
43
+ def [](line_number, type=:inspect)
44
+ results_for(line_number, type)
45
+ end
46
+
47
+ def each(&block)
48
+ return to_enum :each unless block
49
+ (1..num_lines).each { |line_number| block.call self[line_number] }
50
+ end
51
+
52
+ def max_line_captures
53
+ @max_line_captures || Float::INFINITY
54
+ end
55
+
56
+ def as_json
57
+ # We have both an exception and a list of exceptions because multiple exceptions
58
+ # weren't added until #85, and I don't want to break backwards compatibility right now.
59
+ { stdout: stdout,
60
+ stderr: stderr,
61
+ exitstatus: exitstatus,
62
+ exception: exception_json(exception),
63
+ exceptions: exceptions.map { |e| exception_json e },
64
+ lines: each.with_object(Hash.new)
65
+ .with_index(1) { |(result, hash), line_number| hash[line_number] = result },
66
+ }
67
+ end
68
+
69
+ private
70
+
71
+ def results_for(line_number, type)
72
+ line_results = (results[line_number] ||= Hash.new { |h, k| h[k] = [] })
73
+ line_results[type]
74
+ end
75
+
76
+ def results
77
+ @results ||= Hash.new
78
+ end
79
+
80
+ def exception_json(exception)
81
+ return nil unless exception
82
+ { line_number_in_this_file: exception.line_number,
83
+ class_name: exception.class_name,
84
+ message: exception.message,
85
+ backtrace: exception.backtrace,
86
+ }
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,112 @@
1
+ # require this before anything else, b/c it expects the world to be sane when it is loaded
2
+ class SeeingIsBelieving
3
+ module Safe
4
+
5
+ # Subclasses must refine before superclasses in older Rubies, otherwise
6
+ # it finds the superclass method and behaves unexpectedly.
7
+ refine String.singleton_class do
8
+ alias === ===
9
+ end
10
+
11
+ refine Class do
12
+ alias === ===
13
+ end
14
+
15
+ begin
16
+ refine Kernel do
17
+ alias inspect inspect
18
+ end
19
+ rescue TypeError
20
+ # Ruby < 2.4 can't refine a module,
21
+ # so this optimization is only available on >= 2.4
22
+ end
23
+
24
+ refine Queue do
25
+ alias << <<
26
+ alias shift shift
27
+ alias clear clear
28
+ end
29
+
30
+ refine IO do
31
+ alias sync= sync=
32
+ alias << <<
33
+ alias flush flush
34
+ alias close close
35
+ end
36
+
37
+ refine Symbol do
38
+ alias == ==
39
+ alias to_s to_s
40
+ end
41
+
42
+ refine Symbol.singleton_class do
43
+ alias define_method define_method
44
+ alias class_eval class_eval
45
+ end
46
+
47
+ refine String do
48
+ alias == ==
49
+ alias to_s to_s
50
+ alias to_str to_str
51
+ end
52
+
53
+ refine Integer do
54
+ alias to_s to_s
55
+ alias next next
56
+ alias < <
57
+ end
58
+
59
+ refine Array do
60
+ alias pack pack
61
+ alias map map
62
+ alias size size
63
+ alias join join
64
+ alias [] []
65
+ alias []= []=
66
+ end
67
+
68
+ refine Hash do
69
+ alias [] []
70
+ alias []= []=
71
+ end
72
+
73
+ refine Hash.singleton_class do
74
+ alias new new
75
+ end
76
+
77
+ refine Marshal.singleton_class do
78
+ alias dump dump
79
+ end
80
+
81
+ refine Exception do
82
+ alias message message
83
+ alias backtrace backtrace
84
+ alias class class
85
+ end
86
+
87
+ refine Exception.singleton_class do
88
+ alias define_method define_method
89
+ alias class_eval class_eval
90
+ end
91
+
92
+ refine Thread do
93
+ alias join join
94
+ end
95
+
96
+ refine Thread.singleton_class do
97
+ alias current current
98
+ end
99
+
100
+ refine Method do
101
+ alias call call
102
+ end
103
+
104
+ refine Proc do
105
+ alias call call
106
+ end
107
+
108
+ refine Object do
109
+ alias block_given? block_given?
110
+ end
111
+ end
112
+ end