seeing_is_believing 3.0.0.beta.4 → 3.0.0.beta.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -8
  3. data/Rakefile +1 -1
  4. data/Readme.md +65 -25
  5. data/bin/seeing_is_believing +1 -0
  6. data/docs/sib-streaming.gif +0 -0
  7. data/features/deprecated-flags.feature +62 -2
  8. data/features/errors.feature +12 -7
  9. data/features/examples.feature +143 -4
  10. data/features/flags.feature +89 -29
  11. data/features/regression.feature +58 -14
  12. data/features/support/env.rb +4 -0
  13. data/features/xmpfilter-style.feature +181 -36
  14. data/lib/seeing_is_believing.rb +44 -33
  15. data/lib/seeing_is_believing/binary.rb +31 -88
  16. data/lib/seeing_is_believing/binary/align_chunk.rb +30 -11
  17. data/lib/seeing_is_believing/binary/annotate_end_of_file.rb +10 -16
  18. data/lib/seeing_is_believing/binary/annotate_every_line.rb +5 -25
  19. data/lib/seeing_is_believing/binary/annotate_marked_lines.rb +136 -0
  20. data/lib/seeing_is_believing/binary/comment_lines.rb +8 -10
  21. data/lib/seeing_is_believing/binary/commentable_lines.rb +20 -26
  22. data/lib/seeing_is_believing/binary/config.rb +392 -0
  23. data/lib/seeing_is_believing/binary/data_structures.rb +57 -0
  24. data/lib/seeing_is_believing/binary/engine.rb +104 -0
  25. data/lib/seeing_is_believing/binary/{comment_formatter.rb → format_comment.rb} +6 -6
  26. data/lib/seeing_is_believing/binary/remove_annotations.rb +29 -28
  27. data/lib/seeing_is_believing/binary/rewrite_comments.rb +42 -43
  28. data/lib/seeing_is_believing/code.rb +105 -49
  29. data/lib/seeing_is_believing/debugger.rb +6 -5
  30. data/lib/seeing_is_believing/error.rb +6 -17
  31. data/lib/seeing_is_believing/evaluate_by_moving_files.rb +78 -129
  32. data/lib/seeing_is_believing/event_stream/consumer.rb +114 -64
  33. data/lib/seeing_is_believing/event_stream/events.rb +169 -11
  34. data/lib/seeing_is_believing/event_stream/handlers/debug.rb +57 -0
  35. data/lib/seeing_is_believing/event_stream/handlers/record_exitstatus.rb +18 -0
  36. data/lib/seeing_is_believing/event_stream/handlers/stream_json_events.rb +45 -0
  37. data/lib/seeing_is_believing/event_stream/handlers/update_result.rb +39 -0
  38. data/lib/seeing_is_believing/event_stream/producer.rb +25 -24
  39. data/lib/seeing_is_believing/hash_struct.rb +206 -0
  40. data/lib/seeing_is_believing/result.rb +20 -3
  41. data/lib/seeing_is_believing/the_matrix.rb +20 -12
  42. data/lib/seeing_is_believing/version.rb +1 -1
  43. data/lib/seeing_is_believing/wrap_expressions.rb +55 -115
  44. data/lib/seeing_is_believing/wrap_expressions_with_inspect.rb +14 -0
  45. data/seeing_is_believing.gemspec +1 -1
  46. data/spec/binary/alignment_specs.rb +27 -0
  47. data/spec/binary/comment_lines_spec.rb +3 -2
  48. data/spec/binary/config_spec.rb +657 -0
  49. data/spec/binary/engine_spec.rb +97 -0
  50. data/spec/binary/{comment_formatter_spec.rb → format_comment_spec.rb} +2 -2
  51. data/spec/binary/marker_spec.rb +71 -0
  52. data/spec/binary/options_spec.rb +0 -0
  53. data/spec/binary/remove_annotations_spec.rb +31 -18
  54. data/spec/binary/rewrite_comments_spec.rb +26 -11
  55. data/spec/code_spec.rb +190 -6
  56. data/spec/debugger_spec.rb +4 -0
  57. data/spec/evaluate_by_moving_files_spec.rb +38 -20
  58. data/spec/event_stream_spec.rb +265 -116
  59. data/spec/hash_struct_spec.rb +514 -0
  60. data/spec/seeing_is_believing_spec.rb +108 -46
  61. data/spec/spec_helper.rb +9 -0
  62. data/spec/wrap_expressions_spec.rb +207 -172
  63. metadata +30 -18
  64. data/docs/for-presentations +0 -33
  65. data/lib/seeing_is_believing/binary/annotate_xmpfilter_style.rb +0 -128
  66. data/lib/seeing_is_believing/binary/interpret_flags.rb +0 -156
  67. data/lib/seeing_is_believing/binary/parse_args.rb +0 -263
  68. data/lib/seeing_is_believing/event_stream/update_result.rb +0 -24
  69. data/lib/seeing_is_believing/inspect_expressions.rb +0 -21
  70. data/lib/seeing_is_believing/parser_helpers.rb +0 -82
  71. data/spec/binary/interpret_flags_spec.rb +0 -332
  72. data/spec/binary/parse_args_spec.rb +0 -415
@@ -20,13 +20,18 @@ RSpec.describe SeeingIsBelieving do
20
20
  options.delete :debug
21
21
  options[:debugger] = SeeingIsBelieving::Debugger.new(stream: $stderr, colour: true)
22
22
  end
23
- described_class.new(input, options).call
23
+ described_class.new(input, options).call.result
24
24
  end
25
25
 
26
26
  def values_for(input, options={})
27
27
  invoke(input, options).to_a
28
28
  end
29
29
 
30
+ root_path = File.expand_path("../..", __FILE__)
31
+ proving_grounds = File.expand_path('proving_grounds', root_path)
32
+ before(:all) { Dir.mkdir proving_grounds unless Dir.exist? proving_grounds }
33
+ around { |spec| Dir.chdir proving_grounds, &spec }
34
+
30
35
  let(:proving_grounds_dir) { File.expand_path '../../proving_grounds', __FILE__ }
31
36
 
32
37
  it 'takes a string and returns a result of the line numbers (counting from 1) and each inspected result from that line' do
@@ -35,6 +40,10 @@ RSpec.describe SeeingIsBelieving do
35
40
  expect(invoke(input)[2]).to eq ['"22"']
36
41
  end
37
42
 
43
+ it 'blows up if given unknown options' do
44
+ expect { invoke '', not_an_option: 123 }.to raise_error KeyError, /not_an_option/
45
+ end
46
+
38
47
  it 'only invokes inspect once' do
39
48
  input = "class Fixnum; def inspect; 'NUM'\nend\nend\n1"
40
49
  expect(invoke(input)[1]).to eq ['"NUM"']
@@ -45,10 +54,10 @@ RSpec.describe SeeingIsBelieving do
45
54
  end
46
55
 
47
56
  it 'records various useful information on the result' do
48
- result = invoke '', number_of_captures: 10, filename: 'abc.rb'
57
+ result = invoke '', max_line_captures: 10, filename: 'abc.rb'
49
58
  expect(result.sib_version).to eq SeeingIsBelieving::VERSION
50
59
  expect(result.ruby_version).to eq RUBY_VERSION
51
- expect(result.number_of_captures).to eq 10 # TODO: rename to max_captures_per_line ?? (also max_line_captures would prob get renamed if we do this)
60
+ expect(result.max_line_captures).to eq 10
52
61
  expect(result.num_lines).to eq 1
53
62
  expect(result.filename).to eq 'abc.rb'
54
63
  end
@@ -58,14 +67,12 @@ RSpec.describe SeeingIsBelieving do
58
67
  end
59
68
 
60
69
  it 'allows uers to pass in their own inspection recorder' do
61
- wrapper = lambda { |program, filename, num_captures|
62
- SeeingIsBelieving::InspectExpressions.call \
63
- program,
64
- filename,
65
- num_captures,
66
- after_each: -> line_number { ").tap { $SiB.record_result(:inspect, #{line_number}, 'zomg') }" }
70
+ wrapper = lambda { |program|
71
+ SeeingIsBelieving::WrapExpressions.call program,
72
+ before_each: -> * { '(' },
73
+ after_each: -> line_number { ").tap { $SiB.record_result(:inspect, #{line_number}, 'zomg') }" }
67
74
  }
68
- expect(invoke(':body', record_expressions: wrapper)[1]).to eq ['"zomg"']
75
+ expect(invoke(':body', rewrite_code: wrapper)[1]).to eq ['"zomg"']
69
76
  end
70
77
 
71
78
  it 'remembers context of previous lines' do
@@ -74,8 +81,8 @@ RSpec.describe SeeingIsBelieving do
74
81
 
75
82
  it 'can be invoked multiple times, returning the same result' do
76
83
  believer = described_class.new("$xyz||=1\n$xyz+=1")
77
- expect(believer.call.to_a).to eq [['1'], ['2']]
78
- expect(believer.call.to_a).to eq [['1'], ['2']]
84
+ expect(believer.call).to eq believer.call
85
+ expect(believer.call.result.to_a).to eq [['1'], ['2']]
79
86
  end
80
87
 
81
88
  it 'is evaluated at the toplevel' do
@@ -179,7 +186,24 @@ RSpec.describe SeeingIsBelieving do
179
186
  end
180
187
  end
181
188
 
189
+ it 'supports catch/throw' do
190
+ values = values_for("catch :zomg do\n"\
191
+ " 1\n"\
192
+ " throw :zomg\n"\
193
+ " 2\n"\
194
+ "end")
195
+ expect(values).to eq [[], ['1'], [], [], ['nil']]
182
196
 
197
+ result = invoke("throw :zomg")
198
+ expect(result.exception.message).to match /:zomg/
199
+ end
200
+
201
+ it 'does not fuck up the __ENCODING__ macro' do
202
+ expect(values_for("# encoding: utf-8
203
+ __ENCODING__")).to eq [[], ["#<Encoding:UTF-8>"]]
204
+ expect(values_for("# encoding: ascii-8bit
205
+ __ENCODING__")).to eq [[], ["#<Encoding:ASCII-8BIT>"]]
206
+ end
183
207
 
184
208
  it 'does not fuck up __LINE__ macro' do
185
209
  expect(values_for( '__LINE__
@@ -314,7 +338,7 @@ RSpec.describe SeeingIsBelieving do
314
338
  it 'can deal with methods that are invoked entirely on the next line' do
315
339
  expect(values_for("a = 1\n.even?\na")).to eq [['1'], ['false'], ['false']]
316
340
  expect(values_for("a = 1.\neven?\na")).to eq [['1'], ['false'], ['false']]
317
- expect(values_for("1\n.even?\n__END__")).to eq [['1'], ['false'], []] # TODO: would be nice if we could consolidate this shit so we could find out things like __END__ without repeatedly parsing, I'd prefer if this did not imply there were results on this line -.-
341
+ expect(values_for("1\n.even?\n__END__")).to eq [['1'], ['false'], []]
318
342
  end
319
343
 
320
344
  it 'does not record leading comments' do
@@ -325,15 +349,35 @@ RSpec.describe SeeingIsBelieving do
325
349
  end
326
350
 
327
351
  it 'times out if the timeout limit is exceeded' do
328
- expect { invoke "sleep 0.2", timeout: 0.1 }.to raise_error Timeout::Error
352
+ expect { invoke "sleep 0.2", timeout_seconds: 0.1 }.to raise_error Timeout::Error
329
353
  end
330
354
 
331
355
  it 'records the exit status' do
332
- expect(invoke('raise "omg"').exitstatus).to eq 1
333
- expect(invoke('exit 123').exitstatus).to eq 123
334
- expect(invoke('at_exit { exit 121 }').exitstatus).to eq 121
356
+ expect(invoke("" ).exitstatus).to eq 0 # happy path: no exceptions
357
+ expect(invoke('raise "omg"' ).exitstatus).to eq 1 # exceptions: status is 1
358
+ expect(invoke('exit' ).exitstatus).to eq 0 # call exit, but with no args
359
+ expect(invoke('exit 0' ).exitstatus).to eq 0 # set numeric status with exit
360
+ expect(invoke('exit 123' ).exitstatus).to eq 123
361
+ expect(invoke('exit true' ).exitstatus).to eq 0 # set boolean status with exit
362
+ expect(invoke('exit false' ).exitstatus).to eq 1
363
+ expect(invoke('at_exit { exit 121 }').exitstatus).to eq 121 # when status is set in an at_exit hook
364
+
365
+ # setting status with exit!
366
+ # since we might be overriding this (a questionable decision) we make sure it behaves as expected (no at_exit hooks are called)
367
+ result = invoke 'at_exit { puts "omg" }; exit!'
368
+ expect([result.exitstatus, result.stdout, result.stderr]).to eq [1, '', '']
369
+
370
+ result = invoke 'at_exit { puts "omg" }; exit! 100'
371
+ expect([result.exitstatus, result.stdout]).to eq [100, '']
372
+
373
+ result = invoke 'at_exit { puts "omg" }; Kernel.exit! 101'
374
+ expect([result.exitstatus, result.stdout]).to eq [101, '']
375
+
376
+ result = invoke 'at_exit { puts "omg" }; Kernel.exit! 102'
377
+ expect([result.exitstatus, result.stdout]).to eq [102, '']
335
378
  end
336
379
 
380
+
337
381
  it 'records lines that have comments on them' do
338
382
  expect(values_for('1+1 # comment uno
339
383
  #comment dos
@@ -373,23 +417,28 @@ RSpec.describe SeeingIsBelieving do
373
417
  ['[6, 12]']]
374
418
  end
375
419
 
376
- it 'can be limited to a specific number of captures' do
377
- expect(values_for "2.times do\n1\nend", number_of_captures: 1).to \
420
+ it 'can be limited to a specific number of captures per line' do
421
+ expect(values_for "2.times do\n1\nend", max_line_captures: 1).to \
378
422
  eq [['2'],
379
423
  ['1', '...'],
380
424
  ['2']]
381
425
  end
382
426
 
383
427
  describe 'BEGIN and END' do
428
+ it 'doesn\'t fuck up when the BEGIN block exits / raises' do
429
+ expect(invoke("BEGIN { exit 100 }").exitstatus).to eq 100
430
+ expect(invoke("BEGIN { exit! 100 }").exitstatus).to eq 100
431
+ expect(invoke("BEGIN { raise Exception, 'wat'}").exception.message).to eq 'wat'
432
+ end
433
+
384
434
  it 'Executes in the appropriate order' do
385
- pending 'not implemented'
386
- expect(invoke <<-CODE).stdout.to eq "1\n2\n3\n4\n5\n6\n7\n8\n9\n"
435
+ expect(invoke(<<-CODE).stdout).to eq "1\n2\n3\n4\n5\n6\n7\n8\n9\n"
387
436
  p 3
388
- END { p 8 }
437
+ END { p 9 }
389
438
  p 4
390
439
  BEGIN { p 1 }
391
440
  p 5
392
- END { p 9 }
441
+ END { p 8 }
393
442
  p 6
394
443
  BEGIN { p 2 }
395
444
  p 7
@@ -397,7 +446,6 @@ RSpec.describe SeeingIsBelieving do
397
446
  end
398
447
 
399
448
  it 'Maintains correct line numbers' do
400
- pending 'not implemented'
401
449
  expected_values = [
402
450
  ['1'],
403
451
  [],
@@ -434,7 +482,7 @@ RSpec.describe SeeingIsBelieving do
434
482
  result = invoke(%[def self.inspect
435
483
  self
436
484
  end
437
- self], filename: 'blowsup.rb') # TODO This actually writes the file into the root of SiB
485
+ self], filename: 'blowsup.rb')
438
486
  expect(result).to have_exception
439
487
  expect(result.exception.class_name).to eq 'SystemStackError'
440
488
  expect(result.exception.backtrace.grep(/blowsup.rb/)).to_not be_empty # backtrace includes a line that we can show
@@ -454,34 +502,48 @@ RSpec.describe SeeingIsBelieving do
454
502
  let(:debugger) { SeeingIsBelieving::Debugger.new stream: stream }
455
503
 
456
504
  def call
457
- invoke "1", debugger: debugger
505
+ result = invoke "1", debugger: debugger
506
+ expect(result[1]).to eq ["1"]
507
+ result
458
508
  end
459
509
 
460
510
  it 'prints the pre-evaluated program' do
461
511
  call
462
- expect(stream.string).to include "TRANSLATED PROGRAM:"
463
- expect(stream.string).to include "$SiB.num_lines" # there is more, but we're just interested in showing that it wound up in the stream
512
+ expect(stream.string).to include "REWRITTEN PROGRAM:"
513
+ expect(stream.string).to include "$SiB.record_result" # there is more, but we're just interested in showing that it wound up in the stream
464
514
  end
465
515
 
466
- it 'prints the result' do
516
+ it 'records eventstream information' do
467
517
  call
468
- expect(stream.string).to include "RESULT:"
469
- expect(stream.string).to include 'SIB::Result'
470
- expect(stream.string).to include '@results={'
518
+ expect(stream.string).to include "EVENTS"
519
+ end
520
+ end
521
+
522
+ describe 'exec' do
523
+ it 'passes stdin, stdout, stderr, and actually does exec the process' do
524
+ result = invoke \
525
+ "1+1\n"\
526
+ "$stdout.puts *1..1000\n"\
527
+ "$stderr.puts *1..1000\n"\
528
+ "exec %(ruby -e '$stdout.puts %{from stdin: } + gets.inspect
529
+ $stdout.puts %[out from exec]
530
+ $stderr.puts %[err from exec]')\n"\
531
+ "$stdout.puts 'this will never be executed'",
532
+ stdin: "the-stdin-dataz"
533
+ expect(result[1]).to eq ['2']
534
+ nums = (1..1000).map { |n| "#{n}\n" }.join('')
535
+ expect(result.stdout).to eq "#{nums}from stdin: \"the-stdin-dataz\"\nout from exec\n"
536
+ expect(result.stderr).to eq "#{nums}err from exec\n"
537
+ end
538
+
539
+ it 'works for Kernel#exec, Kernel.exec, Process.exec' do
540
+ expect(invoke('exec "ruby", "-e", "puts %(hello)"').stdout).to eq "hello\n"
541
+ expect(invoke('Kernel.exec "ruby", "-e", "puts %(hello)"').stdout).to eq "hello\n"
542
+ expect(invoke('Process.exec "ruby", "-e", "puts %(hello)"').stdout).to eq "hello\n"
543
+ end
544
+
545
+ it 'gets the exit status off of the child process' do
546
+ expect(invoke('exec "ruby", "-e", "exit 5"').exitstatus).to eq 5
471
547
  end
472
- # should ProgramRewriter have some debug options?
473
- end
474
-
475
- it 'can deal with exec' do
476
- result = invoke \
477
- "1+1\n"\
478
- "$stdout.puts *1..1000\n"\
479
- "$stderr.puts *1..1000\n"\
480
- "exec %(ruby -e '$stdout.puts %[out from exec];
481
- $stderr.puts %[err from exec]')"
482
- expect(result[1]).to eq ['2']
483
- nums = (1..1000).map { |n| "#{n}\n" }.join('')
484
- expect(result.stdout).to eq "#{nums}out from exec\n"
485
- expect(result.stderr).to eq "#{nums}err from exec\n"
486
548
  end
487
549
  end
@@ -1,3 +1,12 @@
1
+ module SibSpecHelpers
2
+ def pending!(message="Not yet implemented")
3
+ pending message
4
+ raise message
5
+ end
6
+ end
7
+
1
8
  RSpec.configure do |c|
2
9
  c.disable_monkey_patching!
10
+ c.include SibSpecHelpers
11
+ c.filter_run_excluding :not_implemented
3
12
  end
@@ -2,12 +2,29 @@ require 'spec_helper'
2
2
  require 'seeing_is_believing/wrap_expressions'
3
3
 
4
4
  RSpec.describe SeeingIsBelieving::WrapExpressions do
5
- def wrap(code)
6
- described_class.call code,
7
- before_each: -> * { '<' },
8
- after_each: -> * { '>' }
5
+ def wrap(code, overrides={})
6
+ code = code + "\n" unless code.end_with? "\n"
7
+ described_class.call(code,
8
+ before_all: -> { overrides.fetch :before_all, '' },
9
+ after_all: -> { overrides.fetch :after_all, '' },
10
+ before_each: -> * { overrides.fetch :before_each, '<' },
11
+ after_each: -> * { overrides.fetch :after_each, '>' }
12
+ ).chomp
9
13
  end
10
14
 
15
+ def wrap_with_body(code, overrides={})
16
+ wrap code, { before_all: '[',
17
+ after_all: ']',
18
+ }.merge(overrides)
19
+ end
20
+
21
+ def heredoc_wrap(code, overrides={})
22
+ wrap_with_body code, { before_each: '{'.freeze,
23
+ after_each: '}'.freeze,
24
+ }.merge(overrides)
25
+ end
26
+
27
+
11
28
  it 'raises a SyntaxError if the program is invalid' do
12
29
  expect { wrap '+' }.to raise_error SyntaxError
13
30
  end
@@ -17,34 +34,31 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
17
34
  end
18
35
 
19
36
  describe 'wrapping the body' do
20
- let(:options) { { before_all: -> { "[".freeze },
21
- after_all: -> { "]".freeze },
22
- before_each: -> * { '<'.freeze },
23
- after_each: -> * { '>'.freeze } } }
24
-
25
37
  it 'wraps the entire body, ignoring leading comments and the data segment' do
26
- expect(described_class.call("#comment\nA\n__END__\n1", options)).to eq "#comment\n[<A>]\n__END__\n1"
38
+ expect(wrap_with_body "#comment\nA\n__END__\n1").to eq "#comment\n[<A>]\n__END__\n1"
39
+ expect(wrap_with_body "#comment\n__END__\n1").to eq "[]#comment\n__END__\n1"
27
40
  end
28
41
 
29
- it 'comes in on blank lines' do
30
- expect(described_class.call('', options)).to eq '[]'
42
+ it 'wraps when code is an empty string' do
43
+ expect(wrap_with_body '').to eq '[]'
31
44
  end
32
45
 
33
- it 'comes in first when there are only comments' do
34
- expect(described_class.call("# abc", options)).to eq "[]# abc"
46
+ it 'places body before first comment when there are only comments' do
47
+ expect(wrap_with_body "# abc").to eq "[]# abc"
35
48
  end
36
49
 
37
- it 'comes in before trailing comments' do
38
- expect(described_class.call("1# abc", options)).to eq "[<1>]# abc"
50
+ it 'places body before trailing comments, but still wraps code' do
51
+ expect(wrap_with_body "1# abc").to eq "[<1>]# abc"
39
52
  end
40
53
 
41
54
  # this changes the number of lines, annoyingly, though it shouldn't mess anything up,
42
55
  # unless you were trying to reopen the file to read it, in which case, *surprise* the whole thing's been rewritten
43
56
  it 'injects a newline if there is a data segment and the after block doesn\'t end in a newline' do
44
- expect(described_class.call("__END__", options)).to eq "[]\n__END__"
45
- expect(described_class.call("\n__END__", options)).to eq "[]\n__END__"
46
- expect(described_class.call("\n\n__END__", options)).to eq "[]\n\n__END__"
47
- expect(described_class.call("__END__!", options)).to eq "[<__END__!>]"
57
+ expect(wrap_with_body "__END__").to eq "[]\n__END__"
58
+ expect(wrap_with_body "\n__END__").to eq "[]\n__END__"
59
+ expect(wrap_with_body "\n\n__END__").to eq "[]\n\n__END__"
60
+ expect(wrap_with_body "__END__!").to eq "[<__END__!>]"
61
+ expect(wrap_with_body "%(\n__END__\n)").to eq "[<%(\n__END__\n)>]"
48
62
  end
49
63
 
50
64
  it 'wraps bodies that are wrapped in parentheses' do
@@ -54,35 +68,28 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
54
68
 
55
69
  context 'fucking heredocs' do
56
70
  example 'single heredoc' do
57
- expect(described_class.call("<<A\nA", options)).to eq "[<<<A>]\nA"
71
+ expect(heredoc_wrap "<<A\nA").to eq "[{<<A}]\nA"
58
72
  end
59
73
 
60
74
  example 'multiple heredocs' do
61
- # a stupid implementation issue from hacking around heredocs
62
- # causes the toplevel begin to wrap the whole file.
63
- # It's fine b/c it is ultimately the same, but that's why it's
64
- # "[<<<<A>\nA\n<<B>]\nB"
65
- # instead of
66
- # "[<<<A>\nA\n<<<B>]\nB"
67
- expect(described_class.call("<<A\nA\n<<B\nB", options)).to eq "[<<<<A>\nA\n<<B>]\nB"
75
+ expect(heredoc_wrap "<<A\nA\n<<B\nB").to eq "[{<<A}\nA\n{<<B}]\nB"
68
76
  end
69
77
 
70
78
  example 'heredocs as targets and arguments to methods' do
71
- expect(described_class.call("<<A.size 1\nA", options)).to eq "[<<<A.size 1>]\nA"
72
- expect(described_class.call("<<A.size\nA", options)).to eq "[<<<A.size>]\nA"
73
- expect(described_class.call("<<A.size()\nA", options)).to eq "[<<<A.size()>]\nA"
74
- expect(described_class.call("a.size <<A\nA", options)).to eq "[<a.size <<A>]\nA"
75
- expect(described_class.call("<<A.size <<B\nA\nB", options)).to eq "[<<<A.size <<B>]\nA\nB"
76
- expect(described_class.call("<<A.size(<<B)\nA\nB", options)).to eq "[<<<A.size(<<B)>]\nA\nB"
79
+ expect(heredoc_wrap "<<A.size 1\nA").to eq "[{<<A.size 1}]\nA"
80
+ expect(heredoc_wrap "<<A.size\nA").to eq "[{<<A.size}]\nA"
81
+ expect(heredoc_wrap "<<A.size()\nA").to eq "[{<<A.size()}]\nA"
82
+ expect(heredoc_wrap "a.size <<A\nA").to eq "[{a.size <<A}]\nA"
83
+ expect(heredoc_wrap "<<A.size <<B\nA\nB").to eq "[{<<A.size <<B}]\nA\nB"
84
+ expect(heredoc_wrap "<<A.size(<<B)\nA\nB").to eq "[{<<A.size(<<B)}]\nA\nB"
77
85
  end
78
86
  end
79
87
 
80
88
  it 'identifies the last line of the body' do
81
- expect(described_class.call("a\n"\
82
- "def b\n"\
83
- " c = 1\n"\
84
- "end",
85
- options)
89
+ expect(wrap_with_body "a\n"\
90
+ "def b\n"\
91
+ " c = 1\n"\
92
+ "end"
86
93
  ).to eq "[<a>\n"\
87
94
  "<def b\n"\
88
95
  " <c = 1>\n"\
@@ -92,7 +99,7 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
92
99
 
93
100
  it 'passes the current line number to the before_each and after_each wrappers' do
94
101
  pre_line_num = post_line_num = nil
95
- described_class.call("\na",
102
+ described_class.call("\na\n",
96
103
  before_each: -> _pre_line_num { pre_line_num = _pre_line_num; '<' },
97
104
  after_each: -> _post_line_num { post_line_num = _post_line_num; '>' }
98
105
  )
@@ -101,8 +108,7 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
101
108
  end
102
109
 
103
110
  it 'does nothing for an empty program' do
104
- expect(wrap("")).to eq ""
105
- expect(wrap("\n")).to eq "\n"
111
+ expect(wrap("")).to eq "" # note that code will fix the missing newline, and wrap will chomp it from the result for convenience
106
112
  end
107
113
 
108
114
  it 'ignores comments' do
@@ -112,79 +118,6 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
112
118
  expect(wrap "=begin\n1\n=end\n2").to eq "=begin\n1\n=end\n<2>"
113
119
  end
114
120
 
115
- describe 'void value expressions' do
116
- def void_value?(ast)
117
- klass = described_class.new '', {}
118
- klass.__send__(:void_value?, ast)
119
- end
120
-
121
- def ast_for(code)
122
- Parser::CurrentRuby.parse code
123
- end
124
-
125
- it 'knows a `return`, `next`, `redo`, `retry`, and `break` are void values' do
126
- expect(void_value?(ast_for("def a; return; end").children.last)).to be true
127
- expect(void_value?(ast_for("loop { next }" ).children.last)).to be true
128
- expect(void_value?(ast_for("loop { redo }" ).children.last)).to be true
129
- expect(void_value?(ast_for("loop { break }" ).children.last)).to be true
130
-
131
- the_retry = ast_for("begin; rescue; retry; end").children.first.children[1].children.last
132
- expect(the_retry.type).to eq :retry
133
- expect(void_value? the_retry).to eq true
134
- end
135
- it 'knows an `if` is a void value if either side is a void value' do
136
- the_if = ast_for("def a; if 1; return 2; else; 3; end; end").children.last
137
- expect(the_if.type).to eq :if
138
- expect(void_value?(the_if)).to be true
139
-
140
- the_if = ast_for("def a; if 1; 2; else; return 3; end; end").children.last
141
- expect(the_if.type).to eq :if
142
- expect(void_value?(the_if)).to be true
143
-
144
- the_if = ast_for("def a; if 1; 2; else; 3; end; end").children.last
145
- expect(the_if.type).to eq :if
146
- expect(void_value?(the_if)).to be false
147
- end
148
- it 'knows a begin is a void value if its last element is a void value' do
149
- the_begin = ast_for("loop { begin; break; end }").children.last
150
- expect([:begin, :kwbegin]).to include the_begin.type
151
- expect(void_value?(the_begin)).to be true
152
-
153
- the_begin = ast_for("loop { begin; 1; end }").children.last
154
- expect([:begin, :kwbegin]).to include the_begin.type
155
- expect(void_value?(the_begin)).to be false
156
- end
157
- it 'knows a rescue is a void value if its last child or its else is a void value' do
158
- the_rescue = ast_for("begin; rescue; retry; end").children.first
159
- expect(the_rescue.type).to eq :rescue
160
- expect(void_value?(the_rescue)).to be true
161
-
162
- the_rescue = ast_for("begin; rescue; 1; else; retry; end").children.first
163
- expect(the_rescue.type).to eq :rescue
164
- expect(void_value?(the_rescue)).to be true
165
-
166
- the_rescue = ast_for("begin; rescue; 1; else; 2; end").children.first
167
- expect(the_rescue.type).to eq :rescue
168
- expect(void_value?(the_rescue)).to be false
169
- end
170
- it 'knows an ensure is a void value if its body or ensure portion are void values' do
171
- the_ensure = ast_for("loop { begin; break; ensure; 1; end }").children.last.children.last
172
- expect(the_ensure.type).to eq :ensure
173
- expect(void_value?(the_ensure)).to be true
174
-
175
- the_ensure = ast_for("loop { begin; 1; ensure; break; end }").children.last.children.last
176
- expect(the_ensure.type).to eq :ensure
177
- expect(void_value?(the_ensure)).to be true
178
-
179
- the_ensure = ast_for("loop { begin; 1; ensure; 2; end }").children.last.children.last
180
- expect(the_ensure.type).to eq :ensure
181
- expect(void_value?(the_ensure)).to be false
182
- end
183
- it 'knows other things are not void values' do
184
- expect(void_value?(ast_for "123")).to be false
185
- end
186
- end
187
-
188
121
  describe 'basic expressions' do
189
122
  it 'wraps an expression' do
190
123
  expect(wrap("A")).to eq "<A>"
@@ -200,6 +133,10 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
200
133
  it 'does not wrap multiple expressions when they constitute a void value' do
201
134
  expect(wrap("def a\n1\nreturn 2\nend")).to eq "<def a\n<1>\nreturn <2>\nend>"
202
135
  expect(wrap("def a\nreturn 1\n2\nend")).to eq "<def a\nreturn <1>\n<2>\nend>"
136
+ # BUG, but I'm skipping it, b/c it's borderline invalid.
137
+ # To the point that Parser doesn't even emit the else clause in the AST
138
+ # And Ruby will warn you that it's useless
139
+ # expect(wrap("begin\n1\nelse\nbreak\nend")).to eq "begin\n<1>\nelse\nbreak\nend"
203
140
  end
204
141
 
205
142
  it 'wraps nested expressions' do
@@ -212,9 +149,6 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
212
149
 
213
150
  # many of these taken from http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Literals
214
151
  it 'wraps simple literals' do
215
- # should maybe also do %i[] and %I[] for symbols,
216
- # but that's only Ruby 2.0, so I'm ignoring it for now
217
- # (I expect it to handle them just fine)
218
152
  %w|123
219
153
  -123
220
154
  1_123
@@ -236,6 +170,7 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
236
170
  1...2
237
171
 
238
172
  (true==true)..(1==2)
173
+ (true==true)...(1==2)
239
174
 
240
175
  true
241
176
  false
@@ -268,16 +203,16 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
268
203
  end
269
204
 
270
205
  it 'wraps macros' do
271
- # should this actually replace __FILE__ and __LINE__ so as to avoid fucking up values with the rewrite?
272
- # there is also __dir__, but it's only 2.0
273
206
  expect(wrap("__FILE__")).to eq "<__FILE__>"
274
207
  expect(wrap("__LINE__")).to eq "<__LINE__>"
208
+ expect(wrap("__ENCODING__")).to eq "<__ENCODING__>"
275
209
  expect(wrap("defined? a")).to eq "<defined? a>"
276
210
  end
277
211
 
278
212
  it 'does not wrap alias, undef' do
279
213
  expect(wrap("alias tos to_s")).to eq "alias tos to_s"
280
214
  expect(wrap("undef tos")).to eq "undef tos"
215
+ expect(wrap("alias $a $b")).to eq "alias $a $b"
281
216
  end
282
217
 
283
218
  it 'wraps syscalls, but not code interpolated into them' do
@@ -290,6 +225,7 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
290
225
  it 'wraps them' do
291
226
  expect(wrap('a')).to eq "<a>"
292
227
  expect(wrap("$a")).to eq "<$a>"
228
+ expect(wrap("$1")).to eq "<$1>"
293
229
  expect(wrap("@a")).to eq "<@a>"
294
230
  expect(wrap("@@a")).to eq "<@@a>"
295
231
  end
@@ -311,6 +247,8 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
311
247
  expect(wrap("a { }")).to eq "<a { }>"
312
248
  expect(wrap("a {\n}")).to eq "<a {\n}>"
313
249
  expect(wrap("a(b) {\n}")).to eq "<a(b) {\n}>"
250
+ expect(wrap("a(&b\n)")).to eq "<a(&<b>\n)>"
251
+ expect(wrap("a(&lambda { }\n)")).to eq "<a(&<lambda { }>\n)>"
314
252
  end
315
253
 
316
254
  it 'wraps method calls with an explicit receiver' do
@@ -345,9 +283,9 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
345
283
  expect(wrap("a 1,\n2")).to eq "<a <1>,\n2>"
346
284
  end
347
285
 
348
- it 'does not wrap splat args' do
349
- expect(wrap("a(\n*a\n)")).to eq "<a(\n*a\n)>"
350
- expect(wrap("a(\n*1..2\n)")).to eq "<a(\n*1..2\n)>"
286
+ it 'does wraps splat args' do
287
+ expect(wrap("a(\n*a\n)")).to eq "<a(\n*<a>\n)>"
288
+ expect(wrap("a(\n*1..2\n)")).to eq "<a(\n*<1..2>\n)>"
351
289
  end
352
290
 
353
291
  it 'does not wrap hash args' do
@@ -407,11 +345,13 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
407
345
  expect(wrap("a -= 1")).to eq "<a -= 1>"
408
346
  expect(wrap("a /= 1")).to eq "<a /= 1>"
409
347
  expect(wrap("a **= 1")).to eq "<a **= 1>"
348
+ expect(wrap("a != 1")).to eq "<a != 1>"
410
349
  expect(wrap("a |= 1")).to eq "<a |= 1>"
411
350
  expect(wrap("a &= 1")).to eq "<a &= 1>"
412
351
  expect(wrap("a ||= 1")).to eq "<a ||= 1>"
413
352
  expect(wrap("a &&= 1")).to eq "<a &&= 1>"
414
353
  expect(wrap("a[1] = 2")).to eq "<a[1] = 2>"
354
+ expect(wrap("a[1,2] = 3")).to eq "<a[1,2] = 3>"
415
355
  expect(wrap("a[1] ||= 2")).to eq "<a[1] ||= 2>"
416
356
  expect(wrap("@a ||= 123")).to eq "<@a ||= 123>"
417
357
  expect(wrap("$a ||= 123")).to eq "<$a ||= 123>"
@@ -444,6 +384,7 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
444
384
  expect(wrap("a,@b={\n},{\n}")).to eq "<a,@b=<{\n}>,{\n}>"
445
385
  expect(wrap("a,@@b={\n},{\n}")).to eq "<a,@@b=<{\n}>,{\n}>"
446
386
  expect(wrap("a,$b={\n},{\n}")).to eq "<a,$b=<{\n}>,{\n}>"
387
+ expect(wrap("a,$b={\n},{\n}")).to eq "<a,$b=<{\n}>,{\n}>"
447
388
 
448
389
  # repeated assignments
449
390
  expect(wrap("a=\nb={\n}")).to eq "<a=\nb={\n}>"
@@ -502,8 +443,9 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
502
443
  expect(wrap("1 if 2")).to eq "<1 if 2>"
503
444
  end
504
445
 
505
- it 'ignores conditionals that are implicit regexes' do
506
- expect(wrap("if /a/\n1\nend")).to eq "<if /a/\n<1>\nend>"
446
+ it 'wraps implicit regexes, retaining their magic behaviour by prepending a ~' do
447
+ expect(wrap("if /a/\n1\nend")).to eq "<if <~/a/>\n<1>\nend>"
448
+ expect(wrap("/a/ &&\n1")).to eq "<</a/> &&\n1>"
507
449
  end
508
450
 
509
451
  it 'wraps ternaries' do
@@ -545,6 +487,8 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
545
487
  # not sure if I actually want this, or if it's just easier b/c it falls out of the current implementation
546
488
  it 'wraps the conditional from an inline if, when it cannot wrap the entire if' do
547
489
  expect(wrap("def a\nreturn if 1\nend")).to eq "<def a\nreturn if <1>\nend>"
490
+ # could maybe do this:
491
+ # `return 1 if b` -> `return <1> if (b) || <nil>`
548
492
  end
549
493
 
550
494
  it 'does not wrap &&, and, ||, or, not' do
@@ -567,15 +511,15 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
567
511
  expect(wrap("while 1\n2\nend")).to eq "<while <1>\n<2>\nend>"
568
512
  expect(wrap("1 while 2")).to eq "<1 while 2>"
569
513
  expect(wrap("begin\n1\nend while true")).to eq "<begin\n<1>\nend while true>"
514
+ expect(wrap("begin\n1\nend until true")).to eq "<begin\n<1>\nend until true>"
570
515
  end
571
516
  it 'wraps for/in loops collections and bodies' do
572
517
  expect(wrap("for a in range;1;end")).to eq "<for a in range;1;end>"
573
518
  expect(wrap("for a in range\n1\nend")).to eq "<for a in <range>\n<1>\nend>"
574
519
  expect(wrap("for a in range do\n1\nend")).to eq "<for a in <range> do\n<1>\nend>"
575
520
  expect(wrap("for a,b in whatev\n1\nend")).to eq "<for a,b in <whatev>\n<1>\nend>"
576
- # this one just isn't worth it for now, too edge and I'm fucking tired
577
- # wrap("for char in <<HERE.each_char\nabc\nHERE\nputs char\nend").should ==
578
- # "<for char in <<<HERE.each_char>\nabc\nHERE\n<puts char>\nend>"
521
+ expect(wrap("for char in <<HERE.each_char\nabc\nHERE\nputs char\nend"))
522
+ .to eq "<for char in <<<HERE.each_char>\nabc\nHERE\n<puts char>\nend>"
579
523
  end
580
524
  it 'does not wrap redo' do
581
525
  expect(wrap("loop do\nredo\nend")).to eq "<loop do\nredo\nend>"
@@ -596,6 +540,7 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
596
540
  it 'wraps namespaced constant access' do
597
541
  expect(wrap("::A")).to eq "<::A>"
598
542
  expect(wrap("A::B")).to eq "<A::B>"
543
+ expect(wrap("a::B")).to eq "<a::B>"
599
544
  end
600
545
  end
601
546
 
@@ -618,8 +563,8 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
618
563
  expect(wrap("%w[\n1\n]")).to eq "<%w[\n1\n]>"
619
564
  end
620
565
 
621
- it 'does not wrap splat elements' do
622
- expect(wrap("[1,\n*2..3,\n4\n]")).to eq "<[<1>,\n*2..3,\n<4>\n]>"
566
+ it 'does wraps splat elements' do
567
+ expect(wrap("[1,\n*2..3,\n4\n]")).to eq "<[<1>,\n*<2..3>,\n<4>\n]>"
623
568
  end
624
569
  end
625
570
 
@@ -683,45 +628,46 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
683
628
  end
684
629
 
685
630
  it 'wraps heredocs with call defined on them (edge cases on edge cases *sigh*)' do
686
- expect(wrap("<<HERE.()\na\nHERE")).to eq "<<<HERE.()>\na\nHERE"
631
+ expect(heredoc_wrap "<<HERE.()\na\nHERE")
632
+ .to eq "[{<<HERE.()}]\na\nHERE"
687
633
  end
688
634
  end
689
635
 
690
636
  describe 'heredocs' do
691
637
  it 'wraps heredocs on their first line' do
692
- expect(wrap("<<A\nA")).to eq "<<<A>\nA"
693
- expect(wrap("<<A\n123\nA")).to eq "<<<A>\n123\nA"
694
- expect(wrap("<<-A\nA")).to eq "<<<-A>\nA"
695
- expect(wrap("<<-A\n123\nA")).to eq "<<<-A>\n123\nA"
696
- expect(wrap("1\n<<A\nA")).to eq "<<1>\n<<A>\nA"
697
- expect(wrap("<<A + <<B\n1\nA\n2\nB")).to eq "<<<A + <<B>\n1\nA\n2\nB"
698
- expect(wrap("<<A\n1\nA\n<<B\n2\nB")).to eq "<<<<A>\n1\nA\n<<B>\n2\nB"
699
- expect(wrap("puts <<A\nA\nputs <<B\nB")).to eq "<puts <<A>\nA\n<puts <<B>\nB"
638
+ expect(heredoc_wrap "<<A\nA").to eq "[{<<A}]\nA"
639
+ expect(heredoc_wrap "<<A\n123\nA").to eq "[{<<A}]\n123\nA"
640
+ expect(heredoc_wrap "<<-A\nA").to eq "[{<<-A}]\nA"
641
+ expect(heredoc_wrap "<<-A\n123\nA").to eq "[{<<-A}]\n123\nA"
642
+ expect(heredoc_wrap "1\n<<A\nA").to eq "[{1}\n{<<A}]\nA"
643
+ expect(heredoc_wrap "<<A + <<B\n1\nA\n2\nB").to eq "[{<<A + <<B}]\n1\nA\n2\nB"
644
+ expect(heredoc_wrap "<<A\n1\nA\n<<B\n2\nB").to eq "[{<<A}\n1\nA\n{<<B}]\n2\nB"
645
+ expect(heredoc_wrap "puts <<A\nA\nputs <<B\nB").to eq "[{puts <<A}\nA\n{puts <<B}]\nB"
700
646
  end
701
647
 
702
648
  it "wraps methods that wrap heredocs, even whent hey don't have parentheses" do
703
- expect(wrap("a(<<HERE)\nHERE")).to eq "<a(<<HERE)>\nHERE"
704
- expect(wrap("a <<HERE\nHERE")).to eq "<a <<HERE>\nHERE"
705
- expect(wrap("a 1, <<HERE\nHERE")).to eq "<a 1, <<HERE>\nHERE"
706
- expect(wrap("a.b 1, 2, <<HERE1, <<-HERE2 \nHERE1\n HERE2")).to eq\
707
- "<a.b 1, 2, <<HERE1, <<-HERE2> \nHERE1\n HERE2"
708
- expect(wrap("a.b 1,\n2,\n<<HERE\nHERE")).to eq "<a.b <1>,\n<2>,\n<<HERE>\nHERE"
649
+ expect(heredoc_wrap "a(<<HERE)\nHERE").to eq "[{a(<<HERE)}]\nHERE"
650
+ expect(heredoc_wrap "a <<HERE\nHERE").to eq "[{a <<HERE}]\nHERE"
651
+ expect(heredoc_wrap "a 1, <<HERE\nHERE").to eq "[{a 1, <<HERE}]\nHERE"
652
+ expect(heredoc_wrap "a.b 1, 2, <<HERE1, <<-HERE2 \nHERE1\n HERE2").to eq\
653
+ "[{a.b 1, 2, <<HERE1, <<-HERE2}] \nHERE1\n HERE2"
654
+ expect(heredoc_wrap "a.b 1,\n2,\n<<HERE\nHERE").to eq "[{a.b {1},\n{2},\n<<HERE}]\nHERE"
709
655
  end
710
656
 
711
657
  it "wraps assignments whose value is a heredoc" do
712
- expect(wrap("a=<<A\nA")).to eq "<a=<<A>\nA"
713
- expect(wrap("a,b=<<A,<<B\nA\nB")).to eq "<a,b=<<A,<<B>\nA\nB"
714
- expect(wrap("a,b=1,<<B\nB")).to eq "<a,b=1,<<B>\nB"
715
- expect(wrap("a,b=<<A,1\nA")).to eq "<a,b=<<A,1>\nA"
658
+ expect(heredoc_wrap "a=<<A\nA").to eq "[{a=<<A}]\nA"
659
+ expect(heredoc_wrap "a,b=<<A,<<B\nA\nB").to eq "[{a,b=<<A,<<B}]\nA\nB"
660
+ expect(heredoc_wrap "a,b=1,<<B\nB").to eq "[{a,b=1,<<B}]\nB"
661
+ expect(heredoc_wrap "a,b=<<A,1\nA").to eq "[{a,b=<<A,1}]\nA"
716
662
  end
717
663
 
718
664
  it 'wraps methods tacked onto the end of heredocs' do
719
- expect(wrap("<<A.size\nA")).to eq "<<<A.size>\nA"
720
- expect(wrap("<<A.size 1\nA")).to eq "<<<A.size 1>\nA"
721
- expect(wrap("<<A.size(1)\nA")).to eq "<<<A.size(1)>\nA"
722
- expect(wrap("<<A.whatever <<B\nA\nB")).to eq "<<<A.whatever <<B>\nA\nB"
723
- expect(wrap("<<A.whatever(<<B)\nA\nB")).to eq "<<<A.whatever(<<B)>\nA\nB"
724
- expect(wrap("<<A.size()\nA")).to eq "<<<A.size()>\nA"
665
+ expect(heredoc_wrap "<<A.size\nA").to eq "[{<<A.size}]\nA"
666
+ expect(heredoc_wrap "<<A.size 1\nA").to eq "[{<<A.size 1}]\nA"
667
+ expect(heredoc_wrap "<<A.size(1)\nA").to eq "[{<<A.size(1)}]\nA"
668
+ expect(heredoc_wrap "<<A.whatever <<B\nA\nB").to eq "[{<<A.whatever <<B}]\nA\nB"
669
+ expect(heredoc_wrap "<<A.whatever(<<B)\nA\nB").to eq "[{<<A.whatever(<<B)}]\nA\nB"
670
+ expect(heredoc_wrap "<<A.size()\nA").to eq "[{<<A.size()}]\nA"
725
671
  end
726
672
  end
727
673
 
@@ -760,36 +706,30 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
760
706
  end
761
707
  end
762
708
 
763
- # eventually, don't wrap these b/c they're spammy, but can be annoying since they can be accidentally wraped
764
- # by e.g. a begin/end
765
- # ignoring public/private/protected for now, b/c they're just methods, not keywords
766
709
  describe 'class definitions' do
767
710
  it 'does not wrap the class definition, does wrap the body' do
768
- expect(wrap("class A\n1\nend")).to eq "class A\n<1>\nend"
711
+ expect(wrap("class A\n1\nend")).to eq "<class A\n<1>\nend>"
769
712
  end
770
713
 
771
714
  it 'does not wrap the superclass definition' do
772
- expect(wrap("class A < B\nend")).to eq "class A < B\nend"
715
+ expect(wrap("class A < B\nend")).to eq "<class A < <B>\nend>"
773
716
  end
774
717
 
775
718
  it 'wraps the rescue body' do
776
- expect(wrap("class A < B\n1\nrescue\n2\nend")).to eq "class A < B\n<1>\nrescue\n<2>\nend"
719
+ expect(wrap("class A < B\n1\nrescue\n2\nend")).to eq "<class A < <B>\n<1>\nrescue\n<2>\nend>"
777
720
  end
778
721
 
779
722
  it 'does not wrap the singleton class' do
780
- expect(wrap("class << self\n end")).to eq "class << self\n end"
723
+ expect(wrap("class << self\n end")).to eq "<class << <self>\n end>"
781
724
  end
782
725
  end
783
726
 
784
- # eventually, don't wrap these b/c they're spammy, but can be annoying since they can be accidentally wraped
785
- # by e.g. a begin/end
786
- # ignoring public/private/protected for now, b/c they're just methods, not keywords
787
727
  describe 'module definitions' do
788
728
  it 'does not wrap the definition, does wrap the body' do
789
- expect(wrap("module A\n1\nend")).to eq "module A\n<1>\nend"
729
+ expect(wrap("module A\n1\nend")).to eq "<module A\n<1>\nend>"
790
730
  end
791
731
  it 'wraps the rescue portion' do
792
- expect(wrap("module A\n1\nrescue\n2\nend")).to eq "module A\n<1>\nrescue\n<2>\nend"
732
+ expect(wrap("module A\n1\nrescue\n2\nend")).to eq "<module A\n<1>\nrescue\n<2>\nend>"
793
733
  end
794
734
  end
795
735
 
@@ -812,11 +752,15 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
812
752
 
813
753
  it 'wraps calls to yield' do
814
754
  expect(wrap("def a\nyield\nend")).to eq "<def a\n<yield>\nend>"
755
+ expect(wrap("def a\nyield 1\nend")).to eq "<def a\n<yield 1>\nend>"
756
+ expect(wrap("def a\nyield(\n1\n)\nend")).to eq "<def a\n<yield(\n<1>\n)>\nend>"
815
757
  end
816
758
 
817
759
  it 'wraps calls to super' do
818
760
  expect(wrap("def a\nsuper\nend")).to eq "<def a\n<super>\nend>"
819
761
  expect(wrap("def a\nsuper 1\nend")).to eq "<def a\n<super 1>\nend>"
762
+ expect(wrap("def a\nsuper(1)\nend")).to eq "<def a\n<super(1)>\nend>"
763
+ expect(wrap("def a\nsuper(\n1\n)\nend")).to eq "<def a\n<super(\n<1>\n)>\nend>"
820
764
  end
821
765
 
822
766
  it 'wraps the bodies of returns' do
@@ -828,11 +772,17 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
828
772
  expect(wrap("def a\n1\nrescue\n2\nensure\n3\nend")).to eq "<def a\n<1>\nrescue\n<2>\nensure\n<3>\nend>"
829
773
  expect(wrap("def a\n1\nensure\n2\nend")).to eq "<def a\n<1>\nensure\n<2>\nend>"
830
774
  end
775
+
776
+ it 'wrap a definition as a call to an invocation' do
777
+ expect(wrap("a def b\nc\nend,\nd")).to eq "<a <def b\n<c>\nend>,\nd>"
778
+ end
831
779
  end
832
780
 
833
781
  describe 'lambdas' do
834
782
  it 'wraps the lambda' do
835
783
  expect(wrap("lambda { }")).to eq "<lambda { }>"
784
+ expect(wrap("lambda { |;a| }")).to eq "<lambda { |;a| }>"
785
+ expect(wrap("lambda { |a,b=1,*c,&d| }")).to eq "<lambda { |a,b=1,*c,&d| }>"
836
786
  expect(wrap("-> { }")).to eq "<-> { }>"
837
787
  expect(wrap("-> a, b { }")).to eq "<-> a, b { }>"
838
788
  expect(wrap("-> {\n1\n}")).to eq "<-> {\n<1>\n}>"
@@ -865,15 +815,100 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
865
815
  # <2>
866
816
  # <3>]
867
817
  #
868
- # idk, but then we also need to deal with the fact that we're changing result of __LINE__
869
- # which we could do with some meta, just replacing it with the literal when we parse it
870
- # but still, moving this out of here will be really annoying, and no one is going to use it, so fuck it
871
- it 'does not record them', not_implemented: true do
872
- pending 'not implemented, and probably never will be'
818
+ # Because not iw matters why you want to wrap it. Are you doing this because you want
819
+ # to catch an exception? then maybe your wrapping code needs to go around the inside of each begin block
820
+ # or maybe the begin blocks need to get consolidated into a single begin block.
821
+ # or maybe removed from the begin block and just stuck as normal fkn code at the top of the file?
822
+ # but we do need to rewrite __LINE__ expressions now, because they are changing.
823
+ # which... maybe that's fine.
824
+ #
825
+ # Or, you might just be interested in having your code execute first. In which case,
826
+ # it doesn't need to wrap the body, it just needs its own BEGIN block.
827
+ #
828
+ # note that there are also things line nested BEGINs and nested ENDs
829
+ # but you can't nest a BEGIN inside an END.
830
+ it 'does not record them' do
873
831
  expect(wrap("BEGIN {}")).to eq "BEGIN {}"
874
832
  expect(wrap("END {}")).to eq "END {}"
875
833
  expect(wrap("BEGIN {\n123\n}")).to eq "BEGIN {\n<123>\n}"
876
834
  expect(wrap("END {\n123\n}")).to eq "END {\n<123>\n}"
877
835
  end
836
+
837
+ it 'moves them out of the body', not_implemented: true do
838
+ expect(wrap_with_body(<<-HERE)).to eq(<<-THERE)
839
+ # encoding: utf-8
840
+ p [1, __LINE__]
841
+ BEGIN {
842
+ p [2, __LINE__]
843
+ }
844
+ p [3, __LINE__]
845
+ END {
846
+ p [4, __LINE__]
847
+ }
848
+ p [5, __LINE__]
849
+ BEGIN { p [6, __LINE__] }
850
+ END { p [7, __LINE__] }
851
+ p [8, __LINE__]
852
+ HERE
853
+ # encoding: utf-8
854
+ BEGIN {
855
+ <p [2, 4]>
856
+ }
857
+ BEGIN { <p [6, 11]> }
858
+ [<p [1, 2]>
859
+ <p [3, 6]>
860
+ <p [5, 10]>
861
+ <p [8, 13]>]
862
+ END {
863
+ <p [4, 8]>
864
+ }
865
+ END { <p [7, 12]> }
866
+ THERE
867
+ end
868
+ end
869
+
870
+ # only checking on 2.2 b/c its hard to figure out when different pieces were introduced
871
+ # we'll assume that if it passes on 2.2, it will pass on 2.0 or 2.1, if the feature is available on that Ruby
872
+ major, minor, * = RUBY_VERSION.split(".").map(&:to_i)
873
+ if major > 2 || (major == 2 && minor >= 2)
874
+ describe 'Ruby 2 syntaxes', :'2.x' => true do
875
+ it 'respects __dir__ macro' do
876
+ expect(wrap('__dir__')).to eq '<__dir__>'
877
+ end
878
+
879
+ it 'does not wrap keyword/keywordrest arguments' do
880
+ expect(wrap("def a(b,c=1,*d,e:,f:1,**g, &h)\n1\nend"))
881
+ .to eq "<def a(b,c=1,*d,e:,f:1,**g, &h)\n<1>\nend>"
882
+ expect(wrap("def a b:\n1\nend")).to eq "<def a b:\n<1>\nend>"
883
+ expect(wrap("def a b:\nreturn 1\nend")).to eq "<def a b:\nreturn <1>\nend>"
884
+ expect(wrap("def a b:\nreturn\nend")).to eq "<def a b:\nreturn\nend>"
885
+ expect(wrap("a b:1, **c")).to eq "<a b:1, **c>"
886
+ expect(wrap("{\na:1,\n**b\n}")).to eq "<{\na:<1>,\n**<b>\n}>"
887
+ expect(wrap("a(b:1,\n **c\n)")).to eq "<a(b:<1>,\n **<c>\n)>"
888
+ pending 'Fixed this in Parser, but it\'s not released yet https://github.com/whitequark/parser/commit/aeb95c776d11e212ed95b92c69e96d1cccdb6424'
889
+ expect(wrap("def a(*, **)\n1\nend")).to eq "<def a(*, **)\n<1>\nend>"
890
+ end
891
+
892
+ it 'tags javascript style hashes' do
893
+ expect(wrap(%[{\na:1,\n'b':2,\n"c":3\n}])).to eq %[<{\na:<1>,\n'b':<2>,\n"c":<3>\n}>]
894
+ expect(wrap(%[a b: 1,\n'c': 2,\n"d": 3,\n:e => 4])).to eq %[<a b: <1>,\n'c': <2>,\n"d": <3>,\n:e => 4>]
895
+ end
896
+
897
+ it 'wraps symbol literals' do
898
+ expect(wrap("%i[abc]")).to eq "<%i[abc]>"
899
+ expect(wrap("%I[abc]")).to eq "<%I[abc]>"
900
+ expect(wrap("%I[a\nb\nc]")).to eq "<%I[a\nb\nc]>"
901
+ end
902
+
903
+ it 'wraps complex and rational' do
904
+ expect(wrap("1i")).to eq "<1i>"
905
+ expect(wrap("5+1i")).to eq "<5+1i>"
906
+ expect(wrap("1r")).to eq "<1r>"
907
+ expect(wrap("1.5r")).to eq "<1.5r>"
908
+ expect(wrap("1/2r")).to eq "<1/2r>"
909
+ expect(wrap("2/1r")).to eq "<2/1r>"
910
+ expect(wrap("1ri")).to eq "<1ri>"
911
+ end
912
+ end
878
913
  end
879
914
  end