render_sync 0.5.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 +7 -0
- data/CHANGELOG.md +153 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +521 -0
- data/Rakefile +9 -0
- data/app/assets/javascripts/sync.coffee +355 -0
- data/app/controllers/sync/refetches_controller.rb +56 -0
- data/app/helpers/render_sync/config_helper.rb +15 -0
- data/config/routes.rb +3 -0
- data/config/sync.yml +21 -0
- data/lib/generators/render_sync/install_generator.rb +14 -0
- data/lib/generators/render_sync/templates/sync.ru +14 -0
- data/lib/generators/render_sync/templates/sync.yml +34 -0
- data/lib/render_sync.rb +174 -0
- data/lib/render_sync/action.rb +39 -0
- data/lib/render_sync/actions.rb +114 -0
- data/lib/render_sync/channel.rb +23 -0
- data/lib/render_sync/clients/dummy.rb +22 -0
- data/lib/render_sync/clients/faye.rb +104 -0
- data/lib/render_sync/clients/pusher.rb +77 -0
- data/lib/render_sync/controller_helpers.rb +33 -0
- data/lib/render_sync/engine.rb +24 -0
- data/lib/render_sync/erb_tracker.rb +49 -0
- data/lib/render_sync/faye_extension.rb +45 -0
- data/lib/render_sync/model.rb +174 -0
- data/lib/render_sync/model_actions.rb +60 -0
- data/lib/render_sync/model_change_tracking.rb +97 -0
- data/lib/render_sync/model_syncing.rb +65 -0
- data/lib/render_sync/model_touching.rb +35 -0
- data/lib/render_sync/partial.rb +112 -0
- data/lib/render_sync/partial_creator.rb +47 -0
- data/lib/render_sync/reactor.rb +48 -0
- data/lib/render_sync/refetch_model.rb +21 -0
- data/lib/render_sync/refetch_partial.rb +43 -0
- data/lib/render_sync/refetch_partial_creator.rb +21 -0
- data/lib/render_sync/renderer.rb +19 -0
- data/lib/render_sync/resource.rb +115 -0
- data/lib/render_sync/scope.rb +113 -0
- data/lib/render_sync/scope_definition.rb +30 -0
- data/lib/render_sync/view_helpers.rb +106 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/app/views/sync/users/_show.html.erb +1 -0
- data/test/dummy/app/views/sync/users/refetch/_show.html.erb +1 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +22 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +8 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +29 -0
- data/test/dummy/config/environments/production.rb +80 -0
- data/test/dummy/config/environments/test.rb +36 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +12 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +56 -0
- data/test/dummy/log/test.log +626 -0
- data/test/dummy/public/404.html +58 -0
- data/test/dummy/public/422.html +58 -0
- data/test/dummy/public/500.html +57 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/em_minitest_spec.rb +100 -0
- data/test/fixtures/sync_auth_token_missing.yml +6 -0
- data/test/fixtures/sync_erb.yml +7 -0
- data/test/fixtures/sync_faye.yml +7 -0
- data/test/fixtures/sync_pusher.yml +8 -0
- data/test/models/group.rb +3 -0
- data/test/models/project.rb +2 -0
- data/test/models/todo.rb +8 -0
- data/test/models/user.rb +82 -0
- data/test/sync/abstract_controller.rb +3 -0
- data/test/sync/action_test.rb +82 -0
- data/test/sync/channel_test.rb +15 -0
- data/test/sync/config_test.rb +25 -0
- data/test/sync/erb_tracker_test.rb +72 -0
- data/test/sync/faye_extension_test.rb +87 -0
- data/test/sync/message_test.rb +159 -0
- data/test/sync/model_test.rb +315 -0
- data/test/sync/partial_creator_test.rb +35 -0
- data/test/sync/partial_test.rb +107 -0
- data/test/sync/protected_attributes_test.rb +39 -0
- data/test/sync/reactor_test.rb +18 -0
- data/test/sync/refetch_model_test.rb +26 -0
- data/test/sync/refetch_partial_creator_test.rb +16 -0
- data/test/sync/refetch_partial_test.rb +74 -0
- data/test/sync/renderer_test.rb +19 -0
- data/test/sync/resource_test.rb +181 -0
- data/test/sync/scope_definition_test.rb +39 -0
- data/test/sync/scope_test.rb +113 -0
- data/test/test_helper.rb +66 -0
- data/test/travis/sync.ru +14 -0
- data/test/travis/sync.yml +21 -0
- metadata +317 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>The page you were looking for doesn't exist (404)</title>
|
5
|
+
<style>
|
6
|
+
body {
|
7
|
+
background-color: #EFEFEF;
|
8
|
+
color: #2E2F30;
|
9
|
+
text-align: center;
|
10
|
+
font-family: arial, sans-serif;
|
11
|
+
}
|
12
|
+
|
13
|
+
div.dialog {
|
14
|
+
width: 25em;
|
15
|
+
margin: 4em auto 0 auto;
|
16
|
+
border: 1px solid #CCC;
|
17
|
+
border-right-color: #999;
|
18
|
+
border-left-color: #999;
|
19
|
+
border-bottom-color: #BBB;
|
20
|
+
border-top: #B00100 solid 4px;
|
21
|
+
border-top-left-radius: 9px;
|
22
|
+
border-top-right-radius: 9px;
|
23
|
+
background-color: white;
|
24
|
+
padding: 7px 4em 0 4em;
|
25
|
+
}
|
26
|
+
|
27
|
+
h1 {
|
28
|
+
font-size: 100%;
|
29
|
+
color: #730E15;
|
30
|
+
line-height: 1.5em;
|
31
|
+
}
|
32
|
+
|
33
|
+
body > p {
|
34
|
+
width: 33em;
|
35
|
+
margin: 0 auto 1em;
|
36
|
+
padding: 1em 0;
|
37
|
+
background-color: #F7F7F7;
|
38
|
+
border: 1px solid #CCC;
|
39
|
+
border-right-color: #999;
|
40
|
+
border-bottom-color: #999;
|
41
|
+
border-bottom-left-radius: 4px;
|
42
|
+
border-bottom-right-radius: 4px;
|
43
|
+
border-top-color: #DADADA;
|
44
|
+
color: #666;
|
45
|
+
box-shadow:0 3px 8px rgba(50, 50, 50, 0.17);
|
46
|
+
}
|
47
|
+
</style>
|
48
|
+
</head>
|
49
|
+
|
50
|
+
<body>
|
51
|
+
<!-- This file lives in public/404.html -->
|
52
|
+
<div class="dialog">
|
53
|
+
<h1>The page you were looking for doesn't exist.</h1>
|
54
|
+
<p>You may have mistyped the address or the page may have moved.</p>
|
55
|
+
</div>
|
56
|
+
<p>If you are the application owner check the logs for more information.</p>
|
57
|
+
</body>
|
58
|
+
</html>
|
@@ -0,0 +1,58 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>The change you wanted was rejected (422)</title>
|
5
|
+
<style>
|
6
|
+
body {
|
7
|
+
background-color: #EFEFEF;
|
8
|
+
color: #2E2F30;
|
9
|
+
text-align: center;
|
10
|
+
font-family: arial, sans-serif;
|
11
|
+
}
|
12
|
+
|
13
|
+
div.dialog {
|
14
|
+
width: 25em;
|
15
|
+
margin: 4em auto 0 auto;
|
16
|
+
border: 1px solid #CCC;
|
17
|
+
border-right-color: #999;
|
18
|
+
border-left-color: #999;
|
19
|
+
border-bottom-color: #BBB;
|
20
|
+
border-top: #B00100 solid 4px;
|
21
|
+
border-top-left-radius: 9px;
|
22
|
+
border-top-right-radius: 9px;
|
23
|
+
background-color: white;
|
24
|
+
padding: 7px 4em 0 4em;
|
25
|
+
}
|
26
|
+
|
27
|
+
h1 {
|
28
|
+
font-size: 100%;
|
29
|
+
color: #730E15;
|
30
|
+
line-height: 1.5em;
|
31
|
+
}
|
32
|
+
|
33
|
+
body > p {
|
34
|
+
width: 33em;
|
35
|
+
margin: 0 auto 1em;
|
36
|
+
padding: 1em 0;
|
37
|
+
background-color: #F7F7F7;
|
38
|
+
border: 1px solid #CCC;
|
39
|
+
border-right-color: #999;
|
40
|
+
border-bottom-color: #999;
|
41
|
+
border-bottom-left-radius: 4px;
|
42
|
+
border-bottom-right-radius: 4px;
|
43
|
+
border-top-color: #DADADA;
|
44
|
+
color: #666;
|
45
|
+
box-shadow:0 3px 8px rgba(50, 50, 50, 0.17);
|
46
|
+
}
|
47
|
+
</style>
|
48
|
+
</head>
|
49
|
+
|
50
|
+
<body>
|
51
|
+
<!-- This file lives in public/422.html -->
|
52
|
+
<div class="dialog">
|
53
|
+
<h1>The change you wanted was rejected.</h1>
|
54
|
+
<p>Maybe you tried to change something you didn't have access to.</p>
|
55
|
+
</div>
|
56
|
+
<p>If you are the application owner check the logs for more information.</p>
|
57
|
+
</body>
|
58
|
+
</html>
|
@@ -0,0 +1,57 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>We're sorry, but something went wrong (500)</title>
|
5
|
+
<style>
|
6
|
+
body {
|
7
|
+
background-color: #EFEFEF;
|
8
|
+
color: #2E2F30;
|
9
|
+
text-align: center;
|
10
|
+
font-family: arial, sans-serif;
|
11
|
+
}
|
12
|
+
|
13
|
+
div.dialog {
|
14
|
+
width: 25em;
|
15
|
+
margin: 4em auto 0 auto;
|
16
|
+
border: 1px solid #CCC;
|
17
|
+
border-right-color: #999;
|
18
|
+
border-left-color: #999;
|
19
|
+
border-bottom-color: #BBB;
|
20
|
+
border-top: #B00100 solid 4px;
|
21
|
+
border-top-left-radius: 9px;
|
22
|
+
border-top-right-radius: 9px;
|
23
|
+
background-color: white;
|
24
|
+
padding: 7px 4em 0 4em;
|
25
|
+
}
|
26
|
+
|
27
|
+
h1 {
|
28
|
+
font-size: 100%;
|
29
|
+
color: #730E15;
|
30
|
+
line-height: 1.5em;
|
31
|
+
}
|
32
|
+
|
33
|
+
body > p {
|
34
|
+
width: 33em;
|
35
|
+
margin: 0 auto 1em;
|
36
|
+
padding: 1em 0;
|
37
|
+
background-color: #F7F7F7;
|
38
|
+
border: 1px solid #CCC;
|
39
|
+
border-right-color: #999;
|
40
|
+
border-bottom-color: #999;
|
41
|
+
border-bottom-left-radius: 4px;
|
42
|
+
border-bottom-right-radius: 4px;
|
43
|
+
border-top-color: #DADADA;
|
44
|
+
color: #666;
|
45
|
+
box-shadow:0 3px 8px rgba(50, 50, 50, 0.17);
|
46
|
+
}
|
47
|
+
</style>
|
48
|
+
</head>
|
49
|
+
|
50
|
+
<body>
|
51
|
+
<!-- This file lives in public/500.html -->
|
52
|
+
<div class="dialog">
|
53
|
+
<h1>We're sorry, but something went wrong.</h1>
|
54
|
+
</div>
|
55
|
+
<p>If you are the application owner check the logs for more information.</p>
|
56
|
+
</body>
|
57
|
+
</html>
|
File without changes
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# Copyright (c) 2011 Pete Higgins
|
2
|
+
# https://github.com/phiggins/em-minitest-spec
|
3
|
+
#
|
4
|
+
require 'eventmachine'
|
5
|
+
|
6
|
+
module EM # :nodoc:
|
7
|
+
module MiniTest # :nodoc:
|
8
|
+
module Spec # :nodoc
|
9
|
+
VERSION = '1.1.0' # :nodoc:
|
10
|
+
|
11
|
+
##
|
12
|
+
# +wait+ indicates that the spec is not expected to be completed when
|
13
|
+
# the block is finished running. A call to +done+ is required when using
|
14
|
+
# +wait+.
|
15
|
+
#
|
16
|
+
# # setup my spec to use EM::MiniTest::Spec
|
17
|
+
# describe MyClass do
|
18
|
+
# include EM::MiniTest::Spec
|
19
|
+
#
|
20
|
+
# # The call to defer will return immediately, so the spec code
|
21
|
+
# # needs to keep running until callback is called.
|
22
|
+
# it "does some async things" do
|
23
|
+
# defer_me = lambda do
|
24
|
+
# # some async stuff
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# callback = lambda do
|
28
|
+
# done!
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# EM.defer defer_me, callback
|
32
|
+
#
|
33
|
+
# wait!
|
34
|
+
# end
|
35
|
+
def wait
|
36
|
+
@wait = true
|
37
|
+
end
|
38
|
+
alias wait! wait
|
39
|
+
|
40
|
+
##
|
41
|
+
# Indicates that an async spec is finished. See +wait+ for example usage.
|
42
|
+
def done
|
43
|
+
EM.cancel_timer(@timeout)
|
44
|
+
EM.stop
|
45
|
+
end
|
46
|
+
alias done! done
|
47
|
+
|
48
|
+
##
|
49
|
+
# A helper method for the use case of waiting for some operation to
|
50
|
+
# complete that is not necessarily under the control of the spec code.
|
51
|
+
#
|
52
|
+
# # These are exactly equivalent
|
53
|
+
# it "waits with the helper" do
|
54
|
+
# wait_for do
|
55
|
+
# assert true
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# it "waits manually" do
|
60
|
+
# EM.next_tick do
|
61
|
+
# assert true
|
62
|
+
# done!
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# wait!
|
66
|
+
# end
|
67
|
+
def wait_for
|
68
|
+
EM.next_tick do
|
69
|
+
yield
|
70
|
+
done!
|
71
|
+
end
|
72
|
+
|
73
|
+
wait!
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.included base # :nodoc:
|
77
|
+
base.extend(ClassMethods)
|
78
|
+
end
|
79
|
+
|
80
|
+
module ClassMethods # :nodoc:
|
81
|
+
def it *args, &block # :nodoc:
|
82
|
+
return super unless block_given?
|
83
|
+
|
84
|
+
super do
|
85
|
+
@wait = false
|
86
|
+
|
87
|
+
EM.run do
|
88
|
+
@timeout = EM.add_timer(0.1) do
|
89
|
+
flunk "test timed out!"
|
90
|
+
end
|
91
|
+
|
92
|
+
instance_eval(&block)
|
93
|
+
done! unless @wait
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/test/models/todo.rb
ADDED
data/test/models/user.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
class User < ActiveRecord::Base
|
2
|
+
belongs_to :group
|
3
|
+
|
4
|
+
def id
|
5
|
+
1
|
6
|
+
end
|
7
|
+
|
8
|
+
sync :all
|
9
|
+
sync_scope :cool, -> { where(cool: true) }
|
10
|
+
sync_scope :in_group, ->(group) { where(group_id: group.id)}
|
11
|
+
sync_scope :with_group_id, ->(group_id) { where(group_id: group_id)}
|
12
|
+
sync_scope :with_min_age_in_group, ->(age, group_id) { where(group_id: group_id).where(["age >= ?", age])}
|
13
|
+
end
|
14
|
+
|
15
|
+
class UserWithoutScopes < ActiveRecord::Base
|
16
|
+
self.table_name = :users
|
17
|
+
|
18
|
+
def id
|
19
|
+
1
|
20
|
+
end
|
21
|
+
|
22
|
+
sync :all
|
23
|
+
end
|
24
|
+
|
25
|
+
class UserWithDefaultScope < ActiveRecord::Base
|
26
|
+
self.table_name = :users
|
27
|
+
belongs_to :group
|
28
|
+
|
29
|
+
sync :all, default_scope: :group
|
30
|
+
end
|
31
|
+
|
32
|
+
class UserWithSimpleScope < ActiveRecord::Base
|
33
|
+
self.table_name = :users
|
34
|
+
|
35
|
+
sync :all
|
36
|
+
sync_scope :old, -> { where(["users.age >= ?", 90]) }
|
37
|
+
end
|
38
|
+
|
39
|
+
class UserWithAdvancedScope < ActiveRecord::Base
|
40
|
+
self.table_name = :users
|
41
|
+
belongs_to :group
|
42
|
+
|
43
|
+
sync :all
|
44
|
+
sync_scope :in_group, ->(group) { where(group_id: group.id) }
|
45
|
+
end
|
46
|
+
|
47
|
+
class UserTouchingGroup < ActiveRecord::Base
|
48
|
+
self.table_name = :users
|
49
|
+
belongs_to :group
|
50
|
+
|
51
|
+
sync :all
|
52
|
+
sync_touch :group
|
53
|
+
end
|
54
|
+
|
55
|
+
class UserJustTouchingGroup < ActiveRecord::Base
|
56
|
+
self.table_name = :users
|
57
|
+
belongs_to :group
|
58
|
+
|
59
|
+
sync_touch :group
|
60
|
+
end
|
61
|
+
|
62
|
+
class UserTouchingGroupAndProject < ActiveRecord::Base
|
63
|
+
self.table_name = :users
|
64
|
+
belongs_to :group
|
65
|
+
belongs_to :project
|
66
|
+
|
67
|
+
sync :all
|
68
|
+
sync_touch :group, :project
|
69
|
+
end
|
70
|
+
|
71
|
+
# Setup test user with protected attributes (only allow cool)
|
72
|
+
# if Rails < 4 or Rails > 4 with gem protected_attributes
|
73
|
+
class UserWithProtectedAttributes < ActiveRecord::Base
|
74
|
+
attr_accessible :cool if Rails.version < "4"
|
75
|
+
self.table_name = :users
|
76
|
+
sync :all
|
77
|
+
end
|
78
|
+
|
79
|
+
if Rails.version < "4"
|
80
|
+
UserWithProtectedAttributes.mass_assignment_sanitizer = :strict
|
81
|
+
end
|
82
|
+
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
require 'mocha/setup'
|
3
|
+
require 'rails/all'
|
4
|
+
|
5
|
+
describe RenderSync::Action do
|
6
|
+
include TestHelper
|
7
|
+
|
8
|
+
describe '#initialize' do
|
9
|
+
it 'sets instance variables on initialize without scopes' do
|
10
|
+
action = RenderSync::Action.new("record", :new)
|
11
|
+
assert_equal "record", action.record
|
12
|
+
assert_equal :new, action.name
|
13
|
+
assert_equal [], action.scope
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'sets instance variables on initialize with scope' do
|
17
|
+
action = RenderSync::Action.new("record", :new, scope: "scope")
|
18
|
+
assert_equal ["scope"], action.scope
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'sets instance variables on initialize with default_scope' do
|
22
|
+
action = RenderSync::Action.new("record", :new, default_scope: "scope")
|
23
|
+
assert_equal ["scope"], action.scope
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'sets instance variables on initialize with scope and default_scope' do
|
27
|
+
action = RenderSync::Action.new("record", :new, scope: "scope", default_scope: "default")
|
28
|
+
assert_equal ["default", "scope"], action.scope
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'sets instance variables on initialize with default_scope' do
|
32
|
+
user = User.create!
|
33
|
+
scope = User.cool
|
34
|
+
action = RenderSync::Action.new(user, :new, scope: scope, default_scope: :group)
|
35
|
+
assert_equal [:group, scope], action.scope
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
it 'sets instance variables on initialize with nested scopes' do
|
40
|
+
action = RenderSync::Action.new("record", :new, scope: ["nested", "scopes"], default_scope: ["my", "cool"])
|
41
|
+
assert_equal [["my", "cool"], ["nested", "scopes"]], action.scope
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#perform" do
|
46
|
+
|
47
|
+
it 'calls different actions without scope' do
|
48
|
+
record = User.new name: "Foo"
|
49
|
+
|
50
|
+
action = RenderSync::Action.new(record, :new)
|
51
|
+
action.expects(:sync_new).with(record, scope: [])
|
52
|
+
action.perform
|
53
|
+
|
54
|
+
action = RenderSync::Action.new(record, :update)
|
55
|
+
action.expects(:sync_update).with(record, scope: [])
|
56
|
+
action.perform
|
57
|
+
|
58
|
+
action = RenderSync::Action.new(record, :destroy)
|
59
|
+
action.expects(:sync_destroy).with(record, scope: [])
|
60
|
+
action.perform
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'calls different actions with scope' do
|
64
|
+
record = User.new name: "Foo"
|
65
|
+
|
66
|
+
action = RenderSync::Action.new(record, :new, scope: "scope", default_scope: "default")
|
67
|
+
action.expects(:sync_new).with(record, scope: ["default", "scope"])
|
68
|
+
action.perform
|
69
|
+
|
70
|
+
action = RenderSync::Action.new(record, :update, scope: "scope", default_scope: "default")
|
71
|
+
action.expects(:sync_update).with(record, scope: ["default", "scope"])
|
72
|
+
action.perform
|
73
|
+
|
74
|
+
action = RenderSync::Action.new(record, :destroy, scope: "scope", default_scope: "default")
|
75
|
+
action.expects(:sync_destroy).with(record, scope: ["default", "scope"])
|
76
|
+
action.perform
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|