vmc 0.4.2 → 0.4.3
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/lib/vmc.rb +2 -5
- data/lib/vmc/cli/app/apps.rb +4 -1
- data/lib/vmc/cli/app/push.rb +38 -221
- data/lib/vmc/cli/app/push/create.rb +125 -0
- data/lib/vmc/cli/app/push/interaction.rb +64 -0
- data/lib/vmc/cli/app/push/sync.rb +59 -0
- data/lib/vmc/cli/app/rename.rb +1 -1
- data/lib/vmc/cli/organization/base.rb +14 -0
- data/lib/vmc/cli/organization/create_org.rb +28 -0
- data/lib/vmc/cli/organization/delete_org.rb +65 -0
- data/lib/vmc/cli/organization/org.rb +46 -0
- data/lib/vmc/cli/organization/orgs.rb +35 -0
- data/lib/vmc/cli/organization/rename.rb +32 -0
- data/lib/vmc/cli/service/base.rb +8 -0
- data/lib/vmc/cli/service/binding.rb +66 -0
- data/lib/vmc/cli/service/create.rb +104 -0
- data/lib/vmc/cli/service/delete.rb +84 -0
- data/lib/vmc/cli/service/rename.rb +32 -0
- data/lib/vmc/cli/service/service.rb +45 -0
- data/lib/vmc/cli/service/services.rb +118 -0
- data/lib/vmc/cli/space/base.rb +21 -0
- data/lib/vmc/cli/space/create.rb +57 -0
- data/lib/vmc/cli/space/delete.rb +92 -0
- data/lib/vmc/cli/space/rename.rb +36 -0
- data/lib/vmc/cli/space/space.rb +67 -0
- data/lib/vmc/cli/space/spaces.rb +57 -0
- data/lib/vmc/cli/space/take.rb +18 -0
- data/lib/vmc/cli/start/base.rb +100 -0
- data/lib/vmc/cli/start/colors.rb +14 -0
- data/lib/vmc/cli/start/info.rb +124 -0
- data/lib/vmc/cli/start/login.rb +94 -0
- data/lib/vmc/cli/start/logout.rb +14 -0
- data/lib/vmc/cli/start/register.rb +38 -0
- data/lib/vmc/cli/start/target.rb +68 -0
- data/lib/vmc/cli/start/targets.rb +17 -0
- data/lib/vmc/version.rb +1 -1
- data/spec/factories/app_factory.rb +5 -0
- data/spec/factories/client_factory.rb +10 -1
- data/spec/factories/domain_factory.rb +2 -1
- data/spec/factories/factory.rb +1 -0
- data/spec/factories/framework_factory.rb +1 -0
- data/spec/factories/organization_factory.rb +18 -0
- data/spec/factories/route_factory.rb +1 -0
- data/spec/factories/runtime_factory.rb +10 -0
- data/spec/factories/service_binding_factory.rb +9 -0
- data/spec/factories/service_factory.rb +17 -0
- data/spec/factories/service_instance_factory.rb +10 -0
- data/spec/factories/service_plan_factory.rb +11 -0
- data/spec/factories/space_factory.rb +10 -0
- data/spec/support/interact_helpers.rb +7 -3
- data/spec/vmc/cli/app/push/create_spec.rb +450 -0
- data/spec/vmc/cli/app/push_spec.rb +303 -9
- data/spec/vmc/cli/app/rename_spec.rb +9 -4
- data/spec/vmc/cli/organization/rename_spec.rb +113 -0
- data/spec/vmc/cli/route/delete_route_spec.rb +2 -2
- data/spec/vmc/cli/service/rename_spec.rb +114 -0
- data/spec/vmc/cli/space/rename_spec.rb +114 -0
- metadata +109 -64
- data/lib/vmc/cli/organization.rb +0 -176
- data/lib/vmc/cli/service.rb +0 -387
- data/lib/vmc/cli/space.rb +0 -284
- data/lib/vmc/cli/start.rb +0 -432
- data/spec/assets/hello-sinatra/Gemfile.lock +0 -17
@@ -2,10 +2,12 @@ require 'spec_helper'
|
|
2
2
|
require "vmc/cli/app/push"
|
3
3
|
|
4
4
|
describe VMC::App::Push do
|
5
|
-
let(:
|
5
|
+
let(:global) { { :color => false, :quiet => true } }
|
6
6
|
let(:inputs) { {} }
|
7
7
|
let(:given) { {} }
|
8
|
+
let(:path) { "somepath" }
|
8
9
|
let(:client) { FactoryGirl.build(:client) }
|
10
|
+
let(:push) { VMC::App::Push.new(Mothership.commands[:push]) }
|
9
11
|
|
10
12
|
before do
|
11
13
|
any_instance_of(VMC::CLI) do |cli|
|
@@ -14,21 +16,313 @@ describe VMC::App::Push do
|
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
17
|
-
describe '
|
18
|
-
|
19
|
+
describe 'metadata' do
|
20
|
+
let(:command) { Mothership.commands[:push] }
|
19
21
|
|
20
|
-
|
22
|
+
describe 'command' do
|
23
|
+
subject { command }
|
24
|
+
its(:description) { should eq "Push an application, syncing changes if it exists" }
|
25
|
+
it { expect(Mothership::Help.group(:apps, :manage)).to include(subject) }
|
21
26
|
end
|
22
27
|
|
23
|
-
|
28
|
+
describe 'inputs' do
|
29
|
+
subject { command.inputs }
|
30
|
+
|
31
|
+
it "is not missing any descriptions" do
|
32
|
+
subject.each do |input, attrs|
|
33
|
+
expect(attrs[:description]).to be
|
34
|
+
expect(attrs[:description].strip).to_not be_empty
|
35
|
+
end
|
36
|
+
end
|
24
37
|
end
|
25
|
-
end
|
26
38
|
|
27
|
-
|
28
|
-
|
39
|
+
describe 'arguments' do
|
40
|
+
subject { command.arguments }
|
41
|
+
it 'has the correct argument order' do
|
42
|
+
should eq([{ :type => :normal, :value => nil, :name => :name }])
|
43
|
+
end
|
44
|
+
end
|
29
45
|
end
|
30
46
|
|
31
47
|
describe '#sync_app' do
|
48
|
+
let(:app) { FactoryGirl.build(:app) }
|
49
|
+
|
50
|
+
before do
|
51
|
+
stub(app).upload
|
52
|
+
app.changes = {}
|
53
|
+
end
|
54
|
+
|
55
|
+
subject do
|
56
|
+
push.input = Mothership::Inputs.new(nil, push, inputs, {}, global)
|
57
|
+
push.sync_app(app, path)
|
58
|
+
end
|
59
|
+
|
60
|
+
shared_examples 'common tests for inputs' do |*args|
|
61
|
+
context 'when the new input is the same as the old' do
|
62
|
+
type, input = args
|
63
|
+
input ||= type
|
64
|
+
|
65
|
+
let(:inputs) { {input => old} }
|
66
|
+
|
67
|
+
it "does not update the app's #{type}" do
|
68
|
+
dont_allow(push).line
|
69
|
+
dont_allow(app).update!
|
70
|
+
expect { subject }.not_to change { app.send(type) }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'uploads the app' do
|
76
|
+
mock(app).upload(path)
|
77
|
+
subject
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'when no inputs are given' do
|
81
|
+
let(:inputs) { {} }
|
82
|
+
|
83
|
+
it 'should not update the app' do
|
84
|
+
dont_allow(app).update!
|
85
|
+
subject
|
86
|
+
end
|
87
|
+
|
88
|
+
[:memory=, :framework=].each do |property|
|
89
|
+
it "should not set #{property} on the app" do
|
90
|
+
dont_allow(app).__send__(property)
|
91
|
+
subject
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'when memory is given' do
|
97
|
+
let(:old) { 1024 }
|
98
|
+
let(:new) { "2G" }
|
99
|
+
let(:app) { FactoryGirl.build(:app, :memory => old) }
|
100
|
+
let(:inputs) { { :memory => new } }
|
101
|
+
|
102
|
+
it 'updates the app memory, converting to megabytes' do
|
103
|
+
stub(push).line(anything)
|
104
|
+
mock(app).update!
|
105
|
+
expect { subject }.to change { app.memory }.from(old).to(2048)
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'outputs the changed memory in human readable sizes' do
|
109
|
+
mock(push).line("Changes:")
|
110
|
+
mock(push).line("memory: 1G -> 2G")
|
111
|
+
stub(app).update!
|
112
|
+
subject
|
113
|
+
end
|
114
|
+
|
115
|
+
include_examples 'common tests for inputs', :memory
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'when instances is given' do
|
119
|
+
let(:old) { 1 }
|
120
|
+
let(:new) { 2 }
|
121
|
+
let(:app) { FactoryGirl.build(:app, :total_instances => old) }
|
122
|
+
let(:inputs) { { :instances => new } }
|
123
|
+
|
124
|
+
it 'updates the app instances' do
|
125
|
+
stub(push).line(anything)
|
126
|
+
mock(app).update!
|
127
|
+
expect { subject }.to change { app.total_instances }.from(old).to(new)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'outputs the changed instances' do
|
131
|
+
mock(push).line("Changes:")
|
132
|
+
mock(push).line("instances: 1 -> 2")
|
133
|
+
stub(app).update!
|
134
|
+
subject
|
135
|
+
end
|
136
|
+
|
137
|
+
include_examples 'common tests for inputs', :total_instances, :instances
|
138
|
+
end
|
139
|
+
|
140
|
+
context 'when framework is given' do
|
141
|
+
let(:old) { FactoryGirl.build(:framework, :name => "Old Framework") }
|
142
|
+
let(:new) { FactoryGirl.build(:framework, :name => "New Framework") }
|
143
|
+
let(:app) { FactoryGirl.build(:app, :framework => old) }
|
144
|
+
let(:inputs) { { :framework => new } }
|
145
|
+
|
146
|
+
it 'updates the app framework' do
|
147
|
+
stub(push).line(anything)
|
148
|
+
mock(app).update!
|
149
|
+
expect { subject }.to change { app.framework }.from(old).to(new)
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'outputs the changed framework using the name' do
|
153
|
+
mock(push).line("Changes:")
|
154
|
+
mock(push).line("framework: Old Framework -> New Framework")
|
155
|
+
stub(app).update!
|
156
|
+
subject
|
157
|
+
end
|
158
|
+
|
159
|
+
include_examples 'common tests for inputs', :framework
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'when runtime is given' do
|
163
|
+
let(:old) { FactoryGirl.build(:runtime, :name => "Old Runtime") }
|
164
|
+
let(:new) { FactoryGirl.build(:runtime, :name => "New Runtime") }
|
165
|
+
let(:app) { FactoryGirl.build(:app, :runtime => old) }
|
166
|
+
let(:inputs) { { :runtime => new } }
|
167
|
+
|
168
|
+
it 'updates the app runtime' do
|
169
|
+
stub(push).line(anything)
|
170
|
+
mock(app).update!
|
171
|
+
expect { subject }.to change { app.runtime }.from(old).to(new)
|
172
|
+
end
|
32
173
|
|
174
|
+
it 'outputs the changed runtime using the name' do
|
175
|
+
mock(push).line("Changes:")
|
176
|
+
mock(push).line("runtime: Old Runtime -> New Runtime")
|
177
|
+
stub(app).update!
|
178
|
+
subject
|
179
|
+
end
|
180
|
+
|
181
|
+
include_examples 'common tests for inputs', :runtime
|
182
|
+
end
|
183
|
+
|
184
|
+
context 'when command is given' do
|
185
|
+
let(:old) { "./start" }
|
186
|
+
let(:new) { "./start foo " }
|
187
|
+
let(:app) { FactoryGirl.build(:app, :command => old) }
|
188
|
+
let(:inputs) { { :command => new } }
|
189
|
+
|
190
|
+
it 'updates the app command' do
|
191
|
+
stub(push).line(anything)
|
192
|
+
mock(app).update!
|
193
|
+
expect { subject }.to change { app.command }.from("./start").to("./start foo ")
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'outputs the changed command in single quotes' do
|
197
|
+
mock(push).line("Changes:")
|
198
|
+
mock(push).line("command: './start' -> './start foo '")
|
199
|
+
stub(app).update!
|
200
|
+
subject
|
201
|
+
end
|
202
|
+
|
203
|
+
include_examples 'common tests for inputs', :command
|
204
|
+
end
|
205
|
+
|
206
|
+
context 'when plan is given' do
|
207
|
+
let(:old) { "d100" }
|
208
|
+
let(:new) { "p100" }
|
209
|
+
let(:inputs) { { :plan => new } }
|
210
|
+
|
211
|
+
include_examples 'common tests for inputs', :production, :plan
|
212
|
+
|
213
|
+
%w{p100 P100 P200}.each do |plan|
|
214
|
+
context "when the given plan is #{plan}" do
|
215
|
+
let(:inputs) { { :plan => plan } }
|
216
|
+
|
217
|
+
it 'sets production to true' do
|
218
|
+
stub(push).line(anything)
|
219
|
+
mock(app).update!
|
220
|
+
expect { subject }.to change { app.production }.from(false).to(true)
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'outputs the changed plan in single quotes' do
|
224
|
+
mock(push).line("Changes:")
|
225
|
+
mock(push).line("production: false -> true")
|
226
|
+
stub(app).update!
|
227
|
+
subject
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
%w{d100 D100 D200 fizzbuzz}.each do |plan|
|
233
|
+
context "when the given plan is #{plan}" do
|
234
|
+
let(:app) { FactoryGirl.build(:app, :production => true) }
|
235
|
+
|
236
|
+
let(:inputs) { { :plan => plan } }
|
237
|
+
|
238
|
+
it 'sets production to false' do
|
239
|
+
stub(push).line(anything)
|
240
|
+
mock(app).update!
|
241
|
+
expect { subject }.to change { app.production }.from(true).to(false)
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'outputs the changed plan in single quotes' do
|
245
|
+
mock(push).line("Changes:")
|
246
|
+
mock(push).line("production: true -> false")
|
247
|
+
stub(app).update!
|
248
|
+
subject
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
context 'when restart is given' do
|
255
|
+
let(:inputs) { { :restart => true, :memory => 4096 } }
|
256
|
+
|
257
|
+
|
258
|
+
context 'when the app is already started' do
|
259
|
+
let(:app) { FactoryGirl.build(:app, :state => "STARTED") }
|
260
|
+
|
261
|
+
it 'invokes the restart command' do
|
262
|
+
stub(push).line
|
263
|
+
mock(app).update!
|
264
|
+
mock(push).invoke(:restart, :app => app)
|
265
|
+
subject
|
266
|
+
end
|
267
|
+
|
268
|
+
context 'but there are no changes' do
|
269
|
+
let(:inputs) { { :restart => true} }
|
270
|
+
|
271
|
+
it 'does not restart' do
|
272
|
+
stub(push).line
|
273
|
+
dont_allow(app).update!
|
274
|
+
dont_allow(push).invoke
|
275
|
+
subject
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
context 'when the app is not already started' do
|
281
|
+
let(:app) { FactoryGirl.build(:app, :state => "STOPPED") }
|
282
|
+
|
283
|
+
it 'does not invoke the restart command' do
|
284
|
+
stub(push).line
|
285
|
+
mock(app).update!
|
286
|
+
dont_allow(push).invoke(:restart, :app => app)
|
287
|
+
subject
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
describe '#setup_new_app (integration spec!!)' do
|
294
|
+
let(:app) { FactoryGirl.build(:app, :guid => nil) }
|
295
|
+
let(:framework) { FactoryGirl.build(:framework) }
|
296
|
+
let(:runtime) { FactoryGirl.build(:runtime) }
|
297
|
+
let(:url) { "https://www.foobar.com" }
|
298
|
+
let(:inputs) do
|
299
|
+
{ :name => "some-app",
|
300
|
+
:instances => 2,
|
301
|
+
:framework => framework,
|
302
|
+
:runtime => runtime,
|
303
|
+
:memory => 1024,
|
304
|
+
:url => url
|
305
|
+
}
|
306
|
+
end
|
307
|
+
let(:global) { {:quiet => true, :color => false, :force => true} }
|
308
|
+
|
309
|
+
before do
|
310
|
+
stub(client).app { app }
|
311
|
+
end
|
312
|
+
|
313
|
+
subject do
|
314
|
+
push.input = Mothership::Inputs.new(Mothership.commands[:push], push, inputs, global, global)
|
315
|
+
push.setup_new_app(path)
|
316
|
+
end
|
317
|
+
|
318
|
+
it 'creates the app' do
|
319
|
+
mock(app).create!
|
320
|
+
mock(app).upload(path)
|
321
|
+
mock(push).filter(:create_app, app) { app }
|
322
|
+
mock(push).filter(:push_app, app) { app }
|
323
|
+
mock(push).invoke :map, :app => app, :url => url
|
324
|
+
mock(push).invoke :start, :app => app
|
325
|
+
subject
|
326
|
+
end
|
33
327
|
end
|
34
|
-
end
|
328
|
+
end
|
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
require "vmc/cli/app/rename"
|
3
3
|
|
4
4
|
describe VMC::App::Rename do
|
5
|
-
let(:
|
5
|
+
let(:global) { { :color => false, :quiet => true } }
|
6
6
|
let(:inputs) { {} }
|
7
7
|
let(:given) { {} }
|
8
8
|
let(:client) { FactoryGirl.build(:client) }
|
@@ -16,7 +16,7 @@ describe VMC::App::Rename do
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
subject { Mothership.new.invoke(:rename, inputs, given,
|
19
|
+
subject { Mothership.new.invoke(:rename, inputs, given, global) }
|
20
20
|
|
21
21
|
describe 'metadata' do
|
22
22
|
let(:command) { Mothership.commands[:rename] }
|
@@ -29,8 +29,13 @@ describe VMC::App::Rename do
|
|
29
29
|
|
30
30
|
describe 'inputs' do
|
31
31
|
subject { command.inputs }
|
32
|
-
|
33
|
-
it
|
32
|
+
|
33
|
+
it "is not missing any descriptions" do
|
34
|
+
subject.each do |input, attrs|
|
35
|
+
expect(attrs[:description]).to be
|
36
|
+
expect(attrs[:description].strip).to_not be_empty
|
37
|
+
end
|
38
|
+
end
|
34
39
|
end
|
35
40
|
|
36
41
|
describe 'arguments' do
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require "vmc/cli/organization/rename"
|
3
|
+
|
4
|
+
describe VMC::Organization::Rename do
|
5
|
+
let(:global) { { :color => false, :quiet => true } }
|
6
|
+
let(:inputs) { {} }
|
7
|
+
let(:given) { {} }
|
8
|
+
let(:organizations) { FactoryGirl.build_list(:organization, 3) }
|
9
|
+
let(:client) { FactoryGirl.build(:client, :organizations => organizations) }
|
10
|
+
let(:new_name) { "some-new-name" }
|
11
|
+
|
12
|
+
before do
|
13
|
+
any_instance_of(VMC::CLI) do |cli|
|
14
|
+
stub(cli).client { client }
|
15
|
+
stub(cli).precondition { nil }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
subject { Mothership.new.invoke(:rename_org, inputs, given, global) }
|
20
|
+
|
21
|
+
describe 'metadata' do
|
22
|
+
let(:command) { Mothership.commands[:rename_org] }
|
23
|
+
|
24
|
+
describe 'command' do
|
25
|
+
subject { command }
|
26
|
+
its(:description) { should eq "Rename an organization" }
|
27
|
+
it { expect(Mothership::Help.group(:organizations)).to include(subject) }
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'inputs' do
|
31
|
+
subject { command.inputs }
|
32
|
+
|
33
|
+
it "is not missing any descriptions" do
|
34
|
+
subject.each do |input, attrs|
|
35
|
+
expect(attrs[:description]).to be
|
36
|
+
expect(attrs[:description].strip).to_not be_empty
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'arguments' do
|
42
|
+
subject { command.arguments }
|
43
|
+
it 'has the correct argument order' do
|
44
|
+
should eq([
|
45
|
+
{ :type => :optional, :value => nil, :name => :organization },
|
46
|
+
{ :type => :optional, :value => nil, :name => :name }
|
47
|
+
])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when there are no organizations' do
|
53
|
+
let(:organizations) { [] }
|
54
|
+
|
55
|
+
context 'and an organization is given' do
|
56
|
+
let(:given) { { :organization => "some-invalid-organization" } }
|
57
|
+
it { expect { subject }.to raise_error(VMC::UserError, "Unknown organization 'some-invalid-organization'.") }
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'and an organization is not given' do
|
61
|
+
it { expect { subject }.to raise_error(VMC::UserError, "No organizations.") }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'when there are organizations' do
|
66
|
+
let(:renamed_organization) { organizations.first }
|
67
|
+
|
68
|
+
context 'when the defaults are used' do
|
69
|
+
it 'asks for the organization and new name and renames' do
|
70
|
+
mock_ask("Rename which organization?", anything) { renamed_organization }
|
71
|
+
mock_ask("New name") { new_name }
|
72
|
+
mock(renamed_organization).name=(new_name)
|
73
|
+
mock(renamed_organization).update!
|
74
|
+
subject
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when no name is provided, but an organization is' do
|
79
|
+
let(:given) { { :organization => renamed_organization.name } }
|
80
|
+
|
81
|
+
it 'asks for the new name and renames' do
|
82
|
+
dont_allow_ask("Rename which organization?", anything)
|
83
|
+
mock_ask("New name") { new_name }
|
84
|
+
mock(renamed_organization).name=(new_name)
|
85
|
+
mock(renamed_organization).update!
|
86
|
+
subject
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'when an organization is provided and a name' do
|
91
|
+
let(:inputs) { { :organization => renamed_organization, :name => new_name } }
|
92
|
+
|
93
|
+
it 'renames the organization' do
|
94
|
+
mock(renamed_organization).update!
|
95
|
+
subject
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'displays the progress' do
|
99
|
+
mock_with_progress("Renaming to #{new_name}")
|
100
|
+
mock(renamed_organization).update!
|
101
|
+
|
102
|
+
subject
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'and the name already exists' do
|
106
|
+
it 'fails' do
|
107
|
+
mock(renamed_organization).update! { raise CFoundry::OrganizationNameTaken }
|
108
|
+
expect { subject }.to raise_error(CFoundry::OrganizationNameTaken)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|