fast_serializer_ruby 0.2.1 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.travis.yml +4 -2
- data/CODE_OF_CONDUCT.md +1 -1
- data/Gemfile +18 -1
- data/Gemfile.lock +53 -42
- data/LICENSE.txt +1 -1
- data/README.md +45 -33
- data/fast_serializer.gemspec +6 -13
- data/lib/fast_serializer.rb +2 -1
- data/lib/fast_serializer/configuration.rb +14 -2
- data/lib/fast_serializer/json_model/array.rb +6 -2
- data/lib/fast_serializer/json_model/attribute.rb +109 -15
- data/lib/fast_serializer/json_model/has_many_relationship.rb +5 -3
- data/lib/fast_serializer/json_model/has_one_relationship.rb +4 -1
- data/lib/fast_serializer/json_model/node.rb +11 -3
- data/lib/fast_serializer/json_model/object.rb +16 -6
- data/lib/fast_serializer/json_model/relationship.rb +31 -4
- data/lib/fast_serializer/schema.rb +107 -105
- data/lib/fast_serializer/schema/mixin.rb +71 -0
- data/lib/fast_serializer/utils.rb +17 -0
- data/lib/fast_serializer/version.rb +1 -1
- metadata +11 -134
@@ -3,7 +3,11 @@
|
|
3
3
|
module FastSerializer
|
4
4
|
module JsonModel
|
5
5
|
class Array < Relationship
|
6
|
-
|
6
|
+
# @param resource [Object]
|
7
|
+
# @param params [Hash]
|
8
|
+
# @param context [Hash]
|
9
|
+
# @return [Array]
|
10
|
+
def serialize(resources, params, context)
|
7
11
|
return if resources.nil?
|
8
12
|
|
9
13
|
if @serializer_klass
|
@@ -13,7 +17,7 @@ module FastSerializer
|
|
13
17
|
end
|
14
18
|
end
|
15
19
|
|
16
|
-
def included?(
|
20
|
+
def included?(*)
|
17
21
|
true
|
18
22
|
end
|
19
23
|
end
|
@@ -3,32 +3,126 @@
|
|
3
3
|
module FastSerializer
|
4
4
|
module JsonModel
|
5
5
|
class Attribute < Node
|
6
|
-
|
7
|
-
|
6
|
+
attr_accessor :mixin,
|
7
|
+
:method_name,
|
8
|
+
:method_arity,
|
9
|
+
:cond,
|
10
|
+
:cond_arity,
|
11
|
+
:cond_method_name,
|
12
|
+
:injected
|
8
13
|
|
9
|
-
|
14
|
+
def initialize(*)
|
15
|
+
super
|
10
16
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
17
|
+
@mixin = nil
|
18
|
+
@method_name = nil
|
19
|
+
@injected = false
|
20
|
+
@cond_method_name = nil
|
21
|
+
@cond = nil
|
22
|
+
@cond = @opts[:if] || @opts[:unless] || @cond
|
16
23
|
|
17
|
-
|
18
|
-
|
19
|
-
|
24
|
+
init_with_proc if method.is_a?(Proc)
|
25
|
+
init_with_cond if !cond.nil? && cond.is_a?(Proc)
|
26
|
+
end
|
27
|
+
|
28
|
+
def injectable?
|
29
|
+
!mixin.nil?
|
20
30
|
end
|
21
31
|
|
22
|
-
def
|
23
|
-
|
32
|
+
def inject(context)
|
33
|
+
context.include(mixin)
|
34
|
+
self.injected = true
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param resource [Object]
|
38
|
+
# @param params [Hash]
|
39
|
+
# @param context [Hash]
|
40
|
+
# @return [Object]
|
41
|
+
def serialize(resource, params, context)
|
42
|
+
can_execute_on_mixin = injected && !method_name.nil? && !context.nil?
|
24
43
|
|
25
|
-
|
44
|
+
val = if can_execute_on_mixin
|
45
|
+
call_method_on_context(context, method_name, method_arity, resource, params)
|
46
|
+
elsif method.is_a?(Proc)
|
47
|
+
call_proc_binding_to_context(context, method, method_arity, resource, params)
|
48
|
+
else
|
49
|
+
resource.public_send(method)
|
50
|
+
end
|
51
|
+
|
52
|
+
val.freeze
|
53
|
+
|
54
|
+
val
|
55
|
+
end
|
56
|
+
|
57
|
+
# @param resource [Object]
|
58
|
+
# @param params [Hash]
|
59
|
+
# @param context [Hash]
|
60
|
+
# @return [Boolean]
|
61
|
+
def included?(resource, params, context)
|
62
|
+
return true if cond.nil?
|
63
|
+
|
64
|
+
can_execute_on_mixin = injected && !cond_method_name.nil? && !context.nil?
|
65
|
+
|
66
|
+
res = if can_execute_on_mixin
|
67
|
+
call_method_on_context(context, cond_method_name, cond_arity, resource, params)
|
68
|
+
elsif cond.is_a?(Proc)
|
69
|
+
call_proc_binding_to_context(context, cond, cond_arity, resource, params)
|
70
|
+
else
|
71
|
+
context.public_send(cond)
|
72
|
+
end
|
26
73
|
|
27
|
-
res = context.instance_exec(resource, params, &cond)
|
28
74
|
res = !res unless @opts[:unless].nil?
|
29
75
|
|
30
76
|
res
|
31
77
|
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def init_with_cond
|
82
|
+
@cond_method_name = "__#{key}_cond__"
|
83
|
+
@cond_arity = cond.arity.abs
|
84
|
+
@mixin ||= Module.new
|
85
|
+
|
86
|
+
if RUBY_VERSION <= '2.5.0'
|
87
|
+
@mixin.redefine_method @cond_method_name, &cond
|
88
|
+
else
|
89
|
+
@mixin.define_method @cond_method_name, &cond
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def init_with_proc
|
94
|
+
@method_name = "__#{key}__"
|
95
|
+
@method_arity = method.arity.abs
|
96
|
+
@mixin = Module.new
|
97
|
+
|
98
|
+
if RUBY_VERSION <= '2.5.0'
|
99
|
+
@mixin.redefine_method @method_name, &method
|
100
|
+
else
|
101
|
+
@mixin.define_method @method_name, &method
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def call_proc_binding_to_context(context, prc, arity, resource, params)
|
106
|
+
case arity
|
107
|
+
when 1
|
108
|
+
context.instance_exec(resource, &prc)
|
109
|
+
when 2
|
110
|
+
context.instance_exec(resource, params, &prc)
|
111
|
+
when 0
|
112
|
+
context.instance_exec(&prc)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def call_method_on_context(context, method_name, arity, resource, params)
|
117
|
+
case arity
|
118
|
+
when 0
|
119
|
+
context.public_send(method_name)
|
120
|
+
when 1
|
121
|
+
context.public_send(method_name, resource)
|
122
|
+
when 2
|
123
|
+
context.public_send(method_name, resource, params)
|
124
|
+
end
|
125
|
+
end
|
32
126
|
end
|
33
127
|
end
|
34
128
|
end
|
@@ -3,16 +3,18 @@
|
|
3
3
|
module FastSerializer
|
4
4
|
module JsonModel
|
5
5
|
class HasManyRelationship < Relationship
|
6
|
-
|
6
|
+
# @param resource [Object]
|
7
|
+
# @param params [Hash]
|
8
|
+
# @return [Array<Hash>]
|
9
|
+
def serialize(resource, params, _context)
|
7
10
|
collection = resource.public_send(method)
|
8
11
|
return if collection.nil?
|
9
12
|
|
10
13
|
if @serializer_klass
|
11
14
|
@serializer_klass.new(collection, params).serializable_hash
|
12
15
|
elsif @schema
|
13
|
-
collection.map { |
|
16
|
+
collection.map { |entry| @schema.serialize_resource(entry, params) }
|
14
17
|
end
|
15
|
-
|
16
18
|
end
|
17
19
|
end
|
18
20
|
end
|
@@ -3,7 +3,10 @@
|
|
3
3
|
module FastSerializer
|
4
4
|
module JsonModel
|
5
5
|
class HasOneRelationship < Relationship
|
6
|
-
|
6
|
+
# @param resource [Object]
|
7
|
+
# @param params [Hash]
|
8
|
+
# @return [Hash]
|
9
|
+
def serialize(resource, params, _)
|
7
10
|
relation = resource.public_send(method)
|
8
11
|
|
9
12
|
if @serializer_klass
|
@@ -5,17 +5,25 @@ module FastSerializer
|
|
5
5
|
class Node
|
6
6
|
attr_accessor :key, :method, :context
|
7
7
|
|
8
|
+
# @param key [String]
|
9
|
+
# @param method [String]
|
10
|
+
# @param opts [Hash]
|
8
11
|
def initialize(key: nil, method: nil, opts: {}, **_)
|
9
|
-
@key = key
|
12
|
+
@key = key&.to_sym
|
10
13
|
@method = method || key
|
11
14
|
@opts = opts || {}
|
12
15
|
end
|
13
16
|
|
14
|
-
|
17
|
+
# @return [Boolean]
|
18
|
+
def injectable?
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
def serialize(_resource, _params, _context = nil)
|
15
23
|
raise NotImplementedError
|
16
24
|
end
|
17
25
|
|
18
|
-
def included?(_resource, _params
|
26
|
+
def included?(_resource, _params, _context = nil)
|
19
27
|
raise NotImplementedError
|
20
28
|
end
|
21
29
|
end
|
@@ -5,27 +5,37 @@ module FastSerializer
|
|
5
5
|
class Object < Node
|
6
6
|
attr_accessor :attributes
|
7
7
|
|
8
|
-
def initialize(
|
8
|
+
def initialize(args = {})
|
9
9
|
super
|
10
10
|
@attributes = {}
|
11
11
|
end
|
12
12
|
|
13
|
+
# @param attribute [FastSerializer::JsonModel::Node]
|
13
14
|
def add_attribute(attribute)
|
14
15
|
attributes[attribute.key] = attribute
|
15
16
|
end
|
16
17
|
|
17
|
-
|
18
|
+
# @param resource [Object]
|
19
|
+
# @param params [Hash]
|
20
|
+
# @param context [Hash]
|
21
|
+
# @return [Hash]
|
22
|
+
def serialize(resource, params, context)
|
18
23
|
return if resource.nil?
|
19
24
|
|
20
|
-
|
21
|
-
|
25
|
+
result = {}
|
26
|
+
|
27
|
+
attributes.each do |_, attribute|
|
28
|
+
next unless attribute.included?(resource, params, context)
|
22
29
|
|
23
30
|
val = attribute.serialize(resource, params, context)
|
24
|
-
|
31
|
+
result[attribute.key] = val
|
25
32
|
end
|
33
|
+
|
34
|
+
result
|
26
35
|
end
|
27
36
|
|
28
|
-
|
37
|
+
# @return [Boolean]
|
38
|
+
def included?(*)
|
29
39
|
true
|
30
40
|
end
|
31
41
|
end
|
@@ -5,22 +5,49 @@ module FastSerializer
|
|
5
5
|
class Relationship < Attribute
|
6
6
|
attr_accessor :serialization_schema
|
7
7
|
|
8
|
-
|
8
|
+
# @param serializer [FastSerializer::Schema::Mixin]
|
9
|
+
# @param schema [FastSerializer::Schema]
|
10
|
+
def initialize(serializer: nil, schema: nil, **)
|
9
11
|
super
|
12
|
+
|
10
13
|
@serializer_klass = serializer
|
11
14
|
@schema = schema
|
12
15
|
|
13
|
-
|
16
|
+
if @serializer_klass.nil? && @schema.nil?
|
17
|
+
raise ArgumentError, 'must provide serializer or schema'
|
18
|
+
end
|
14
19
|
end
|
15
20
|
|
21
|
+
# @param resource [Object]
|
22
|
+
# @param params [Hash]
|
23
|
+
# @param context [Hash]
|
24
|
+
# @return [Boolean]
|
16
25
|
def included?(resource, params, context)
|
17
|
-
super
|
26
|
+
super && include_relation?(params)
|
18
27
|
end
|
19
28
|
|
29
|
+
# @param params [Hash]
|
30
|
+
# @return [Boolean]
|
20
31
|
def include_relation?(params)
|
32
|
+
include?(params) && !exclude?(params)
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param params [Hash]
|
36
|
+
# @return [Boolean]
|
37
|
+
def exclude?(params)
|
38
|
+
return false if params[:exclude].nil?
|
39
|
+
return false if params[:exclude].empty?
|
40
|
+
|
41
|
+
params[:exclude_index].key?(key)
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param params [Hash]
|
45
|
+
# @return [Boolean]
|
46
|
+
def include?(params)
|
21
47
|
return true if params[:include].nil?
|
48
|
+
return false if params[:include].empty?
|
22
49
|
|
23
|
-
params[:
|
50
|
+
params[:include_index].key?(key)
|
24
51
|
end
|
25
52
|
end
|
26
53
|
end
|
@@ -3,91 +3,125 @@
|
|
3
3
|
require 'forwardable'
|
4
4
|
|
5
5
|
module FastSerializer
|
6
|
-
|
7
6
|
class Schema
|
8
|
-
|
9
|
-
|
10
|
-
def initialize(params = {})
|
11
|
-
@root
|
12
|
-
@
|
13
|
-
@
|
14
|
-
@params
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
7
|
+
attr_reader :_root, :serialization_schema, :params, :strict
|
8
|
+
|
9
|
+
def initialize(params = {}, root = nil, strict = nil)
|
10
|
+
@root = root
|
11
|
+
@strict = strict || FastSerializer.config.strict
|
12
|
+
@serialization_schema = FastSerializer::JsonModel::Object.new
|
13
|
+
@params = FastSerializer::Utils.symbolize_keys(params || {})
|
14
|
+
@params[:self] = self
|
15
|
+
@params[:include_index] = {}
|
16
|
+
@params[:exclude_index] = {}
|
17
|
+
|
18
|
+
self.include = @params.delete(:include)
|
19
|
+
self.exclude = @params.delete(:exclude)
|
20
|
+
end
|
21
|
+
|
22
|
+
def include=(list)
|
23
|
+
return unless list
|
24
|
+
return if list.empty?
|
25
|
+
|
26
|
+
@params[:include] = list.map(&:to_sym)
|
27
|
+
@params[:include_index] = @params[:include].map { |key| [key, nil] }.to_h
|
21
28
|
end
|
22
29
|
|
23
|
-
|
30
|
+
def exclude=(list)
|
31
|
+
return unless list
|
32
|
+
return if list.empty?
|
33
|
+
|
34
|
+
@params[:exclude] = list.map(&:to_sym)
|
35
|
+
@params[:exclude_index] = @params[:exclude].map { |key| [key, nil] }.to_h
|
36
|
+
end
|
37
|
+
|
38
|
+
# Defines a list of attributes for serialization
|
39
|
+
#
|
40
|
+
# @param attribute_names [Array<String, Symbol>] a list of attributes to serialize
|
41
|
+
# each of these attributes value is fetched calling a corresponding method from a resource instance
|
42
|
+
# passed to the serializer
|
24
43
|
def attributes(*attribute_names)
|
25
44
|
attribute_names.each do |attribute_name|
|
26
|
-
serialization_schema.add_attribute
|
27
|
-
key:
|
28
|
-
method: attribute_name
|
45
|
+
serialization_schema.add_attribute(
|
46
|
+
JsonModel::Attribute.new(key: attribute_name, method: attribute_name)
|
29
47
|
)
|
30
48
|
end
|
31
49
|
end
|
32
50
|
|
33
|
-
#
|
34
|
-
#
|
35
|
-
# @param [
|
51
|
+
# Defines an attribute for serialization
|
52
|
+
#
|
53
|
+
# @param attribute_name [String, Symbol] an attribute name
|
54
|
+
# @param opts [Hash] attribute options
|
55
|
+
# @option opts [Proc] :if conditional clause. accepts a proc/lambda which has to return a boolean
|
56
|
+
# @option opts [Proc] :unless (see opts:if)
|
57
|
+
# @param block [Proc] result is used as the attribute value
|
58
|
+
#
|
36
59
|
def attribute(attribute_name, opts = {}, &block)
|
37
|
-
serialization_schema.add_attribute
|
38
|
-
|
39
|
-
|
40
|
-
|
60
|
+
serialization_schema.add_attribute(
|
61
|
+
JsonModel::Attribute.new(
|
62
|
+
key: attribute_name,
|
63
|
+
method: block,
|
64
|
+
opts: opts
|
65
|
+
)
|
41
66
|
)
|
42
67
|
end
|
43
68
|
|
44
|
-
#
|
45
|
-
#
|
69
|
+
# Defines an attribute for serialization
|
70
|
+
#
|
71
|
+
# @param attribute_name [String, Symbol] an attribute name
|
72
|
+
# @param opts [Hash] attribute options
|
73
|
+
# @option opts [Proc] :if conditional clause. accepts a proc/lambda which has to return a boolean
|
74
|
+
# @option opts [Proc] :unless (see opts:if)
|
75
|
+
# @option opts [FastSerializer::Schema::Mixin, nil] :serializer a serializer class with injected module or a inherited class
|
76
|
+
# @option opts [FastSerializer::Schema] :schema
|
77
|
+
#
|
46
78
|
def has_one(attribute_name, opts = {})
|
47
79
|
serialization_schema.add_attribute JsonModel::HasOneRelationship.new(
|
48
|
-
key:
|
49
|
-
method:
|
50
|
-
opts:
|
51
|
-
schema:
|
80
|
+
key: opts.delete(:key) || attribute_name,
|
81
|
+
method: opts.delete(:method) || attribute_name,
|
82
|
+
opts: opts,
|
83
|
+
schema: opts.delete(:schema),
|
52
84
|
serializer: opts.delete(:serializer)
|
53
85
|
)
|
54
86
|
end
|
55
87
|
|
56
88
|
alias belongs_to has_one
|
57
89
|
|
58
|
-
# @param [String]
|
59
|
-
# @param [Hash]
|
90
|
+
# @param attribute_name [String]
|
91
|
+
# @param opts [Hash] attribute options
|
60
92
|
def has_many(attribute_name, opts = {})
|
61
|
-
serialization_schema.add_attribute
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
93
|
+
serialization_schema.add_attribute(
|
94
|
+
JsonModel::HasManyRelationship.new(
|
95
|
+
key: opts.delete(:key) || attribute_name,
|
96
|
+
method: opts.delete(:method) || attribute_name,
|
97
|
+
opts: opts,
|
98
|
+
schema: opts.delete(:schema),
|
99
|
+
serializer: opts.delete(:serializer)
|
100
|
+
)
|
67
101
|
)
|
68
102
|
end
|
69
103
|
|
70
104
|
# @param [String] attribute_name
|
71
105
|
# @param [Hash] opts - attribute options
|
72
106
|
def list(attribute_name, opts = {})
|
73
|
-
serialization_schema.add_attribute
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
107
|
+
serialization_schema.add_attribute(
|
108
|
+
JsonModel::Array.new(
|
109
|
+
key: attribute_name,
|
110
|
+
method: attribute_name,
|
111
|
+
opts: opts,
|
112
|
+
schema: opts.delete(:schema),
|
113
|
+
serializer: opts.delete(:serializer)
|
114
|
+
)
|
79
115
|
)
|
80
116
|
end
|
81
117
|
|
82
118
|
# @param [String] root_key - a key under which serialization result is nested
|
83
119
|
def root(root_key)
|
84
|
-
|
120
|
+
@_root = root_key
|
85
121
|
end
|
86
122
|
|
87
123
|
def deep_copy
|
88
|
-
schema
|
89
|
-
schema.params = params
|
90
|
-
schema._root = _root
|
124
|
+
schema = FastSerializer::Schema.new(params, _root, strict)
|
91
125
|
|
92
126
|
serialization_schema.attributes.each do |key, attribute|
|
93
127
|
schema.serialization_schema.attributes[key] = attribute
|
@@ -97,20 +131,33 @@ module FastSerializer
|
|
97
131
|
end
|
98
132
|
|
99
133
|
def serialize_resource(resource, params = {}, context = self)
|
100
|
-
|
134
|
+
Utils.ref_merge(self.params, params)
|
135
|
+
_params_dup = FastSerializer::Utils.symbolize_keys(self.params)
|
101
136
|
meta = _params_dup.delete(:meta)
|
102
137
|
|
103
|
-
is_collection =
|
138
|
+
is_collection = if _params_dup.key?(:is_collection)
|
139
|
+
_params_dup.delete(:is_collection)
|
140
|
+
params.delete(:is_collection)
|
141
|
+
else
|
142
|
+
resource.respond_to?(:each) && !resource.respond_to?(:each_pair)
|
143
|
+
end
|
144
|
+
|
145
|
+
root = (_root || _params_dup.delete(:root))
|
146
|
+
|
147
|
+
res = if is_collection
|
104
148
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
149
|
+
if !context.is_a?(self.class)
|
150
|
+
# need to bind context
|
151
|
+
resource.map { |entry| context.class.new(entry, _params_dup).serializable_hash }
|
152
|
+
else
|
153
|
+
JsonModel::Array.new(schema: serialization_schema).serialize(resource, _params_dup, context)
|
154
|
+
end
|
110
155
|
|
111
|
-
|
112
|
-
|
113
|
-
|
156
|
+
else
|
157
|
+
serialization_schema.serialize(resource, _params_dup, context)
|
158
|
+
end
|
159
|
+
|
160
|
+
res = { root => res } if root && !root.empty?
|
114
161
|
|
115
162
|
res[:meta] = meta if res.is_a?(Hash) && meta
|
116
163
|
|
@@ -118,52 +165,7 @@ module FastSerializer
|
|
118
165
|
end
|
119
166
|
|
120
167
|
def serialize_resource_to_json(resource, params = {}, context = self)
|
121
|
-
FastSerializer.config.coder.dump(serialize_resource(resource, params))
|
122
|
-
end
|
123
|
-
|
124
|
-
module Mixin
|
125
|
-
|
126
|
-
module ClassMethods
|
127
|
-
attr_accessor :__schema__
|
128
|
-
|
129
|
-
def inherited(subclass)
|
130
|
-
subclass.__schema__ = self.__schema__.deep_copy
|
131
|
-
end
|
132
|
-
|
133
|
-
def method_missing(method, *args, &block)
|
134
|
-
if __schema__.respond_to?(method)
|
135
|
-
__schema__.public_send(method, *args, &block)
|
136
|
-
else
|
137
|
-
super
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
module InstanceMethods
|
143
|
-
attr_accessor :resource, :params
|
144
|
-
|
145
|
-
def initialize(resource, params = {})
|
146
|
-
self.resource = resource
|
147
|
-
self.params = params || {}
|
148
|
-
end
|
149
|
-
|
150
|
-
def serializable_hash
|
151
|
-
self.class.__schema__.serialize_resource(resource, params, self)
|
152
|
-
end
|
153
|
-
|
154
|
-
def serialized_json
|
155
|
-
self.class.__schema__.serialize_resource_to_json(resource, params, self)
|
156
|
-
end
|
157
|
-
|
158
|
-
alias as_json serializable_hash
|
159
|
-
alias to_json serialized_json
|
160
|
-
end
|
161
|
-
|
162
|
-
def self.included(base)
|
163
|
-
base.extend ClassMethods
|
164
|
-
base.include InstanceMethods
|
165
|
-
base.__schema__ = FastSerializer::Schema.new
|
166
|
-
end
|
168
|
+
FastSerializer.config.coder.dump(serialize_resource(resource, params, context))
|
167
169
|
end
|
168
170
|
end
|
169
171
|
end
|