seeing_is_believing 1.0.1 → 2.0.0.beta1
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/Readme.md +24 -3
- data/features/errors.feature +2 -2
- data/features/examples.feature +9 -9
- data/features/flags.feature +12 -10
- data/features/regression.feature +17 -3
- data/lib/seeing_is_believing.rb +23 -106
- data/lib/seeing_is_believing/binary/add_annotations.rb +3 -3
- data/lib/seeing_is_believing/program_rewriter.rb +280 -0
- data/lib/seeing_is_believing/remove_inline_comments.rb +1 -1
- data/lib/seeing_is_believing/result.rb +15 -5
- data/lib/seeing_is_believing/syntax_analyzer.rb +0 -155
- data/lib/seeing_is_believing/version.rb +1 -1
- data/seeing_is_believing.gemspec +1 -1
- data/spec/program_rewriter_spec.rb +687 -0
- data/spec/seeing_is_believing_spec.rb +66 -114
- data/spec/syntax_analyzer_spec.rb +3 -316
- metadata +10 -10
- data/lib/seeing_is_believing/expression_list.rb +0 -101
- data/spec/expression_list_spec.rb +0 -277
@@ -26,7 +26,7 @@ class SeeingIsBelieving
|
|
26
26
|
|
27
27
|
def record_result(line_number, value)
|
28
28
|
track_line_number line_number
|
29
|
-
|
29
|
+
results_for(line_number) << value.inspect
|
30
30
|
value
|
31
31
|
end
|
32
32
|
|
@@ -36,22 +36,32 @@ class SeeingIsBelieving
|
|
36
36
|
exception.backtrace
|
37
37
|
self.exception = recorded_exception
|
38
38
|
track_line_number line_number
|
39
|
-
|
39
|
+
results_for(line_number).exception = recorded_exception
|
40
40
|
end
|
41
41
|
|
42
42
|
def [](line_number)
|
43
|
-
|
43
|
+
results_for(line_number)
|
44
44
|
end
|
45
45
|
|
46
46
|
def each(&block)
|
47
47
|
(min_line_number..max_line_number).each { |line_number| block.call self[line_number] }
|
48
48
|
end
|
49
49
|
|
50
|
+
def inspect
|
51
|
+
results
|
52
|
+
"#<SIB::Result #{
|
53
|
+
instance_variables.map { |name| "#{name}=#{instance_variable_get(name).inspect}" }.join("\n ")
|
54
|
+
}>"
|
55
|
+
end
|
56
|
+
|
50
57
|
private
|
51
58
|
|
52
|
-
def
|
59
|
+
def results_for(line_number)
|
60
|
+
results[line_number] ||= Line.new
|
61
|
+
end
|
62
|
+
|
63
|
+
def results
|
53
64
|
@results ||= Hash.new
|
54
|
-
@results[line_number] ||= Line.new
|
55
65
|
end
|
56
66
|
end
|
57
67
|
end
|
@@ -12,46 +12,8 @@ class SeeingIsBelieving
|
|
12
12
|
instance
|
13
13
|
end
|
14
14
|
|
15
|
-
# We have to do this b/c Ripper sometimes calls on_tstring_end even when the string doesn't get ended
|
16
|
-
# e.g. SyntaxAnalyzer.new('"a').parse
|
17
|
-
def ends_match?(beginning, ending)
|
18
|
-
return false unless beginning && ending
|
19
|
-
return beginning == ending if beginning.size == 1 && ending.size == 1
|
20
|
-
case beginning[-1]
|
21
|
-
when '<' then '>' == ending
|
22
|
-
when '(' then ')' == ending
|
23
|
-
when '[' then ']' == ending
|
24
|
-
when '{' then '}' == ending
|
25
|
-
when '/' then ending =~ /\A\// # example: /a/x
|
26
|
-
else
|
27
|
-
# example: %Q.a. %_a_ %r|a| ...
|
28
|
-
beginning.start_with?('%') && beginning.end_with?(ending)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
15
|
# SYNTACTIC VALIDITY
|
33
16
|
|
34
|
-
# I don't actually know if all of the error methods should set @has_error
|
35
|
-
# or just parse errors. I don't actually know how to produce the other errors O.o
|
36
|
-
#
|
37
|
-
# Here is what it is defining as of ruby-1.9.3-p125:
|
38
|
-
# on_alias_error
|
39
|
-
# on_assign_error
|
40
|
-
# on_class_name_error
|
41
|
-
# on_param_error
|
42
|
-
# on_parse_error
|
43
|
-
instance_methods.grep(/error/i).each do |error_meth|
|
44
|
-
super_meth = instance_method error_meth
|
45
|
-
define_method error_meth do |*args, &block|
|
46
|
-
@has_error = true
|
47
|
-
super_meth.bind(self).call(*args, &block)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.valid_ruby?(code)
|
52
|
-
parsed(code).valid_ruby? && begin_and_end_comments_are_complete?(code)
|
53
|
-
end
|
54
|
-
|
55
17
|
def self.begins_multiline_comment?(line)
|
56
18
|
line == '=begin'
|
57
19
|
end
|
@@ -66,72 +28,12 @@ class SeeingIsBelieving
|
|
66
28
|
.all? { |b, e| b == '=begin' && e == '=end' }
|
67
29
|
end
|
68
30
|
|
69
|
-
def valid_ruby?
|
70
|
-
!invalid_ruby?
|
71
|
-
end
|
72
|
-
|
73
|
-
def invalid_ruby?
|
74
|
-
@has_error || unclosed_string? || unclosed_regexp?
|
75
|
-
end
|
76
|
-
|
77
31
|
# MISC
|
78
32
|
|
79
33
|
def self.begins_data_segment?(line)
|
80
34
|
line == '__END__'
|
81
35
|
end
|
82
36
|
|
83
|
-
def self.next_line_modifies_current?(line)
|
84
|
-
line =~ /^\s*\./
|
85
|
-
end
|
86
|
-
|
87
|
-
# STRINGS
|
88
|
-
|
89
|
-
def self.unclosed_string?(code)
|
90
|
-
parsed(code).unclosed_string?
|
91
|
-
end
|
92
|
-
|
93
|
-
def string_opens
|
94
|
-
@string_opens ||= []
|
95
|
-
end
|
96
|
-
|
97
|
-
def on_tstring_beg(beginning)
|
98
|
-
string_opens.push beginning
|
99
|
-
super
|
100
|
-
end
|
101
|
-
|
102
|
-
def on_tstring_end(ending)
|
103
|
-
string_opens.pop if ends_match? string_opens.last, ending
|
104
|
-
super
|
105
|
-
end
|
106
|
-
|
107
|
-
def unclosed_string?
|
108
|
-
string_opens.any?
|
109
|
-
end
|
110
|
-
|
111
|
-
# REGEXPS
|
112
|
-
|
113
|
-
def self.unclosed_regexp?(code)
|
114
|
-
parsed(code).unclosed_regexp?
|
115
|
-
end
|
116
|
-
|
117
|
-
def regexp_opens
|
118
|
-
@regexp_opens ||= []
|
119
|
-
end
|
120
|
-
|
121
|
-
def on_regexp_beg(beginning)
|
122
|
-
regexp_opens.push beginning
|
123
|
-
super
|
124
|
-
end
|
125
|
-
|
126
|
-
def on_regexp_end(ending)
|
127
|
-
regexp_opens.pop if ends_match? regexp_opens.last, ending
|
128
|
-
super
|
129
|
-
end
|
130
|
-
|
131
|
-
def unclosed_regexp?
|
132
|
-
regexp_opens.any?
|
133
|
-
end
|
134
|
-
|
135
37
|
# COMMENTS
|
136
38
|
|
137
39
|
def self.line_is_comment?(line)
|
@@ -155,62 +57,5 @@ class SeeingIsBelieving
|
|
155
57
|
@has_comment = true
|
156
58
|
super
|
157
59
|
end
|
158
|
-
|
159
|
-
# RETURNS
|
160
|
-
|
161
|
-
def self.void_value_expression?(code_or_ast)
|
162
|
-
ast = code_or_ast
|
163
|
-
ast = Parser::CurrentRuby.parse(code_or_ast) if code_or_ast.kind_of? String
|
164
|
-
|
165
|
-
case ast && ast.type
|
166
|
-
when :begin, :kwbegin, :resbody # begin and kwbegin should be the same thing, but it changed in parser 1.4.1 to 2.0.0, so just adding them both for safety
|
167
|
-
void_value_expression?(ast.children[-1])
|
168
|
-
when :rescue, :ensure
|
169
|
-
ast.children.any? { |child| void_value_expression? child }
|
170
|
-
when :if
|
171
|
-
void_value_expression?(ast.children[1]) || void_value_expression?(ast.children[2])
|
172
|
-
when :return, :next, :redo, :retry, :break
|
173
|
-
true
|
174
|
-
else
|
175
|
-
false
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
# HERE DOCS
|
180
|
-
|
181
|
-
def self.here_doc?(code)
|
182
|
-
instance = parsed code
|
183
|
-
instance.has_heredoc? && code.scan("\n").size.next <= instance.here_doc_last_line_number
|
184
|
-
end
|
185
|
-
|
186
|
-
def self.unfinished_here_doc?(code)
|
187
|
-
instance = parsed code
|
188
|
-
instance.heredocs.any? { |heredoc| heredoc.size == 1 }
|
189
|
-
end
|
190
|
-
|
191
|
-
def heredocs
|
192
|
-
@heredocs ||= []
|
193
|
-
end
|
194
|
-
|
195
|
-
def on_heredoc_beg(beginning)
|
196
|
-
heredocs << [beginning]
|
197
|
-
super
|
198
|
-
end
|
199
|
-
|
200
|
-
def on_heredoc_end(ending)
|
201
|
-
result = super
|
202
|
-
line_number = result.last.first
|
203
|
-
doc = heredocs.find { |(beginning)| beginning.include? ending.strip }
|
204
|
-
doc << ending << line_number
|
205
|
-
result
|
206
|
-
end
|
207
|
-
|
208
|
-
def has_heredoc?
|
209
|
-
heredocs.any?
|
210
|
-
end
|
211
|
-
|
212
|
-
def here_doc_last_line_number
|
213
|
-
heredocs.last.last
|
214
|
-
end
|
215
60
|
end
|
216
61
|
end
|
data/seeing_is_believing.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
20
|
s.require_paths = ["lib"]
|
21
21
|
|
22
|
-
s.add_dependency "parser", "
|
22
|
+
s.add_dependency "parser", "~> 2.0.0.pre6"
|
23
23
|
|
24
24
|
s.add_development_dependency "haiti", "~> 0.0.3"
|
25
25
|
s.add_development_dependency "rake", "~> 10.0.3"
|
@@ -0,0 +1,687 @@
|
|
1
|
+
require 'seeing_is_believing/program_rewriter'
|
2
|
+
|
3
|
+
# eventually make this not record BEGIN and END
|
4
|
+
# but for now, leave it b/c it's convenient to be able to make it blow up
|
5
|
+
# Probably replace this with some macro like __INVALID_SYNTAX__ that blows it up :)
|
6
|
+
|
7
|
+
describe SeeingIsBelieving::ProgramReWriter do
|
8
|
+
def wrap(code)
|
9
|
+
described_class.call code,
|
10
|
+
before_each: -> * { '<' },
|
11
|
+
after_each: -> * { '>' }
|
12
|
+
end
|
13
|
+
|
14
|
+
# when we get to 2.0 syntax:
|
15
|
+
# example 'ah' do
|
16
|
+
# wrap '-> { }.()'
|
17
|
+
# end
|
18
|
+
|
19
|
+
it 'raises a SyntaxError if the program is invalid' do
|
20
|
+
expect { wrap '+' }.to raise_error SyntaxError
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'wraps the entire body, ignoring leading comments and the data segment' do
|
24
|
+
described_class.call("#comment\nA\n__END__\n1",
|
25
|
+
before_all: "[",
|
26
|
+
after_all: "]",
|
27
|
+
before_each: -> * { '<' },
|
28
|
+
after_each: -> * { '>' }
|
29
|
+
)
|
30
|
+
.should == "#comment\n[<A>]\n__END__\n1"
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'passes the current line number to the before_each and after_each wrappers' do
|
34
|
+
pre_line_num = post_line_num = nil
|
35
|
+
described_class.call("\na",
|
36
|
+
before_each: -> _pre_line_num { pre_line_num = _pre_line_num; '<' },
|
37
|
+
after_each: -> _post_line_num { post_line_num = _post_line_num; '>' }
|
38
|
+
)
|
39
|
+
pre_line_num.should == 2
|
40
|
+
post_line_num.should == 2
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'does nothing for an empty program' do
|
44
|
+
wrap("").should == ""
|
45
|
+
wrap("\n").should == "\n"
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'ignores comments' do
|
49
|
+
wrap("# comment").should == "# comment"
|
50
|
+
wrap("1 #abc\n#def").should == "<1> #abc\n#def"
|
51
|
+
wrap("1\n=begin\n2\n=end").should == "<1>\n=begin\n2\n=end"
|
52
|
+
wrap("=begin\n1\n=end\n2").should == "=begin\n1\n=end\n<2>"
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'void value expressions' do
|
56
|
+
def void_value?(ast)
|
57
|
+
klass = described_class.new '', {}
|
58
|
+
klass.__send__(:void_value?, ast)
|
59
|
+
end
|
60
|
+
|
61
|
+
def ast_for(code)
|
62
|
+
Parser::CurrentRuby.parse code
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'knows a `return`, `next`, `redo`, `retry`, and `break` are void values' do
|
66
|
+
void_value?(ast_for("def a; return; end").children.last).should be_true
|
67
|
+
void_value?(ast_for("loop { next }").children.last).should be_true
|
68
|
+
void_value?(ast_for("loop { redo }").children.last).should be_true
|
69
|
+
void_value?(ast_for("loop { break }").children.last).should be_true
|
70
|
+
|
71
|
+
the_retry = ast_for("begin; rescue; retry; end").children.first.children[1].children.last
|
72
|
+
the_retry.type.should == :retry
|
73
|
+
void_value?(the_retry).should be_true
|
74
|
+
end
|
75
|
+
it 'knows an `if` is a void value if either side is a void value' do
|
76
|
+
the_if = ast_for("def a; if 1; return 2; else; 3; end; end").children.last
|
77
|
+
the_if.type.should == :if
|
78
|
+
void_value?(the_if).should be_true
|
79
|
+
|
80
|
+
the_if = ast_for("def a; if 1; 2; else; return 3; end; end").children.last
|
81
|
+
the_if.type.should == :if
|
82
|
+
void_value?(the_if).should be_true
|
83
|
+
|
84
|
+
the_if = ast_for("def a; if 1; 2; else; 3; end; end").children.last
|
85
|
+
the_if.type.should == :if
|
86
|
+
void_value?(the_if).should be_false
|
87
|
+
end
|
88
|
+
it 'knows a begin is a void value if its last element is a void value' do
|
89
|
+
the_begin = ast_for("loop { begin; break; end }").children.last
|
90
|
+
[:begin, :kwbegin].should include the_begin.type
|
91
|
+
void_value?(the_begin).should be_true
|
92
|
+
|
93
|
+
the_begin = ast_for("loop { begin; 1; end }").children.last
|
94
|
+
[:begin, :kwbegin].should include the_begin.type
|
95
|
+
void_value?(the_begin).should be_false
|
96
|
+
end
|
97
|
+
it 'knows a rescue is a void value if its last child or its else is a void value' do
|
98
|
+
the_rescue = ast_for("begin; rescue; retry; end").children.first
|
99
|
+
the_rescue.type.should == :rescue
|
100
|
+
void_value?(the_rescue).should be_true
|
101
|
+
|
102
|
+
the_rescue = ast_for("begin; rescue; 1; else; retry; end").children.first
|
103
|
+
the_rescue.type.should == :rescue
|
104
|
+
void_value?(the_rescue).should be_true
|
105
|
+
|
106
|
+
the_rescue = ast_for("begin; rescue; 1; else; 2; end").children.first
|
107
|
+
the_rescue.type.should == :rescue
|
108
|
+
void_value?(the_rescue).should be_false
|
109
|
+
end
|
110
|
+
it 'knows an ensure is a void value if its body or ensure portion are void values' do
|
111
|
+
the_ensure = ast_for("loop { begin; break; ensure; 1; end }").children.last.children.last
|
112
|
+
the_ensure.type.should == :ensure
|
113
|
+
void_value?(the_ensure).should be_true
|
114
|
+
|
115
|
+
the_ensure = ast_for("loop { begin; 1; ensure; break; end }").children.last.children.last
|
116
|
+
the_ensure.type.should == :ensure
|
117
|
+
void_value?(the_ensure).should be_true
|
118
|
+
|
119
|
+
the_ensure = ast_for("loop { begin; 1; ensure; 2; end }").children.last.children.last
|
120
|
+
the_ensure.type.should == :ensure
|
121
|
+
void_value?(the_ensure).should be_false
|
122
|
+
end
|
123
|
+
it 'knows other things are not void values' do
|
124
|
+
void_value?(ast_for "123").should be_false
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe 'basic expressions' do
|
129
|
+
it 'wraps an expression' do
|
130
|
+
wrap("A").should == "<A>"
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'wraps multiple expressions' do
|
134
|
+
wrap("A\nB").should == "<A>\n<B>"
|
135
|
+
wrap("(1\n2)").should == "(<1>\n<2>)"
|
136
|
+
# wrap("(1\n2\n)").should == "<(<1>\n<2>\n)>" # just not worth the effort of identifying it, really
|
137
|
+
wrap("begin\n1\n2\nend").should == "<begin\n<1>\n<2>\nend>"
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'does not wrap multiple expressions when they constitute a void value' do
|
141
|
+
wrap("def a\n1\nreturn 2\nend").should == "def a\n<1>\nreturn <2>\nend"
|
142
|
+
wrap("def a\nreturn 1\n2\nend").should == "def a\nreturn <1>\n<2>\nend"
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'wraps nested expressions' do
|
146
|
+
wrap("A do\nB\nend").should == "<A do\n<B>\nend>"
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'wraps multiple expressions on the same line' do
|
150
|
+
wrap("a;b").should == "a;<b>"
|
151
|
+
end
|
152
|
+
|
153
|
+
# many of these taken from http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Literals
|
154
|
+
it 'wraps simple literals' do
|
155
|
+
# should maybe also do %i[] and %I[] for symbols,
|
156
|
+
# but that's only Ruby 2.0, so I'm ignoring it for now
|
157
|
+
# (I expect it to handle them just fine)
|
158
|
+
%w|123
|
159
|
+
-123
|
160
|
+
1_123
|
161
|
+
-543
|
162
|
+
123_456_789_123_456_789
|
163
|
+
123.45
|
164
|
+
1.2e-3
|
165
|
+
0xaabb
|
166
|
+
0377
|
167
|
+
-0b1010
|
168
|
+
0b001_001
|
169
|
+
|
170
|
+
?a
|
171
|
+
?\C-a
|
172
|
+
?\M-a
|
173
|
+
?\M-\C-a
|
174
|
+
|
175
|
+
1..2
|
176
|
+
1...2
|
177
|
+
|
178
|
+
(true==true)..(1==2)
|
179
|
+
|
180
|
+
true
|
181
|
+
false
|
182
|
+
nil
|
183
|
+
self
|
184
|
+
|
185
|
+
[1,2,3]
|
186
|
+
[1,*a,*[2,3,4]]
|
187
|
+
%w(1)
|
188
|
+
%W(2)
|
189
|
+
|
190
|
+
%x[ls]
|
191
|
+
|
192
|
+
/abc/
|
193
|
+
%r(abc)
|
194
|
+
%r.abc.
|
195
|
+
|
196
|
+
:abc
|
197
|
+
:'abc'
|
198
|
+
:"abc"
|
199
|
+
:"a\#{1}"
|
200
|
+
|
201
|
+
{1=>2}
|
202
|
+
{a:1}
|
203
|
+
|.each do |literal|
|
204
|
+
actual = wrap(literal)
|
205
|
+
expected = "<#{literal}>"
|
206
|
+
actual.should eq(expected), "expected #{literal.inspect} to equal #{expected.inspect}, got #{actual.inspect}"
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'wraps macros' do
|
211
|
+
# should this actually replace __FILE__ and __LINE__ so as to avoid fucking up values with the rewrite?
|
212
|
+
# there is also __dir__, but it's only 2.0
|
213
|
+
wrap("__FILE__").should == "<__FILE__>"
|
214
|
+
wrap("__LINE__").should == "<__LINE__>"
|
215
|
+
wrap("defined? a").should == "<defined? a>"
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'does not wrap alias, undef' do
|
219
|
+
wrap("alias tos to_s").should == "alias tos to_s"
|
220
|
+
wrap("undef tos").should == "undef tos"
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'wraps syscalls, but not code interpolated into them' do
|
224
|
+
wrap("`a\nb`").should == "<`a\nb`>"
|
225
|
+
wrap("`a\n\#{1\n2\n3}b`").should == "<`a\n\#{1\n2\n3}b`>"
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe 'variable lookups' do
|
230
|
+
it 'wraps them' do
|
231
|
+
wrap('a').should == "<a>"
|
232
|
+
wrap("$a").should == "<$a>"
|
233
|
+
wrap("@a").should == "<@a>"
|
234
|
+
wrap("@@a").should == "<@@a>"
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
describe 'method invocations' do
|
239
|
+
it 'wraps the whole invocation with or without parens' do
|
240
|
+
wrap("a").should == "<a>"
|
241
|
+
wrap("a()").should == "<a()>"
|
242
|
+
wrap("a()").should == "<a()>"
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'does not wrap arguments' do
|
246
|
+
wrap("a b").should == "<a b>"
|
247
|
+
wrap("a(b,c=1,*d,&e)").should == "<a(b,c=1,*d,&e)>"
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'wraps blocks' do
|
251
|
+
wrap("a { }").should == "<a { }>"
|
252
|
+
wrap("a {\n}").should == "<a {\n}>"
|
253
|
+
wrap("a(b) {\n}").should == "<a(b) {\n}>"
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'wraps method calls with an explicit receiver' do
|
257
|
+
wrap("1.mod(2)").should == "<1.mod(2)>"
|
258
|
+
wrap("1.mod 2").should == "<1.mod 2>"
|
259
|
+
end
|
260
|
+
|
261
|
+
it 'wraps operators calls' do
|
262
|
+
wrap("1+1").should == "<1+1>"
|
263
|
+
wrap("a.b+1").should == "<a.b+1>"
|
264
|
+
wrap("a.b - 1").should == "<a.b - 1>"
|
265
|
+
wrap("a.b -1").should == "<a.b -1>"
|
266
|
+
wrap("!1").should == "<!1>"
|
267
|
+
wrap("~1").should == "<~1>"
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'wraps methods that end in bangs and questions' do
|
271
|
+
wrap("a.b!").should == "<a.b!>"
|
272
|
+
wrap("a.b?").should == "<a.b?>"
|
273
|
+
end
|
274
|
+
|
275
|
+
it 'wraps method invocations that span multiple lines' do
|
276
|
+
wrap("a\n.b\n.c").should == "<<<a>\n.b>\n.c>"
|
277
|
+
wrap("a\n.b{\n}").should == "<<a>\n.b{\n}>"
|
278
|
+
wrap("a\n.b{}").should == "<<a>\n.b{}>"
|
279
|
+
wrap("[*1..5]\n.map { |n| n * 2 }\n.take(2).\nsize").should ==
|
280
|
+
"<<<<[*1..5]>\n.map { |n| n * 2 }>\n.take(2)>.\nsize>"
|
281
|
+
wrap("a = b\n.c\na").should == "<a = <b>\n.c>\n<a>"
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'wraps args in method arguments when the method spans multiple lines' do
|
285
|
+
wrap("a 1,\n2").should == "<a <1>,\n2>"
|
286
|
+
end
|
287
|
+
|
288
|
+
it 'does not wrap splat args' do
|
289
|
+
wrap("a(\n*a\n)").should == "<a(\n*a\n)>"
|
290
|
+
wrap("a(\n*1..2\n)").should == "<a(\n*1..2\n)>"
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
describe 'assignment' do
|
295
|
+
it 'wraps entire simple assignment' do
|
296
|
+
wrap("a=1").should == "<a=1>"
|
297
|
+
wrap("a.b=1").should == "<a.b=1>"
|
298
|
+
wrap("A=1").should == "<A=1>"
|
299
|
+
wrap("A::B=1").should == "<A::B=1>"
|
300
|
+
wrap("@a=1").should == "<@a=1>"
|
301
|
+
wrap("@@a=1").should == "<@@a=1>"
|
302
|
+
wrap("$a=1").should == "<$a=1>"
|
303
|
+
end
|
304
|
+
|
305
|
+
it 'wraps multiple assignments' do
|
306
|
+
wrap("a,b=1,2").should == "<a,b=1,2>"
|
307
|
+
wrap("a,b.c=1,2").should == "<a,b.c=1,2>"
|
308
|
+
wrap("a,B=1,2").should == "<a,B=1,2>"
|
309
|
+
wrap("a,B::C=1,2").should == "<a,B::C=1,2>"
|
310
|
+
wrap("a,@b=1,2").should == "<a,@b=1,2>"
|
311
|
+
wrap("a,@@b=1,2").should == "<a,@@b=1,2>"
|
312
|
+
wrap("a,$b=1,2").should == "<a,$b=1,2>"
|
313
|
+
end
|
314
|
+
|
315
|
+
it 'wraps multiple assignment on each line' do
|
316
|
+
wrap("a,b=1,\n2").should == "<a,b=<1>,\n2>"
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'wraps multiple assignment with splats' do
|
320
|
+
wrap("a,* =1,2,3").should == "<a,* =1,2,3>"
|
321
|
+
end
|
322
|
+
|
323
|
+
it 'wraps the array equivalent' do
|
324
|
+
wrap("a,* =[1,2,3]").should == "<a,* =[1,2,3]>"
|
325
|
+
wrap("a,* = [ 1,2,3 ] ").should == "<a,* = [ 1,2,3 ]> "
|
326
|
+
end
|
327
|
+
|
328
|
+
it 'wraps repeated assignments' do
|
329
|
+
wrap("a=b=1").should == "<a=b=1>"
|
330
|
+
wrap("a=b=\n1").should == "<a=b=\n1>"
|
331
|
+
wrap("a=\nb=\n1").should == "<a=\nb=\n1>"
|
332
|
+
end
|
333
|
+
|
334
|
+
it 'wraps operator assignment' do
|
335
|
+
wrap("a += 1").should == "<a += 1>"
|
336
|
+
wrap("a *= 1").should == "<a *= 1>"
|
337
|
+
wrap("a -= 1").should == "<a -= 1>"
|
338
|
+
wrap("a /= 1").should == "<a /= 1>"
|
339
|
+
wrap("a **= 1").should == "<a **= 1>"
|
340
|
+
wrap("a |= 1").should == "<a |= 1>"
|
341
|
+
wrap("a &= 1").should == "<a &= 1>"
|
342
|
+
wrap("a ||= 1").should == "<a ||= 1>"
|
343
|
+
wrap("a &&= 1").should == "<a &&= 1>"
|
344
|
+
wrap("a[1] = 2").should == "<a[1] = 2>"
|
345
|
+
wrap("a[1] ||= 2").should == "<a[1] ||= 2>"
|
346
|
+
end
|
347
|
+
|
348
|
+
it 'wraps arguments in the assignment' do
|
349
|
+
wrap("a[1\n]=2").should == "<a[<1>\n]=2>"
|
350
|
+
wrap("a[1,\n2\n]=3").should == "<a[<1>,\n<2>\n]=3>"
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
describe 'conditionals' do
|
355
|
+
it 'wraps if/elsif/else/end, the whole thing, their conditionals, and their bodies' do
|
356
|
+
wrap("if 1\n2\nelsif 2\n3\nelsif 4\n5\nend").should == "<if <1>\n<2>\nelsif <2>\n<3>\nelsif <4>\n<5>\nend>" # multiple elsif
|
357
|
+
wrap("if 1\n2\nelsif 2\n3\nelse\n4\nend").should == "<if <1>\n<2>\nelsif <2>\n<3>\nelse\n<4>\nend>" # elisf and else
|
358
|
+
wrap("if 1\n2\nelsif 3\n4\nend").should == "<if <1>\n<2>\nelsif <3>\n<4>\nend>" # elsif only
|
359
|
+
wrap("if 1\n2\nelse\n2\nend").should == "<if <1>\n<2>\nelse\n<2>\nend>" # else only
|
360
|
+
wrap("if 1\n2\nend").should == "<if <1>\n<2>\nend>" # if only
|
361
|
+
|
362
|
+
# same as above, but with then
|
363
|
+
wrap("if 1 then\n2\nelsif 2 then\n3\nelsif 4 then\n5\nend").should == "<if <1> then\n<2>\nelsif <2> then\n<3>\nelsif <4> then\n<5>\nend>"
|
364
|
+
wrap("if 1 then\n2\nelsif 2 then\n3\nelse\n4\nend").should == "<if <1> then\n<2>\nelsif <2> then\n<3>\nelse\n<4>\nend>"
|
365
|
+
wrap("if 1 then\n2\nelsif 3 then\n4\nend").should == "<if <1> then\n<2>\nelsif <3> then\n<4>\nend>"
|
366
|
+
wrap("if 1 then\n2\nelse\n2\nend").should == "<if <1> then\n<2>\nelse\n<2>\nend>"
|
367
|
+
wrap("if 1 then\n2\nend").should == "<if <1> then\n<2>\nend>"
|
368
|
+
|
369
|
+
# inline
|
370
|
+
wrap("1 if 2").should == "<1 if 2>"
|
371
|
+
end
|
372
|
+
|
373
|
+
it 'ignores conditionals that are implicit regexes' do
|
374
|
+
wrap("if /a/\n1\nend").should == "<if /a/\n<1>\nend>"
|
375
|
+
end
|
376
|
+
|
377
|
+
it 'wraps ternaries' do
|
378
|
+
wrap("1 ? 2 : 3").should == "<1 ? 2 : 3>"
|
379
|
+
wrap("1\\\n?\\\n2\\\n:\\\n3").should == "<<1>\\\n?\\\n<2>\\\n:\\\n3>"
|
380
|
+
end
|
381
|
+
|
382
|
+
it 'wraps "unless" statements' do
|
383
|
+
wrap("unless 1\n2\nelse\n3\nend").should == "<unless <1>\n<2>\nelse\n<3>\nend>"
|
384
|
+
wrap("unless 1\n2\nend").should == "<unless <1>\n<2>\nend>"
|
385
|
+
wrap("unless 1 then\n2\nelse\n3\nend").should == "<unless <1> then\n<2>\nelse\n<3>\nend>"
|
386
|
+
wrap("unless 1 then\n2\nend").should == "<unless <1> then\n<2>\nend>"
|
387
|
+
wrap("1 unless 2").should == "<1 unless 2>"
|
388
|
+
end
|
389
|
+
|
390
|
+
it 'wraps case statements, and the value they are initialized with, but not the conditionals' do
|
391
|
+
wrap("case 1\nwhen 2\n3\nwhen 4, 5\nelse\n6\nend").should == "<case <1>\nwhen 2\n<3>\nwhen 4, 5\nelse\n<6>\nend>"
|
392
|
+
wrap("case 1\nwhen 2\nend").should == "<case <1>\nwhen 2\nend>"
|
393
|
+
wrap("case\nwhen 2\nend").should == "<case\nwhen 2\nend>"
|
394
|
+
wrap("case\nwhen 2, 3\n4\n5\nend").should == "<case\nwhen 2, 3\n<4>\n<5>\nend>"
|
395
|
+
|
396
|
+
wrap("case 1\nwhen 2 then\n3\nwhen 4, 5 then\nelse\n6\nend").should == "<case <1>\nwhen 2 then\n<3>\nwhen 4, 5 then\nelse\n<6>\nend>"
|
397
|
+
wrap("case 1\nwhen 2 then\nend").should == "<case <1>\nwhen 2 then\nend>"
|
398
|
+
wrap("case\nwhen 2 then\nend").should == "<case\nwhen 2 then\nend>"
|
399
|
+
wrap("case\nwhen 2, 3 then\n4\n5\nend").should == "<case\nwhen 2, 3 then\n<4>\n<5>\nend>"
|
400
|
+
end
|
401
|
+
|
402
|
+
it 'does not record if the last value in any portion is a void value expression' do
|
403
|
+
wrap("def a\nif true\nreturn 1\nend\nend").should == "def a\nif <true>\nreturn <1>\nend\nend"
|
404
|
+
wrap("def a\nif true\n1\nelse\nreturn 2\nend\nend").should == "def a\nif <true>\n<1>\nelse\nreturn <2>\nend\nend"
|
405
|
+
wrap("def a\nif true\n1\nelsif true\n2\nelse\nreturn 3\nend\nend").should == "def a\nif <true>\n<1>\nelsif <true>\n<2>\nelse\nreturn <3>\nend\nend"
|
406
|
+
wrap("def a\nif true\nif true\nreturn 1\nend\nend\nend").should == "def a\nif <true>\nif <true>\nreturn <1>\nend\nend\nend"
|
407
|
+
wrap("def a\nunless true\nreturn 1\nend\nend").should == "def a\nunless <true>\nreturn <1>\nend\nend"
|
408
|
+
wrap("def a\nunless true\n1\nelse\nreturn 2\nend\nend").should == "def a\nunless <true>\n<1>\nelse\nreturn <2>\nend\nend"
|
409
|
+
wrap("def a\ntrue ?\n(return 1) :\n2\nend").should == "def a\n<true> ?\n(return <1>) :\n<2>\nend"
|
410
|
+
wrap("def a\ntrue ?\n1 :\n(return 2)\nend").should == "def a\n<true> ?\n<1> :\n(return <2>)\nend"
|
411
|
+
end
|
412
|
+
|
413
|
+
# not sure if I actually want this, or if it's just easier b/c it falls out of the current implementation
|
414
|
+
it 'wraps the conditional from an inline if, when it cannot wrap the entire if' do
|
415
|
+
wrap("def a\nreturn if 1\nend").should == "def a\nreturn if <1>\nend"
|
416
|
+
end
|
417
|
+
|
418
|
+
it 'does not wrap &&, and, ||, or, not' do
|
419
|
+
wrap("1\\\n&& 2").should == "<<1>\\\n&& 2>"
|
420
|
+
wrap("1\\\nand 2").should == "<<1>\\\nand 2>"
|
421
|
+
wrap("1\\\n|| 2").should == "<<1>\\\n|| 2>"
|
422
|
+
wrap("1\\\nor 2").should == "<<1>\\\nor 2>"
|
423
|
+
wrap("not\\\n1").should == "<not\\\n1>"
|
424
|
+
wrap("!\\\n1").should == "<!\\\n1>"
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
describe 'loops' do
|
429
|
+
it 'wraps the until condition and body' do
|
430
|
+
wrap("until 1\n2\nend").should == "<until <1>\n<2>\nend>"
|
431
|
+
wrap("1 until 2").should == "<1 until 2>"
|
432
|
+
wrap("begin\n1\nend until true").should == "<begin\n<1>\nend until true>"
|
433
|
+
end
|
434
|
+
it 'wraps the while condition and body' do
|
435
|
+
wrap("while 1\n2\nend").should == "<while <1>\n<2>\nend>"
|
436
|
+
wrap("1 while 2").should == "<1 while 2>"
|
437
|
+
wrap("begin\n1\nend while true").should == "<begin\n<1>\nend while true>"
|
438
|
+
end
|
439
|
+
it 'wraps for/in loops collections and bodies' do
|
440
|
+
wrap("for a in range;1;end").should == "<for a in range;1;end>"
|
441
|
+
wrap("for a in range\n1\nend").should == "<for a in <range>\n<1>\nend>"
|
442
|
+
wrap("for a in range do\n1\nend").should == "<for a in <range> do\n<1>\nend>"
|
443
|
+
wrap("for a,b in whatev\n1\nend").should == "<for a,b in <whatev>\n<1>\nend>"
|
444
|
+
# this one just isn't worth it for now, too edge and I'm fucking tired
|
445
|
+
# wrap("for char in <<HERE.each_char\nabc\nHERE\nputs char\nend").should ==
|
446
|
+
# "<for char in <<<HERE.each_char>\nabc\nHERE\n<puts char>\nend>"
|
447
|
+
end
|
448
|
+
it 'does not wrap redo' do
|
449
|
+
wrap("loop do\nredo\nend").should == "<loop do\nredo\nend>"
|
450
|
+
end
|
451
|
+
it 'wraps the value of break' do
|
452
|
+
wrap("loop do\nbreak 1\nend").should == "<loop do\nbreak <1>\nend>"
|
453
|
+
end
|
454
|
+
it 'wraps the value of next' do
|
455
|
+
wrap("loop do\nnext 10\nend").should == "<loop do\nnext <10>\nend>"
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
describe 'constant access' do
|
460
|
+
it 'wraps simple constant access' do
|
461
|
+
wrap("A").should == "<A>"
|
462
|
+
end
|
463
|
+
|
464
|
+
it 'wraps namespaced constant access' do
|
465
|
+
wrap("::A").should == "<::A>"
|
466
|
+
wrap("A::B").should == "<A::B>"
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
describe 'hash literals' do
|
471
|
+
it 'wraps the whole hash and values that are on their own lines' do
|
472
|
+
wrap("{}").should == "<{}>"
|
473
|
+
wrap("{\n1 => 2}").should == "<{\n1 => 2}>"
|
474
|
+
wrap("{\n1 => 2,\n:abc => 3,\ndef: 4\n}").should == "<{\n1 => <2>,\n:abc => <3>,\ndef: <4>\n}>"
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
describe 'array literals' do
|
479
|
+
it 'records the array and each element that is on its own line' do
|
480
|
+
wrap("[1]").should == "<[1]>"
|
481
|
+
wrap("[1,\n2,\n]").should == "<[<1>,\n<2>,\n]>"
|
482
|
+
wrap("[1, 2,\n]").should == "<[1, <2>,\n]>"
|
483
|
+
end
|
484
|
+
|
485
|
+
it 'does not record splat elements' do
|
486
|
+
wrap("[1,\n*2..3,\n4\n]").should == "<[<1>,\n*2..3,\n<4>\n]>"
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
describe 'regex literals' do
|
491
|
+
it 'wraps regexes' do
|
492
|
+
wrap("/a/").should == "</a/>"
|
493
|
+
wrap("/(?<a>x)/").should == "</(?<a>x)/>"
|
494
|
+
end
|
495
|
+
|
496
|
+
it 'wraps regexes with %r' do
|
497
|
+
wrap("%r(a)").should == "<%r(a)>"
|
498
|
+
wrap("%r'a'").should == "<%r'a'>"
|
499
|
+
end
|
500
|
+
|
501
|
+
it 'records regexes that span mulitple lines' do
|
502
|
+
wrap("/a\nb/").should == "</a\nb/>"
|
503
|
+
wrap("/a\nb/i").should == "</a\nb/i>"
|
504
|
+
end
|
505
|
+
|
506
|
+
# eventually it would be nice if it recorded the interpolated portion,
|
507
|
+
# when the end of the line was not back inside the regexp
|
508
|
+
it 'records regexes with interpolation, but not the interpolated portion' do
|
509
|
+
wrap("/a\#{1}/").should == "</a\#{1}/>"
|
510
|
+
wrap("/a\n\#{1}\nb/").should == "</a\n\#{1}\nb/>"
|
511
|
+
wrap("/a\n\#{1\n}b/").should == "</a\n\#{1\n}b/>"
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
describe 'string literals (except heredocs)' do
|
516
|
+
it 'records single and double quoted strings' do
|
517
|
+
wrap("'a'").should == "<'a'>"
|
518
|
+
wrap('"a"').should == '<"a">'
|
519
|
+
end
|
520
|
+
|
521
|
+
it 'records strings with %, %Q, and %q' do
|
522
|
+
wrap("%'a'").should == "<%'a'>"
|
523
|
+
wrap("%q'a'").should == "<%q'a'>"
|
524
|
+
wrap("%Q'a'").should == "<%Q'a'>"
|
525
|
+
end
|
526
|
+
|
527
|
+
it 'records strings that span mulitple lines' do
|
528
|
+
wrap("'a\nb'").should == "<'a\nb'>"
|
529
|
+
wrap(%'"a\nb"').should == %'<"a\nb">'
|
530
|
+
end
|
531
|
+
|
532
|
+
# eventually it would be nice if it recorded the interpolated portion,
|
533
|
+
# when the end of the line was not back inside the string
|
534
|
+
it 'records strings with interpolation, but not the interpolated portion' do
|
535
|
+
wrap('"a#{1}"').should == '<"a#{1}">'
|
536
|
+
wrap(%'"a\n\#{1}\nb"').should == %'<"a\n\#{1}\nb">'
|
537
|
+
wrap(%'"a\n\#{1\n}b"').should == %'<"a\n\#{1\n}b">'
|
538
|
+
end
|
539
|
+
|
540
|
+
it 'records %, %q, %Q' do
|
541
|
+
wrap('%(A)').should == '<%(A)>'
|
542
|
+
wrap('%.A.').should == '<%.A.>'
|
543
|
+
wrap('%q(A)').should == '<%q(A)>'
|
544
|
+
wrap('%q.A.').should == '<%q.A.>'
|
545
|
+
wrap('%Q(A)').should == '<%Q(A)>'
|
546
|
+
wrap('%Q.A.').should == '<%Q.A.>'
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
describe 'heredocs' do
|
551
|
+
it 'records heredocs on their first line' do
|
552
|
+
wrap("<<A\nA").should == "<<<A>\nA"
|
553
|
+
wrap("<<A\n123\nA").should == "<<<A>\n123\nA"
|
554
|
+
wrap("<<-A\nA").should == "<<<-A>\nA"
|
555
|
+
wrap("<<-A\n123\nA").should == "<<<-A>\n123\nA"
|
556
|
+
wrap("1\n<<A\nA").should == "<<1>\n<<A>\nA"
|
557
|
+
wrap("<<A + <<B\n1\nA\n2\nB").should == "<<<A + <<B>\n1\nA\n2\nB"
|
558
|
+
wrap("<<A\n1\nA\n<<B\n2\nB").should == "<<<<A>\n1\nA\n<<B>\n2\nB"
|
559
|
+
pending 'turtles all the way down :(' do
|
560
|
+
wrap("puts <<A\nA\nputs <<B\nB").should == "<<puts <<A>\nA\nputs <<B>\nB"
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
it "records methods that wrap heredocs, even whent hey don't have parentheses" do
|
565
|
+
wrap("a(<<HERE)\nHERE").should == "<a(<<HERE)>\nHERE"
|
566
|
+
wrap("a <<HERE\nHERE").should == "<a <<HERE>\nHERE"
|
567
|
+
wrap("a 1, <<HERE\nHERE").should == "<a 1, <<HERE>\nHERE"
|
568
|
+
wrap("a.b 1, 2, <<HERE1, <<-HERE2 \nHERE1\n HERE2").should ==
|
569
|
+
"<a.b 1, 2, <<HERE1, <<-HERE2> \nHERE1\n HERE2"
|
570
|
+
wrap("a.b 1,\n2,\n<<HERE\nHERE").should == "<a.b <1>,\n<2>,\n<<HERE>\nHERE"
|
571
|
+
end
|
572
|
+
|
573
|
+
it "records assignments whose value is a heredoc" do
|
574
|
+
wrap("a=<<A\nA").should == "<a=<<A>\nA"
|
575
|
+
wrap("a,b=<<A,<<B\nA\nB").should == "<a,b=<<A,<<B>\nA\nB"
|
576
|
+
wrap("a,b=1,<<B\nB").should == "<a,b=1,<<B>\nB"
|
577
|
+
wrap("a,b=<<A,1\nA").should == "<a,b=<<A,1>\nA"
|
578
|
+
end
|
579
|
+
|
580
|
+
it 'records methods tacked onto the end of heredocs' do
|
581
|
+
wrap("<<A.size\nA").should == "<<<A.size>\nA"
|
582
|
+
wrap("<<A.whatever <<B\nA\nB").should == "<<<A.whatever <<B>\nA\nB"
|
583
|
+
wrap("<<A.whatever(<<B)\nA\nB").should == "<<<A.whatever(<<B)>\nA\nB"
|
584
|
+
wrap("<<A.size()\nA").should == "<<<A.size()>\nA"
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
# raises can be safely ignored, they're just method invocations
|
589
|
+
describe 'begin/rescue/else/ensure/end blocks' do
|
590
|
+
it 'wraps begin/rescue/else/ensure/end blocks' do
|
591
|
+
wrap("begin\nrescue\nelse\nensure\nend").should == "<begin\nrescue\nelse\nensure\nend>"
|
592
|
+
wrap("begin\nrescue e\ne\nend").should == "<begin\nrescue e\n<e>\nend>"
|
593
|
+
wrap("begin\nrescue Exception\n$!\nend").should == "<begin\nrescue Exception\n<$!>\nend>"
|
594
|
+
end
|
595
|
+
it 'wraps inline rescues' do
|
596
|
+
pending "can't figure out how to identify these as different from begin/rescue/end" do
|
597
|
+
wrap("1 rescue nil").should == "<1 rescue nil>"
|
598
|
+
end
|
599
|
+
end
|
600
|
+
it 'wraps the bodies' do
|
601
|
+
wrap("begin\n1\nrescue\n2\nelse\n3\nensure\n4\nend").should ==
|
602
|
+
"<begin\n<1>\nrescue\n<2>\nelse\n<3>\nensure\n<4>\nend>"
|
603
|
+
end
|
604
|
+
it 'wraps bodies with various pieces missing' do
|
605
|
+
wrap("begin\n1\nrescue\n2\nelse\n3\nensure\n4\nend").should == "<begin\n<1>\nrescue\n<2>\nelse\n<3>\nensure\n<4>\nend>"
|
606
|
+
wrap("begin\n1\nrescue\n2\nelse\n3\nend").should == "<begin\n<1>\nrescue\n<2>\nelse\n<3>\nend>"
|
607
|
+
wrap("begin\n1\nrescue\n2\nend").should == "<begin\n<1>\nrescue\n<2>\nend>"
|
608
|
+
wrap("begin\n1\nend").should == "<begin\n<1>\nend>"
|
609
|
+
wrap("begin\nend").should == "<begin\nend>"
|
610
|
+
wrap("begin\n1\nensure\n2\nend").should == "<begin\n<1>\nensure\n<2>\nend>"
|
611
|
+
end
|
612
|
+
it 'does not record retry' do
|
613
|
+
# in this case, it could record the retry
|
614
|
+
# but I don't know how to tell the difference between this and
|
615
|
+
# "loop { begin; retry; end }" so w/e
|
616
|
+
wrap("begin\nrescue\nretry\nend").should == "<begin\nrescue\nretry\nend>"
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
# eventually, don't wrap these b/c they're spammy, but can be annoying since they can be accidentally recorded
|
621
|
+
# by e.g. a begin/end
|
622
|
+
# ignoring public/private/protected for now, b/c they're just methods, not keywords
|
623
|
+
describe 'class definitions' do
|
624
|
+
it 'does not wrap the class definition, does wrap the body' do
|
625
|
+
wrap("class A\n1\nend").should == "class A\n<1>\nend"
|
626
|
+
end
|
627
|
+
|
628
|
+
it 'does not wrap the superclass definition' do
|
629
|
+
wrap("class A < B\nend").should == "class A < B\nend"
|
630
|
+
end
|
631
|
+
|
632
|
+
it 'wraps the rescue body' do
|
633
|
+
wrap("class A < B\n1\nrescue\n2\nend").should == "class A < B\n<1>\nrescue\n<2>\nend"
|
634
|
+
end
|
635
|
+
|
636
|
+
it 'does not wrap the singleton class' do
|
637
|
+
wrap("class << self\n end").should == "class << self\n end"
|
638
|
+
end
|
639
|
+
end
|
640
|
+
|
641
|
+
# eventually, don't wrap these b/c they're spammy, but can be annoying since they can be accidentally recorded
|
642
|
+
# by e.g. a begin/end
|
643
|
+
# ignoring public/private/protected for now, b/c they're just methods, not keywords
|
644
|
+
describe 'module definitions' do
|
645
|
+
it 'does not wrap the definition, does wrap the body' do
|
646
|
+
wrap("module A\n1\nend").should == "module A\n<1>\nend"
|
647
|
+
end
|
648
|
+
it 'wraps the rescue portion' do
|
649
|
+
wrap("module A\n1\nrescue\n2\nend").should == "module A\n<1>\nrescue\n<2>\nend"
|
650
|
+
end
|
651
|
+
end
|
652
|
+
|
653
|
+
describe 'method definitions' do
|
654
|
+
it 'does not wrap the definition or arguments' do
|
655
|
+
wrap("def a(b,c=1,*d,&e)\nend").should == "def a(b,c=1,*d,&e)\nend"
|
656
|
+
end
|
657
|
+
|
658
|
+
it 'wraps the body' do
|
659
|
+
wrap("def a\n1\nend").should == "def a\n<1>\nend"
|
660
|
+
wrap("def a()\n1\nend").should == "def a()\n<1>\nend"
|
661
|
+
end
|
662
|
+
|
663
|
+
it 'does not try to record singleton method definitions' do
|
664
|
+
wrap("def a.b\n1\nend").should == "def a.b\n<1>\nend"
|
665
|
+
wrap("def a.b()\n1\nend").should == "def a.b()\n<1>\nend"
|
666
|
+
end
|
667
|
+
|
668
|
+
it 'wraps calls to yield' do
|
669
|
+
wrap("def a\nyield\nend").should == "def a\n<yield>\nend"
|
670
|
+
end
|
671
|
+
|
672
|
+
it 'wraps calls to super' do
|
673
|
+
wrap("def a\nsuper\nend").should == "def a\n<super>\nend"
|
674
|
+
wrap("def a\nsuper 1\nend").should == "def a\n<super 1>\nend"
|
675
|
+
end
|
676
|
+
|
677
|
+
it 'wraps the bodies of returns' do
|
678
|
+
wrap("def a\nreturn 1\nend").should == "def a\nreturn <1>\nend"
|
679
|
+
end
|
680
|
+
|
681
|
+
it 'wraps the rescue and ensure portion' do
|
682
|
+
wrap("def a\n1\nrescue\n2\nend").should == "def a\n<1>\nrescue\n<2>\nend"
|
683
|
+
wrap("def a\n1\nrescue\n2\nensure\n3\nend").should == "def a\n<1>\nrescue\n<2>\nensure\n<3>\nend"
|
684
|
+
wrap("def a\n1\nensure\n2\nend").should == "def a\n<1>\nensure\n<2>\nend"
|
685
|
+
end
|
686
|
+
end
|
687
|
+
end
|