curlybars 0.9.13
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/lib/curlybars.rb +108 -0
- data/lib/curlybars/configuration.rb +41 -0
- data/lib/curlybars/dependency_tracker.rb +8 -0
- data/lib/curlybars/error/base.rb +18 -0
- data/lib/curlybars/error/compile.rb +11 -0
- data/lib/curlybars/error/lex.rb +22 -0
- data/lib/curlybars/error/parse.rb +41 -0
- data/lib/curlybars/error/presenter/not_found.rb +23 -0
- data/lib/curlybars/error/render.rb +11 -0
- data/lib/curlybars/error/validate.rb +18 -0
- data/lib/curlybars/lexer.rb +60 -0
- data/lib/curlybars/method_whitelist.rb +69 -0
- data/lib/curlybars/node/block_helper_else.rb +108 -0
- data/lib/curlybars/node/boolean.rb +24 -0
- data/lib/curlybars/node/each_else.rb +69 -0
- data/lib/curlybars/node/if_else.rb +33 -0
- data/lib/curlybars/node/item.rb +31 -0
- data/lib/curlybars/node/literal.rb +28 -0
- data/lib/curlybars/node/option.rb +25 -0
- data/lib/curlybars/node/output.rb +24 -0
- data/lib/curlybars/node/partial.rb +24 -0
- data/lib/curlybars/node/path.rb +137 -0
- data/lib/curlybars/node/root.rb +29 -0
- data/lib/curlybars/node/string.rb +24 -0
- data/lib/curlybars/node/template.rb +32 -0
- data/lib/curlybars/node/text.rb +24 -0
- data/lib/curlybars/node/unless_else.rb +33 -0
- data/lib/curlybars/node/variable.rb +34 -0
- data/lib/curlybars/node/with_else.rb +54 -0
- data/lib/curlybars/parser.rb +183 -0
- data/lib/curlybars/position.rb +7 -0
- data/lib/curlybars/presenter.rb +288 -0
- data/lib/curlybars/processor/tilde.rb +31 -0
- data/lib/curlybars/processor/token_factory.rb +9 -0
- data/lib/curlybars/railtie.rb +18 -0
- data/lib/curlybars/rendering_support.rb +222 -0
- data/lib/curlybars/safe_buffer.rb +11 -0
- data/lib/curlybars/template_handler.rb +93 -0
- data/lib/curlybars/version.rb +3 -0
- data/spec/acceptance/application_layout_spec.rb +60 -0
- data/spec/acceptance/collection_blocks_spec.rb +28 -0
- data/spec/acceptance/global_helper_spec.rb +25 -0
- data/spec/curlybars/configuration_spec.rb +57 -0
- data/spec/curlybars/error/base_spec.rb +41 -0
- data/spec/curlybars/error/compile_spec.rb +19 -0
- data/spec/curlybars/error/lex_spec.rb +25 -0
- data/spec/curlybars/error/parse_spec.rb +74 -0
- data/spec/curlybars/error/render_spec.rb +19 -0
- data/spec/curlybars/error/validate_spec.rb +19 -0
- data/spec/curlybars/lexer_spec.rb +466 -0
- data/spec/curlybars/method_whitelist_spec.rb +168 -0
- data/spec/curlybars/processor/tilde_spec.rb +60 -0
- data/spec/curlybars/rendering_support_spec.rb +426 -0
- data/spec/curlybars/safe_buffer_spec.rb +46 -0
- data/spec/curlybars/template_handler_spec.rb +222 -0
- data/spec/integration/cache_spec.rb +124 -0
- data/spec/integration/comment_spec.rb +60 -0
- data/spec/integration/exception_spec.rb +31 -0
- data/spec/integration/node/block_helper_else_spec.rb +422 -0
- data/spec/integration/node/each_else_spec.rb +204 -0
- data/spec/integration/node/each_spec.rb +291 -0
- data/spec/integration/node/escape_spec.rb +27 -0
- data/spec/integration/node/helper_spec.rb +176 -0
- data/spec/integration/node/if_else_spec.rb +129 -0
- data/spec/integration/node/if_spec.rb +143 -0
- data/spec/integration/node/output_spec.rb +68 -0
- data/spec/integration/node/partial_spec.rb +66 -0
- data/spec/integration/node/path_spec.rb +286 -0
- data/spec/integration/node/root_spec.rb +15 -0
- data/spec/integration/node/template_spec.rb +86 -0
- data/spec/integration/node/unless_else_spec.rb +129 -0
- data/spec/integration/node/unless_spec.rb +130 -0
- data/spec/integration/node/with_spec.rb +116 -0
- data/spec/integration/processor/tilde_spec.rb +38 -0
- data/spec/integration/processors_spec.rb +30 -0
- metadata +358 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
describe '`\` as an escaping character' do
|
2
|
+
let(:post) { double("post") }
|
3
|
+
let(:presenter) { IntegrationTest::Presenter.new(double("view_context"), post: post) }
|
4
|
+
let(:global_helpers_providers) { [] }
|
5
|
+
|
6
|
+
it "escapes `{`" do
|
7
|
+
template = Curlybars.compile(<<-HBS)
|
8
|
+
text \\{{! text
|
9
|
+
HBS
|
10
|
+
|
11
|
+
expect(eval(template)).to resemble('text {{! text')
|
12
|
+
end
|
13
|
+
|
14
|
+
it "escapes `\\`" do
|
15
|
+
template = Curlybars.compile(<<-HBS)
|
16
|
+
text \\ text
|
17
|
+
HBS
|
18
|
+
|
19
|
+
expect(eval(template)).to resemble('text \\ text')
|
20
|
+
end
|
21
|
+
|
22
|
+
it "escapes `\\` at the end of the string" do
|
23
|
+
template = Curlybars.compile('text \\')
|
24
|
+
|
25
|
+
expect(eval(template)).to resemble('text \\')
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
describe "{{helper context key=value}}" do
|
2
|
+
let(:global_helpers_providers) { [] }
|
3
|
+
|
4
|
+
describe "#compile" do
|
5
|
+
let(:post) { double("post") }
|
6
|
+
let(:presenter) { IntegrationTest::Presenter.new(double("view_context"), post: post) }
|
7
|
+
|
8
|
+
it "passes two arguments" do
|
9
|
+
template = Curlybars.compile(<<-HBS)
|
10
|
+
{{print_args_and_options 'first' 'second'}}
|
11
|
+
HBS
|
12
|
+
|
13
|
+
expect(eval(template)).to resemble(<<-HTML)
|
14
|
+
first, second, key=
|
15
|
+
HTML
|
16
|
+
end
|
17
|
+
|
18
|
+
it "passes two arguments and options" do
|
19
|
+
template = Curlybars.compile(<<-HBS)
|
20
|
+
{{print_args_and_options 'first' 'second' key='value'}}
|
21
|
+
HBS
|
22
|
+
|
23
|
+
expect(eval(template)).to resemble(<<-HTML)
|
24
|
+
first, second, key=value
|
25
|
+
HTML
|
26
|
+
end
|
27
|
+
|
28
|
+
it "renders a helper with expression and options" do
|
29
|
+
template = Curlybars.compile(<<-HBS)
|
30
|
+
{{date user.created_at class='metadata'}}
|
31
|
+
HBS
|
32
|
+
|
33
|
+
expect(eval(template)).to resemble(<<-HTML)
|
34
|
+
<time datetime="2015-02-03T13:25:06Z" class="metadata">
|
35
|
+
February 3, 2015 13:25
|
36
|
+
</time>
|
37
|
+
HTML
|
38
|
+
end
|
39
|
+
|
40
|
+
it "renders a helper with only expression" do
|
41
|
+
template = Curlybars.compile(<<-HBS)
|
42
|
+
<script src="{{asset "jquery_plugin.js"}}"></script>
|
43
|
+
HBS
|
44
|
+
|
45
|
+
expect(eval(template)).to resemble(<<-HTML)
|
46
|
+
<script src="http://cdn.example.com/jquery_plugin.js"></script>
|
47
|
+
HTML
|
48
|
+
end
|
49
|
+
|
50
|
+
it "renders a helper with only options" do
|
51
|
+
template = Curlybars.compile(<<-HBS)
|
52
|
+
{{#with new_comment_form}}
|
53
|
+
{{input title class="form-control"}}
|
54
|
+
{{/with}}
|
55
|
+
HBS
|
56
|
+
|
57
|
+
expect(eval(template)).to resemble(<<-HTML)
|
58
|
+
<input name="community_post[title]"
|
59
|
+
id="community_post_title"
|
60
|
+
type="text"
|
61
|
+
class="form-control"
|
62
|
+
value="some value persisted in the DB">
|
63
|
+
HTML
|
64
|
+
end
|
65
|
+
|
66
|
+
it "renders correctly a return type of integer" do
|
67
|
+
template = Curlybars.compile(<<-HBS)
|
68
|
+
{{integer 'ignored'}}
|
69
|
+
HBS
|
70
|
+
|
71
|
+
expect(eval(template)).to resemble(<<-HTML)
|
72
|
+
0
|
73
|
+
HTML
|
74
|
+
end
|
75
|
+
|
76
|
+
it "renders correctly a return type of boolean" do
|
77
|
+
template = Curlybars.compile(<<-HBS)
|
78
|
+
{{boolean 'ignored'}}
|
79
|
+
HBS
|
80
|
+
|
81
|
+
expect(eval(template)).to resemble(<<-HTML)
|
82
|
+
true
|
83
|
+
HTML
|
84
|
+
end
|
85
|
+
|
86
|
+
it "handles correctly a method that invokes `yield`, returning empty string" do
|
87
|
+
template = Curlybars.compile(<<-HBS)
|
88
|
+
{{this_method_yields}}
|
89
|
+
HBS
|
90
|
+
|
91
|
+
expect(eval(template)).to resemble("")
|
92
|
+
end
|
93
|
+
|
94
|
+
it "doesn't render if the path returns a presenter" do
|
95
|
+
template = Curlybars.compile(<<-HBS)
|
96
|
+
{{user}}
|
97
|
+
HBS
|
98
|
+
|
99
|
+
expect(eval(template)).to resemble("")
|
100
|
+
end
|
101
|
+
|
102
|
+
it "doesn't render if the path returns a collection of presenters" do
|
103
|
+
template = Curlybars.compile(<<-HBS)
|
104
|
+
{{array_of_users}}
|
105
|
+
HBS
|
106
|
+
|
107
|
+
expect(eval(template)).to resemble("")
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "#validate" do
|
112
|
+
let(:presenter_class) { double(:presenter_class) }
|
113
|
+
|
114
|
+
it "with errors" do
|
115
|
+
dependency_tree = {}
|
116
|
+
|
117
|
+
source = <<-HBS
|
118
|
+
{{helper}}
|
119
|
+
HBS
|
120
|
+
|
121
|
+
errors = Curlybars.validate(dependency_tree, source)
|
122
|
+
|
123
|
+
expect(errors).not_to be_empty
|
124
|
+
end
|
125
|
+
|
126
|
+
it "raises when using a partial as an helper" do
|
127
|
+
dependency_tree = { partial: :partial }
|
128
|
+
|
129
|
+
source = <<-HBS
|
130
|
+
{{partial}}
|
131
|
+
HBS
|
132
|
+
|
133
|
+
errors = Curlybars.validate(dependency_tree, source)
|
134
|
+
|
135
|
+
expect(errors).not_to be_empty
|
136
|
+
end
|
137
|
+
|
138
|
+
it "without errors" do
|
139
|
+
dependency_tree = { helper: :helper }
|
140
|
+
|
141
|
+
source = <<-HBS
|
142
|
+
{{helper}}
|
143
|
+
HBS
|
144
|
+
|
145
|
+
errors = Curlybars.validate(dependency_tree, source)
|
146
|
+
|
147
|
+
expect(errors).to be_empty
|
148
|
+
end
|
149
|
+
|
150
|
+
it "validates {{helper.invoked_on_nil}} with errors" do
|
151
|
+
dependency_tree = { helper: :helper }
|
152
|
+
|
153
|
+
source = <<-HBS
|
154
|
+
{{helper.invoked_on_nil}}
|
155
|
+
HBS
|
156
|
+
|
157
|
+
errors = Curlybars.validate(dependency_tree, source)
|
158
|
+
|
159
|
+
expect(errors).not_to be_empty
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "with context" do
|
163
|
+
it "without errors in block_helper" do
|
164
|
+
dependency_tree = { helper: :helper, context: nil }
|
165
|
+
|
166
|
+
source = <<-HBS
|
167
|
+
{{helper context}}
|
168
|
+
HBS
|
169
|
+
|
170
|
+
errors = Curlybars.validate(dependency_tree, source)
|
171
|
+
|
172
|
+
expect(errors).to be_empty
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
describe "{{#if}}...{{else}}...{{/if}}" do
|
2
|
+
let(:global_helpers_providers) { [] }
|
3
|
+
|
4
|
+
describe "#compile" do
|
5
|
+
let(:post) { double("post") }
|
6
|
+
let(:presenter) { IntegrationTest::Presenter.new(double("view_context"), post: post) }
|
7
|
+
|
8
|
+
it "renders the if_template" do
|
9
|
+
allow(IntegrationTest::Presenter).to receive(:allows_method?).with(:return_true) { true }
|
10
|
+
allow(presenter).to receive(:return_true) { true }
|
11
|
+
|
12
|
+
template = Curlybars.compile(<<-HBS)
|
13
|
+
{{#if return_true}}
|
14
|
+
if_template
|
15
|
+
{{else}}
|
16
|
+
else_template
|
17
|
+
{{/if}}
|
18
|
+
HBS
|
19
|
+
|
20
|
+
expect(eval(template)).to resemble(<<-HTML)
|
21
|
+
if_template
|
22
|
+
HTML
|
23
|
+
end
|
24
|
+
|
25
|
+
it "renders the else_template" do
|
26
|
+
allow(IntegrationTest::Presenter).to receive(:allows_method?).with(:return_false) { true }
|
27
|
+
allow(presenter).to receive(:return_false) { false }
|
28
|
+
|
29
|
+
template = Curlybars.compile(<<-HBS)
|
30
|
+
{{#if return_false}}
|
31
|
+
if_template
|
32
|
+
{{else}}
|
33
|
+
else_template
|
34
|
+
{{/if}}
|
35
|
+
HBS
|
36
|
+
|
37
|
+
expect(eval(template)).to resemble(<<-HTML)
|
38
|
+
else_template
|
39
|
+
HTML
|
40
|
+
end
|
41
|
+
|
42
|
+
it "allows empty if_template" do
|
43
|
+
allow(IntegrationTest::Presenter).to receive(:allows_method?).with(:valid) { true }
|
44
|
+
allow(presenter).to receive(:valid) { false }
|
45
|
+
|
46
|
+
template = Curlybars.compile(<<-HBS)
|
47
|
+
{{#if valid}}{{else}}
|
48
|
+
else_template
|
49
|
+
{{/if}}
|
50
|
+
HBS
|
51
|
+
|
52
|
+
expect(eval(template)).to resemble(<<-HTML)
|
53
|
+
else_template
|
54
|
+
HTML
|
55
|
+
end
|
56
|
+
|
57
|
+
it "allows empty else_template" do
|
58
|
+
allow(IntegrationTest::Presenter).to receive(:allows_method?).with(:valid) { true }
|
59
|
+
allow(presenter).to receive(:valid) { true }
|
60
|
+
|
61
|
+
template = Curlybars.compile(<<-HBS)
|
62
|
+
{{#if valid}}
|
63
|
+
if_template
|
64
|
+
{{else}}{{/if}}
|
65
|
+
HBS
|
66
|
+
|
67
|
+
expect(eval(template)).to resemble(<<-HTML)
|
68
|
+
if_template
|
69
|
+
HTML
|
70
|
+
end
|
71
|
+
|
72
|
+
it "allows empty if_template and else_template" do
|
73
|
+
allow(IntegrationTest::Presenter).to receive(:allows_method?).with(:valid) { true }
|
74
|
+
allow(presenter).to receive(:valid) { true }
|
75
|
+
|
76
|
+
template = Curlybars.compile(<<-HBS)
|
77
|
+
{{#if valid}}{{else}}{{/if}}
|
78
|
+
HBS
|
79
|
+
|
80
|
+
expect(eval(template)).to resemble("")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#validate" do
|
85
|
+
let(:presenter_class) { double(:presenter_class) }
|
86
|
+
|
87
|
+
it "validates with errors the condition" do
|
88
|
+
dependency_tree = {}
|
89
|
+
|
90
|
+
source = <<-HBS
|
91
|
+
{{#if condition}}{{else}}{{/if}}
|
92
|
+
HBS
|
93
|
+
|
94
|
+
errors = Curlybars.validate(dependency_tree, source)
|
95
|
+
|
96
|
+
expect(errors).not_to be_empty
|
97
|
+
end
|
98
|
+
|
99
|
+
it "validates with errors the nested if_template" do
|
100
|
+
dependency_tree = { condition: nil }
|
101
|
+
|
102
|
+
source = <<-HBS
|
103
|
+
{{#if condition}}
|
104
|
+
{{unallowed_method}}
|
105
|
+
{{else}}
|
106
|
+
{{/if}}
|
107
|
+
HBS
|
108
|
+
|
109
|
+
errors = Curlybars.validate(dependency_tree, source)
|
110
|
+
|
111
|
+
expect(errors).not_to be_empty
|
112
|
+
end
|
113
|
+
|
114
|
+
it "validates with errors the nested else_template" do
|
115
|
+
dependency_tree = { condition: nil }
|
116
|
+
|
117
|
+
source = <<-HBS
|
118
|
+
{{#if condition}}
|
119
|
+
{{else}}
|
120
|
+
{{unallowed_method}}
|
121
|
+
{{/if}}
|
122
|
+
HBS
|
123
|
+
|
124
|
+
errors = Curlybars.validate(dependency_tree, source)
|
125
|
+
|
126
|
+
expect(errors).not_to be_empty
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
describe "{{#if}}...{{/if}}" do
|
2
|
+
let(:global_helpers_providers) { [] }
|
3
|
+
|
4
|
+
describe "#compile" do
|
5
|
+
let(:post) { double("post") }
|
6
|
+
let(:presenter) { IntegrationTest::Presenter.new(double("view_context"), post: post) }
|
7
|
+
|
8
|
+
it "returns positive branch when condition is true" do
|
9
|
+
allow(presenter).to receive(:allows_method?).with(:valid) { true }
|
10
|
+
allow(presenter).to receive(:valid) { true }
|
11
|
+
|
12
|
+
template = Curlybars.compile(<<-HBS)
|
13
|
+
{{#if valid}}
|
14
|
+
if_template
|
15
|
+
{{/if}}
|
16
|
+
HBS
|
17
|
+
|
18
|
+
expect(eval(template)).to resemble(<<-HTML)
|
19
|
+
if_template
|
20
|
+
HTML
|
21
|
+
end
|
22
|
+
|
23
|
+
it "doesn't return positive branch when condition is false" do
|
24
|
+
allow(presenter).to receive(:allows_method?).with(:valid) { true }
|
25
|
+
allow(presenter).to receive(:valid) { false }
|
26
|
+
|
27
|
+
template = Curlybars.compile(<<-HBS)
|
28
|
+
{{#if valid}}
|
29
|
+
if_template
|
30
|
+
{{/if}}
|
31
|
+
HBS
|
32
|
+
|
33
|
+
expect(eval(template)).to resemble("")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "doesn't return positive branch when condition is empty array" do
|
37
|
+
allow(presenter).to receive(:allows_method?).with(:collection) { true }
|
38
|
+
allow(presenter).to receive(:collection) { [] }
|
39
|
+
|
40
|
+
template = Curlybars.compile(<<-HBS)
|
41
|
+
{{#if collection}}
|
42
|
+
if_template
|
43
|
+
{{/if}}
|
44
|
+
HBS
|
45
|
+
|
46
|
+
expect(eval(template)).to resemble("")
|
47
|
+
end
|
48
|
+
|
49
|
+
it "works with nested `if blocks` (double positive)" do
|
50
|
+
allow(presenter).to receive(:allows_method?).with(:valid) { true }
|
51
|
+
allow(presenter).to receive(:allows_method?).with(:visible) { true }
|
52
|
+
allow(presenter).to receive(:valid) { true }
|
53
|
+
allow(presenter).to receive(:visible) { true }
|
54
|
+
|
55
|
+
template = Curlybars.compile(<<-HBS)
|
56
|
+
{{#if valid}}
|
57
|
+
{{#if visible}}
|
58
|
+
inner_if_template
|
59
|
+
{{/if}}
|
60
|
+
outer_if_template
|
61
|
+
{{/if}}
|
62
|
+
HBS
|
63
|
+
|
64
|
+
expect(eval(template)).to resemble(<<-HTML)
|
65
|
+
inner_if_template
|
66
|
+
outer_if_template
|
67
|
+
HTML
|
68
|
+
end
|
69
|
+
|
70
|
+
it "works with nested `if blocks` (positive and negative)" do
|
71
|
+
allow(presenter).to receive(:allows_method?).with(:valid) { true }
|
72
|
+
allow(presenter).to receive(:allows_method?).with(:visible) { true }
|
73
|
+
allow(presenter).to receive(:valid) { true }
|
74
|
+
allow(presenter).to receive(:visible) { false }
|
75
|
+
|
76
|
+
template = Curlybars.compile(<<-HBS)
|
77
|
+
{{#if valid}}
|
78
|
+
{{#if visible}}
|
79
|
+
inner_if_template
|
80
|
+
{{/if}}
|
81
|
+
outer_if_template
|
82
|
+
{{/if}}
|
83
|
+
HBS
|
84
|
+
|
85
|
+
expect(eval(template)).to resemble(<<-HTML)
|
86
|
+
outer_if_template
|
87
|
+
HTML
|
88
|
+
end
|
89
|
+
|
90
|
+
it "allows empty if_template" do
|
91
|
+
allow(presenter).to receive(:allows_method?).with(:valid) { true }
|
92
|
+
allow(presenter).to receive(:valid) { true }
|
93
|
+
|
94
|
+
template = Curlybars.compile(<<-HBS)
|
95
|
+
{{#if valid}}{{/if}}
|
96
|
+
HBS
|
97
|
+
|
98
|
+
expect(eval(template)).to resemble("")
|
99
|
+
end
|
100
|
+
|
101
|
+
it "allows usage of variables in condition" do
|
102
|
+
template = Curlybars.compile(<<-HBS)
|
103
|
+
{{#each two_elements}}
|
104
|
+
{{#if @first}}I am the first!{{/if}}
|
105
|
+
{{/each}}
|
106
|
+
HBS
|
107
|
+
|
108
|
+
expect(eval(template)).to resemble(<<-HTML)
|
109
|
+
I am the first!
|
110
|
+
HTML
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "#validate" do
|
115
|
+
let(:presenter_class) { double(:presenter_class) }
|
116
|
+
|
117
|
+
it "validates with errors the condition" do
|
118
|
+
dependency_tree = {}
|
119
|
+
|
120
|
+
source = <<-HBS
|
121
|
+
{{#if condition}}{{/if}}
|
122
|
+
HBS
|
123
|
+
|
124
|
+
errors = Curlybars.validate(dependency_tree, source)
|
125
|
+
|
126
|
+
expect(errors).not_to be_empty
|
127
|
+
end
|
128
|
+
|
129
|
+
it "validates with errors the nested template" do
|
130
|
+
dependency_tree = { condition: nil }
|
131
|
+
|
132
|
+
source = <<-HBS
|
133
|
+
{{#if condition}}
|
134
|
+
{{unallowed_method}}
|
135
|
+
{{/if}}
|
136
|
+
HBS
|
137
|
+
|
138
|
+
errors = Curlybars.validate(dependency_tree, source)
|
139
|
+
|
140
|
+
expect(errors).not_to be_empty
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|