pry-stack 0.5.0
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.
- checksums.yaml +7 -0
- data/.gemspec +30 -0
- data/.gemtest +0 -0
- data/.gitignore +7 -0
- data/.travis.yml +15 -0
- data/.yardopts +1 -0
- data/CHANGELOG +0 -0
- data/Gemfile +2 -0
- data/LICENSE +25 -0
- data/README.md +39 -0
- data/Rakefile +30 -0
- data/VERSION +1 -0
- data/examples/example.rb +45 -0
- data/examples/example2.rb +18 -0
- data/examples/example3.rb +46 -0
- data/lib/pry-stack.rb +133 -0
- data/lib/pry-stack/commands.rb +316 -0
- data/lib/pry-stack/frame_manager.rb +85 -0
- data/lib/pry-stack/when_started_hook.rb +94 -0
- data/test/helper.rb +92 -0
- data/test/test_commands.rb +358 -0
- data/test/test_frame_manager.rb +65 -0
- data/test/test_stack.rb +393 -0
- data/tester.rb +27 -0
- metadata +140 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
Pry.config.output = StringIO.new
|
4
|
+
|
5
|
+
describe PryStack::FrameManager do
|
6
|
+
|
7
|
+
before do
|
8
|
+
@pry_instance = Pry.new
|
9
|
+
@bindings = [binding, binding, binding, binding]
|
10
|
+
@bindings.each_with_index { |v, i| v.eval("x = #{i}") }
|
11
|
+
@pry_instance.binding_stack.push @bindings.last
|
12
|
+
@frame_manager = PE::FrameManager.new(@bindings, @pry_instance)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "creation" do
|
16
|
+
it "should make bindings accessible via 'bindings' method" do
|
17
|
+
@frame_manager.bindings.should == @bindings
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should set binding_index to 0" do
|
21
|
+
@frame_manager.binding_index.should == 0
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should set current_frame to first frame" do
|
25
|
+
@frame_manager.current_frame.should == @bindings.first
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "FrameManager#change_frame_to" do
|
30
|
+
it 'should change the frame to the given one' do
|
31
|
+
@frame_manager.change_frame_to(1)
|
32
|
+
|
33
|
+
@frame_manager.binding_index.should == 1
|
34
|
+
@frame_manager.current_frame.should == @bindings[1]
|
35
|
+
@pry_instance.binding_stack.last.should == @frame_manager.current_frame
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should accept negative indices when specifying frame' do
|
39
|
+
@frame_manager.change_frame_to(-1)
|
40
|
+
|
41
|
+
# negative index is converted to a positive one inside change_frame_to
|
42
|
+
@frame_manager.binding_index.should == @bindings.size - 1
|
43
|
+
|
44
|
+
@frame_manager.current_frame.should == @bindings[-1]
|
45
|
+
@pry_instance.binding_stack.last.should == @frame_manager.current_frame
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "FrameManager#refresh_frame" do
|
50
|
+
it 'should change the Pry frame to the active one in the FrameManager' do
|
51
|
+
@frame_manager.binding_index = 2
|
52
|
+
@frame_manager.refresh_frame
|
53
|
+
|
54
|
+
@pry_instance.binding_stack.last.should == @frame_manager.current_frame
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "FrameManager is Enumerable" do
|
59
|
+
it 'should perform an Enumerable#map on the frames' do
|
60
|
+
@frame_manager.map { |v| v.eval("x") }.should == (0..(@bindings.size - 1)).to_a
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
data/test/test_stack.rb
ADDED
@@ -0,0 +1,393 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe PryStack do
|
4
|
+
|
5
|
+
describe "Pry.start" do
|
6
|
+
before do
|
7
|
+
Pry.config.hooks.add_hook(:when_started, :save_caller_bindings, WhenStartedHook)
|
8
|
+
Pry.config.hooks.add_hook(:after_session, :delete_frame_manager, AfterSessionHook)
|
9
|
+
|
10
|
+
@o = Object.new
|
11
|
+
class << @o; attr_reader :frame; end
|
12
|
+
def @o.bing() bong end
|
13
|
+
def @o.bong() bang end
|
14
|
+
def @o.bang() Pry.start(binding) end
|
15
|
+
end
|
16
|
+
|
17
|
+
after do
|
18
|
+
Pry.config.hooks.delete_hook(:when_started, :save_caller_bindings)
|
19
|
+
Pry.config.hooks.delete_hook(:after_session, :delete_frame_manager)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe ":initial_frame option" do
|
23
|
+
it 'should default to first frame when no option provided' do
|
24
|
+
redirect_pry_io(StringIO.new("@frame = __method__\nexit\n"), out=StringIO.new) do
|
25
|
+
@o.bing
|
26
|
+
end
|
27
|
+
|
28
|
+
@o.frame.should == :bang
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should begin at correct frame even if Pry.start is monkey-patched (only works with one monkey-patch currently)' do
|
32
|
+
class << Pry
|
33
|
+
alias_method :old_start, :start
|
34
|
+
|
35
|
+
def start(*args, &block)
|
36
|
+
old_start(*args, &block)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
o = Object.new
|
41
|
+
class << o; attr_reader :frames; end
|
42
|
+
def o.bing() bong end
|
43
|
+
def o.bong() bang end
|
44
|
+
def o.bang() Pry.start(binding) end
|
45
|
+
|
46
|
+
redirect_pry_io(InputTester.new(
|
47
|
+
"@frames = SE.frame_manager(_pry_).bindings.take(3)",
|
48
|
+
"exit-all")) do
|
49
|
+
o.bing
|
50
|
+
end
|
51
|
+
|
52
|
+
o.frames.map { |f| f.eval("__method__") }.should == [:bang, :bong, :bing]
|
53
|
+
|
54
|
+
class << Pry
|
55
|
+
alias_method :start, :old_start
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should begin session at specified frame' do
|
60
|
+
o = Object.new
|
61
|
+
class << o; attr_reader :frame; end
|
62
|
+
def o.bing() bong end
|
63
|
+
def o.bong() bang end
|
64
|
+
def o.bang() Pry.start(binding, :initial_frame => 1) end #*
|
65
|
+
|
66
|
+
redirect_pry_io(StringIO.new("@frame = __method__\nexit-all\n"), out=StringIO.new) do
|
67
|
+
o.bing
|
68
|
+
end
|
69
|
+
|
70
|
+
o.frame.should == :bong
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should begin session at specified frame when using :call_stack' do
|
74
|
+
o = Object.new
|
75
|
+
class << o; attr_accessor :frame; end
|
76
|
+
def o.alpha() binding end
|
77
|
+
def o.beta() binding end
|
78
|
+
def o.gamma() binding end
|
79
|
+
|
80
|
+
redirect_pry_io(StringIO.new("@frame = __method__\nexit\n"), out=StringIO.new) do
|
81
|
+
Pry.start(binding, :call_stack => [o.gamma, o.beta, o.alpha], :initial_frame => 1)
|
82
|
+
end
|
83
|
+
|
84
|
+
o.frame.should == :beta
|
85
|
+
end
|
86
|
+
|
87
|
+
# regression test for #12
|
88
|
+
it 'does not infinite loop when pry is started in MyObject#==' do
|
89
|
+
o = Object.new
|
90
|
+
def o.==(other)
|
91
|
+
binding.pry
|
92
|
+
end
|
93
|
+
|
94
|
+
redirect_pry_io(InputTester.new(":hello", "exit-all"), out=StringIO.new) do
|
95
|
+
o.==(1)
|
96
|
+
end
|
97
|
+
|
98
|
+
out.string.should =~ /hello/
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe ":call_stack option" do
|
103
|
+
it 'should invoke a session with the call stack set' do
|
104
|
+
redirect_pry_io(StringIO.new("show-stack\nexit\n"), out=StringIO.new) do
|
105
|
+
@o.bing
|
106
|
+
end
|
107
|
+
|
108
|
+
out.string.should =~ /bang.*?bong.*?bing/m
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should set no call stack when :call_stack => false' do
|
112
|
+
o = Object.new
|
113
|
+
def o.bing() bong end
|
114
|
+
def o.bong() bang end
|
115
|
+
def o.bang() Pry.start(binding, :call_stack => false) end
|
116
|
+
|
117
|
+
redirect_pry_io(StringIO.new("show-stack\nexit\n"), out=StringIO.new) do
|
118
|
+
o.bing
|
119
|
+
end
|
120
|
+
|
121
|
+
out.string.should =~ /No caller stack/
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should set custom call stack when :call_stack => [b1, b2]' do
|
125
|
+
o = Object.new
|
126
|
+
def o.alpha() binding end
|
127
|
+
def o.beta() binding end
|
128
|
+
def o.gamma() binding end
|
129
|
+
|
130
|
+
redirect_pry_io(StringIO.new("show-stack\nexit\n"), out=StringIO.new) do
|
131
|
+
Pry.start(binding, :call_stack => [o.beta, o.gamma, o.alpha])
|
132
|
+
end
|
133
|
+
|
134
|
+
out.string.should =~ /beta.*?gamma.*?alpha/m
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'should raise if custom call stack does not contain bindings' do
|
138
|
+
o = OpenStruct.new
|
139
|
+
redirect_pry_io(StringIO.new("self.errors = _pry_.hooks.errors\nexit\n")) do
|
140
|
+
Pry.start(o, :call_stack => [1, 2, 3])
|
141
|
+
end
|
142
|
+
o.errors.first.is_a?(ArgumentError).should == true
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should raise if custom call stack is empty' do
|
146
|
+
o = OpenStruct.new
|
147
|
+
redirect_pry_io(StringIO.new("self.errors = _pry_.hooks.errors\nexit\n")) do
|
148
|
+
Pry.start o, :call_stack => []
|
149
|
+
end
|
150
|
+
o.errors.first.is_a?(ArgumentError).should == true
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "unit tests for PryStack class methods" do
|
156
|
+
before do
|
157
|
+
@pry_instance = Pry.new
|
158
|
+
@bindings = [binding, binding]
|
159
|
+
end
|
160
|
+
|
161
|
+
after do
|
162
|
+
PE.clear_frame_managers(@pry_instance)
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "PryStack.create_and_push_frame_manager" do
|
166
|
+
|
167
|
+
it "should create and push one new FrameManager" do
|
168
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
169
|
+
PE.frame_manager(@pry_instance).is_a?(PE::FrameManager).should == true
|
170
|
+
PE.frame_managers(@pry_instance).count.should == 1
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should refresh Pry instance to use FrameManager's active binding" do
|
174
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
175
|
+
@pry_instance.binding_stack.size.should == 1
|
176
|
+
@pry_instance.binding_stack.first.should == @bindings.first
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should save prior binding in FrameManager instance' do
|
180
|
+
_pry_ = Pry.new
|
181
|
+
_pry_.binding_stack.push(b=binding)
|
182
|
+
PryStack.create_and_push_frame_manager(@bindings, _pry_)
|
183
|
+
PryStack.frame_manager(_pry_).prior_binding.should == b
|
184
|
+
end
|
185
|
+
|
186
|
+
describe ":initial_frame option" do
|
187
|
+
it 'should start on specified frame' do
|
188
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance, :initial_frame => 1)
|
189
|
+
@pry_instance.binding_stack.size.should == 1
|
190
|
+
@pry_instance.binding_stack.first.should == @bindings.last
|
191
|
+
end
|
192
|
+
|
193
|
+
describe "negative numbers" do
|
194
|
+
it 'should work with negative frame number (-1)' do
|
195
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance, :initial_frame => -1)
|
196
|
+
@pry_instance.binding_stack.size.should == 1
|
197
|
+
@pry_instance.binding_stack.first.should == @bindings.last
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'should work with negative frame number (-2)' do
|
201
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance, :initial_frame => -2)
|
202
|
+
@pry_instance.binding_stack.size.should == 1
|
203
|
+
@pry_instance.binding_stack.first.should == @bindings.first
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'should save prior backtrace in FrameManager instance' do
|
209
|
+
_pry_ = Pry.new
|
210
|
+
_pry_.backtrace = ["my backtrace"]
|
211
|
+
PryStack.create_and_push_frame_manager(@bindings, _pry_)
|
212
|
+
PryStack.frame_manager(_pry_).prior_backtrace.should == _pry_.backtrace
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should create and push multiple FrameManagers" do
|
216
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
217
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
218
|
+
PE.frame_managers(@pry_instance).count.should == 2
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'should push FrameManagers to stacks based on Pry instance' do
|
222
|
+
p2 = Pry.new
|
223
|
+
bindings = [binding, binding]
|
224
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
225
|
+
PE.create_and_push_frame_manager(bindings, p2)
|
226
|
+
PE.frame_managers(@pry_instance).count.should == 1
|
227
|
+
PE.frame_managers(p2).count.should == 1
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
describe "PryStack.frame_manager" do
|
232
|
+
it "should have the correct bindings" do
|
233
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
234
|
+
PE.frame_manager(@pry_instance).bindings.should == @bindings
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should return the last pushed FrameManager" do
|
238
|
+
bindings = [binding, binding]
|
239
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
240
|
+
PE.create_and_push_frame_manager(bindings, @pry_instance)
|
241
|
+
PE.frame_manager(@pry_instance).bindings.should == bindings
|
242
|
+
end
|
243
|
+
|
244
|
+
it "should return the correct FrameManager for the given Pry instance" do
|
245
|
+
bindings = [binding, binding]
|
246
|
+
p2 = Pry.new
|
247
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
248
|
+
PE.create_and_push_frame_manager(bindings, p2)
|
249
|
+
PE.frame_manager(@pry_instance).bindings.should == @bindings
|
250
|
+
PE.frame_manager(p2).bindings.should == bindings
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
describe "PryStack.pop_frame_manager" do
|
255
|
+
it "should remove FrameManager from stack" do
|
256
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
257
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
258
|
+
PE.pop_frame_manager(@pry_instance)
|
259
|
+
PE.frame_managers(@pry_instance).count.should == 1
|
260
|
+
end
|
261
|
+
|
262
|
+
it "should return the most recently added FrameManager" do
|
263
|
+
bindings = [binding, binding]
|
264
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
265
|
+
PE.create_and_push_frame_manager(bindings, @pry_instance)
|
266
|
+
PE.pop_frame_manager(@pry_instance).bindings.should == bindings
|
267
|
+
end
|
268
|
+
|
269
|
+
it "should remove FrameManager from the appropriate stack based on Pry instance" do
|
270
|
+
p2 = Pry.new
|
271
|
+
bindings = [binding, binding]
|
272
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
273
|
+
PE.create_and_push_frame_manager(bindings, p2)
|
274
|
+
PE.pop_frame_manager(@pry_instance)
|
275
|
+
PE.frame_managers(@pry_instance).count.should == 0
|
276
|
+
PE.frame_managers(p2).count.should == 1
|
277
|
+
end
|
278
|
+
|
279
|
+
it "should remove key when no frames remaining for Pry instance" do
|
280
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
281
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
282
|
+
PE.pop_frame_manager(@pry_instance)
|
283
|
+
PE.pop_frame_manager(@pry_instance)
|
284
|
+
PE.frame_hash.has_key?(@pry_instance).should == false
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'should not change size of binding_stack when popping' do
|
288
|
+
bindings = [bindings, bindings]
|
289
|
+
PE.create_and_push_frame_manager(bindings, @pry_instance)
|
290
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
291
|
+
PE.pop_frame_manager(@pry_instance)
|
292
|
+
@pry_instance.binding_stack.size.should == 1
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'should return nil when popping non-existent frame manager' do
|
296
|
+
PE.pop_frame_manager(@pry_instance).should == nil
|
297
|
+
end
|
298
|
+
|
299
|
+
describe "restoring previous binding" do
|
300
|
+
it 'should restore previous binding for Pry instance on pop, where previous binding is not first frame' do
|
301
|
+
bindings = [binding, binding]
|
302
|
+
PE.create_and_push_frame_manager(bindings, @pry_instance).binding_index = 1
|
303
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
304
|
+
PE.pop_frame_manager(@pry_instance)
|
305
|
+
@pry_instance.binding_stack.first.should == bindings[1]
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'should restore previous binding for Pry instance on pop (previous frame frame manager)' do
|
309
|
+
bindings = [binding, binding]
|
310
|
+
PE.create_and_push_frame_manager(bindings, @pry_instance)
|
311
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
312
|
+
PE.pop_frame_manager(@pry_instance)
|
313
|
+
@pry_instance.binding_stack.first.should == bindings.first
|
314
|
+
end
|
315
|
+
|
316
|
+
it 'should restore previous binding for Pry instance on pop (no previous frame manager)' do
|
317
|
+
b = binding
|
318
|
+
@pry_instance.binding_stack = [b]
|
319
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
320
|
+
PE.pop_frame_manager(@pry_instance)
|
321
|
+
@pry_instance.binding_stack.first.should == b
|
322
|
+
end
|
323
|
+
|
324
|
+
it 'should restore previous binding for Pry instance on pop (no previous frame manager AND no empty binding_stack)' do
|
325
|
+
b = binding
|
326
|
+
@pry_instance.binding_stack = [b]
|
327
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
328
|
+
@pry_instance.binding_stack.clear
|
329
|
+
PE.pop_frame_manager(@pry_instance)
|
330
|
+
@pry_instance.binding_stack.first.should == b
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
describe "_pry_.backtrace" do
|
335
|
+
it "should restore backtrace when frame is popped" do
|
336
|
+
p1 = Pry.new
|
337
|
+
bindings = [binding, binding]
|
338
|
+
p1.backtrace = "my backtrace1"
|
339
|
+
PE.create_and_push_frame_manager(bindings, p1)
|
340
|
+
p1.backtrace = "my backtrace2"
|
341
|
+
PE.create_and_push_frame_manager(bindings, p1)
|
342
|
+
p1.backtrace = "my backtrace3"
|
343
|
+
|
344
|
+
PE.pop_frame_manager(p1)
|
345
|
+
p1.backtrace.should == "my backtrace2"
|
346
|
+
PE.pop_frame_manager(p1)
|
347
|
+
p1.backtrace.should == "my backtrace1"
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
describe "PryStack.clear_frame_managers" do
|
353
|
+
it "should clear all FrameManagers for a Pry instance" do
|
354
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
355
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
356
|
+
PE.clear_frame_managers(@pry_instance)
|
357
|
+
PE.frame_hash.has_key?(@pry_instance).should == false
|
358
|
+
end
|
359
|
+
|
360
|
+
it "should clear all FrameManagers for a Pry instance but leave others untouched" do
|
361
|
+
p2 = Pry.new
|
362
|
+
bindings = [binding, binding]
|
363
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
364
|
+
PE.create_and_push_frame_manager(bindings, p2)
|
365
|
+
PE.clear_frame_managers(@pry_instance)
|
366
|
+
PE.frame_managers(p2).count.should == 1
|
367
|
+
PE.frame_hash.has_key?(@pry_instance).should == false
|
368
|
+
end
|
369
|
+
|
370
|
+
it "should remove key" do
|
371
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
372
|
+
PE.create_and_push_frame_manager(@bindings, @pry_instance)
|
373
|
+
PE.clear_frame_managers(@pry_instance)
|
374
|
+
PE.frame_hash.has_key?(@pry_instance).should == false
|
375
|
+
end
|
376
|
+
|
377
|
+
describe "_pry_.backtrace" do
|
378
|
+
it "should restore backtrace to initial one when frame managers are cleared" do
|
379
|
+
p1 = Pry.new
|
380
|
+
bindings = [binding, binding]
|
381
|
+
p1.backtrace = "my backtrace1"
|
382
|
+
PE.create_and_push_frame_manager(bindings, p1)
|
383
|
+
p1.backtrace = "my backtrace2"
|
384
|
+
PE.create_and_push_frame_manager(bindings, p1)
|
385
|
+
p1.backtrace = "my backtrace3"
|
386
|
+
|
387
|
+
PE.clear_frame_managers(p1)
|
388
|
+
p1.backtrace.should == "my backtrace1"
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|