seeing_is_believing 0.0.8 → 0.0.9
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.
- 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
|
+
[](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
|