seeing_is_believing 0.0.5 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- seeing_is_believing (0.0.4)
4
+ seeing_is_believing (0.0.6)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
@@ -17,6 +17,7 @@ GEM
17
17
  json (>= 1.4.6)
18
18
  ichannel (5.1.1)
19
19
  json (1.7.6)
20
+ rake (10.0.3)
20
21
  rspec (2.12.0)
21
22
  rspec-core (~> 2.12.0)
22
23
  rspec-expectations (~> 2.12.0)
@@ -32,5 +33,6 @@ PLATFORMS
32
33
  DEPENDENCIES
33
34
  cucumber (~> 1.2.1)
34
35
  ichannel (~> 5.1.1)
36
+ rake (~> 10.0.3)
35
37
  rspec (~> 2.12.0)
36
38
  seeing_is_believing!
data/Readme.md CHANGED
@@ -5,10 +5,11 @@ Evaluates a file, recording the results of each line of code.
5
5
  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
6
  Except, obviously, his is like a million better.
7
7
 
8
- Reeaally rough at the moment, but it works for simple examples.
9
-
10
8
  Also comes with a binary to show how it might be used.
11
9
 
10
+ For whatever reason, I can't embed videos, but here's a ~1 minute [video][video] showing it off.
11
+
12
+
12
13
  Use The Binary
13
14
  ==============
14
15
 
@@ -63,19 +64,42 @@ result[2] # => ['"A"', '"B"', '"C"']
63
64
  Install
64
65
  =======
65
66
 
67
+ For now, since Rubygems is not allowing pushes:
68
+
69
+ $ git clone https://github.com/JoshCheek/seeing_is_believing/
70
+ $ cd seeing_is_believing
71
+ $ gem build seeing_is_believing.gemspec
72
+ $ gem install seeing_is_believing-0.0.7.gem
73
+ $ cd ..
74
+ $ rm -rf "./seeing_is_believing"
75
+
76
+ When Rubygems gets back up:
77
+
66
78
  $ gem install seeing_is_believing
67
79
 
68
80
  Or if you haven't fixed your gem home, and you aren't using any version managers:
69
81
 
70
82
  $ sudo gem install seeing_is_believing
71
83
 
84
+ Hook it into TextMate
85
+ =====================
86
+
87
+ Go to the bundle editor, create this new command in the Ruby bundle:
88
+
89
+ "${TM_RUBY}" -r seeing_is_believing/binary -e '
90
+ SeeingIsBelieving::Binary.new(ARGV, $stdin, $stdout, $stderr).call
91
+ ' $TM_FILEPATH 2>/dev/null
92
+
93
+ It should look like this:
94
+
95
+ ![textmate-integration][textmate-integration]
96
+
72
97
  Known Issues
73
98
  ============
74
99
 
75
- * No idea what happens if you give it a syntactically invalid file. It probably just raises an exception, but might possibly freeze up or something.
76
- * heredocs breaks things maybe also `BEGIN/END` and `=begin/=end`
77
100
  * There are expressions which continue on the next line even though the previous line is a valid expression, e.g. "3\n.times { |i| i }" which will blow up. This is a fundamental flaw in the algorithm and will either require a smarter algorithm, or some sort of more sophisticated parsing in order to handle correctly
78
- * Probably doesn't handle stdin correctly
101
+ * `BEGIN/END` breaks things and I probably won't take the time to fix it, becuase it's nontrivial, but there is currently a cuke for it
102
+ * Heredocs aren't recorded. It might actually be possible if the ExpressionList were to get smarter
79
103
 
80
104
  License
81
105
  =======
@@ -97,3 +121,5 @@ License
97
121
 
98
122
 
99
123
  [inventing_on_principle]: http://vimeo.com/36579366
124
+ [textmate-integration]: https://raw.github.com/JoshCheek/seeing_is_believing/master/textmate-integration.png
125
+ [video]: http://vimeo.com/58766950
@@ -2,7 +2,5 @@
2
2
 
3
3
  $LOAD_PATH.unshift File.expand_path '../../lib', __FILE__
4
4
 
5
- require 'open3'
6
5
  require 'seeing_is_believing/binary'
7
-
8
6
  exit SeeingIsBelieving::Binary.new(ARGV, $stdin, $stdout, $stderr).exitstatus
@@ -21,10 +21,20 @@ Feature: Running the binary successfully
21
21
  meth "12"
22
22
  meth "34"
23
23
 
24
+ =begin
25
+ I don't ever actually write
26
+ comments like this
27
+ =end
28
+
24
29
  # multilinezzz
25
30
  "a
26
31
  b
27
32
  c"
33
+
34
+ # don't record heredocs b/c they're just too fucking different
35
+ <<HERE
36
+ is a doc
37
+ HERE
28
38
  """
29
39
  When I run "seeing_is_believing basic_functionality.rb"
30
40
  Then stderr is empty
@@ -43,10 +53,20 @@ Feature: Running the binary successfully
43
53
  meth "12" # => "12"
44
54
  meth "34" # => "34"
45
55
 
56
+ =begin
57
+ I don't ever actually write
58
+ comments like this
59
+ =end
60
+
46
61
  # multilinezzz
47
62
  "a
48
63
  b
49
64
  c" # => "a\n b\n c"
65
+
66
+ # don't record heredocs b/c they're just too fucking different
67
+ <<HERE
68
+ is a doc
69
+ HERE
50
70
  """
51
71
 
52
72
  Scenario: Passing previous output back into input
@@ -170,6 +190,26 @@ Feature: Running the binary successfully
170
190
  # >> 2
171
191
  """
172
192
 
173
- Scenario: Requiring other files
174
- Scenario: Evaluating a file that requires other files, from a different directory
193
+ Scenario: Reading from stdin
194
+ Given I have the stdin content "hi!"
195
+ And the file "reads_from_stdin.rb":
196
+ """
197
+ puts "You said: #{gets}"
198
+ """
199
+ When I run "seeing_is_believing reads_from_stdin.rb"
200
+ Then stderr is empty
201
+ And the exit status is 0
202
+ And stdout is:
203
+ """
204
+ puts "You said: #{gets}" # => nil
205
+
206
+ # >> You said: hi!
207
+ """
208
+
175
209
  Scenario: Passing the file on stdin
210
+ Given I have the stdin content "1 + 1"
211
+ When I run "seeing_is_believing"
212
+ Then stderr is empty
213
+ And the exit status is 0
214
+ And stdout is "1 + 1 # => 2"
215
+
@@ -2,8 +2,12 @@ Given 'the file "$filename":' do |filename, body|
2
2
  CommandLineHelpers.write_file filename, body
3
3
  end
4
4
 
5
+ Given 'I have the stdin content "$content"' do |content|
6
+ @stdin_data = content
7
+ end
8
+
5
9
  When 'I run "$command"' do |command|
6
- @last_executed = CommandLineHelpers.execute command
10
+ @last_executed = CommandLineHelpers.execute command, @stdin_data
7
11
  end
8
12
 
9
13
  Then /^(stderr|stdout) is:$/ do |stream_name, output|
@@ -18,10 +18,11 @@ module CommandLineHelpers
18
18
  end
19
19
  end
20
20
 
21
- def execute(command)
21
+ def execute(command, stdin_data=nil)
22
+ stdin_data ||= ''
22
23
  in_proving_grounds do
23
24
  bin_in_path = {'PATH' => "#{bin_dir}:#{ENV['PATH']}"}
24
- Invocation.new *Open3.capture3(bin_in_path, command)
25
+ Invocation.new *Open3.capture3(bin_in_path, command, stdin_data: stdin_data)
25
26
  end
26
27
  end
27
28
 
@@ -14,6 +14,7 @@ class SeeingIsBelieving
14
14
  @string = string_or_stream
15
15
  @stream = to_stream string_or_stream
16
16
  @filename = options[:filename]
17
+ @stdin = to_stream options.fetch(:stdin, '')
17
18
  end
18
19
 
19
20
  def call
@@ -35,7 +36,7 @@ class SeeingIsBelieving
35
36
  on_complete: lambda { |line, children, completions, line_number|
36
37
  track_line_number line_number
37
38
  expression = [line, *children, *completions].map(&:chomp).join("\n")
38
- if expression =~ BLANK_REGEX || SyntaxAnalyzer.ends_in_comment?(expression) || SyntaxAnalyzer.will_return?(expression)
39
+ if do_not_record? expression
39
40
  expression + "\n"
40
41
  else
41
42
  record_yahself(expression, line_number) + "\n"
@@ -65,7 +66,7 @@ class SeeingIsBelieving
65
66
  def result_for(program, min_line_number, max_line_number)
66
67
  Dir.mktmpdir "seeing_is_believing_temp_dir" do |dir|
67
68
  filename = @filename || File.join(dir, 'program.rb')
68
- EvaluateByMovingFiles.new(program, filename).call.tap do |result|
69
+ EvaluateByMovingFiles.new(program, filename, input_stream: @stdin).call.tap do |result|
69
70
  result.track_line_number min_line_number
70
71
  result.track_line_number max_line_number
71
72
  end
@@ -100,4 +101,11 @@ class SeeingIsBelieving
100
101
  def the_rest_of_the_stream
101
102
  get_next_line << "\n" << stream.read
102
103
  end
104
+
105
+ def do_not_record?(code)
106
+ code =~ BLANK_REGEX ||
107
+ SyntaxAnalyzer.ends_in_comment?(code) ||
108
+ SyntaxAnalyzer.will_return?(code) ||
109
+ SyntaxAnalyzer.here_doc?(code)
110
+ end
103
111
  end
@@ -15,27 +15,10 @@ class SeeingIsBelieving
15
15
  return if @already_called
16
16
  @already_called = true
17
17
 
18
- unless File.exist? filename
19
- @exitstatus = 1
20
- stderr.puts "#{filename} does not exist!"
21
- return
22
- end
23
-
24
- out, err, syntax_status = Open3.capture3('ruby', '-c', filename)
25
- unless syntax_status.success?
26
- @exitstatus = 1
27
- stderr.puts err
28
- return
29
- end
30
-
31
- believer = SeeingIsBelieving::PrintResultsNextToLines.new File.read(filename), filename
32
- stdout.puts believer.call
33
- if believer.has_exception?
34
- stderr.puts believer.exception.message
35
- @exitstatus = 1
36
- else
37
- @exitstatus = 0
38
- end
18
+ file_exists_or_is_on_stdin &&
19
+ syntax_is_valid &&
20
+ print_program &&
21
+ display_exceptions
39
22
  end
40
23
 
41
24
  def exitstatus
@@ -45,8 +28,52 @@ class SeeingIsBelieving
45
28
 
46
29
  private
47
30
 
31
+ def on_stdin?
32
+ argv.empty?
33
+ end
34
+
48
35
  def filename
49
36
  argv.first
50
37
  end
38
+
39
+ def believer
40
+ @believer ||= begin
41
+ if on_stdin?
42
+ PrintResultsNextToLines.new stdin.read, ''
43
+ else
44
+ PrintResultsNextToLines.new File.read(filename), stdin, filename
45
+ end
46
+ end
47
+ end
48
+
49
+ def file_exists_or_is_on_stdin
50
+ return true if on_stdin? || File.exist?(filename)
51
+ @exitstatus = 1
52
+ stderr.puts "#{filename} does not exist!"
53
+ false
54
+ end
55
+
56
+ def syntax_is_valid
57
+ return true if on_stdin? # <-- should probably check stdin too
58
+ out, err, syntax_status = Open3.capture3('ruby', '-c', filename)
59
+ return true if syntax_status.success?
60
+ @exitstatus = 1
61
+ stderr.puts err
62
+ false
63
+ end
64
+
65
+ def print_program
66
+ stdout.puts believer.call
67
+ true
68
+ end
69
+
70
+ def display_exceptions
71
+ if believer.has_exception?
72
+ stderr.puts believer.exception.message
73
+ @exitstatus = 1
74
+ else
75
+ @exitstatus = 0
76
+ end
77
+ end
51
78
  end
52
79
  end
@@ -3,5 +3,12 @@ class SeeingIsBelieving
3
3
  # can catch any error generated by this lib
4
4
  SeeingIsBelievingError = Class.new StandardError
5
5
 
6
- TempFileAlreadyExists = Class.new SeeingIsBelievingError
6
+ class TempFileAlreadyExists < SeeingIsBelievingError
7
+ def initialize(from_filename, temp_filename)
8
+ super "Trying to back up #{from_filename.inspect} (FILE) to"\
9
+ " #{temp_filename.inspect} (TEMPFILE) but TEMPFILE already exists."\
10
+ " You should check the contents of these files. If FILE is correct,"\
11
+ " then delete TEMPFILE. Otherwise rename TEMPFILE to FILE."
12
+ end
13
+ end
7
14
  end
@@ -12,6 +12,7 @@
12
12
  # when it sees this.
13
13
 
14
14
  require 'open3'
15
+ require 'stringio'
15
16
  require 'fileutils'
16
17
  require 'seeing_is_believing/error'
17
18
  require 'seeing_is_believing/result'
@@ -19,12 +20,13 @@ require 'seeing_is_believing/hard_core_ensure'
19
20
 
20
21
  class SeeingIsBelieving
21
22
  class EvaluateByMovingFiles
22
- attr_accessor :program, :filename, :error_stream
23
+ attr_accessor :program, :filename, :error_stream, :input_stream
23
24
 
24
25
  def initialize(program, filename, options={})
25
26
  self.program = program
26
27
  self.filename = File.expand_path(filename)
27
- self.error_stream = options.fetch :error_stream, $stderr
28
+ self.error_stream = options.fetch :error_stream, $stderr # hmm, not really liking the global here
29
+ self.input_stream = options.fetch :input_stream, StringIO.new('')
28
30
  end
29
31
 
30
32
  def call
@@ -61,11 +63,7 @@ class SeeingIsBelieving
61
63
  attr_accessor :stdout, :stderr, :exitstatus
62
64
 
63
65
  def dont_overwrite_existing_tempfile!
64
- return unless File.exist? temp_filename
65
- raise TempFileAlreadyExists,
66
- "Trying to back up #{filename.inspect} (FILE) to #{temp_filename.inspect} (TEMPFILE) but TEMPFILE already exists."\
67
- " You should check the contents of these files. If FILE is correct, then delete TEMPFILE."\
68
- " Otherwise rename TEMPFILE to FILE."
66
+ raise TempFileAlreadyExists.new(filename, temp_filename) if File.exist? temp_filename
69
67
  end
70
68
 
71
69
  def move_file_to_tempfile
@@ -84,13 +82,21 @@ class SeeingIsBelieving
84
82
  end
85
83
 
86
84
  def evaluate_file
87
- self.stdout, self.stderr, self.exitstatus = Open3.capture3(
88
- 'ruby', '-W0', # no warnings (b/c I hijack STDOUT/STDERR)
89
- '-I', File.expand_path('../..', __FILE__), # fix load path
90
- '-r', 'seeing_is_believing/the_matrix', # hijack the environment so it can be recorded
91
- '-C', file_directory, # run in the file's directory
92
- filename
93
- )
85
+ Open3.popen3 'ruby', '-W0', # no warnings (b/c I hijack STDOUT/STDERR)
86
+ '-I', File.expand_path('../..', __FILE__), # fix load path
87
+ '-r', 'seeing_is_believing/the_matrix', # hijack the environment so it can be recorded
88
+ '-C', file_directory, # run in the file's directory
89
+ filename do |i, o, e, t|
90
+ out_reader = Thread.new { o.read }
91
+ err_reader = Thread.new { e.read }
92
+ Thread.new do
93
+ input_stream.each_char { |char| i.write char }
94
+ i.close
95
+ end
96
+ self.stdout = out_reader.value
97
+ self.stderr = err_reader.value
98
+ self.exitstatus = t.value
99
+ end
94
100
  end
95
101
 
96
102
  def fail
@@ -84,7 +84,7 @@ class SeeingIsBelieving
84
84
  def children_will_never_be_valid?(expression)
85
85
  analyzer = SyntaxAnalyzer.new(expression)
86
86
  analyzer.parse
87
- analyzer.unclosed_string? || analyzer.unclosed_regexp?
87
+ analyzer.unclosed_string? || analyzer.unclosed_regexp? || SyntaxAnalyzer.unclosed_comment?(expression)
88
88
  end
89
89
  end
90
90
  end
@@ -10,9 +10,10 @@ class SeeingIsBelieving
10
10
  EXCEPTION_PREFIX = '# ~>'
11
11
  RESULT_PREFIX = '# =>'
12
12
 
13
- def initialize(body, filename=nil)
13
+ def initialize(body, stdin, filename=nil)
14
14
  self.body = remove_previous_output_from body
15
15
  self.filename = filename
16
+ self.stdin = stdin
16
17
  end
17
18
 
18
19
  def new_body
@@ -26,15 +27,15 @@ class SeeingIsBelieving
26
27
  add_stdout
27
28
  add_stderr
28
29
  add_data_segment
29
- new_body
30
+ return new_body
30
31
  end
31
32
 
32
33
  private
33
34
 
34
- attr_accessor :body, :filename, :file_result
35
+ attr_accessor :body, :filename, :file_result, :stdin
35
36
 
36
37
  def evaluate_program
37
- self.file_result = SeeingIsBelieving.new(body, filename: filename).call
38
+ self.file_result = SeeingIsBelieving.new(body, filename: filename, stdin: stdin).call
38
39
  end
39
40
 
40
41
  def inherit_exception
@@ -58,11 +59,12 @@ class SeeingIsBelieving
58
59
  line.chomp == '__END__'
59
60
  end
60
61
 
61
- # max line length of the body + 2 spaces for padding
62
+ # max line length of the body (exempting coments) + 2 spaces for padding
62
63
  def line_length
63
64
  @line_length ||= 2 + body.each_line
64
65
  .map(&:chomp)
65
66
  .take_while { |line| not start_of_data_segment? line }
67
+ .select { |line| not (line == "=begin") .. (line == "=end") }
66
68
  .reject { |line| SyntaxAnalyzer.ends_in_comment? line }
67
69
  .map(&:length)
68
70
  .max
@@ -70,8 +72,8 @@ class SeeingIsBelieving
70
72
 
71
73
  def remove_previous_output_from(string)
72
74
  string.gsub(/\s+(#{EXCEPTION_PREFIX}|#{RESULT_PREFIX}).*?$/, '')
73
- .gsub(/(\n)?(^#{STDOUT_PREFIX}[^\n]*\r?\n?)+/m, '')
74
- .gsub(/(\n)?(^#{STDERR_PREFIX}[^\n]*\r?\n?)+/m, '')
75
+ .gsub(/\n?(^#{STDOUT_PREFIX}[^\n]*\r?\n?)+/m, '')
76
+ .gsub(/\n?(^#{STDERR_PREFIX}[^\n]*\r?\n?)+/m, '')
75
77
  end
76
78
 
77
79
  def add_stdout
@@ -46,7 +46,13 @@ class SeeingIsBelieving
46
46
  end
47
47
 
48
48
  def self.valid_ruby?(code)
49
- parsed(code).valid_ruby?
49
+ parsed(code).valid_ruby? && begin_and_end_comments_are_complete?(code)
50
+ end
51
+
52
+ def self.begin_and_end_comments_are_complete?(code)
53
+ code.scan(/^=(?:begin|end)$/)
54
+ .each_slice(2)
55
+ .all? { |b, e| b == '=begin' && e == '=end' }
50
56
  end
51
57
 
52
58
  def valid_ruby?
@@ -108,7 +114,11 @@ class SeeingIsBelieving
108
114
  # COMMENTS
109
115
 
110
116
  def self.ends_in_comment?(code)
111
- parsed(code.lines.to_a.last.to_s).has_comment?
117
+ code =~ /^=end\Z/ || parsed(code.lines.to_a.last.to_s).has_comment?
118
+ end
119
+
120
+ def self.unclosed_comment?(code)
121
+ !begin_and_end_comments_are_complete?(code)
112
122
  end
113
123
 
114
124
  def has_comment?
@@ -127,5 +137,37 @@ class SeeingIsBelieving
127
137
  def self.will_return?(code)
128
138
  /(^|\s)return.*?\Z$/ =~ code
129
139
  end
140
+
141
+ # HERE DOCS
142
+
143
+ def self.here_doc?(code)
144
+ instance = parsed code
145
+ instance.has_heredoc? && code.scan("\n").size.next <= instance.here_doc_last_line_number
146
+ end
147
+
148
+ def heredocs
149
+ @heredocs ||= []
150
+ end
151
+
152
+ def on_heredoc_beg(beginning)
153
+ heredocs << [beginning]
154
+ super
155
+ end
156
+
157
+ def on_heredoc_end(ending)
158
+ result = super
159
+ line_number = result.last.first
160
+ doc = heredocs.find { |(beginning)| beginning.include? ending.strip }
161
+ doc << ending << line_number
162
+ result
163
+ end
164
+
165
+ def has_heredoc?
166
+ heredocs.any?
167
+ end
168
+
169
+ def here_doc_last_line_number
170
+ heredocs.last.last
171
+ end
130
172
  end
131
173
  end
@@ -1,3 +1,3 @@
1
1
  class SeeingIsBelieving
2
- VERSION = '0.0.5'
2
+ VERSION = '0.0.7'
3
3
  end
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ["lib"]
20
20
 
21
+ s.add_development_dependency "rake", "~> 10.0.3"
21
22
  s.add_development_dependency "rspec", "~> 2.12.0"
22
23
  s.add_development_dependency "cucumber", "~> 1.2.1"
23
24
  s.add_development_dependency "ichannel", "~> 5.1.1"
@@ -4,8 +4,8 @@ describe SeeingIsBelieving::EvaluateByMovingFiles do
4
4
  let(:filedir) { File.expand_path '../../proving_grounds', __FILE__ }
5
5
  let(:filename) { File.join filedir, 'some_filename' }
6
6
 
7
- def invoke(program)
8
- evaluator = described_class.new(program, filename)
7
+ def invoke(program, options={})
8
+ evaluator = described_class.new(program, filename, options)
9
9
  FileUtils.rm_f evaluator.temp_filename
10
10
  evaluator.call
11
11
  end
@@ -65,4 +65,11 @@ describe SeeingIsBelieving::EvaluateByMovingFiles do
65
65
  expect { evaluator.call }.to raise_error
66
66
  stderr.string.should include "It blew up"
67
67
  end
68
+
69
+ it "doesn't block waiting for io on stdin" do
70
+ reader, writer = IO.pipe
71
+ thread = Thread.new { invoke '1', input_stream: reader }
72
+ sleep 0.05
73
+ thread.should_not be_alive
74
+ end
68
75
  end
@@ -125,6 +125,27 @@ describe SeeingIsBelieving::ExpressionList do
125
125
  block_invocations.should == 1
126
126
  end
127
127
 
128
+ example "example: =begin/=end comments" do
129
+ block_invocations = 0
130
+ call ['=begin', '1', '=end'] do |*expressions, line_number|
131
+ expressions.join('').should == "=begin1=end"
132
+ line_number.should == 3
133
+ block_invocations += 1
134
+ end
135
+ block_invocations.should == 1
136
+ end
137
+
138
+ example "example: heredoc" do
139
+ pending 'Not sure how to do this, for now just catch it at a higher level' do
140
+ result = call ['strings = [<<A, <<-B]', '1', 'A', '2', ' B'] do |*expressions, line_number|
141
+ line_number.should == 1
142
+ expressions.should == ['strings = [<<A, <<B]']
143
+ 'zomg!'
144
+ end
145
+ result.should == "zomg!\n1\nA\n2\n B"
146
+ end
147
+ end
148
+
128
149
  example "example: smoke test debug option" do
129
150
  stream = StringIO.new
130
151
  call(%w[a+ b], debug: true, debug_stream: stream) { |*expressions, _| expressions.join("\n") }
@@ -2,8 +2,8 @@ require 'seeing_is_believing'
2
2
  require 'stringio'
3
3
 
4
4
  describe SeeingIsBelieving do
5
- def invoke(input)
6
- described_class.new(input).call
5
+ def invoke(input, options={})
6
+ described_class.new(input, options).call
7
7
  end
8
8
 
9
9
  def values_for(input)
@@ -104,6 +104,15 @@ describe SeeingIsBelieving do
104
104
  3 # at end of program").should == [['1'], [], []]
105
105
  end
106
106
 
107
+ it "does not record expressions that are here docs (only really b/c it's not smart enough)" do
108
+ values_for("<<A\n1\nA").should be_all &:empty?
109
+ values_for(" <<A\n1\nA").should be_all &:empty?
110
+ values_for("<<-A\n1\n A").should be_all &:empty?
111
+ values_for(" <<-A\n1\n A").should be_all &:empty?
112
+ values_for("s=<<-A\n1\n A").should be_all &:empty?
113
+ values_for("def meth\n<<-A\n1\nA\nend").should == [[], [], [], [], ['nil']]
114
+ end
115
+
107
116
  it 'has no output for empty lines' do
108
117
  values_for('').should == [[]]
109
118
  values_for(' ').should == [[]]
@@ -185,15 +194,16 @@ describe SeeingIsBelieving do
185
194
  it 'can be told to run as a given file (in a given dir/with a given filename)' do
186
195
  filename = File.join proving_grounds_dir, 'mah_file.rb'
187
196
  FileUtils.rm_f filename
188
- result = described_class.new('print File.expand_path __FILE__', filename: filename).call
197
+ result = invoke 'print File.expand_path __FILE__', filename: filename
189
198
  result.stdout.should == filename
190
199
  end
191
200
 
201
+ # this can be refactored to use invoke
192
202
  specify 'cwd is the directory of the file' do
193
203
  filename = File.join proving_grounds_dir, 'mah_file.rb'
194
204
  FileUtils.rm_f filename
195
- result = described_class.new('print File.expand_path __FILE__', filename: filename).call
196
- result = described_class.new('print File.expand_path(Dir.pwd)', filename: filename).call
205
+ result = invoke 'print File.expand_path __FILE__', filename: filename
206
+ result = invoke 'print File.expand_path(Dir.pwd)', filename: filename
197
207
  result.stdout.should == proving_grounds_dir
198
208
  end
199
209
 
@@ -204,4 +214,16 @@ describe SeeingIsBelieving do
204
214
  it 'raises a SyntaxError when the whole program is invalid' do
205
215
  expect { invoke '"' }.to raise_error SyntaxError
206
216
  end
217
+
218
+ it 'can be given a stdin stream' do
219
+ invoke('$stdin.read', stdin: StringIO.new("input"))[1].should == ['"input"']
220
+ end
221
+
222
+ it 'can be given a stdin string' do
223
+ invoke('$stdin.read', stdin: "input")[1].should == ['"input"']
224
+ end
225
+
226
+ it 'defaults the stdin stream to an empty string' do
227
+ invoke('$stdin.read')[1].should == ['""']
228
+ end
207
229
  end
@@ -5,21 +5,61 @@ describe SeeingIsBelieving::SyntaxAnalyzer do
5
5
  is_valid = lambda { |code| described_class.valid_ruby? code }
6
6
  is_valid['1+2'].should be_true
7
7
  is_valid['+'].should be_false
8
+ is_valid["=begin\n1\n=end"].should be_true
8
9
 
9
10
  # due to what are possibly bugs in Ripper
10
11
  # these don't raise any errors, so have to check them explicitly
11
12
  is_valid["'"].should be_false
12
13
  is_valid["/"].should be_false
14
+ is_valid["=begin"].should be_false
15
+ is_valid[" =begin"].should be_false
16
+ is_valid[" = begin"].should be_false
17
+ is_valid["=begin\n1"].should be_false
18
+ is_valid["=begin\n1\n=end\n=begin"].should be_false
19
+ is_valid["=begin\n1\n=end\n=end"].should be_false
20
+ end
21
+
22
+ it 'knows if the expression is a heredoc' do
23
+ is_here_doc = lambda { |code| described_class.here_doc? code }
24
+ is_here_doc["<<A\nA"].should be_true
25
+ is_here_doc["a=<<A\nabc\nA"].should be_true
26
+ is_here_doc["meth(<<A)\nabc\nA"].should be_true
27
+ is_here_doc["meth(<<A)\nabc\nA"].should be_true
28
+ is_here_doc["meth(<<-A)\n abc\n A"].should be_true
29
+ is_here_doc["meth(<<-\"a b\")\n abc\n a b"].should be_true
30
+ is_here_doc["meth(<<-\"a b\", <<something)\n 1\n a b\n2\nsomething"].should be_true
31
+
32
+ is_here_doc["a=<<A\nabc\nA\na"].should be_false
33
+ is_here_doc["def meth\nwhateva(<<A)\nabc\nA\nend"].should be_false
34
+ is_here_doc["a << b\nb"].should be_false
35
+ is_here_doc["a<<b\nb"].should be_false
13
36
  end
14
37
 
15
38
  it 'knows if the last line is a comment' do
16
39
  is_comment = lambda { |code| described_class.ends_in_comment? code }
40
+
41
+ # true
17
42
  is_comment['# whatev'].should be_true
18
43
  is_comment['a # whatev'].should be_true
19
44
  is_comment["a \n b # whatev"].should be_true
45
+ is_comment["=begin\n1\n=end"].should be_true
46
+
47
+ # false
20
48
  is_comment['a'].should be_false
21
49
  is_comment["a # whatev \n b"].should be_false
22
50
  is_comment[""].should be_false
51
+ is_comment["=begin\n=end\n\n =end"].should be_false
52
+ end
53
+
54
+ it 'knows if it contains an unclosed comment' do
55
+ unclosed_comment = lambda { |code| described_class.unclosed_comment? code }
56
+ unclosed_comment["=begin"].should be_true
57
+ unclosed_comment["=begin\n"].should be_true
58
+ unclosed_comment["=begin\n1"].should be_true
59
+ unclosed_comment["1\n=begin\n1\n"].should be_true
60
+ unclosed_comment["1\n=begin\n1\n =end"].should be_true
61
+ unclosed_comment["1\n=begin\n1\n=end"].should be_false
62
+ unclosed_comment[" =begin"].should be_false
23
63
  end
24
64
 
25
65
  # probably don't really need this many tests, but I'm unfamiliar with how thorough Ripper is
Binary file
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.5
4
+ version: 0.0.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-29 00:00:00.000000000 Z
12
+ date: 2013-02-02 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: &70192747454180 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 10.0.3
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70192747454180
14
25
  - !ruby/object:Gem::Dependency
15
26
  name: rspec
16
- requirement: &70333793492220 !ruby/object:Gem::Requirement
27
+ requirement: &70192747453400 !ruby/object:Gem::Requirement
17
28
  none: false
18
29
  requirements:
19
30
  - - ~>
@@ -21,10 +32,10 @@ dependencies:
21
32
  version: 2.12.0
22
33
  type: :development
23
34
  prerelease: false
24
- version_requirements: *70333793492220
35
+ version_requirements: *70192747453400
25
36
  - !ruby/object:Gem::Dependency
26
37
  name: cucumber
27
- requirement: &70333793491440 !ruby/object:Gem::Requirement
38
+ requirement: &70192747452720 !ruby/object:Gem::Requirement
28
39
  none: false
29
40
  requirements:
30
41
  - - ~>
@@ -32,10 +43,10 @@ dependencies:
32
43
  version: 1.2.1
33
44
  type: :development
34
45
  prerelease: false
35
- version_requirements: *70333793491440
46
+ version_requirements: *70192747452720
36
47
  - !ruby/object:Gem::Dependency
37
48
  name: ichannel
38
- requirement: &70333793490760 !ruby/object:Gem::Requirement
49
+ requirement: &70192747452260 !ruby/object:Gem::Requirement
39
50
  none: false
40
51
  requirements:
41
52
  - - ~>
@@ -43,7 +54,7 @@ dependencies:
43
54
  version: 5.1.1
44
55
  type: :development
45
56
  prerelease: false
46
- version_requirements: *70333793490760
57
+ version_requirements: *70192747452260
47
58
  description: Records the results of every line of code in your file (intended to be
48
59
  like xmpfilter), inspired by Bret Victor's JavaScript example in his talk "Inventing
49
60
  on Principle"
@@ -83,6 +94,7 @@ files:
83
94
  - spec/hard_core_ensure_spec.rb
84
95
  - spec/seeing_is_believing_spec.rb
85
96
  - spec/syntax_analyzer_spec.rb
97
+ - textmate-integration.png
86
98
  homepage: https://github.com/JoshCheek/seeing_is_believing
87
99
  licenses: []
88
100
  post_install_message: