kumade 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +0 -1
- data/README.md +19 -21
- data/bin/kumade +1 -1
- data/kumade.gemspec +2 -0
- data/lib/kumade.rb +16 -10
- data/lib/kumade/base.rb +14 -9
- data/lib/kumade/cli.rb +86 -0
- data/lib/kumade/configuration.rb +14 -0
- data/lib/kumade/deployer.rb +27 -128
- data/lib/kumade/git.rb +11 -12
- data/lib/kumade/heroku.rb +46 -0
- data/lib/kumade/packager.rb +94 -0
- data/lib/kumade/version.rb +1 -1
- data/lib/tasks/deploy.rake +1 -1
- data/spec/kumade/base_spec.rb +89 -8
- data/spec/kumade/cli_spec.rb +109 -0
- data/spec/kumade/configuration_spec.rb +36 -0
- data/spec/kumade/deployer_spec.rb +45 -415
- data/spec/kumade/git_spec.rb +88 -21
- data/spec/kumade/heroku_spec.rb +121 -0
- data/spec/kumade/packager_spec.rb +318 -0
- data/spec/kumade_spec.rb +18 -0
- data/spec/spec_helper.rb +19 -3
- data/spec/support/heroku.rb +33 -0
- metadata +63 -22
- data/lib/kumade/runner.rb +0 -70
- data/spec/kumade/runner_spec.rb +0 -66
data/spec/kumade/git_spec.rb
CHANGED
@@ -1,33 +1,46 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Kumade::Git, "#heroku_remote?" do
|
4
|
-
|
5
|
-
|
6
|
-
let(:not_a_heroku_env) { 'fake_heroku' }
|
7
|
-
let(:not_a_heroku_url) { 'git@github.com:gabebw/kumade.git' }
|
8
|
-
let(:another_heroku_url) { 'git@heroku.work:my-app.git' }
|
4
|
+
context "when the environment is a Heroku repository" do
|
5
|
+
let(:environment) { 'staging' }
|
9
6
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
7
|
+
before do
|
8
|
+
force_add_heroku_remote(environment)
|
9
|
+
Kumade.configuration.environment = environment
|
10
|
+
end
|
15
11
|
|
16
|
-
|
17
|
-
remove_remote(environment)
|
18
|
-
remove_remote(not_a_heroku_env)
|
19
|
-
remove_remote(another_heroku_environment)
|
20
|
-
end
|
12
|
+
after { remove_remote(environment) }
|
21
13
|
|
22
|
-
|
23
|
-
Kumade::Git.new(false, environment).heroku_remote?.should be_true
|
24
|
-
Kumade::Git.new(false, another_heroku_environment).heroku_remote?.should be_true
|
14
|
+
its(:heroku_remote?) { should == true }
|
25
15
|
end
|
26
16
|
|
27
|
-
|
28
|
-
|
17
|
+
context "when the environment is a Heroku repository managed with heroku-accounts" do
|
18
|
+
let(:another_heroku_environment) { 'another_staging' }
|
19
|
+
let(:another_heroku_url) { 'git@heroku.work:my-app.git' }
|
20
|
+
|
21
|
+
before do
|
22
|
+
force_add_heroku_remote(another_heroku_environment)
|
23
|
+
Kumade.configuration.environment = another_heroku_environment
|
24
|
+
end
|
25
|
+
|
26
|
+
after { remove_remote(another_heroku_environment) }
|
27
|
+
|
28
|
+
its(:heroku_remote?) { should == true }
|
29
29
|
end
|
30
30
|
|
31
|
+
context "when the environment is not a Heroku repository" do
|
32
|
+
let(:not_a_heroku_env) { 'fake_heroku' }
|
33
|
+
let(:not_a_heroku_url) { 'git@github.com:gabebw/kumade.git' }
|
34
|
+
|
35
|
+
before do
|
36
|
+
`git remote add #{not_a_heroku_env} #{not_a_heroku_url}`
|
37
|
+
Kumade.configuration.environment = not_a_heroku_env
|
38
|
+
end
|
39
|
+
|
40
|
+
after { remove_remote(not_a_heroku_env) }
|
41
|
+
|
42
|
+
its(:heroku_remote?) { should == false }
|
43
|
+
end
|
31
44
|
end
|
32
45
|
|
33
46
|
describe Kumade::Git, ".environments" do
|
@@ -45,7 +58,61 @@ describe Kumade::Git, ".environments" do
|
|
45
58
|
remove_remote(not_a_heroku_env)
|
46
59
|
end
|
47
60
|
|
48
|
-
it "returns all environments" do
|
61
|
+
it "returns all Heroku environments" do
|
49
62
|
Kumade::Git.environments.should == ["staging"]
|
50
63
|
end
|
51
64
|
end
|
65
|
+
|
66
|
+
describe Kumade::Git, "#branch_exist?" do
|
67
|
+
let(:command_line) { mock("Cocaine::CommandLine") }
|
68
|
+
let(:branch) { "branch" }
|
69
|
+
|
70
|
+
before do
|
71
|
+
command_line.stubs(:run)
|
72
|
+
Cocaine::CommandLine.expects(:new).with("git show-ref #{branch}").returns(command_line)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "returns true when the branch exists" do
|
76
|
+
subject.branch_exist?("branch").should be_true
|
77
|
+
|
78
|
+
command_line.should have_received(:run)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "returns false if the branch doesn't exist" do
|
82
|
+
command_line.stubs(:run).raises(Cocaine::ExitStatusError)
|
83
|
+
|
84
|
+
subject.branch_exist?("branch").should be_false
|
85
|
+
|
86
|
+
command_line.should have_received(:run)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe Kumade::Git, "#dirty?" do
|
91
|
+
context "when dirty" do
|
92
|
+
let(:failing_command_line) { mock("CommandLine instance") }
|
93
|
+
|
94
|
+
before do
|
95
|
+
failing_command_line.stubs(:run).raises(Cocaine::ExitStatusError)
|
96
|
+
|
97
|
+
Cocaine::CommandLine.expects(:new).
|
98
|
+
with("git diff --exit-code").
|
99
|
+
returns(failing_command_line)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "returns true" do
|
103
|
+
subject.dirty?.should == true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "when clean" do
|
108
|
+
before do
|
109
|
+
Cocaine::CommandLine.expects(:new).
|
110
|
+
with("git diff --exit-code").
|
111
|
+
returns(stub("Successful CommandLine", :run => true))
|
112
|
+
end
|
113
|
+
|
114
|
+
it "returns false" do
|
115
|
+
subject.dirty?.should == false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Kumade::Heroku, "DEPLOY_BRANCH" do
|
4
|
+
subject { Kumade::Heroku::DEPLOY_BRANCH }
|
5
|
+
|
6
|
+
it { should == "deploy" }
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Kumade::Heroku, "#sync" do
|
10
|
+
let(:environment) { 'staging' }
|
11
|
+
|
12
|
+
before do
|
13
|
+
force_add_heroku_remote(environment)
|
14
|
+
subject.git.stubs(:create)
|
15
|
+
subject.git.stubs(:push)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "creates and pushes the deploy branch" do
|
19
|
+
subject.sync
|
20
|
+
|
21
|
+
subject.git.should have_received(:create).with("deploy")
|
22
|
+
subject.git.should have_received(:push).with("deploy:master", environment, true)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe Kumade::Heroku, "#migrate_database" do
|
27
|
+
let(:environment) { 'staging' }
|
28
|
+
|
29
|
+
before do
|
30
|
+
STDOUT.stubs(:puts)
|
31
|
+
subject.stubs(:heroku)
|
32
|
+
force_add_heroku_remote(environment)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "runs db:migrate with the correct app" do
|
36
|
+
subject.migrate_database
|
37
|
+
|
38
|
+
subject.should have_received(:heroku).with("rake db:migrate")
|
39
|
+
end
|
40
|
+
|
41
|
+
context "when pretending" do
|
42
|
+
before do
|
43
|
+
STDOUT.stubs(:puts)
|
44
|
+
Kumade.configuration.pretending = true
|
45
|
+
end
|
46
|
+
|
47
|
+
it "does not run the command" do
|
48
|
+
subject.migrate_database
|
49
|
+
|
50
|
+
subject.should_not have_received(:heroku)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "prints a message" do
|
54
|
+
subject.migrate_database
|
55
|
+
|
56
|
+
STDOUT.should have_received(:puts).with(regexp_matches(/Migrated #{environment}/))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe Kumade::Heroku, "#heroku" do
|
62
|
+
let(:command_line_instance) { stub("Cocaine::CommandLine instance", :run => true) }
|
63
|
+
|
64
|
+
before do
|
65
|
+
STDOUT.stubs(:puts)
|
66
|
+
end
|
67
|
+
|
68
|
+
context "when on Cedar" do
|
69
|
+
include_context "when on Cedar"
|
70
|
+
|
71
|
+
it "runs commands with `run`" do
|
72
|
+
Cocaine::CommandLine.expects(:new).
|
73
|
+
with(regexp_matches(/bundle exec heroku run/)).
|
74
|
+
returns(command_line_instance)
|
75
|
+
|
76
|
+
subject.heroku("rake")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "when not on Cedar" do
|
81
|
+
include_context "when not on Cedar"
|
82
|
+
|
83
|
+
it "runs commands without `run`" do
|
84
|
+
Cocaine::CommandLine.expects(:new).
|
85
|
+
with(regexp_matches(/bundle exec heroku rake/)).
|
86
|
+
returns(command_line_instance)
|
87
|
+
|
88
|
+
subject.heroku("rake")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe Kumade::Heroku, "#cedar?" do
|
94
|
+
context "when on Cedar" do
|
95
|
+
include_context "when on Cedar"
|
96
|
+
|
97
|
+
it "returns true" do
|
98
|
+
subject.cedar?.should == true
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context "when not on Cedar" do
|
103
|
+
include_context "when not on Cedar"
|
104
|
+
|
105
|
+
it "returns false" do
|
106
|
+
subject.cedar?.should == false
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe Kumade::Heroku, "#delete_deploy_branch" do
|
112
|
+
before { STDOUT.stubs(:puts) }
|
113
|
+
|
114
|
+
it "deletes the deploy branch" do
|
115
|
+
Cocaine::CommandLine.expects(:new).
|
116
|
+
with("git checkout master && git branch -D deploy").
|
117
|
+
returns(stub(:run => true))
|
118
|
+
|
119
|
+
subject.delete_deploy_branch
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,318 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'jammit'
|
4
|
+
|
5
|
+
shared_context "with a fake Git" do
|
6
|
+
let(:git) { stub() }
|
7
|
+
subject { Kumade::Packager.new(git) }
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Kumade::Packager, "#run" do
|
11
|
+
include_context "with a fake Git"
|
12
|
+
|
13
|
+
before do
|
14
|
+
subject.stubs(:package_with_jammit)
|
15
|
+
end
|
16
|
+
|
17
|
+
context "with Jammit installed" do
|
18
|
+
it "calls package_with_jammit" do
|
19
|
+
subject.run
|
20
|
+
|
21
|
+
subject.should have_received(:package_with_jammit)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "with Jammit not installed" do
|
26
|
+
before { subject.stubs(:jammit_installed? => false) }
|
27
|
+
|
28
|
+
it "does not call package_with_jammit" do
|
29
|
+
subject.run
|
30
|
+
|
31
|
+
subject.should_not have_received(:package_with_jammit)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "with More installed" do
|
36
|
+
before do
|
37
|
+
subject.stubs(:jammit_installed? => false)
|
38
|
+
subject.stubs(:more_installed? => true)
|
39
|
+
subject.stubs(:package_with_more)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "calls package_with_more" do
|
43
|
+
subject.run
|
44
|
+
|
45
|
+
subject.should have_received(:package_with_more)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "with More not installed" do
|
50
|
+
before do
|
51
|
+
subject.stubs(:jammit_installed? => false)
|
52
|
+
subject.stubs(:more_installed? => false)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "does not call package_with_more" do
|
56
|
+
subject.run
|
57
|
+
|
58
|
+
subject.should_not have_received(:package_with_more)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "with custom rake task installed" do
|
63
|
+
before do
|
64
|
+
subject.stubs(:jammit_installed? => false,
|
65
|
+
:more_installed? => false,
|
66
|
+
:invoke_custom_task => nil,
|
67
|
+
:custom_task? => true)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "invokes custom task" do
|
71
|
+
subject.run
|
72
|
+
|
73
|
+
subject.should have_received(:invoke_custom_task)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "with custom rake task not installed" do
|
78
|
+
before do
|
79
|
+
subject.stubs(:jammit_installed? => false,
|
80
|
+
:more_installed? => false,
|
81
|
+
:invoke_custom_task => nil,
|
82
|
+
:custom_task? => false)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "does not invoke custom task" do
|
86
|
+
subject.run
|
87
|
+
|
88
|
+
subject.should_not have_received(:invoke_custom_task)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe Kumade::Packager, "#invoke_custom_task" do
|
94
|
+
include_context "with a fake Git"
|
95
|
+
|
96
|
+
let(:task) { stub('kumade:before_asset_compilation task', :invoke => nil) }
|
97
|
+
|
98
|
+
before do
|
99
|
+
subject.stubs(:say)
|
100
|
+
Rake::Task.stubs(:[] => task)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "calls deploy task" do
|
104
|
+
Rake::Task.expects(:[]).with("kumade:before_asset_compilation").returns(task)
|
105
|
+
|
106
|
+
subject.invoke_custom_task
|
107
|
+
|
108
|
+
task.should have_received(:invoke)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe Kumade::Packager, "#custom_task?" do
|
113
|
+
include_context "with a fake Git"
|
114
|
+
|
115
|
+
before do
|
116
|
+
Rake::Task.clear
|
117
|
+
end
|
118
|
+
|
119
|
+
it "returns true if it task found" do
|
120
|
+
namespace :kumade do
|
121
|
+
task :before_asset_compilation do
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
subject.custom_task?.should be_true
|
126
|
+
end
|
127
|
+
|
128
|
+
it "returns false if task not found" do
|
129
|
+
subject.custom_task?.should be_false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe Kumade::Packager, "#package_with_jammit" do
|
134
|
+
include_context "with a fake Git"
|
135
|
+
|
136
|
+
before do
|
137
|
+
subject.stubs(:git_add_and_commit_all_assets_in)
|
138
|
+
subject.stubs(:success)
|
139
|
+
subject.stubs(:error)
|
140
|
+
Jammit.stubs(:package!)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "calls Jammit.package!" do
|
144
|
+
subject.package_with_jammit
|
145
|
+
|
146
|
+
Jammit.should have_received(:package!).once
|
147
|
+
end
|
148
|
+
|
149
|
+
context "with updated assets" do
|
150
|
+
before { subject.git.stubs(:dirty? => true) }
|
151
|
+
|
152
|
+
it "prints the correct message" do
|
153
|
+
subject.package_with_jammit
|
154
|
+
|
155
|
+
subject.should have_received(:success).with("Packaged assets with Jammit")
|
156
|
+
end
|
157
|
+
|
158
|
+
it "calls git_add_and_commit_all_assets_in" do
|
159
|
+
subject.stubs(:jammit_assets_path => 'jammit-assets')
|
160
|
+
subject.expects(:git_add_and_commit_all_assets_in).
|
161
|
+
with('jammit-assets').
|
162
|
+
returns(true)
|
163
|
+
|
164
|
+
subject.package_with_jammit
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
it "prints an error if packaging failed" do
|
169
|
+
Jammit.expects(:package!).raises(Jammit::MissingConfiguration.new("random Jammit error"))
|
170
|
+
|
171
|
+
subject.package_with_jammit
|
172
|
+
|
173
|
+
subject.should have_received(:error).with("Error: Jammit::MissingConfiguration: random Jammit error")
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe Kumade::Packager, "#package_with_more" do
|
178
|
+
include_context "with a fake Git"
|
179
|
+
|
180
|
+
before do
|
181
|
+
subject.stubs(:git_add_and_commit_all_assets_in => true,
|
182
|
+
:more_assets_path => 'assets')
|
183
|
+
subject.stubs(:say)
|
184
|
+
end
|
185
|
+
|
186
|
+
it "calls the more:generate task" do
|
187
|
+
git.expects(:dirty?).returns(true)
|
188
|
+
subject.expects(:run).with("bundle exec rake more:generate")
|
189
|
+
subject.package_with_more
|
190
|
+
end
|
191
|
+
|
192
|
+
context "with changed assets" do
|
193
|
+
before do
|
194
|
+
git.stubs(:dirty? => true)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "prints a success message" do
|
198
|
+
subject.stubs(:run).with("bundle exec rake more:generate")
|
199
|
+
subject.expects(:success).with("Packaged assets with More")
|
200
|
+
|
201
|
+
subject.package_with_more
|
202
|
+
end
|
203
|
+
|
204
|
+
it "calls git_add_and_commit_all_assets_in if assets were added" do
|
205
|
+
subject.stubs(:more_assets_path => 'blerg')
|
206
|
+
subject.stubs(:run).with("bundle exec rake more:generate")
|
207
|
+
subject.expects(:git_add_and_commit_all_assets_in).
|
208
|
+
with('blerg').
|
209
|
+
returns(true)
|
210
|
+
|
211
|
+
subject.package_with_more
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
context "with no changed assets" do
|
216
|
+
it "prints no message" do
|
217
|
+
subject.stubs(:run).with("bundle exec rake more:generate")
|
218
|
+
subject.stubs(:git => mock(:dirty? => false))
|
219
|
+
subject.expects(:say).never
|
220
|
+
|
221
|
+
subject.package_with_more
|
222
|
+
end
|
223
|
+
|
224
|
+
it "does not call git_add_and_commit_all_more_assets" do
|
225
|
+
subject.stubs(:run).with("bundle exec rake more:generate")
|
226
|
+
subject.stubs(:git => mock(:dirty? => false))
|
227
|
+
subject.expects(:git_add_and_commit_all_assets_in).never
|
228
|
+
|
229
|
+
subject.package_with_more
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
it "prints an error if packaging failed" do
|
234
|
+
subject.stubs(:run).with("bundle exec rake more:generate").raises("blerg")
|
235
|
+
|
236
|
+
subject.expects(:error).with("Error: RuntimeError: blerg")
|
237
|
+
|
238
|
+
subject.package_with_more
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
describe Kumade::Packager, "#git_add_and_commit_all_assets_in" do
|
243
|
+
include_context "with a fake Git"
|
244
|
+
|
245
|
+
it "should call git.add_and_commit_all_in" do
|
246
|
+
git.expects(:add_and_commit_all_in).with("dir", 'deploy', 'Compiled assets', "Added and committed all assets", "couldn't commit assets")
|
247
|
+
subject.git_add_and_commit_all_assets_in("dir")
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
describe Kumade::Packager, "#jammit_assets_path" do
|
252
|
+
let(:git) { stub() }
|
253
|
+
let(:packager) { Kumade::Packager.new(git) }
|
254
|
+
|
255
|
+
before do
|
256
|
+
Jammit.stubs(:package_path).returns('blerg')
|
257
|
+
end
|
258
|
+
|
259
|
+
subject { packager.jammit_assets_path }
|
260
|
+
|
261
|
+
it { should == File.join(Jammit::PUBLIC_ROOT, 'blerg') }
|
262
|
+
end
|
263
|
+
|
264
|
+
describe Kumade::Packager, "#more_assets_path" do
|
265
|
+
include_context "with a fake Git"
|
266
|
+
|
267
|
+
it "returns the correct asset path" do
|
268
|
+
module ::Less
|
269
|
+
class More
|
270
|
+
def self.destination_path
|
271
|
+
'blerg'
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
subject.more_assets_path.should == 'public/blerg'
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
describe Kumade::Packager, "#jammit_installed?" do
|
280
|
+
include_context "with a fake Git"
|
281
|
+
|
282
|
+
it "returns true because it's loaded by the Gemfile" do
|
283
|
+
subject.jammit_installed?.should be_true
|
284
|
+
end
|
285
|
+
|
286
|
+
it "returns false if jammit is not installed" do
|
287
|
+
subject.jammit_installed?.should be_true
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
describe Kumade::Packager, "#more_installed?" do
|
292
|
+
include_context "with a fake Git"
|
293
|
+
|
294
|
+
context "when More is not installed" do
|
295
|
+
before do
|
296
|
+
if defined?(Less)
|
297
|
+
Object.send(:remove_const, :Less)
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
it "returns false" do
|
302
|
+
subject.more_installed?.should be_false
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
context "when More is installed" do
|
307
|
+
before do
|
308
|
+
module Less
|
309
|
+
class More
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
it "returns true" do
|
315
|
+
subject.more_installed?.should be_true
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|