seeing_is_believing 1.0.1 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|