mortar 0.1.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.
- data/README.md +36 -0
- data/bin/mortar +13 -0
- data/lib/mortar.rb +23 -0
- data/lib/mortar/auth.rb +312 -0
- data/lib/mortar/cli.rb +54 -0
- data/lib/mortar/command.rb +267 -0
- data/lib/mortar/command/auth.rb +96 -0
- data/lib/mortar/command/base.rb +319 -0
- data/lib/mortar/command/clusters.rb +41 -0
- data/lib/mortar/command/describe.rb +97 -0
- data/lib/mortar/command/generate.rb +121 -0
- data/lib/mortar/command/help.rb +166 -0
- data/lib/mortar/command/illustrate.rb +97 -0
- data/lib/mortar/command/jobs.rb +174 -0
- data/lib/mortar/command/pigscripts.rb +45 -0
- data/lib/mortar/command/projects.rb +128 -0
- data/lib/mortar/command/validate.rb +94 -0
- data/lib/mortar/command/version.rb +42 -0
- data/lib/mortar/errors.rb +24 -0
- data/lib/mortar/generators/generator_base.rb +107 -0
- data/lib/mortar/generators/macro_generator.rb +37 -0
- data/lib/mortar/generators/pigscript_generator.rb +40 -0
- data/lib/mortar/generators/project_generator.rb +67 -0
- data/lib/mortar/generators/udf_generator.rb +28 -0
- data/lib/mortar/git.rb +233 -0
- data/lib/mortar/helpers.rb +488 -0
- data/lib/mortar/project.rb +156 -0
- data/lib/mortar/snapshot.rb +39 -0
- data/lib/mortar/templates/macro/macro.pig +14 -0
- data/lib/mortar/templates/pigscript/pigscript.pig +38 -0
- data/lib/mortar/templates/pigscript/python_udf.py +13 -0
- data/lib/mortar/templates/project/Gemfile +3 -0
- data/lib/mortar/templates/project/README.md +8 -0
- data/lib/mortar/templates/project/gitignore +4 -0
- data/lib/mortar/templates/project/macros/gitkeep +0 -0
- data/lib/mortar/templates/project/pigscripts/pigscript.pig +35 -0
- data/lib/mortar/templates/project/udfs/python/python_udf.py +13 -0
- data/lib/mortar/templates/udf/python_udf.py +13 -0
- data/lib/mortar/version.rb +20 -0
- data/lib/vendor/mortar/okjson.rb +598 -0
- data/lib/vendor/mortar/uuid.rb +312 -0
- data/spec/mortar/auth_spec.rb +156 -0
- data/spec/mortar/command/auth_spec.rb +46 -0
- data/spec/mortar/command/base_spec.rb +82 -0
- data/spec/mortar/command/clusters_spec.rb +61 -0
- data/spec/mortar/command/describe_spec.rb +135 -0
- data/spec/mortar/command/generate_spec.rb +139 -0
- data/spec/mortar/command/illustrate_spec.rb +140 -0
- data/spec/mortar/command/jobs_spec.rb +364 -0
- data/spec/mortar/command/pigscripts_spec.rb +70 -0
- data/spec/mortar/command/projects_spec.rb +165 -0
- data/spec/mortar/command/validate_spec.rb +119 -0
- data/spec/mortar/command_spec.rb +122 -0
- data/spec/mortar/git_spec.rb +278 -0
- data/spec/mortar/helpers_spec.rb +82 -0
- data/spec/mortar/project_spec.rb +76 -0
- data/spec/mortar/snapshot_spec.rb +46 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +278 -0
- data/spec/support/display_message_matcher.rb +68 -0
- metadata +259 -0
@@ -0,0 +1,278 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2012 Mortar Data Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require "spec_helper"
|
18
|
+
require "mortar/git"
|
19
|
+
|
20
|
+
module Mortar
|
21
|
+
describe Git do
|
22
|
+
|
23
|
+
before do
|
24
|
+
@git = Mortar::Git::Git.new
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
context "has_any_commits" do
|
29
|
+
|
30
|
+
it "finds no commits on a blank project" do
|
31
|
+
with_blank_project do |p|
|
32
|
+
@git.has_commits?.should be_false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it "finds commits when a project has them" do
|
37
|
+
with_git_initialized_project do |p|
|
38
|
+
@git.has_commits?.should be_true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "git-rev" do
|
44
|
+
it "looks up a revision" do
|
45
|
+
with_git_initialized_project do |p|
|
46
|
+
@git.git_ref("master").nil?.should be_false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "git" do
|
52
|
+
|
53
|
+
it "raises on git failure" do
|
54
|
+
with_blank_project do |p|
|
55
|
+
lambda { @git.git("not_a_git_command") }.should raise_error(Mortar::Git::GitError)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it "does not raise on git success" do
|
60
|
+
with_blank_project do |p|
|
61
|
+
lambda { @git.git("--version") }.should_not raise_error
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it "raises error on no .git file" do
|
66
|
+
with_no_git_directory do
|
67
|
+
lambda {@git.git("--version") }.should raise_error(Mortar::Git::GitError)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it "does not raise error on no .git file with check disabled" do
|
72
|
+
with_no_git_directory do
|
73
|
+
lambda {@git.git("--version", true, false) }.should_not raise_error(Mortar::Git::GitError)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
context "working directory" do
|
80
|
+
it "finds clean and dirty working directories" do
|
81
|
+
with_git_initialized_project do |p|
|
82
|
+
@git.is_clean_working_directory?.should be_true
|
83
|
+
write_file(File.join(p.root_path, "new_file.txt"))
|
84
|
+
@git.is_clean_working_directory?.should be_false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "untracked_files" do
|
90
|
+
it "does not find ignored files" do
|
91
|
+
with_git_initialized_project do |p|
|
92
|
+
write_file(File.join(p.root_path, ".gitignore"), "tmp\n")
|
93
|
+
write_file(File.join(p.root_path, "tmp", "ignored_file.txt"), "some text")
|
94
|
+
write_file(File.join(p.root_path, "included_file.txt"), "some text")
|
95
|
+
untracked_files = @git.untracked_files
|
96
|
+
untracked_files.include?(".gitignore").should be_true
|
97
|
+
untracked_files.include?("included_file.txt").should be_true
|
98
|
+
untracked_files.include?("tmp/ignored_file.txt").should be_false
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "stash" do
|
104
|
+
context "did_stash_changes" do
|
105
|
+
it "finds that no changes were stashed" do
|
106
|
+
with_git_initialized_project do |p|
|
107
|
+
@git.did_stash_changes?("No local changes to save").should be_false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
it "finds that changes were stashed" do
|
112
|
+
stash_message = <<-STASH
|
113
|
+
Saved working directory and index state On master: myproject
|
114
|
+
HEAD is now at f3d74e8 Add an example
|
115
|
+
STASH
|
116
|
+
@git.did_stash_changes?(stash_message).should be_true
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "stash_working_dir" do
|
121
|
+
it "does not error on a clean working directory" do
|
122
|
+
with_git_initialized_project do |p|
|
123
|
+
@git.stash_working_dir("my_description").should be_false
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
it "stashes an added file" do
|
128
|
+
with_git_initialized_project do |p|
|
129
|
+
git_add_file(@git, p)
|
130
|
+
@git.is_clean_working_directory?.should be_false
|
131
|
+
|
132
|
+
# stash
|
133
|
+
@git.stash_working_dir("my_description").should be_true
|
134
|
+
@git.is_clean_working_directory?.should be_true
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
it "stashes an untracked file" do
|
139
|
+
with_git_initialized_project do |p|
|
140
|
+
git_create_untracked_file(p)
|
141
|
+
@git.is_clean_working_directory?.should be_false
|
142
|
+
|
143
|
+
# stash
|
144
|
+
@git.stash_working_dir("my_description").should be_true
|
145
|
+
@git.is_clean_working_directory?.should be_true
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context "branch" do
|
152
|
+
|
153
|
+
it "fetches current branch with one branch" do
|
154
|
+
with_git_initialized_project do |p|
|
155
|
+
@git.current_branch.should == "master"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
it "fetches current branch with multiple branches" do
|
160
|
+
with_git_initialized_project do |p|
|
161
|
+
@git.git("checkout -b branch_01")
|
162
|
+
@git.git("checkout -b branch_02")
|
163
|
+
@git.current_branch.should == "branch_02"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
it "raises if no branches found" do
|
168
|
+
with_blank_project do |p|
|
169
|
+
lambda { @git.current_branch }.should raise_error(Mortar::Git::GitError)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
it "deletes a branch" do
|
174
|
+
with_git_initialized_project do |p|
|
175
|
+
new_branch_name = "new_branch_to_delete"
|
176
|
+
@git.current_branch.should == "master"
|
177
|
+
@git.git("checkout -b #{new_branch_name}")
|
178
|
+
@git.branches.include?(new_branch_name).should be_true
|
179
|
+
@git.git("checkout master")
|
180
|
+
@git.branch_delete(new_branch_name)
|
181
|
+
@git.branches.include?(new_branch_name).should be_false
|
182
|
+
@git.current_branch.should == "master"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context "remotes" do
|
188
|
+
it "handles when no remotes are defined" do
|
189
|
+
with_git_initialized_project do |p|
|
190
|
+
@git.git("remote rm mortar")
|
191
|
+
@git.remotes("mortarcode").empty?.should be_true
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
it "finds a single remote" do
|
196
|
+
with_git_initialized_project do |p|
|
197
|
+
remotes = @git.remotes("mortarcode")
|
198
|
+
remotes["mortar"].should == p.name
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
context "status" do
|
205
|
+
it "detects conflicts" do
|
206
|
+
with_git_initialized_project do |p|
|
207
|
+
|
208
|
+
@git.is_clean_working_directory?.should be_true
|
209
|
+
@git.has_conflicts?.should be_false
|
210
|
+
git_create_conflict(@git, p)
|
211
|
+
@git.is_clean_working_directory?.should be_false
|
212
|
+
@git.has_conflicts?.should be_true
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
it "detects no conflicts" do
|
217
|
+
with_git_initialized_project do |p|
|
218
|
+
@git.has_conflicts?.should be_false
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
context "snapshot" do
|
224
|
+
it "raises when no commits are found in the repo" do
|
225
|
+
with_blank_project do |p|
|
226
|
+
lambda { @git.create_snapshot_branch }.should raise_error(Mortar::Git::GitError)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
it "raises when a conflict exists in working directory" do
|
231
|
+
with_git_initialized_project do |p|
|
232
|
+
git_create_conflict(@git, p)
|
233
|
+
lambda { @git.create_snapshot_branch }.should raise_error(Mortar::Git::GitError)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
it "creates a snapshot branch for a clean working directory" do
|
238
|
+
with_git_initialized_project do |p|
|
239
|
+
starting_status = @git.status
|
240
|
+
snapshot_branch = @git.create_snapshot_branch
|
241
|
+
post_validate_git_snapshot(@git, starting_status, snapshot_branch)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
it "creates a snapshot branch for an added file" do
|
246
|
+
with_git_initialized_project do |p|
|
247
|
+
git_add_file(@git, p)
|
248
|
+
starting_status = @git.status
|
249
|
+
snapshot_branch = @git.create_snapshot_branch
|
250
|
+
post_validate_git_snapshot(@git, starting_status, snapshot_branch)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
it "creates a snapshot branch for an untracked file" do
|
255
|
+
with_git_initialized_project do |p|
|
256
|
+
git_create_untracked_file(p)
|
257
|
+
starting_status = @git.status
|
258
|
+
snapshot_branch = @git.create_snapshot_branch
|
259
|
+
post_validate_git_snapshot(@git, starting_status, snapshot_branch)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
264
|
+
|
265
|
+
context "clone" do
|
266
|
+
it "clones repo successfully" do
|
267
|
+
with_no_git_directory do
|
268
|
+
File.directory?("rollup").should be_false
|
269
|
+
@git.clone("git@github.com:mortarcode-dev/4fbbd83cce875be8a4000000_rollup", "rollup")
|
270
|
+
File.directory?("rollup").should be_true
|
271
|
+
Dir.chdir("rollup")
|
272
|
+
lambda { @git.git("--version") }.should_not raise_error
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2012 Mortar Data Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
# Portions of this code from heroku (https://github.com/heroku/heroku/) Copyright Heroku 2008 - 2012,
|
17
|
+
# used under an MIT license (https://github.com/heroku/heroku/blob/master/LICENSE).
|
18
|
+
#
|
19
|
+
|
20
|
+
require "spec_helper"
|
21
|
+
require "mortar/helpers"
|
22
|
+
|
23
|
+
module Mortar
|
24
|
+
describe Helpers do
|
25
|
+
include Mortar::Helpers
|
26
|
+
|
27
|
+
context "display_object" do
|
28
|
+
|
29
|
+
it "should display Array correctly" do
|
30
|
+
capture_stdout do
|
31
|
+
display_object([1,2,3])
|
32
|
+
end.should == <<-OUT
|
33
|
+
1
|
34
|
+
2
|
35
|
+
3
|
36
|
+
OUT
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should display { :header => [] } list correctly" do
|
40
|
+
capture_stdout do
|
41
|
+
display_object({:first_header => [1,2,3], :last_header => [7,8,9]})
|
42
|
+
end.should == <<-OUT
|
43
|
+
=== first_header
|
44
|
+
1
|
45
|
+
2
|
46
|
+
3
|
47
|
+
|
48
|
+
=== last_header
|
49
|
+
7
|
50
|
+
8
|
51
|
+
9
|
52
|
+
|
53
|
+
OUT
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should display String properly" do
|
57
|
+
capture_stdout do
|
58
|
+
display_object('string')
|
59
|
+
end.should == <<-OUT
|
60
|
+
string
|
61
|
+
OUT
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
context "write_to_file" do
|
67
|
+
it "should write data to a directory that does not yet exist" do
|
68
|
+
|
69
|
+
# use FakeFS file system
|
70
|
+
include FakeFS::SpecHelpers
|
71
|
+
|
72
|
+
filepath = File.join(Dir.tmpdir, "my_new_dir", "my_new_file.txt")
|
73
|
+
data = "foo\nbar"
|
74
|
+
write_to_file(data, filepath)
|
75
|
+
File.exists?(filepath).should be_true
|
76
|
+
infile = File.open(filepath, "r")
|
77
|
+
infile.read.should == data
|
78
|
+
infile.close()
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2012 Mortar Data Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require "spec_helper"
|
18
|
+
require 'fakefs/spec_helpers'
|
19
|
+
require "mortar/project"
|
20
|
+
|
21
|
+
module Mortar
|
22
|
+
describe Project do
|
23
|
+
# use FakeFS file system
|
24
|
+
include FakeFS::SpecHelpers
|
25
|
+
|
26
|
+
context "tmp" do
|
27
|
+
it "creates a tmp dir if one does not exist" do
|
28
|
+
with_blank_project do |p|
|
29
|
+
# create it
|
30
|
+
tmp_path_0 = p.tmp_path
|
31
|
+
File.directory?(tmp_path_0).should be_true
|
32
|
+
|
33
|
+
# reuse it
|
34
|
+
p.tmp_path.should == tmp_path_0
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "pigscripts" do
|
40
|
+
|
41
|
+
it "raise when unable to find pigscripts dir" do
|
42
|
+
with_blank_project do |p|
|
43
|
+
FileUtils.rm_rf p.pigscripts_path
|
44
|
+
lambda { p.pigscripts }.should raise_error(Mortar::Project::ProjectError)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it "finds no pigscripts in an empty dir" do
|
49
|
+
with_blank_project do |p|
|
50
|
+
p.pigscripts.none?.should be_true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it "finds a single pigscript" do
|
55
|
+
with_blank_project do |p|
|
56
|
+
pigscript_path = File.join(p.pigscripts_path, "my_script.pig")
|
57
|
+
write_file pigscript_path
|
58
|
+
p.pigscripts.my_script.name.should == "my_script"
|
59
|
+
p.pigscripts.my_script.path.should == pigscript_path
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it "finds a script stored in a subdirectory" do
|
64
|
+
with_blank_project do |p|
|
65
|
+
pigscript_path = File.join(p.pigscripts_path, "subdir", "my_script.pig")
|
66
|
+
write_file pigscript_path
|
67
|
+
p.pigscripts.my_script.name.should == "my_script"
|
68
|
+
p.pigscripts.my_script.path.should == pigscript_path
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
it "finds multiple scripts in subdirectories with the same name" do
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2012 Mortar Data Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require "spec_helper"
|
18
|
+
require "mortar/helpers"
|
19
|
+
|
20
|
+
module Mortar
|
21
|
+
describe Snapshot do
|
22
|
+
include Mortar::Helpers
|
23
|
+
include Mortar::Snapshot
|
24
|
+
|
25
|
+
before(:each) do
|
26
|
+
@git = Mortar::Git::Git.new
|
27
|
+
end
|
28
|
+
|
29
|
+
it "create and push a snapshot to the remote repository" do
|
30
|
+
with_git_initialized_project do |p|
|
31
|
+
# stub git
|
32
|
+
mock(@git).push("mortar", is_a(String))
|
33
|
+
mock.proxy(@git).create_snapshot_branch
|
34
|
+
|
35
|
+
stub(self).display
|
36
|
+
|
37
|
+
initial_git_branches = @git.branches
|
38
|
+
create_and_push_snapshot_branch(@git, p)
|
39
|
+
|
40
|
+
# ensure that no additional branches are left behind
|
41
|
+
@git.branches.should == initial_git_branches
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|