seeing_is_believing 2.1.3 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 924b2f9837ef55a65692edd1552bb4e28c497e04
4
- data.tar.gz: c527cc92aea505196789779036ff2b9fb3265056
3
+ metadata.gz: 1952064fe97bbd42814728d8562a23f401b11ccc
4
+ data.tar.gz: 41ebff3b5b92e9003ef4e587d25367a610ea45e3
5
5
  SHA512:
6
- metadata.gz: a327a0865eb016d325158d9eb17fb34803634e37b2a0c31037df8385f076b3dbb23627c04498b5c9f5d6f4128efa544afe8c90b802536cb194f0b01bed3e53ad
7
- data.tar.gz: fb8f88092ae5e16ea2ff1ca5f9ee22f7b993062ab8c67f93a4ec02b6ca3a7b0c8876998ee28e6994cbe6affd2af4ac96f6bcdba0fdefa6c2396df3a252542776
6
+ metadata.gz: d40b823e70ff45521c324895cde5d0d76195ef26339ba0dc431d6009ec901bcc1e0e9ad509eb752477a3973035b88667ab6e0ef3ca97e354a2c8c90369a3dc2e
7
+ data.tar.gz: c766a89c201e945347575eed23044fd1635628bfb1b6857bfa4d62bc4641dfe809e15fe91322f7590d344db752e562a533e7d56ce926649befe5e4aa1353b31a
data/.travis.yml CHANGED
@@ -4,3 +4,12 @@ rvm:
4
4
  - 1.9.3
5
5
  - 2.0.0
6
6
  - 2.1.2
7
+ - ruby-head
8
+ - rbx-2
9
+ - jruby-head
10
+
11
+ matrix:
12
+ allow_failures:
13
+ - rvm: ruby-head
14
+ - rvm: jruby-head
15
+ - rvm: rbx-2
data/Readme.md CHANGED
@@ -68,6 +68,7 @@ Editor Integration
68
68
  * [TextMate 2](https://github.com/JoshCheek/text_mate_2-seeing-is_believing)
69
69
  * [vim-ruby-xmpfilter](https://github.com/t9md/vim-ruby-xmpfilter) (has support for `seeing_is_believing`)
70
70
  * [vim-seeing-is-believing](https://github.com/hwartig/vim-seeing-is-believing)
71
+ * [atom-seeing-is-believing](https://github.com/JoshCheek/atom-seeing-is-believing) (prob has best installation instructions)
71
72
 
72
73
  Emacs Integration
73
74
  =================
@@ -561,8 +561,8 @@ Feature: Using flags
561
561
  result = SeeingIsBelieving::Result.new
562
562
  result.record_result(1, /omg/)
563
563
 
564
- require 'yaml'
565
- puts YAML.dump result
564
+ require 'json'
565
+ puts JSON.dump result.to_primitive
566
566
  """
567
567
  When I run "chmod +x fake_ruby"
568
568
  When I run "seeing_is_believing -e 123 --shebang ./fake_ruby"
@@ -287,3 +287,98 @@ Feature:
287
287
  Then stderr includes "1: unterminated string meets end of file"
288
288
  And the exit status is 2
289
289
  And stdout is empty
290
+
291
+ Scenario: A program using system
292
+ Given the file "invoking_system.rb":
293
+ """
294
+ system %(ruby -e '$stdout.puts %(hello)')
295
+ system %(ruby -e '$stderr.puts %(world)')
296
+ """
297
+ When I run "seeing_is_believing invoking_system.rb"
298
+ Then stderr is empty
299
+ And the exit status is 0
300
+ And stdout is:
301
+ """
302
+ system %(ruby -e '$stdout.puts %(hello)') # => true
303
+ system %(ruby -e '$stderr.puts %(world)') # => true
304
+
305
+ # >> hello
306
+
307
+ # !> world
308
+ """
309
+
310
+ Scenario: A program overriding stdout/stderr
311
+ Given the file "black_hole.rb":
312
+ """
313
+ File.open '/dev/null', 'w' do |black_hole|
314
+ STDERR = $stderr = black_hole
315
+ STDOUT = $stdout = black_hole
316
+ puts "You won't see this, it goes into the black hole"
317
+ system %q(ruby -e '$stdout.puts "stdout gets past it b/c of dumb ruby bug"')
318
+ system %q(ruby -e '$stderr.puts "stderr gets past it b/c of dumb ruby bug"')
319
+ end
320
+ """
321
+ When I run "seeing_is_believing black_hole.rb"
322
+ Then stderr is empty
323
+ And the exit status is 0
324
+ And stdout is:
325
+ """
326
+ File.open '/dev/null', 'w' do |black_hole| # => File
327
+ STDERR = $stderr = black_hole # => #<File:/dev/null>
328
+ STDOUT = $stdout = black_hole # => #<File:/dev/null>
329
+ puts "You won't see this, it goes into the black hole" # => nil
330
+ system %q(ruby -e '$stdout.puts "stdout gets past it b/c of dumb ruby bug"') # => true
331
+ system %q(ruby -e '$stderr.puts "stderr gets past it b/c of dumb ruby bug"') # => true
332
+ end # => true
333
+
334
+ # >> stdout gets past it b/c of dumb ruby bug
335
+
336
+ # !> stderr gets past it b/c of dumb ruby bug
337
+ """
338
+
339
+ Scenario: Incorrect wrapping in some programs
340
+ Given the file "incorrect_wrapping.rb":
341
+ """
342
+ a
343
+ class B
344
+ def c
345
+ d = 1
346
+ end
347
+ end
348
+ """
349
+ When I run "seeing_is_believing incorrect_wrapping.rb"
350
+ Then stderr is empty
351
+ And the exit status is 1
352
+ And stdout is:
353
+ """
354
+ a # ~> NameError: undefined local variable or method `a' for main:Object
355
+ class B
356
+ def c
357
+ d = 1
358
+ end
359
+ end
360
+
361
+ # ~> NameError
362
+ # ~> undefined local variable or method `a' for main:Object
363
+ # ~>
364
+ # ~> incorrect_wrapping.rb:1:in `<main>'
365
+ """
366
+
367
+ Scenario: Can deal with hostile environments
368
+ Given the file "bang_object.rb":
369
+ """
370
+ class Object
371
+ def !(a)
372
+ end
373
+ end
374
+ """
375
+ When I run "seeing_is_believing bang_object.rb"
376
+ Then stderr is empty
377
+ And the exit status is 0
378
+ And stdout is:
379
+ """
380
+ class Object
381
+ def !(a)
382
+ end
383
+ end
384
+ """
@@ -8,12 +8,12 @@ end
8
8
 
9
9
 
10
10
  Then 'stdout is exactly:' do |code|
11
- @last_executed.stdout.should == eval_curlies(code)
11
+ expect(@last_executed.stdout).to eq eval_curlies(code)
12
12
  end
13
13
 
14
14
  Then 'stdout is the JSON:' do |json|
15
15
  require 'json'
16
16
  expected = JSON.parse(json)
17
17
  actual = JSON.parse(@last_executed.stdout)
18
- actual.should == expected
18
+ expect(actual).to eq expected
19
19
  end
@@ -52,10 +52,12 @@ class SeeingIsBelieving
52
52
  WrapExpressions.call "#{@program}\n",
53
53
  before_all: "begin; $SiB.number_of_captures = #{number_of_captures_as_str}; ",
54
54
  after_all: ";rescue Exception;"\
55
- "line_number = $!.backtrace.grep(/\#{__FILE__}/).first[/:\\d+/][1..-1].to_i;"\
56
- "$SiB.record_exception line_number, $!;"\
57
- "$SiB.exitstatus = 1;"\
58
- "$SiB.exitstatus = $!.status if $!.kind_of? SystemExit;"\
55
+ "lambda {"\
56
+ "line_number = $!.backtrace.grep(/\#{__FILE__}/).first[/:\\d+/][1..-1].to_i;"\
57
+ "$SiB.record_exception line_number, $!;"\
58
+ "$SiB.exitstatus = 1;"\
59
+ "$SiB.exitstatus = $!.status if $!.kind_of? SystemExit;"\
60
+ "}.call;"\
59
61
  "end",
60
62
  before_each: -> line_number { "$SiB.record_result(#{line_number}, (" },
61
63
  after_each: -> line_number { "))" }
@@ -22,7 +22,7 @@ class SeeingIsBelieving
22
22
  buffer, parser, rewriter = ParserHelpers.initialize_parser code, 'strip_comments'
23
23
  comments = ParserHelpers.comments_from parser, buffer
24
24
 
25
- removed_comments = { result: [], exception: [], stdout: [], stderr: [] }
25
+ removed_comments = { result: [], exception: [], stdout: [], stderr: [] }
26
26
 
27
27
  comments.each do |comment|
28
28
  case comment.text
@@ -1,5 +1,12 @@
1
1
  class SeeingIsBelieving
2
2
  class Binary
3
+ # not sure I like this name, it formats comments that
4
+ # show results e.g. "# => [1, 2, 3]"
5
+ #
6
+ # line_length is the length of the line this comment is being appended to
7
+ #
8
+ # For examples of what the options are, and how they all fit together, see
9
+ # spec/binary/comment_formatter_spec.rb
3
10
  class CommentFormatter
4
11
  def self.call(*args)
5
12
  new(*args).call
@@ -48,7 +55,7 @@ class SeeingIsBelieving
48
55
  end
49
56
 
50
57
  def ellipsify(string)
51
- string.sub(/.{0,3}$/) { |last_chars| last_chars.gsub /./, '.' }
58
+ string.sub(/.{0,3}$/) { |last_chars| '.' * last_chars.size }
52
59
  end
53
60
  end
54
61
  end
@@ -1,6 +1,7 @@
1
1
  require 'seeing_is_believing/binary/commentable_lines'
2
2
 
3
3
  class SeeingIsBelieving
4
+ # spec/binary/comment_lines_spec.rb
4
5
  class Binary
5
6
 
6
7
  # takes a body and a block
@@ -4,6 +4,9 @@ class SeeingIsBelieving
4
4
  class Binary
5
5
 
6
6
  # could possibly be sped up by just reflecting on the tokens instead of the whole ast
7
+ #
8
+ # specs for this class are in spec/binary/comment_lines_spec.rb
9
+ # because it was extracted from that class
7
10
  class CommentableLines
8
11
 
9
12
  include ParserHelpers
@@ -11,7 +11,7 @@
11
11
  # read the wrong file... of course, since we rewrite the file,
12
12
  # its body will be incorrect, anyway.
13
13
 
14
- require 'yaml'
14
+ require 'json'
15
15
  require 'open3'
16
16
  require 'timeout'
17
17
  require 'stringio'
@@ -137,7 +137,7 @@ class SeeingIsBelieving
137
137
  end
138
138
 
139
139
  def deserialize_result
140
- YAML.load stdout
140
+ Result.from_primitive JSON.load stdout
141
141
  end
142
142
 
143
143
  def wrap_error(error)
@@ -2,7 +2,23 @@ class SeeingIsBelieving
2
2
 
3
3
  # We cannot serialize the actual exception because we do not have any guarantee that its class is defined on the SIB side,
4
4
  # so we must use simpler data structures (Strings and arrays)
5
- RecordedException = Struct.new :class_name, :message, :backtrace
5
+ RecordedException = Struct.new :class_name, :message, :backtrace do
6
+ def self.from_primitive(primitive)
7
+ return nil unless primitive
8
+ exception = new
9
+ exception.class_name = primitive['class_name']
10
+ exception.message = primitive['message']
11
+ exception.backtrace = primitive['backtrace']
12
+ exception
13
+ end
14
+
15
+ def to_primitive
16
+ { 'class_name' => class_name,
17
+ 'message' => message,
18
+ 'backtrace' => backtrace,
19
+ }
20
+ end
21
+ end
6
22
 
7
23
  module HasException
8
24
  attr_accessor :exception
@@ -14,6 +14,33 @@ class SeeingIsBelieving
14
14
  end
15
15
  end
16
16
 
17
+ def to_float(n)
18
+ return n unless n == Float::INFINITY
19
+ 'FUCKING_INFINITY_AND_JESUS_FUCKING_CHRIST_JSON_AND_MARSHAL_AND_YAML_WHAT_THE_FUCK?'
20
+ end
21
+
22
+ def from_float(n)
23
+ return n if n.kind_of? Float
24
+ Float::INFINITY
25
+ end
26
+
27
+ def to_primitive
28
+ { 'array' => @array,
29
+ 'max_number_of_captures' => to_float(@max_number_of_captures),
30
+ 'num_results' => @num_results,
31
+ 'total_size' => @total_size,
32
+ 'exception' => (exception && exception.to_primitive)
33
+ }
34
+ end
35
+
36
+ def from_primitive(primitive)
37
+ @array = primitive['array']
38
+ @max_number_of_captures = from_float primitive['max_number_of_captures']
39
+ @num_results = primitive['num_results']
40
+ @total_size = primitive['total_size']
41
+ @exception = RecordedException.from_primitive primitive['exception']
42
+ end
43
+
17
44
  def to_a
18
45
  @array.dup
19
46
  end
@@ -32,6 +59,12 @@ class SeeingIsBelieving
32
59
  inspected = value.inspect.to_str # only invoke inspect once, b/c the inspection may be recorded
33
60
  rescue NoMethodError
34
61
  inspected = "#<no inspect available>"
62
+ rescue SystemStackError
63
+ # this is necessary because SystemStackError won't show the backtrace of the method we tried to call
64
+ # which means there won't be anything showing the user where this came from
65
+ # so we need to re-raise the error to get a backtrace that shows where we came from
66
+ # 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
67
+ raise SystemStackError, "Calling inspect blew the stack (is it recursive w/o a base case?)"
35
68
  end
36
69
 
37
70
  if size < @max_number_of_captures then @array << inspected
@@ -6,6 +6,37 @@ class SeeingIsBelieving
6
6
  include HasException
7
7
  include Enumerable
8
8
 
9
+ def self.from_primitive(primitive)
10
+ new.from_primitive(primitive)
11
+ end
12
+
13
+ def from_primitive(primitive)
14
+ self.exitstatus = primitive['exitstatus']
15
+ self.stdout = primitive['stdout']
16
+ self.stderr = primitive['stderr']
17
+ self.bug_in_sib = primitive['bug_in_sib']
18
+ self.exception = RecordedException.from_primitive primitive['exception']
19
+ primitive['results'].each do |line_number, primitive_line|
20
+ results_for(line_number.to_i).from_primitive(primitive_line)
21
+ end
22
+ self
23
+ end
24
+
25
+ def to_primitive
26
+ primitive = {
27
+ 'exitstatus' => exitstatus,
28
+ 'stdout' => stdout,
29
+ 'stderr' => stderr,
30
+ 'bug_in_sib' => bug_in_sib,
31
+ 'exception' => (exception && exception.to_primitive),
32
+ }
33
+ primitive['results'] = results.each_with_object({}) do |(line_number, line), r|
34
+ r[line_number] = line.to_primitive
35
+ end
36
+ primitive
37
+ end
38
+
39
+
9
40
  attr_accessor :stdout, :stderr, :exitstatus, :bug_in_sib, :number_of_captures
10
41
 
11
42
  alias bug_in_sib? bug_in_sib
@@ -1,24 +1,40 @@
1
1
  # WARNING: DO NOT REQUIRE THIS FILE, IT WILL FUCK YOU UP!!!!!!
2
2
 
3
+ # READ THIS IF YOU WANT TO USE YOUR OWN MATRIX FILE:
4
+ # https://github.com/JoshCheek/seeing_is_believing/issues/24
5
+ #
6
+ # (or if you want to understand why we do the pipe dance)
3
7
 
4
- require 'yaml'
5
- require 'stringio'
6
- real_stdout = STDOUT
7
- real_stderr = STDERR
8
- STDOUT = $stdout = fake_stdout = StringIO.new
9
- STDERR = $stderr = fake_stderr = StringIO.new
10
8
 
9
+ require 'json'
11
10
  require 'seeing_is_believing/result'
12
11
  $SiB = SeeingIsBelieving::Result.new
13
12
 
13
+ stdout_real_obj = STDOUT # the real Ruby object, but its FD is going to keep getting reopened
14
+ stderr_real_obj = STDERR
15
+ stdout_real_fd = STDOUT.dup # duped Ruby object, but with the real file descriptor
16
+ stderr_real_fd = STDERR.dup
17
+
18
+ read_from_mock_out, write_to_mock_out = IO.pipe
19
+ read_from_mock_err, write_to_mock_err = IO.pipe
20
+
21
+ stdout_real_obj.reopen write_to_mock_out
22
+ stderr_real_obj.reopen write_to_mock_err
23
+
14
24
  at_exit do
15
- $SiB.stdout = fake_stdout.string
16
- $SiB.stderr = fake_stderr.string
25
+ stdout_real_obj.reopen stdout_real_fd
26
+ stderr_real_obj.reopen stderr_real_fd
27
+ write_to_mock_out.close unless write_to_mock_out.closed?
28
+ write_to_mock_err.close unless write_to_mock_err.closed?
29
+ $SiB.stdout = read_from_mock_out.read
30
+ $SiB.stderr = read_from_mock_err.read
31
+ read_from_mock_out.close
32
+ read_from_mock_err.close
17
33
 
18
34
  $SiB.exitstatus ||= 0
19
35
  $SiB.exitstatus = 1 if $!
20
36
  $SiB.exitstatus = $!.status if $!.kind_of? SystemExit
21
37
  $SiB.bug_in_sib = $! && ! $!.kind_of?(SystemExit)
22
38
 
23
- real_stdout.write YAML.dump $SiB
39
+ stdout_real_fd.write JSON.dump $SiB.to_primitive
24
40
  end
@@ -1,3 +1,3 @@
1
1
  class SeeingIsBelieving
2
- VERSION = '2.1.3'
2
+ VERSION = '2.1.4'
3
3
  end
@@ -41,10 +41,7 @@ class SeeingIsBelieving
41
41
  rewriter.insert_after range, after_each.call(line_num)
42
42
  end
43
43
 
44
- # another stupid hack to get around a heredoc at the end of the document
45
- # hopefully we can remove this after next version of Parser is released
46
- last_index, (range, col) = wrappings.max_by(&:first)
47
- range ||= root.location.expression
44
+ range = root.location.expression
48
45
  rewriter.insert_after range, after_all
49
46
  end
50
47
 
@@ -141,16 +138,23 @@ class SeeingIsBelieving
141
138
  add_to_wrappings range
142
139
  add_children ast.children.last
143
140
  end
144
- when :lvasgn, :ivasgn, :gvasgn, :cvasgn, :casgn # local variable, instance variable, global variable, class variable, constant
141
+ when :lvasgn, # a = 1
142
+ :ivasgn, # @a = 1
143
+ :gvasgn, # $a = 1
144
+ :cvasgn, # @@a = 1
145
+ :casgn, # A = 1
146
+ :or_asgn, # a ||= b
147
+ :and_asgn, # a &&= b
148
+ :op_asgn # a += b, a -= b, a *= b, etc
149
+
145
150
  # because the RHS can be a heredoc, and parser currently handles heredocs locations incorrectly
146
151
  # we must hack around this
147
-
148
152
  if ast.children.last.kind_of? ::AST::Node
149
153
  begin_pos = ast.location.expression.begin_pos
150
154
  end_pos = heredoc_hack(ast.children.last).location.expression.end_pos
151
155
  range = Parser::Source::Range.new buffer, begin_pos, end_pos
152
156
  add_to_wrappings range
153
- add_children ast
157
+ add_children ast, true
154
158
  end
155
159
  when :send
156
160
  # because the target and the last child can be heredocs
@@ -160,7 +164,11 @@ class SeeingIsBelieving
160
164
  range = ast.location.expression
161
165
 
162
166
  # first two children: target, message, so we want the last child only if it is an argument
163
- target, message, *, last_arg = ast.children
167
+ children = ast.children
168
+ target = children[0]
169
+ message = children[1]
170
+ last_arg = children.size > 2 ? children[-1] : nil
171
+
164
172
 
165
173
  # last arg is a heredoc, use the closing paren, or the end of the first line of the heredoc
166
174
  if heredoc? last_arg