safely_block 0.2.0 → 0.3.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 +5 -5
- data/CHANGELOG.md +12 -0
- data/LICENSE.txt +1 -1
- data/README.md +43 -5
- data/lib/safely/core.rb +18 -14
- data/lib/safely/version.rb +1 -1
- data/lib/safely_block.rb +2 -1
- metadata +16 -26
- data/.gitignore +0 -22
- data/Gemfile +0 -4
- data/Rakefile +0 -8
- data/safely_block.gemspec +0 -26
- data/test/safely_test.rb +0 -136
- data/test/test_helper.rb +0 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ffb5e297c082239df9675faa3fb796e46e59c4409754738d3209c791961487a5
|
|
4
|
+
data.tar.gz: 56cb3d1229faf04817dbf6d8cfdb210cd91171bbfb65379362a98647b6283441
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 67c689e80de505ba63395e611868074213860b740f470098e6ceb6fdf2bf1768740397905dbf0e31f5000514400562901cfe842b3326556c612ca24edba8215a
|
|
7
|
+
data.tar.gz: 5db94cbe2670b3b362646ee84a78872206753adf65e0afd7f85dc9dbe44277defac662afa11bca3318de06ceb5dd4cb03409f8c7d49f85c79b4df8dac7c5b819
|
data/CHANGELOG.md
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
|
@@ -10,6 +10,18 @@ Exceptions are rescued and automatically reported to your favorite reporting ser
|
|
|
10
10
|
|
|
11
11
|
In development and test environments, exceptions are raised so you can fix them.
|
|
12
12
|
|
|
13
|
+
[Read more](https://ankane.org/safely-pattern)
|
|
14
|
+
|
|
15
|
+
[](https://travis-ci.org/ankane/safely)
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
Add this line to your application’s Gemfile:
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
gem 'safely_block'
|
|
23
|
+
```
|
|
24
|
+
|
|
13
25
|
## Use It Everywhere
|
|
14
26
|
|
|
15
27
|
“Oh no, analytics brought down search”
|
|
@@ -30,6 +42,14 @@ Also aliased as `yolo`.
|
|
|
30
42
|
|
|
31
43
|
## Features
|
|
32
44
|
|
|
45
|
+
Pass extra context to be reported with exceptions
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
safely context: {user_id: 123} do
|
|
49
|
+
# code
|
|
50
|
+
end
|
|
51
|
+
```
|
|
52
|
+
|
|
33
53
|
Specify a default value to return on exceptions
|
|
34
54
|
|
|
35
55
|
```ruby
|
|
@@ -79,7 +99,8 @@ Reports exceptions to a variety of services out of the box thanks to [Errbase](h
|
|
|
79
99
|
- [Airbrake](https://airbrake.io/)
|
|
80
100
|
- [Appsignal](https://appsignal.com/)
|
|
81
101
|
- [Bugsnag](https://bugsnag.com/)
|
|
82
|
-
- [
|
|
102
|
+
- [Exception Notification](https://github.com/smartinez87/exception_notification)
|
|
103
|
+
- [Google Stackdriver](https://cloud.google.com/stackdriver/)
|
|
83
104
|
- [Honeybadger](https://www.honeybadger.io/)
|
|
84
105
|
- [Opbeat](https://opbeat.com/)
|
|
85
106
|
- [Raygun](https://raygun.io/)
|
|
@@ -89,7 +110,7 @@ Reports exceptions to a variety of services out of the box thanks to [Errbase](h
|
|
|
89
110
|
Customize reporting with:
|
|
90
111
|
|
|
91
112
|
```ruby
|
|
92
|
-
Safely.report_exception_method = ->
|
|
113
|
+
Safely.report_exception_method = ->(e) { Rollbar.error(e) }
|
|
93
114
|
```
|
|
94
115
|
|
|
95
116
|
By default, exception messages are prefixed with `[safely]`. This makes it easier to spot rescued exceptions. Turn this off with:
|
|
@@ -104,14 +125,22 @@ To report exceptions manually:
|
|
|
104
125
|
Safely.report_exception(e)
|
|
105
126
|
```
|
|
106
127
|
|
|
107
|
-
##
|
|
128
|
+
## Data Protection
|
|
108
129
|
|
|
109
|
-
|
|
130
|
+
To protect the privacy of your users, do not send [personal data](https://en.wikipedia.org/wiki/Personally_identifiable_information) to exception services. Filter sensitive form fields, use ids (not email addresses) to identify users, and mask IP addresses.
|
|
131
|
+
|
|
132
|
+
With Rollbar, you can do:
|
|
110
133
|
|
|
111
134
|
```ruby
|
|
112
|
-
|
|
135
|
+
Rollbar.configure do |config|
|
|
136
|
+
config.person_id_method = "id" # default
|
|
137
|
+
config.scrub_fields |= [:birthday]
|
|
138
|
+
config.anonymize_user_ip = true
|
|
139
|
+
end
|
|
113
140
|
```
|
|
114
141
|
|
|
142
|
+
While working on exceptions, be on the lookout for personal data and correct as needed.
|
|
143
|
+
|
|
115
144
|
## History
|
|
116
145
|
|
|
117
146
|
View the [changelog](https://github.com/ankane/safely/blob/master/CHANGELOG.md)
|
|
@@ -124,3 +153,12 @@ Everyone is encouraged to help improve this project. Here are a few ways you can
|
|
|
124
153
|
- Fix bugs and [submit pull requests](https://github.com/ankane/safely/pulls)
|
|
125
154
|
- Write, clarify, or fix documentation
|
|
126
155
|
- Suggest or add new features
|
|
156
|
+
|
|
157
|
+
To get started with development and testing:
|
|
158
|
+
|
|
159
|
+
```sh
|
|
160
|
+
git clone https://github.com/ankane/safely.git
|
|
161
|
+
cd safely
|
|
162
|
+
bundle install
|
|
163
|
+
rake test
|
|
164
|
+
```
|
data/lib/safely/core.rb
CHANGED
|
@@ -7,8 +7,20 @@ module Safely
|
|
|
7
7
|
attr_accessor :raise_envs, :tag, :report_exception_method, :throttle_counter
|
|
8
8
|
attr_writer :env
|
|
9
9
|
|
|
10
|
-
def report_exception(e)
|
|
11
|
-
|
|
10
|
+
def report_exception(e, tag: nil, context: {})
|
|
11
|
+
tag = Safely.tag if tag.nil?
|
|
12
|
+
if tag && e.message
|
|
13
|
+
e = e.dup # leave original exception unmodified
|
|
14
|
+
message = e.message
|
|
15
|
+
e.define_singleton_method(:message) do
|
|
16
|
+
"[#{tag == true ? "safely" : tag}] #{message}"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
if report_exception_method.arity == 1
|
|
20
|
+
report_exception_method.call(e)
|
|
21
|
+
else
|
|
22
|
+
report_exception_method.call(e, context)
|
|
23
|
+
end
|
|
12
24
|
end
|
|
13
25
|
|
|
14
26
|
def env
|
|
@@ -23,8 +35,8 @@ module Safely
|
|
|
23
35
|
end
|
|
24
36
|
end
|
|
25
37
|
|
|
26
|
-
DEFAULT_EXCEPTION_METHOD = proc do |e|
|
|
27
|
-
Errbase.report(e)
|
|
38
|
+
DEFAULT_EXCEPTION_METHOD = proc do |e, context|
|
|
39
|
+
Errbase.report(e, context)
|
|
28
40
|
end
|
|
29
41
|
|
|
30
42
|
self.tag = true
|
|
@@ -34,7 +46,7 @@ module Safely
|
|
|
34
46
|
self.throttle_counter = Hash.new(0)
|
|
35
47
|
|
|
36
48
|
module Methods
|
|
37
|
-
def safely(tag: nil, sample: nil, except: nil, only: nil, silence: nil, throttle: false, default: nil)
|
|
49
|
+
def safely(tag: nil, sample: nil, except: nil, only: nil, silence: nil, throttle: false, default: nil, context: {})
|
|
38
50
|
yield
|
|
39
51
|
rescue *Array(only || StandardError) => e
|
|
40
52
|
raise e if Array(except).any? { |c| e.is_a?(c) }
|
|
@@ -42,15 +54,7 @@ module Safely
|
|
|
42
54
|
if sample ? rand < 1.0 / sample : true
|
|
43
55
|
begin
|
|
44
56
|
unless Array(silence).any? { |c| e.is_a?(c) } || Safely.throttled?(e, throttle)
|
|
45
|
-
|
|
46
|
-
if tag && e.message
|
|
47
|
-
e = e.dup # leave original exception unmodified
|
|
48
|
-
message = e.message
|
|
49
|
-
e.define_singleton_method(:message) do
|
|
50
|
-
"[#{tag == true ? "safely" : tag}] #{message}"
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
Safely.report_exception(e)
|
|
57
|
+
Safely.report_exception(e, tag: tag, context: context)
|
|
54
58
|
end
|
|
55
59
|
rescue => e2
|
|
56
60
|
$stderr.puts "FAIL-SAFE #{e2.class.name}: #{e2.message}"
|
data/lib/safely/version.rb
CHANGED
data/lib/safely_block.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: safely_block
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Kane
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2019-10-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: errbase
|
|
@@ -16,28 +16,28 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
19
|
+
version: 0.1.1
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
26
|
+
version: 0.1.1
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: bundler
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
33
|
+
version: '0'
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - "
|
|
38
|
+
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
40
|
+
version: '0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: rake
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -58,33 +58,26 @@ dependencies:
|
|
|
58
58
|
requirements:
|
|
59
59
|
- - ">="
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
61
|
+
version: '0'
|
|
62
62
|
type: :development
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
66
|
- - ">="
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
69
|
-
description:
|
|
70
|
-
email:
|
|
71
|
-
- andrew@chartkick.com
|
|
68
|
+
version: '0'
|
|
69
|
+
description:
|
|
70
|
+
email: andrew@chartkick.com
|
|
72
71
|
executables: []
|
|
73
72
|
extensions: []
|
|
74
73
|
extra_rdoc_files: []
|
|
75
74
|
files:
|
|
76
|
-
- ".gitignore"
|
|
77
75
|
- CHANGELOG.md
|
|
78
|
-
- Gemfile
|
|
79
76
|
- LICENSE.txt
|
|
80
77
|
- README.md
|
|
81
|
-
- Rakefile
|
|
82
78
|
- lib/safely/core.rb
|
|
83
79
|
- lib/safely/version.rb
|
|
84
80
|
- lib/safely_block.rb
|
|
85
|
-
- safely_block.gemspec
|
|
86
|
-
- test/safely_test.rb
|
|
87
|
-
- test/test_helper.rb
|
|
88
81
|
homepage: https://github.com/ankane/safely
|
|
89
82
|
licenses:
|
|
90
83
|
- MIT
|
|
@@ -97,18 +90,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
97
90
|
requirements:
|
|
98
91
|
- - ">="
|
|
99
92
|
- !ruby/object:Gem::Version
|
|
100
|
-
version: '
|
|
93
|
+
version: '2.4'
|
|
101
94
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
95
|
requirements:
|
|
103
96
|
- - ">="
|
|
104
97
|
- !ruby/object:Gem::Version
|
|
105
98
|
version: '0'
|
|
106
99
|
requirements: []
|
|
107
|
-
|
|
108
|
-
rubygems_version: 2.6.8
|
|
100
|
+
rubygems_version: 3.0.3
|
|
109
101
|
signing_key:
|
|
110
102
|
specification_version: 4
|
|
111
|
-
summary:
|
|
112
|
-
test_files:
|
|
113
|
-
- test/safely_test.rb
|
|
114
|
-
- test/test_helper.rb
|
|
103
|
+
summary: Rescue and report exceptions in non-critical code
|
|
104
|
+
test_files: []
|
data/.gitignore
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
*.gem
|
|
2
|
-
*.rbc
|
|
3
|
-
.bundle
|
|
4
|
-
.config
|
|
5
|
-
.yardoc
|
|
6
|
-
Gemfile.lock
|
|
7
|
-
InstalledFiles
|
|
8
|
-
_yardoc
|
|
9
|
-
coverage
|
|
10
|
-
doc/
|
|
11
|
-
lib/bundler/man
|
|
12
|
-
pkg
|
|
13
|
-
rdoc
|
|
14
|
-
spec/reports
|
|
15
|
-
test/tmp
|
|
16
|
-
test/version_tmp
|
|
17
|
-
tmp
|
|
18
|
-
*.bundle
|
|
19
|
-
*.so
|
|
20
|
-
*.o
|
|
21
|
-
*.a
|
|
22
|
-
mkmf.log
|
data/Gemfile
DELETED
data/Rakefile
DELETED
data/safely_block.gemspec
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# coding: utf-8
|
|
2
|
-
lib = File.expand_path("../lib", __FILE__)
|
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
-
require "safely/version"
|
|
5
|
-
|
|
6
|
-
Gem::Specification.new do |spec|
|
|
7
|
-
spec.name = "safely_block"
|
|
8
|
-
spec.version = Safely::VERSION
|
|
9
|
-
spec.authors = ["Andrew Kane"]
|
|
10
|
-
spec.email = ["andrew@chartkick.com"]
|
|
11
|
-
spec.summary = "Awesome exception handling"
|
|
12
|
-
spec.description = "Awesome exception handling"
|
|
13
|
-
spec.homepage = "https://github.com/ankane/safely"
|
|
14
|
-
spec.license = "MIT"
|
|
15
|
-
|
|
16
|
-
spec.files = `git ls-files -z`.split("\x0")
|
|
17
|
-
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
-
spec.require_paths = ["lib"]
|
|
20
|
-
|
|
21
|
-
spec.add_dependency "errbase"
|
|
22
|
-
|
|
23
|
-
spec.add_development_dependency "bundler", "~> 1.6"
|
|
24
|
-
spec.add_development_dependency "rake"
|
|
25
|
-
spec.add_development_dependency "minitest", ">= 5"
|
|
26
|
-
end
|
data/test/safely_test.rb
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
require_relative "test_helper"
|
|
2
|
-
|
|
3
|
-
class TestSafely < Minitest::Test
|
|
4
|
-
def setup
|
|
5
|
-
Safely.env = "production"
|
|
6
|
-
Safely.tag = true
|
|
7
|
-
Safely.report_exception_method = Safely::DEFAULT_EXCEPTION_METHOD
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def test_development_environment
|
|
11
|
-
Safely.env = "development"
|
|
12
|
-
assert_raises(Safely::TestError) do
|
|
13
|
-
safely do
|
|
14
|
-
raise Safely::TestError
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def test_test_environment
|
|
20
|
-
Safely.env = "test"
|
|
21
|
-
assert_raises(Safely::TestError) do
|
|
22
|
-
safely do
|
|
23
|
-
raise Safely::TestError
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def test_production_environment
|
|
29
|
-
exception = Safely::TestError.new
|
|
30
|
-
mock = MiniTest::Mock.new
|
|
31
|
-
mock.expect :report_exception, nil, [exception]
|
|
32
|
-
Safely.report_exception_method = -> (e) { mock.report_exception(e) }
|
|
33
|
-
safely do
|
|
34
|
-
raise exception
|
|
35
|
-
end
|
|
36
|
-
assert mock.verify
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def test_yolo
|
|
40
|
-
exception = Safely::TestError.new
|
|
41
|
-
mock = MiniTest::Mock.new
|
|
42
|
-
mock.expect :report_exception, nil, [exception]
|
|
43
|
-
Safely.report_exception_method = -> (e) { mock.report_exception(e) }
|
|
44
|
-
yolo do
|
|
45
|
-
raise exception
|
|
46
|
-
end
|
|
47
|
-
assert mock.verify
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def test_tagged
|
|
51
|
-
ex = nil
|
|
52
|
-
Safely.report_exception_method = -> (e) { ex = e }
|
|
53
|
-
safely { raise Safely::TestError, "Boom" }
|
|
54
|
-
assert_equal "[safely] Boom", ex.message
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def test_not_tagged
|
|
58
|
-
Safely.tag = false
|
|
59
|
-
ex = nil
|
|
60
|
-
Safely.report_exception_method = -> (e) { ex = e }
|
|
61
|
-
safely { raise Safely::TestError, "Boom" }
|
|
62
|
-
assert_equal "Boom", ex.message
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def test_local_tag
|
|
66
|
-
ex = nil
|
|
67
|
-
Safely.report_exception_method = -> (e) { ex = e }
|
|
68
|
-
safely(tag: "hi") { raise Safely::TestError, "Boom" }
|
|
69
|
-
assert_equal "[hi] Boom", ex.message
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def test_return_value
|
|
73
|
-
assert_equal 1, safely { 1 }
|
|
74
|
-
assert_nil safely { raise Safely::TestError, "Boom" }
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def test_default
|
|
78
|
-
assert_equal 1, safely(default: 2) { 1 }
|
|
79
|
-
assert_equal 2, safely(default: 2) { raise Safely::TestError, "Boom" }
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def test_only
|
|
83
|
-
assert_nil safely(only: Safely::TestError) { raise Safely::TestError }
|
|
84
|
-
assert_raises(RuntimeError, "Boom") { safely(only: Safely::TestError) { raise "Boom" } }
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def test_only_array
|
|
88
|
-
assert_nil safely(only: [Safely::TestError]) { raise Safely::TestError }
|
|
89
|
-
assert_raises(RuntimeError, "Boom") { safely(only: [Safely::TestError]) { raise "Boom" } }
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def test_except
|
|
93
|
-
assert_raises(Safely::TestError, "Boom") { safely(except: StandardError) { raise Safely::TestError, "Boom" } }
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def test_silence
|
|
97
|
-
safely(silence: StandardError) { raise Safely::TestError, "Boom" }
|
|
98
|
-
assert true
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def test_failsafe
|
|
102
|
-
Safely.report_exception_method = -> (_) { raise "oops" }
|
|
103
|
-
_, err = capture_io do
|
|
104
|
-
safely { raise "boom" }
|
|
105
|
-
end
|
|
106
|
-
assert_equal "FAIL-SAFE RuntimeError: oops\n", err
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def test_throttle
|
|
110
|
-
count = 0
|
|
111
|
-
Safely.report_exception_method = -> (_) { count += 1 }
|
|
112
|
-
5.times do |n|
|
|
113
|
-
safely throttle: {limit: 2, period: 3600} do
|
|
114
|
-
raise Safely::TestError
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
assert_equal 2, count
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def test_throttle_key
|
|
121
|
-
count = 0
|
|
122
|
-
Safely.report_exception_method = -> (_) { count += 1 }
|
|
123
|
-
5.times do |n|
|
|
124
|
-
safely throttle: {limit: 2, period: 3600, key: "boom#{n % 2}"} do
|
|
125
|
-
raise Safely::TestError
|
|
126
|
-
end
|
|
127
|
-
end
|
|
128
|
-
assert_equal 4, count
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def test_bad_argument
|
|
132
|
-
assert_raises(ArgumentError) do
|
|
133
|
-
safely(unknown: true) { }
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
end
|