seeing_is_believing 0.0.15 → 0.0.16

Sign up to get free protection for your applications and to get access to all the features.
@@ -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