seeing_is_believing 0.0.15 → 0.0.16

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.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- seeing_is_believing (0.0.14)
4
+ seeing_is_believing (0.0.16)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
data/Readme.md CHANGED
@@ -132,6 +132,7 @@ Known Issues
132
132
  * Heredocs aren't recorded. It might actually be possible if the ExpressionList were to get smarter
133
133
  * Return statements are dealt with poorly, causing some situations where you could capture and display a value to not capture
134
134
  * errors come out really shitty if you're calling them from another program like TextMate, would be better to put a line in that shows where the error is.
135
+ * Add a time limit to auto-kill it if it gets stuck or something (e.g. stack overflow is painful to wait for)
135
136
 
136
137
  License
137
138
  =======
@@ -255,3 +255,10 @@ Feature: Running the binary successfully
255
255
  And the exit status is 0
256
256
  And stdout is "1 + 1 # => 2"
257
257
 
258
+ Scenario: Regression: A program containing a single comment
259
+ Given I have the stdin content "# single comment"
260
+ When I run "seeing_is_believing"
261
+ Then stderr is empty
262
+ And the exit status is 0
263
+ And stdout is "# single comment"
264
+
@@ -231,6 +231,12 @@ Feature: Using flags
231
231
  And the exit status is 0
232
232
  And stdout is '1+'
233
233
 
234
+ Scenario: --version
235
+ When I run 'seeing_is_believing --version'
236
+ Then stderr is empty
237
+ And the exit status is 0
238
+ And stdout is '{{SeeingIsBelieving::VERSION}}'
239
+
234
240
  Scenario: --help
235
241
  When I run "seeing_is_believing --help"
236
242
  Then stderr is empty
@@ -1,11 +1,11 @@
1
- Given('the file "$filename" "$body"') { |filename, body| CommandLineHelpers.write_file filename, body }
2
- Given('the file "$filename":') { |filename, body| CommandLineHelpers.write_file filename, body }
3
- Given('I have the stdin content "$content"') { |content| @stdin_data = content }
4
- Given('I have the stdin content:') { |content| @stdin_data = content }
1
+ Given('the file "$filename" "$body"') { |filename, body| CommandLineHelpers.write_file filename, eval_curlies(body) }
2
+ Given('the file "$filename":') { |filename, body| CommandLineHelpers.write_file filename, eval_curlies(body) }
3
+ Given('I have the stdin content "$content"') { |content| @stdin_data = eval_curlies(content) }
4
+ Given('I have the stdin content:') { |content| @stdin_data = eval_curlies(content) }
5
5
  When('I run "$command"') { |command| @last_executed = CommandLineHelpers.execute command, @stdin_data }
6
6
  When("I run '$command'") { |command| @last_executed = CommandLineHelpers.execute command, @stdin_data }
7
- Then(/^(stderr|stdout) is:$/) { |stream_name, output| @last_executed.send(stream_name).chomp.should == output }
8
- Then(/^(stderr|stdout) is ["'](.*?)["']$/) { |stream_name, output| @last_executed.send(stream_name).chomp.should == output }
7
+ Then(/^(stderr|stdout) is:$/) { |stream_name, output| @last_executed.send(stream_name).chomp.should == eval_curlies(output) }
8
+ Then(/^(stderr|stdout) is ["'](.*?)["']$/) { |stream_name, output| @last_executed.send(stream_name).chomp.should == eval_curlies(output) }
9
9
  Then(/^(stderr|stdout) is empty$/) { |stream_name| @last_executed.send(stream_name).should == '' }
10
- Then(/^(stderr|stdout) includes "([^"]*)"$/) { |stream_name, content| @last_executed.send(stream_name).should include content }
10
+ Then(/^(stderr|stdout) includes "([^"]*)"$/) { |stream_name, content| @last_executed.send(stream_name).should include eval_curlies(content) }
11
11
  Then('the exit status is $status') { |status| @last_executed.exitstatus.to_s.should == status }
@@ -1,5 +1,6 @@
1
1
  require 'fileutils'
2
2
  require 'open3'
3
+ require_relative '../../lib/seeing_is_believing/version'
3
4
 
4
5
 
5
6
  module CommandLineHelpers
@@ -3,6 +3,7 @@ require 'tmpdir'
3
3
 
4
4
  require 'seeing_is_believing/queue'
5
5
  require 'seeing_is_believing/result'
6
+ require 'seeing_is_believing/version'
6
7
  require 'seeing_is_believing/expression_list'
7
8
  require 'seeing_is_believing/evaluate_by_moving_files'
8
9
 
@@ -35,13 +36,13 @@ class SeeingIsBelieving
35
36
  leading_comments = ''
36
37
 
37
38
  # extract leading comments (e.g. encoding) so they don't get wrapped in begin/rescue/end
38
- while next_line_queue.peek =~ /^\s*#/
39
+ while SyntaxAnalyzer.line_is_comment?(next_line_queue.peek)
39
40
  leading_comments << next_line_queue.dequeue << "\n"
40
41
  @line_number += 1
41
42
  end
42
43
 
43
44
  # extract leading =begin/=end so they don't get wrapped in begin/rescue/end
44
- while next_line_queue.peek == '=begin'
45
+ while SyntaxAnalyzer.begins_multiline_comment?(next_line_queue.peek)
45
46
  lines = next_line_queue.dequeue << "\n"
46
47
  @line_number += 1
47
48
  until SyntaxAnalyzer.begin_and_end_comments_are_complete? lines
@@ -53,7 +54,7 @@ class SeeingIsBelieving
53
54
 
54
55
  # extract program body
55
56
  body = ''
56
- until next_line_queue.peek.nil? || data_segment?
57
+ until next_line_queue.empty? || data_segment?
57
58
  expression, expression_size = expression_list.call
58
59
  body << expression
59
60
  track_line_number @line_number
@@ -128,7 +129,7 @@ class SeeingIsBelieving
128
129
  end
129
130
 
130
131
  def data_segment?
131
- next_line_queue.peek == '__END__'
132
+ SyntaxAnalyzer.begins_data_segment?(next_line_queue.peek)
132
133
  end
133
134
 
134
135
  def next_line_queue
@@ -140,9 +141,9 @@ class SeeingIsBelieving
140
141
  end
141
142
 
142
143
  def do_not_record?(code)
143
- code =~ BLANK_REGEX ||
144
- SyntaxAnalyzer.ends_in_comment?(code) ||
145
- SyntaxAnalyzer.will_return?(code) ||
144
+ code =~ BLANK_REGEX ||
145
+ SyntaxAnalyzer.ends_in_comment?(code) ||
146
+ SyntaxAnalyzer.void_value_expression?(code) ||
146
147
  SyntaxAnalyzer.here_doc?(code)
147
148
  end
148
149
  end
@@ -17,6 +17,7 @@ class SeeingIsBelieving
17
17
  def call
18
18
  @exitstatus ||= if flags_have_errors? then print_errors ; 1
19
19
  elsif should_print_help? then print_help ; 0
20
+ elsif should_print_version? then print_version ; 0
20
21
  elsif has_filename? && file_dne? then print_file_dne ; 1
21
22
  elsif should_clean? then print_cleaned_program ; 0
22
23
  elsif invalid_syntax? then print_syntax_error ; 1
@@ -70,6 +71,14 @@ class SeeingIsBelieving
70
71
  stdout.puts flags[:help]
71
72
  end
72
73
 
74
+ def should_print_version?
75
+ flags[:version]
76
+ end
77
+
78
+ def print_version
79
+ stdout.puts SeeingIsBelieving::VERSION
80
+ end
81
+
73
82
  def file_is_on_stdin?
74
83
  flags[:filename].nil? && flags[:program].nil?
75
84
  end
@@ -16,8 +16,9 @@ class SeeingIsBelieving
16
16
  @result ||= begin
17
17
  until args.empty?
18
18
  case (arg = args.shift)
19
- when '-h', '--help' then options[:help] = self.class.help_screen
20
- when '-c', '--clean' then options[:clean] = true
19
+ when '-h', '--help' then options[:help] = self.class.help_screen
20
+ when '-v', '--version' then options[:version] = true
21
+ when '-c', '--clean' then options[:clean] = true
21
22
  when '-l', '--start-line' then extract_positive_int_for :start_line, arg
22
23
  when '-L', '--end-line' then extract_positive_int_for :end_line, arg
23
24
  when '-d', '--line-length' then extract_positive_int_for :line_length, arg
@@ -57,6 +58,7 @@ class SeeingIsBelieving
57
58
 
58
59
  def options
59
60
  @options ||= {
61
+ version: false,
60
62
  clean: false,
61
63
  program: nil,
62
64
  filename: nil,
@@ -104,6 +106,7 @@ Usage: #{$0} [options] [filename]
104
106
  -K, --encoding encoding # sets file encoding, equivalent to Ruby's -Kx (see `man ruby` for valid values)
105
107
  -a, --as filename # run the program as if it was the specified filename
106
108
  -c, --clean # remove annotations from previous runs of seeing_is_believing
109
+ -v, --version # print the version (#{VERSION})
107
110
  -h, --help # this help screen
108
111
  HELP_SCREEN
109
112
  end
@@ -78,18 +78,19 @@ class SeeingIsBelieving
78
78
  end
79
79
 
80
80
  def start_of_data_segment?(line)
81
- line.chomp == '__END__'
81
+ SyntaxAnalyzer.begins_data_segment?(line.chomp)
82
82
  end
83
83
 
84
- # max line length of the lines to output (exempting coments) + 2 spaces for padding
84
+ # max line length of the lines to output (exempting comments) + 2 spaces for padding
85
85
  def max_source_line_length
86
86
  @max_source_line_length ||= 2 + body.each_line
87
87
  .map(&:chomp)
88
88
  .select.with_index(1) { |line, index| start_line <= index && index <= end_line }
89
89
  .take_while { |line| not start_of_data_segment? line }
90
- .select { |line| not (line == "=begin") .. (line == "=end") }
90
+ .select { |line| not SyntaxAnalyzer.begins_multiline_comment?(line) .. SyntaxAnalyzer.ends_multiline_comment?(line ) }
91
91
  .reject { |line| SyntaxAnalyzer.ends_in_comment? line }
92
92
  .map(&:length)
93
+ .concat([0])
93
94
  .max
94
95
  end
95
96
 
@@ -49,7 +49,7 @@ class SeeingIsBelieving
49
49
  # method invocations can be put on the next line, and begin with a dot.
50
50
  # I think that's the only case we need to worry about.
51
51
  # e.g: `3\n.times { |i| p i }`
52
- peek_next_line.call && peek_next_line.call =~ /^\s*\./
52
+ peek_next_line.call && SyntaxAnalyzer.next_line_modifies_current?(peek_next_line.call)
53
53
  end
54
54
 
55
55
  def inspected_expressions(expressions)
@@ -51,6 +51,14 @@ class SeeingIsBelieving
51
51
  parsed(code).valid_ruby? && begin_and_end_comments_are_complete?(code)
52
52
  end
53
53
 
54
+ def self.begins_multiline_comment?(line)
55
+ line == '=begin'
56
+ end
57
+
58
+ def self.ends_multiline_comment?(line)
59
+ line == '=end'
60
+ end
61
+
54
62
  def self.begin_and_end_comments_are_complete?(code)
55
63
  code.scan(/^=(?:begin|end)$/)
56
64
  .each_slice(2)
@@ -65,6 +73,16 @@ class SeeingIsBelieving
65
73
  @has_error || unclosed_string? || unclosed_regexp?
66
74
  end
67
75
 
76
+ # MISC
77
+
78
+ def self.begins_data_segment?(line)
79
+ line == '__END__'
80
+ end
81
+
82
+ def self.next_line_modifies_current?(line)
83
+ line =~ /^\s*\./
84
+ end
85
+
68
86
  # STRINGS
69
87
 
70
88
  def self.unclosed_string?(code)
@@ -115,6 +133,10 @@ class SeeingIsBelieving
115
133
 
116
134
  # COMMENTS
117
135
 
136
+ def self.line_is_comment?(line)
137
+ line =~ /^\s*#/
138
+ end
139
+
118
140
  def self.ends_in_comment?(code)
119
141
  code =~ /^=end\Z/ || parsed(code.lines.to_a.last.to_s).has_comment?
120
142
  end
@@ -136,8 +158,8 @@ class SeeingIsBelieving
136
158
 
137
159
  # this is conspicuosuly inferior, but I can't figure out how to actually parse it
138
160
  # see: http://www.ruby-forum.com/topic/4409633
139
- def self.will_return?(code)
140
- /(^|\s)return.*?\n?\z/ =~ code
161
+ def self.void_value_expression?(code)
162
+ /(^|\s)(?:return|next|redo|retry|break).*?\n?\z/ =~ code
141
163
  end
142
164
 
143
165
  # HERE DOCS
@@ -1,3 +1,3 @@
1
1
  class SeeingIsBelieving
2
- VERSION = '0.0.15'
2
+ VERSION = '0.0.16'
3
3
  end
@@ -248,5 +248,16 @@ describe SeeingIsBelieving::Binary::ArgParser do
248
248
  parse(%w[--clean])[:clean].should == true
249
249
  end
250
250
  end
251
+
252
+ describe ':version' do
253
+ it 'defaults to false' do
254
+ parse([])[:version].should == false
255
+ end
256
+
257
+ it 'can be set with -v and --version' do
258
+ parse(%w[-v])[:version].should == true
259
+ parse(%w[--version])[:version].should == true
260
+ end
261
+ end
251
262
  end
252
263
 
@@ -172,6 +172,41 @@ describe SeeingIsBelieving do
172
172
  # values_for("-> { return 1 }.call" ).should == [['1']]
173
173
  end
174
174
 
175
+ it 'does not try to record the keyword next' do
176
+ values_for("(1..2).each do |i|\nnext if i == 1\ni\nend").should == [[], [], ['2'], ['1..2']]
177
+ end
178
+
179
+ it 'does not try to record the keyword redo' do
180
+ values_for(<<-DOC).should == [[], ['0'], [], ['1', '2', '3', '4'], [], ['0...3'], ['nil'], ['0...3']]
181
+ def meth
182
+ n = 0
183
+ for i in 0...3
184
+ n += 1
185
+ redo if n == 2
186
+ end
187
+ end
188
+ meth
189
+ DOC
190
+ end
191
+
192
+ it 'does not try to record the keyword retry' do
193
+ values_for(<<-DOC).should == [[], [], [], ['nil']]
194
+ def meth
195
+ rescue
196
+ retry
197
+ end
198
+ DOC
199
+ end
200
+
201
+ it 'does not try to record the keyword retry' do
202
+ values_for(<<-DOC).should == [[], ['0'], [], ['nil']]
203
+ (0..2).each do |n|
204
+ n
205
+ break
206
+ end
207
+ DOC
208
+ end
209
+
175
210
  it 'does not affect its environment' do
176
211
  invoke 'def Object.abc() end'
177
212
  Object.should_not respond_to :abc
@@ -62,6 +62,23 @@ describe SeeingIsBelieving::SyntaxAnalyzer do
62
62
  is_unclosed_comment[" =begin"].should be_false
63
63
  end
64
64
 
65
+ it 'knows if the line begins a multiline comment' do
66
+ described_class.begins_multiline_comment?('=begin').should be_true
67
+ described_class.begins_multiline_comment?('=begins').should be_false
68
+ end
69
+
70
+ it 'knows if the line ends a multiline comment' do
71
+ described_class.ends_multiline_comment?('=end').should be_true
72
+ described_class.ends_multiline_comment?('=ends').should be_false
73
+ end
74
+
75
+ it 'knows when the line is a comment' do
76
+ described_class.line_is_comment?('# abc').should be_true
77
+ described_class.line_is_comment?(' # abc').should be_true
78
+ described_class.line_is_comment?('a # abc').should be_false
79
+ described_class.line_is_comment?('abc').should be_false
80
+ end
81
+
65
82
  # probably don't really need this many tests, but I'm unfamiliar with how thorough Ripper is
66
83
  # and already found areas where it doesn't behave correctly
67
84
  it 'knows if the code contains an unclosed string' do
@@ -154,22 +171,44 @@ describe SeeingIsBelieving::SyntaxAnalyzer do
154
171
  end
155
172
  end
156
173
 
157
- # the commented out ones will require actual parsing to solve
158
- it "knows if the code contains a return (can't capture a void value)" do
159
- will_return = -> code { described_class.will_return? code }
160
- will_return["return 1"].should be_true
161
- will_return["return 1\n"].should be_true
162
- will_return["return 1 if true"].should be_true
163
- will_return["return 1 if false"].should be_true
164
- will_return["o.return"].should be_false
165
- will_return[":return"].should be_false
166
- will_return["'return'"].should be_false
167
- will_return["def a\nreturn 1\nend"].should be_false
168
- will_return["-> {\nreturn 1\n}"].should be_false
169
- will_return["Proc.new {\nreturn 1\n}"].should be_false
170
- pending "this doesn't work because the return detecting code is an insufficient regexp" do
171
- will_return["'return\nreturn\nreturn'"].should be_false
172
- will_return["return \\\n1"].should be_true
174
+ shared_examples_for 'void_value_expression?' do |keyword|
175
+ it "returns true when the expression ends in #{keyword}" do
176
+ described_class.void_value_expression?("#{keyword} 1").should be_true
177
+ described_class.void_value_expression?("#{keyword} 1\n").should be_true
178
+ described_class.void_value_expression?("#{keyword} 1 if true").should be_true
179
+ described_class.void_value_expression?("#{keyword} 1 if false").should be_true
180
+ described_class.void_value_expression?("o.#{keyword}").should be_false
181
+ described_class.void_value_expression?(":#{keyword}").should be_false
182
+ described_class.void_value_expression?("'#{keyword}'").should be_false
183
+ described_class.void_value_expression?("def a\n#{keyword} 1\nend").should be_false
184
+ described_class.void_value_expression?("-> {\n#{keyword} 1\n}").should be_false
185
+ described_class.void_value_expression?("Proc.new {\n#{keyword} 1\n}").should be_false
186
+ end
187
+
188
+ it "doesn't work because the return and next keyword evaluators are insufficient regexps" do
189
+ pending "doesn't pass yet (and prob never will >.<)" do
190
+ described_class.send(evalutor, "'#{keyword}\n#{keyword}\n#{keyword}'").should be_false
191
+ described_class.send(evalutor, "#{keyword} \\\n1").should be_true
192
+ end
173
193
  end
174
194
  end
195
+
196
+ it_should_behave_like 'void_value_expression?', 'return'
197
+ it_should_behave_like 'void_value_expression?', 'next'
198
+ it_should_behave_like 'void_value_expression?', 'redo'
199
+ it_should_behave_like 'void_value_expression?', 'retry'
200
+ it_should_behave_like 'void_value_expression?', 'break'
201
+
202
+ it 'knows when a line opens the data segment' do
203
+ described_class.begins_data_segment?('__END__').should be_true
204
+ described_class.begins_data_segment?('__ENDS__').should be_false
205
+ end
206
+
207
+ it 'knows when the next line modifies the current line' do
208
+ described_class.next_line_modifies_current?('.meth').should be_true
209
+ described_class.next_line_modifies_current?(' .meth').should be_true
210
+
211
+ described_class.next_line_modifies_current?('meth').should be_false
212
+ described_class.next_line_modifies_current?(' meth').should be_false
213
+ end
175
214
  end
metadata CHANGED
@@ -2,14 +2,14 @@
2
2
  name: seeing_is_believing
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.15
5
+ version: 0.0.16
6
6
  platform: ruby
7
7
  authors:
8
8
  - Josh Cheek
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-02 00:00:00.000000000 Z
12
+ date: 2013-04-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  version_requirements: !ruby/object:Gem::Requirement