seeing_is_believing 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +4 -0
- data/Gemfile.lock +1 -1
- data/Readme.md +26 -11
- data/features/{binary_errors.feature → errors.feature} +1 -1
- data/features/{binary_examples.feature → examples.feature} +0 -0
- data/features/flags.feature +62 -0
- data/features/step_definitions/steps.rb +4 -0
- data/lib/seeing_is_believing.rb +9 -18
- data/lib/seeing_is_believing/arg_parser.rb +94 -0
- data/lib/seeing_is_believing/binary.rb +63 -41
- data/lib/seeing_is_believing/print_results_next_to_lines.rb +33 -17
- data/lib/seeing_is_believing/queue.rb +55 -0
- data/lib/seeing_is_believing/version.rb +1 -1
- data/spec/arg_parser_spec.rb +122 -0
- data/spec/evaluate_by_moving_files_spec.rb +3 -0
- data/spec/queue_spec.rb +80 -0
- data/spec/seeing_is_believing_spec.rb +1 -1
- metadata +23 -14
data/.travis.yml
ADDED
data/Gemfile.lock
CHANGED
data/Readme.md
CHANGED
@@ -1,13 +1,15 @@
|
|
1
|
+
[![Build Status](https://secure.travis-ci.org/JoshCheek/seeing_is_believing.png?branch=master)](http://travis-ci.org/JoshCheek/seeing_is_believing)
|
2
|
+
|
1
3
|
Seeing Is Believing
|
2
4
|
===================
|
3
5
|
|
4
6
|
Evaluates a file, recording the results of each line of code.
|
5
7
|
You can then use this to display output values like Bret Victor does with JavaScript in his talk [Inventing on Principle][inventing_on_principle].
|
6
|
-
Except, obviously, his is like a million better.
|
8
|
+
Except, obviously, his is like a million times better.
|
7
9
|
|
8
10
|
Also comes with a binary to show how it might be used.
|
9
11
|
|
10
|
-
For whatever reason, I can't embed videos, but here's a ~1 minute [video][video] showing it off.
|
12
|
+
For whatever reason, I can't embed videos, but **here's a ~1 minute [video][video]** showing it off.
|
11
13
|
|
12
14
|
|
13
15
|
Use The Binary
|
@@ -29,17 +31,12 @@ meth "34" # => "34"
|
|
29
31
|
```
|
30
32
|
|
31
33
|
```ruby
|
32
|
-
# $ bin/seeing_is_believing proving_grounds/raises_exception.rb
|
34
|
+
# $ bin/seeing_is_believing proving_grounds/raises_exception.rb
|
33
35
|
1 + 1 # => 2
|
34
36
|
raise "ZOMG!" # ~> RuntimeError: ZOMG!
|
35
37
|
1 + 1
|
36
38
|
```
|
37
39
|
|
38
|
-
```bash
|
39
|
-
# $ bin/seeing_is_believing proving_grounds/raises_exception.rb 1>/dev/null
|
40
|
-
ZOMG!
|
41
|
-
```
|
42
|
-
|
43
40
|
Use The Lib
|
44
41
|
===========
|
45
42
|
|
@@ -64,7 +61,6 @@ result[2] # => ['"A"', '"B"', '"C"']
|
|
64
61
|
Install
|
65
62
|
=======
|
66
63
|
|
67
|
-
When Rubygems gets back up:
|
68
64
|
|
69
65
|
$ gem install seeing_is_believing
|
70
66
|
|
@@ -81,8 +77,8 @@ Rubygems is allowing pushes again, but if it goes back down, you can install lik
|
|
81
77
|
$ cd ..
|
82
78
|
$ rm -rf "./seeing_is_believing"
|
83
79
|
|
84
|
-
|
85
|
-
|
80
|
+
TextMate Integration
|
81
|
+
====================
|
86
82
|
|
87
83
|
Go to the bundle editor, create this new command in the Ruby bundle:
|
88
84
|
|
@@ -94,6 +90,25 @@ It should look like this:
|
|
94
90
|
|
95
91
|
![textmate-integration][textmate-integration]
|
96
92
|
|
93
|
+
Emacs Integration
|
94
|
+
=================
|
95
|
+
|
96
|
+
Add this function to your Emacs configuration:
|
97
|
+
|
98
|
+
~~~~ scheme
|
99
|
+
(defun seeing-is-believing ()
|
100
|
+
"Replace the current region (or the whole buffer, if none) with the output
|
101
|
+
of seeing_is_believing."
|
102
|
+
(interactive)
|
103
|
+
(let ((beg (if (region-active-p) (region-beginning) (point-min)))
|
104
|
+
(end (if (region-active-p) (region-end) (point-max))))
|
105
|
+
(shell-command-on-region beg end "seeing_is_believing" nil 'replace)))
|
106
|
+
~~~~
|
107
|
+
|
108
|
+
You can now call `seeing-is-believing` to replace the current region
|
109
|
+
or current buffer contents with the output of running it through
|
110
|
+
`seeing_is_believing`.
|
111
|
+
|
97
112
|
Known Issues
|
98
113
|
============
|
99
114
|
|
File without changes
|
@@ -0,0 +1,62 @@
|
|
1
|
+
Feature: Using flags
|
2
|
+
|
3
|
+
Sometimes you want more control over what comes out, for that we give you flags.
|
4
|
+
|
5
|
+
Scenario: --start-line
|
6
|
+
Given the file "start_line.rb":
|
7
|
+
"""
|
8
|
+
1 + 1
|
9
|
+
2
|
10
|
+
3
|
11
|
+
"""
|
12
|
+
When I run "seeing_is_believing --start-line 2 start_line.rb"
|
13
|
+
Then stderr is empty
|
14
|
+
And the exit status is 0
|
15
|
+
And stdout is:
|
16
|
+
"""
|
17
|
+
1 + 1
|
18
|
+
2 # => 2
|
19
|
+
3 # => 3
|
20
|
+
"""
|
21
|
+
|
22
|
+
Scenario: --end-line
|
23
|
+
Given the file "end_line.rb":
|
24
|
+
"""
|
25
|
+
1
|
26
|
+
2
|
27
|
+
3 + 3
|
28
|
+
"""
|
29
|
+
When I run "seeing_is_believing --end-line 2 end_line.rb"
|
30
|
+
Then stderr is empty
|
31
|
+
And the exit status is 0
|
32
|
+
And stdout is:
|
33
|
+
"""
|
34
|
+
1 # => 1
|
35
|
+
2 # => 2
|
36
|
+
3 + 3
|
37
|
+
"""
|
38
|
+
|
39
|
+
Scenario: --start-line and --end-line
|
40
|
+
Given the file "start_and_end_line.rb":
|
41
|
+
"""
|
42
|
+
1 + 1
|
43
|
+
2
|
44
|
+
3
|
45
|
+
4 + 4
|
46
|
+
"""
|
47
|
+
When I run "seeing_is_believing --start-line 2 --end-line 3 start_and_end_line.rb"
|
48
|
+
Then stderr is empty
|
49
|
+
And the exit status is 0
|
50
|
+
And stdout is:
|
51
|
+
"""
|
52
|
+
1 + 1
|
53
|
+
2 # => 2
|
54
|
+
3 # => 3
|
55
|
+
4 + 4
|
56
|
+
"""
|
57
|
+
|
58
|
+
Scenario: --help
|
59
|
+
When I run "seeing_is_believing --help"
|
60
|
+
Then stderr is empty
|
61
|
+
And the exit status is 0
|
62
|
+
And stdout includes "Usage"
|
data/lib/seeing_is_believing.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'stringio'
|
2
2
|
require 'tmpdir'
|
3
3
|
|
4
|
+
require 'seeing_is_believing/queue'
|
4
5
|
require 'seeing_is_believing/result'
|
5
6
|
require 'seeing_is_believing/expression_list'
|
6
7
|
require 'seeing_is_believing/evaluate_by_moving_files'
|
@@ -20,7 +21,7 @@ class SeeingIsBelieving
|
|
20
21
|
def call
|
21
22
|
@memoized_result ||= begin
|
22
23
|
program = ''
|
23
|
-
program << expression_list.call until
|
24
|
+
program << expression_list.call until next_line_queue.peek.nil? || data_segment?
|
24
25
|
program = record_exceptions_in program
|
25
26
|
program << "\n" << the_rest_of_the_stream if data_segment?
|
26
27
|
result_for program, min_line_number, max_line_number
|
@@ -32,8 +33,8 @@ class SeeingIsBelieving
|
|
32
33
|
attr_reader :stream
|
33
34
|
|
34
35
|
def expression_list
|
35
|
-
@expression_list ||= ExpressionList.new get_next_line: lambda {
|
36
|
-
peek_next_line: lambda {
|
36
|
+
@expression_list ||= ExpressionList.new get_next_line: lambda { next_line_queue.dequeue },
|
37
|
+
peek_next_line: lambda { next_line_queue.peek },
|
37
38
|
on_complete: lambda { |line, children, completions, line_number|
|
38
39
|
track_line_number line_number
|
39
40
|
expression = [line, *children, *completions].map(&:chomp).join("\n")
|
@@ -75,32 +76,22 @@ class SeeingIsBelieving
|
|
75
76
|
end
|
76
77
|
|
77
78
|
def eof?
|
78
|
-
|
79
|
+
next_line_queue.peek.nil?
|
79
80
|
end
|
80
81
|
|
81
82
|
def data_segment?
|
82
|
-
|
83
|
+
next_line_queue.peek == '__END__'
|
83
84
|
end
|
84
85
|
|
85
|
-
def
|
86
|
-
@
|
86
|
+
def next_line_queue
|
87
|
+
@next_line_queue ||= Queue.new do
|
87
88
|
line = stream.gets
|
88
89
|
line && line.chomp
|
89
90
|
end
|
90
91
|
end
|
91
92
|
|
92
|
-
def get_next_line
|
93
|
-
if @next_line
|
94
|
-
line = peek_next_line
|
95
|
-
@next_line = nil
|
96
|
-
line
|
97
|
-
else
|
98
|
-
peek_next_line && get_next_line
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
93
|
def the_rest_of_the_stream
|
103
|
-
|
94
|
+
next_line_queue.dequeue << "\n" << stream.read
|
104
95
|
end
|
105
96
|
|
106
97
|
def do_not_record?(code)
|
@@ -0,0 +1,94 @@
|
|
1
|
+
class SeeingIsBelieving
|
2
|
+
class ArgParser
|
3
|
+
def self.parse(args)
|
4
|
+
new(args).call
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_accessor :args
|
8
|
+
|
9
|
+
def initialize(args)
|
10
|
+
self.args = args
|
11
|
+
self.filenames = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
@result ||= begin
|
16
|
+
until args.empty?
|
17
|
+
case (arg = args.shift)
|
18
|
+
|
19
|
+
# help screen
|
20
|
+
when '-h', '--help'
|
21
|
+
options[:help] = self.class.help_screen
|
22
|
+
|
23
|
+
# start line
|
24
|
+
when '-l', '--start-line'
|
25
|
+
start_line = args.shift
|
26
|
+
i_start_line = start_line.to_i
|
27
|
+
if i_start_line.to_s == start_line && !i_start_line.zero?
|
28
|
+
options[:start_line] = start_line.to_i
|
29
|
+
else
|
30
|
+
options[:errors] << "#{arg} expects a positive integer argument"
|
31
|
+
end
|
32
|
+
|
33
|
+
# end line
|
34
|
+
when '-L', '--end-line'
|
35
|
+
end_line = args.shift
|
36
|
+
if end_line.to_i.to_s == end_line
|
37
|
+
options[:end_line] = end_line.to_i
|
38
|
+
else
|
39
|
+
options[:errors] << "#{arg} expect an integer argument"
|
40
|
+
end
|
41
|
+
|
42
|
+
# unknown flags
|
43
|
+
when /^-/
|
44
|
+
options[:errors] << "Unknown option: #{arg.inspect}"
|
45
|
+
|
46
|
+
# filenames
|
47
|
+
else
|
48
|
+
filenames << arg
|
49
|
+
options[:filename] = arg
|
50
|
+
end
|
51
|
+
end
|
52
|
+
normalize_and_validate
|
53
|
+
options
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
attr_accessor :filenames
|
60
|
+
|
61
|
+
def normalize_and_validate
|
62
|
+
if 1 < filenames.size
|
63
|
+
options[:errors] << "Can only have one filename, but had: #{filenames.map(&:inspect).join ', '}"
|
64
|
+
end
|
65
|
+
|
66
|
+
if options[:end_line] < options[:start_line]
|
67
|
+
options[:start_line], options[:end_line] = options[:end_line], options[:start_line]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def options
|
72
|
+
@options ||= {
|
73
|
+
filename: nil,
|
74
|
+
errors: [],
|
75
|
+
start_line: 1,
|
76
|
+
end_line: Float::INFINITY,
|
77
|
+
}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def ArgParser.help_screen
|
82
|
+
<<HELP_SCREEN
|
83
|
+
Usage: #{$0} [options] [filename]
|
84
|
+
|
85
|
+
#{$0} is a program and library that will evaluate a Ruby file and capture/display the results.
|
86
|
+
|
87
|
+
If no filename is provided, the binary will read the program from standard input.
|
88
|
+
|
89
|
+
-l, --start-line # line number to begin showing results on
|
90
|
+
-L, --end-line # line number to stop showing results on
|
91
|
+
-h, --help # this help screen
|
92
|
+
HELP_SCREEN
|
93
|
+
end
|
94
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'seeing_is_believing/arg_parser'
|
1
2
|
require 'seeing_is_believing/print_results_next_to_lines'
|
2
3
|
|
3
4
|
class SeeingIsBelieving
|
@@ -12,68 +13,89 @@ class SeeingIsBelieving
|
|
12
13
|
end
|
13
14
|
|
14
15
|
def call
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
display_exceptions
|
16
|
+
@exitstatus ||= if flags_have_errors? then print_errors ; 1
|
17
|
+
elsif should_print_help? then print_help ; 0
|
18
|
+
elsif has_filename? && file_dne? then print_file_dne ; 1
|
19
|
+
elsif invalid_syntax? then print_syntax_error ; 1
|
20
|
+
else print_program ; (printer.has_exception? ? 1 : 0)
|
21
|
+
end
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
call
|
26
|
-
@exitstatus
|
27
|
-
end
|
24
|
+
alias exitstatus call
|
28
25
|
|
29
26
|
private
|
30
27
|
|
31
|
-
def on_stdin?
|
32
|
-
argv.empty?
|
33
|
-
end
|
34
|
-
|
35
28
|
def filename
|
36
|
-
|
29
|
+
flags[:filename]
|
37
30
|
end
|
31
|
+
alias has_filename? filename
|
38
32
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
33
|
+
# le sigh
|
34
|
+
def printer
|
35
|
+
@printer ||= begin
|
36
|
+
if file_is_on_stdin?
|
37
|
+
body = stdin.read
|
38
|
+
stdin = ''
|
43
39
|
else
|
44
|
-
|
40
|
+
body = File.read(filename)
|
41
|
+
stdin = self.stdin
|
45
42
|
end
|
43
|
+
PrintResultsNextToLines.new body,
|
44
|
+
stdin,
|
45
|
+
filename: filename,
|
46
|
+
start_line: flags[:start_line],
|
47
|
+
end_line: flags[:end_line]
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
51
|
+
def flags
|
52
|
+
@flags ||= ArgParser.parse argv
|
53
|
+
end
|
54
|
+
|
55
|
+
def flags_have_errors?
|
56
|
+
flags[:errors].any?
|
57
|
+
end
|
58
|
+
|
59
|
+
def print_errors
|
60
|
+
stderr.puts flags[:errors].join("\n")
|
61
|
+
end
|
62
|
+
|
63
|
+
def should_print_help?
|
64
|
+
flags[:help]
|
65
|
+
end
|
66
|
+
|
67
|
+
def print_help
|
68
|
+
stdout.puts flags[:help]
|
69
|
+
end
|
70
|
+
|
71
|
+
def file_is_on_stdin?
|
72
|
+
filename.nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
def file_dne?
|
76
|
+
!File.exist?(filename)
|
77
|
+
end
|
78
|
+
|
79
|
+
def print_file_dne
|
52
80
|
stderr.puts "#{filename} does not exist!"
|
53
|
-
false
|
54
81
|
end
|
55
82
|
|
56
|
-
def
|
57
|
-
|
83
|
+
def print_program
|
84
|
+
stdout.puts printer.call
|
85
|
+
end
|
86
|
+
|
87
|
+
def syntax_error_notice
|
88
|
+
return if file_is_on_stdin? # <-- should probably check stdin too
|
58
89
|
out, err, syntax_status = Open3.capture3('ruby', '-c', filename)
|
59
|
-
return
|
60
|
-
@exitstatus = 1
|
61
|
-
stderr.puts err
|
62
|
-
false
|
90
|
+
return err unless syntax_status.success?
|
63
91
|
end
|
64
92
|
|
65
|
-
def
|
66
|
-
|
67
|
-
true
|
93
|
+
def invalid_syntax?
|
94
|
+
!!syntax_error_notice
|
68
95
|
end
|
69
96
|
|
70
|
-
def
|
71
|
-
|
72
|
-
stderr.puts believer.exception.message
|
73
|
-
@exitstatus = 1
|
74
|
-
else
|
75
|
-
@exitstatus = 0
|
76
|
-
end
|
97
|
+
def print_syntax_error
|
98
|
+
stderr.puts syntax_error_notice
|
77
99
|
end
|
78
100
|
end
|
79
101
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'seeing_is_believing'
|
2
|
+
require 'seeing_is_believing/queue'
|
2
3
|
require 'seeing_is_believing/has_exception'
|
3
4
|
|
4
5
|
class SeeingIsBelieving
|
@@ -10,10 +11,12 @@ class SeeingIsBelieving
|
|
10
11
|
EXCEPTION_PREFIX = '# ~>'
|
11
12
|
RESULT_PREFIX = '# =>'
|
12
13
|
|
13
|
-
def initialize(body, stdin,
|
14
|
-
self.body
|
15
|
-
self.
|
16
|
-
self.
|
14
|
+
def initialize(body, stdin, options={})
|
15
|
+
self.body = remove_previous_output_from body
|
16
|
+
self.stdin = stdin
|
17
|
+
self.filename = options.fetch :filename, nil
|
18
|
+
self.start_line = options.fetch :start_line
|
19
|
+
self.end_line = options.fetch :end_line
|
17
20
|
end
|
18
21
|
|
19
22
|
def new_body
|
@@ -23,16 +26,18 @@ class SeeingIsBelieving
|
|
23
26
|
def call
|
24
27
|
evaluate_program
|
25
28
|
inherit_exception
|
26
|
-
|
29
|
+
add_each_line_until_start_or_data_segment
|
30
|
+
add_lines_with_results_until_end_or_data_segment
|
31
|
+
add_lines_until_data_segment
|
27
32
|
add_stdout
|
28
33
|
add_stderr
|
29
|
-
|
34
|
+
add_remaining_lines
|
30
35
|
return new_body
|
31
36
|
end
|
32
37
|
|
33
38
|
private
|
34
39
|
|
35
|
-
attr_accessor :body, :filename, :file_result, :stdin
|
40
|
+
attr_accessor :body, :filename, :file_result, :stdin, :start_line, :end_line
|
36
41
|
|
37
42
|
def evaluate_program
|
38
43
|
self.file_result = SeeingIsBelieving.new(body, filename: filename, stdin: stdin).call
|
@@ -42,27 +47,38 @@ class SeeingIsBelieving
|
|
42
47
|
self.exception = file_result.exception
|
43
48
|
end
|
44
49
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
+
def add_each_line_until_start_or_data_segment
|
51
|
+
line_queue.until { |line, line_number| line_number == start_line || start_of_data_segment?(line) }
|
52
|
+
.each { |line, line_number| new_body << line }
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_lines_with_results_until_end_or_data_segment
|
56
|
+
line_queue.until { |line, line_number| end_line < line_number || start_of_data_segment?(line) }
|
57
|
+
.each { |line, line_number| new_body << format_line(line.chomp, file_result[line_number]) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def add_lines_until_data_segment
|
61
|
+
line_queue.until { |line, line_number| start_of_data_segment?(line) }
|
62
|
+
.each { |line, line_number| new_body << line }
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_remaining_lines
|
66
|
+
line_queue.each { |line, line_number| new_body << line }
|
50
67
|
end
|
51
68
|
|
52
|
-
def
|
53
|
-
body.each_line
|
54
|
-
.drop_while { |line| not start_of_data_segment? line }
|
55
|
-
.each { |line| new_body << line }
|
69
|
+
def line_queue
|
70
|
+
@line_queue ||= Queue.new &body.each_line.with_index(1).to_a.method(:shift)
|
56
71
|
end
|
57
72
|
|
58
73
|
def start_of_data_segment?(line)
|
59
74
|
line.chomp == '__END__'
|
60
75
|
end
|
61
76
|
|
62
|
-
# max line length of the
|
77
|
+
# max line length of the lines to output (exempting coments) + 2 spaces for padding
|
63
78
|
def line_length
|
64
79
|
@line_length ||= 2 + body.each_line
|
65
80
|
.map(&:chomp)
|
81
|
+
.select.with_index(1) { |line, index| start_line <= index && index <= end_line }
|
66
82
|
.take_while { |line| not start_of_data_segment? line }
|
67
83
|
.select { |line| not (line == "=begin") .. (line == "=end") }
|
68
84
|
.reject { |line| SyntaxAnalyzer.ends_in_comment? line }
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class SeeingIsBelieving
|
2
|
+
class Queue
|
3
|
+
class While
|
4
|
+
attr_accessor :queue, :conditional
|
5
|
+
|
6
|
+
def initialize(queue, &conditional)
|
7
|
+
self.queue, self.conditional = queue, conditional
|
8
|
+
end
|
9
|
+
|
10
|
+
def each(&block)
|
11
|
+
block.call queue.dequeue while !queue.empty? && conditional.call(queue.peek)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Queue
|
17
|
+
attr_accessor :value_generator
|
18
|
+
|
19
|
+
def initialize(&value_generator)
|
20
|
+
self.value_generator = value_generator
|
21
|
+
end
|
22
|
+
|
23
|
+
def dequeue
|
24
|
+
return if permanently_empty?
|
25
|
+
peek.tap { @next_value = nil }
|
26
|
+
end
|
27
|
+
|
28
|
+
def peek
|
29
|
+
return if permanently_empty?
|
30
|
+
@next_value ||= value_generator.call.tap { |value| @permanently_empty = value.nil? }
|
31
|
+
end
|
32
|
+
|
33
|
+
def empty?
|
34
|
+
permanently_empty? || peek.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def each(&block)
|
38
|
+
block.call dequeue until empty?
|
39
|
+
end
|
40
|
+
|
41
|
+
def until(&block)
|
42
|
+
While.new(self) { |*args| !block.call(*args) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def while(&block)
|
46
|
+
While.new self, &block
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def permanently_empty?
|
52
|
+
@permanently_empty
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'seeing_is_believing/arg_parser'
|
2
|
+
|
3
|
+
describe SeeingIsBelieving::ArgParser do
|
4
|
+
RSpec::Matchers.define :have_error do |error_assertion|
|
5
|
+
match do |options|
|
6
|
+
options[:errors].find do |error|
|
7
|
+
case error_assertion
|
8
|
+
when Regexp
|
9
|
+
error_assertion =~ error
|
10
|
+
else
|
11
|
+
error_assertion == error
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
failure_message_for_should do |options|
|
17
|
+
"#{error_assertion.inspect} should have matched one of the errors: #{options[:errors].inspect}"
|
18
|
+
end
|
19
|
+
|
20
|
+
failure_message_for_should_not do |options|
|
21
|
+
"#{error_assertion.inspect} should NOT have matched any of the errors: #{options[:errors].inspect}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse(args)
|
26
|
+
described_class.parse args
|
27
|
+
end
|
28
|
+
|
29
|
+
specify 'unknown options set an error' do
|
30
|
+
parse(['--abc']).should have_error 'Unknown option: "--abc"'
|
31
|
+
parse(['-a']).should have_error 'Unknown option: "-a"'
|
32
|
+
end
|
33
|
+
|
34
|
+
example 'example: all the args' do
|
35
|
+
options = parse(%w[filename -l 12 -L 20 -h])
|
36
|
+
options[:filename].should == 'filename'
|
37
|
+
options[:start_line].should == 12
|
38
|
+
options[:end_line].should == 20
|
39
|
+
options[:help].should be_a_kind_of String
|
40
|
+
options[:errors].should be_empty
|
41
|
+
end
|
42
|
+
|
43
|
+
describe ':filename' do
|
44
|
+
it 'defaults to nil' do
|
45
|
+
parse([])[:filename].should be_nil
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'is the first non-flag' do
|
49
|
+
parse(['a'])[:filename].should == 'a'
|
50
|
+
parse(['-x', 'a'])[:filename].should == 'a'
|
51
|
+
parse(['a', '-x'])[:filename].should == 'a'
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'sets an error if given multiple filenames' do
|
55
|
+
parse([]).should_not have_error /name/
|
56
|
+
parse(['a']).should_not have_error /Can only have one filename/
|
57
|
+
parse(['a', 'b']).should have_error 'Can only have one filename, but had: "a", "b"'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe ':start_line' do
|
62
|
+
it 'defaults to 1' do
|
63
|
+
parse([])[:start_line].should equal 1
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'is set with -l and --start-line' do
|
67
|
+
parse(['-l', '1'])[:start_line].should == 1
|
68
|
+
parse(['--start-line', '12'])[:start_line].should == 12
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'sets an error if it cannot be turned into a positive integer' do
|
72
|
+
line_error_assertions = lambda do |flag|
|
73
|
+
parse([flag, '1']).should_not have_error /#{flag}/
|
74
|
+
parse([flag, '0']).should have_error /#{flag}/
|
75
|
+
parse([flag, 'a']).should have_error /#{flag}/
|
76
|
+
parse([flag, '']).should have_error /#{flag}/
|
77
|
+
parse([flag, '1.0']).should have_error /#{flag}/
|
78
|
+
parse([flag]).should have_error /#{flag}/
|
79
|
+
end
|
80
|
+
line_error_assertions['-l']
|
81
|
+
line_error_assertions['--start-line']
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe ':end_line' do
|
86
|
+
it 'defaults to infinity' do
|
87
|
+
parse([])[:end_line].should equal Float::INFINITY
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'is set with -L and --end-line' do
|
91
|
+
parse(['-L', '1'])[:end_line].should == 1
|
92
|
+
parse(['--end-line', '12'])[:end_line].should == 12
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'sets an error if it cannot be turned into an integer' do
|
96
|
+
line_error_assertions = lambda do |flag|
|
97
|
+
parse([flag, '1']).should_not have_error /#{flag}/
|
98
|
+
parse([flag, 'a']).should have_error /#{flag}/
|
99
|
+
parse([flag, '']).should have_error /#{flag}/
|
100
|
+
parse([flag]).should have_error /#{flag}/
|
101
|
+
end
|
102
|
+
line_error_assertions['-L']
|
103
|
+
line_error_assertions['--end-line']
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'swaps start and end line around if they are out of order' do
|
108
|
+
parse(%w[-l 2 -L 1])[:start_line].should == 1
|
109
|
+
parse(%w[-l 2 -L 1])[:end_line].should == 2
|
110
|
+
end
|
111
|
+
|
112
|
+
describe ':help' do
|
113
|
+
it 'defaults to nil' do
|
114
|
+
parse([])[:help].should be_nil
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'is set to the help screen with -h and --help and -help' do
|
118
|
+
parse(['-h'])[:help].should == described_class.help_screen
|
119
|
+
parse(['--help'])[:help].should == described_class.help_screen
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -1,9 +1,12 @@
|
|
1
1
|
require 'seeing_is_believing/evaluate_by_moving_files'
|
2
|
+
require 'fileutils'
|
2
3
|
|
3
4
|
describe SeeingIsBelieving::EvaluateByMovingFiles do
|
4
5
|
let(:filedir) { File.expand_path '../../proving_grounds', __FILE__ }
|
5
6
|
let(:filename) { File.join filedir, 'some_filename' }
|
6
7
|
|
8
|
+
before { FileUtils.mkdir_p filedir }
|
9
|
+
|
7
10
|
def invoke(program, options={})
|
8
11
|
evaluator = described_class.new(program, filename, options)
|
9
12
|
FileUtils.rm_f evaluator.temp_filename
|
data/spec/queue_spec.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'seeing_is_believing/queue'
|
2
|
+
|
3
|
+
describe SeeingIsBelieving::Queue do
|
4
|
+
def queue_for(*values)
|
5
|
+
described_class.new { values.shift }
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'generates values from the block it is initialized with' do
|
9
|
+
queue = queue_for 1, 2
|
10
|
+
queue.dequeue.should == 1
|
11
|
+
queue.dequeue.should == 2
|
12
|
+
queue.dequeue.should == nil
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'can peek ahead' do
|
16
|
+
queue = queue_for 1, 2
|
17
|
+
queue.peek.should == 1
|
18
|
+
queue.peek.should == 1
|
19
|
+
queue.dequeue.should == 1
|
20
|
+
queue.dequeue.should == 2
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'considers a nil value to mean it is empty' do
|
24
|
+
queue = queue_for 1, 2
|
25
|
+
queue.should_not be_empty
|
26
|
+
queue.peek.should == 1
|
27
|
+
queue.should_not be_empty
|
28
|
+
queue.dequeue.should == 1
|
29
|
+
queue.should_not be_empty
|
30
|
+
queue.peek.should == 2
|
31
|
+
queue.should_not be_empty
|
32
|
+
queue.dequeue.should == 2
|
33
|
+
queue.should be_empty
|
34
|
+
queue.peek.should == nil
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'yields nil infinitely after the first time it is seen' do
|
38
|
+
queue = queue_for nil, 1
|
39
|
+
queue.should be_empty
|
40
|
+
queue.peek.should == nil
|
41
|
+
queue.dequeue.should == nil
|
42
|
+
queue.should be_empty
|
43
|
+
queue.peek.should == nil
|
44
|
+
queue.dequeue.should == nil
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'can (destructively) iterate over its items until it is empty' do
|
48
|
+
queue = queue_for *1..5
|
49
|
+
seen = []
|
50
|
+
queue.each { |i| seen << i }
|
51
|
+
seen.should == [*1..5]
|
52
|
+
queue.should be_empty
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'conditional iteration' do
|
56
|
+
it 'will iterate while a condition is met' do
|
57
|
+
queue = queue_for *1..5
|
58
|
+
seen = []
|
59
|
+
queue.while { |arg| arg < 4 }.each { |arg| seen << arg }
|
60
|
+
seen.should == [1, 2, 3]
|
61
|
+
queue.peek.should == 4
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'will iterate until a condition is met' do
|
65
|
+
queue = queue_for *1..5
|
66
|
+
seen = []
|
67
|
+
queue.until { |arg| arg == 4 }.each { |arg| seen << arg }
|
68
|
+
seen.should == [1, 2, 3]
|
69
|
+
queue.peek.should == 4
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'stops iterating when it hits the end of the queue' do
|
73
|
+
queue = queue_for *1..5
|
74
|
+
seen = []
|
75
|
+
queue.while { true }.each { |arg| seen << arg }
|
76
|
+
seen.should == [*1..5]
|
77
|
+
queue.should be_empty
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -227,7 +227,7 @@ describe SeeingIsBelieving do
|
|
227
227
|
invoke('$stdin.read')[1].should == ['""']
|
228
228
|
end
|
229
229
|
|
230
|
-
it 'can deal with methods that are invoked entirely on the next line'
|
230
|
+
it 'can deal with methods that are invoked entirely on the next line' do
|
231
231
|
values_for("1\n.even?").should == [[], ['false']]
|
232
232
|
values_for("1\n.even?\n__END__").should == [[], ['false']]
|
233
233
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: seeing_is_believing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-02-
|
12
|
+
date: 2013-02-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
|
-
requirement: &
|
16
|
+
requirement: &70151257454580 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 10.0.3
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70151257454580
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &70151257453880 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 2.12.0
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70151257453880
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: cucumber
|
38
|
-
requirement: &
|
38
|
+
requirement: &70151257449440 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 1.2.1
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70151257449440
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: ichannel
|
49
|
-
requirement: &
|
49
|
+
requirement: &70151257448960 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,7 +54,7 @@ dependencies:
|
|
54
54
|
version: 5.1.1
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70151257448960
|
58
58
|
description: Records the results of every line of code in your file (intended to be
|
59
59
|
like xmpfilter), inspired by Bret Victor's JavaScript example in his talk "Inventing
|
60
60
|
on Principle"
|
@@ -66,16 +66,19 @@ extensions: []
|
|
66
66
|
extra_rdoc_files: []
|
67
67
|
files:
|
68
68
|
- .gitignore
|
69
|
+
- .travis.yml
|
69
70
|
- Gemfile
|
70
71
|
- Gemfile.lock
|
71
72
|
- Rakefile
|
72
73
|
- Readme.md
|
73
74
|
- bin/seeing_is_believing
|
74
|
-
- features/
|
75
|
-
- features/
|
75
|
+
- features/errors.feature
|
76
|
+
- features/examples.feature
|
77
|
+
- features/flags.feature
|
76
78
|
- features/step_definitions/steps.rb
|
77
79
|
- features/support/env.rb
|
78
80
|
- lib/seeing_is_believing.rb
|
81
|
+
- lib/seeing_is_believing/arg_parser.rb
|
79
82
|
- lib/seeing_is_believing/binary.rb
|
80
83
|
- lib/seeing_is_believing/error.rb
|
81
84
|
- lib/seeing_is_believing/evaluate_by_moving_files.rb
|
@@ -83,15 +86,18 @@ files:
|
|
83
86
|
- lib/seeing_is_believing/hard_core_ensure.rb
|
84
87
|
- lib/seeing_is_believing/has_exception.rb
|
85
88
|
- lib/seeing_is_believing/print_results_next_to_lines.rb
|
89
|
+
- lib/seeing_is_believing/queue.rb
|
86
90
|
- lib/seeing_is_believing/result.rb
|
87
91
|
- lib/seeing_is_believing/syntax_analyzer.rb
|
88
92
|
- lib/seeing_is_believing/the_matrix.rb
|
89
93
|
- lib/seeing_is_believing/tracks_line_numbers_seen.rb
|
90
94
|
- lib/seeing_is_believing/version.rb
|
91
95
|
- seeing_is_believing.gemspec
|
96
|
+
- spec/arg_parser_spec.rb
|
92
97
|
- spec/evaluate_by_moving_files_spec.rb
|
93
98
|
- spec/expression_list_spec.rb
|
94
99
|
- spec/hard_core_ensure_spec.rb
|
100
|
+
- spec/queue_spec.rb
|
95
101
|
- spec/seeing_is_believing_spec.rb
|
96
102
|
- spec/syntax_analyzer_spec.rb
|
97
103
|
- textmate-integration.png
|
@@ -120,12 +126,15 @@ signing_key:
|
|
120
126
|
specification_version: 3
|
121
127
|
summary: Records results of every line of code in your file
|
122
128
|
test_files:
|
123
|
-
- features/
|
124
|
-
- features/
|
129
|
+
- features/errors.feature
|
130
|
+
- features/examples.feature
|
131
|
+
- features/flags.feature
|
125
132
|
- features/step_definitions/steps.rb
|
126
133
|
- features/support/env.rb
|
134
|
+
- spec/arg_parser_spec.rb
|
127
135
|
- spec/evaluate_by_moving_files_spec.rb
|
128
136
|
- spec/expression_list_spec.rb
|
129
137
|
- spec/hard_core_ensure_spec.rb
|
138
|
+
- spec/queue_spec.rb
|
130
139
|
- spec/seeing_is_believing_spec.rb
|
131
140
|
- spec/syntax_analyzer_spec.rb
|