grape 2.3.0 → 2.4.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 +4 -4
- data/CHANGELOG.md +29 -0
- data/CONTRIBUTING.md +1 -1
- data/README.md +36 -14
- data/UPGRADING.md +56 -1
- data/grape.gemspec +1 -1
- data/lib/grape/api/instance.rb +3 -2
- data/lib/grape/api.rb +43 -66
- data/lib/grape/cookies.rb +31 -25
- data/lib/grape/dsl/api.rb +0 -2
- data/lib/grape/dsl/headers.rb +1 -1
- data/lib/grape/dsl/helpers.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +6 -18
- data/lib/grape/dsl/parameters.rb +3 -3
- data/lib/grape/dsl/routing.rb +9 -1
- data/lib/grape/endpoint.rb +30 -33
- data/lib/grape/exceptions/conflicting_types.rb +11 -0
- data/lib/grape/exceptions/invalid_parameters.rb +11 -0
- data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
- data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
- data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +2 -5
- data/lib/grape/extensions/hash.rb +2 -1
- data/lib/grape/extensions/hashie/mash.rb +3 -5
- data/lib/grape/locale/en.yml +44 -44
- data/lib/grape/middleware/auth/base.rb +11 -32
- data/lib/grape/middleware/auth/dsl.rb +23 -29
- data/lib/grape/middleware/base.rb +30 -11
- data/lib/grape/middleware/error.rb +16 -24
- data/lib/grape/middleware/formatter.rb +38 -72
- data/lib/grape/middleware/stack.rb +26 -36
- data/lib/grape/middleware/versioner/accept_version_header.rb +1 -3
- data/lib/grape/middleware/versioner/base.rb +10 -18
- data/lib/grape/middleware/versioner/header.rb +1 -1
- data/lib/grape/middleware/versioner/param.rb +2 -3
- data/lib/grape/params_builder/base.rb +18 -0
- data/lib/grape/params_builder/hash.rb +11 -0
- data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
- data/lib/grape/params_builder/hashie_mash.rb +11 -0
- data/lib/grape/params_builder.rb +32 -0
- data/lib/grape/request.rb +161 -22
- data/lib/grape/router/route.rb +1 -1
- data/lib/grape/router.rb +25 -7
- data/lib/grape/validations/params_scope.rb +8 -3
- data/lib/grape/validations/validators/base.rb +2 -2
- data/lib/grape/validations/validators/except_values_validator.rb +1 -1
- data/lib/grape/validations/validators/presence_validator.rb +1 -1
- data/lib/grape/validations/validators/regexp_validator.rb +1 -1
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +13 -1
- metadata +18 -13
- data/lib/grape/error_formatter/jsonapi.rb +0 -7
- data/lib/grape/http/headers.rb +0 -56
- data/lib/grape/middleware/helpers.rb +0 -12
- data/lib/grape/parser/jsonapi.rb +0 -7
- data/lib/grape/util/lazy/object.rb +0 -45
data/lib/grape/router.rb
CHANGED
@@ -4,12 +4,30 @@ module Grape
|
|
4
4
|
class Router
|
5
5
|
attr_reader :map, :compiled
|
6
6
|
|
7
|
+
# Taken from Rails
|
8
|
+
# normalize_path("/foo") # => "/foo"
|
9
|
+
# normalize_path("/foo/") # => "/foo"
|
10
|
+
# normalize_path("foo") # => "/foo"
|
11
|
+
# normalize_path("") # => "/"
|
12
|
+
# normalize_path("/%ab") # => "/%AB"
|
13
|
+
# https://github.com/rails/rails/blob/00cc4ff0259c0185fe08baadaa40e63ea2534f6e/actionpack/lib/action_dispatch/journey/router/utils.rb#L19
|
7
14
|
def self.normalize_path(path)
|
15
|
+
return +'/' unless path
|
16
|
+
|
17
|
+
# Fast path for the overwhelming majority of paths that don't need to be normalized
|
18
|
+
return path.dup if path == '/' || (path.start_with?('/') && !(path.end_with?('/') || path.match?(%r{%|//})))
|
19
|
+
|
20
|
+
# Slow path
|
21
|
+
encoding = path.encoding
|
8
22
|
path = +"/#{path}"
|
9
23
|
path.squeeze!('/')
|
10
|
-
|
11
|
-
|
12
|
-
|
24
|
+
|
25
|
+
unless path == '/'
|
26
|
+
path.delete_suffix!('/')
|
27
|
+
path.gsub!(/(%[a-f0-9]{2})/) { ::Regexp.last_match(1).upcase }
|
28
|
+
end
|
29
|
+
|
30
|
+
path.force_encoding(encoding)
|
13
31
|
end
|
14
32
|
|
15
33
|
def initialize
|
@@ -24,7 +42,7 @@ module Grape
|
|
24
42
|
|
25
43
|
@union = Regexp.union(@neutral_regexes)
|
26
44
|
@neutral_regexes = nil
|
27
|
-
(Grape::
|
45
|
+
(Grape::HTTP_SUPPORTED_METHODS + ['*']).each do |method|
|
28
46
|
next unless map.key?(method)
|
29
47
|
|
30
48
|
routes = map[method]
|
@@ -93,7 +111,7 @@ module Grape
|
|
93
111
|
return response unless cascade
|
94
112
|
|
95
113
|
# we need to close the body if possible before dismissing
|
96
|
-
response[2].
|
114
|
+
response[2].try(:close)
|
97
115
|
end
|
98
116
|
end
|
99
117
|
end
|
@@ -138,7 +156,7 @@ module Grape
|
|
138
156
|
end
|
139
157
|
|
140
158
|
def default_response
|
141
|
-
headers = Grape::Util::Header.new.merge(
|
159
|
+
headers = Grape::Util::Header.new.merge('X-Cascade' => 'pass')
|
142
160
|
[404, headers, ['404 Not Found']]
|
143
161
|
end
|
144
162
|
|
@@ -162,7 +180,7 @@ module Grape
|
|
162
180
|
end
|
163
181
|
|
164
182
|
def cascade?(response)
|
165
|
-
response && response[1][
|
183
|
+
response && response[1]['X-Cascade'] == 'pass'
|
166
184
|
end
|
167
185
|
|
168
186
|
def string_for(input)
|
@@ -4,7 +4,7 @@ module Grape
|
|
4
4
|
module Validations
|
5
5
|
class ParamsScope
|
6
6
|
attr_accessor :element, :parent, :index
|
7
|
-
attr_reader :type
|
7
|
+
attr_reader :type, :params_meeting_dependency
|
8
8
|
|
9
9
|
include Grape::DSL::Parameters
|
10
10
|
|
@@ -67,6 +67,7 @@ module Grape
|
|
67
67
|
@type = opts[:type]
|
68
68
|
@group = opts[:group]
|
69
69
|
@dependent_on = opts[:dependent_on]
|
70
|
+
@params_meeting_dependency = []
|
70
71
|
@declared_params = []
|
71
72
|
@index = nil
|
72
73
|
|
@@ -94,7 +95,11 @@ module Grape
|
|
94
95
|
def meets_dependency?(params, request_params)
|
95
96
|
return true unless @dependent_on
|
96
97
|
return false if @parent.present? && !@parent.meets_dependency?(@parent.params(request_params), request_params)
|
97
|
-
|
98
|
+
|
99
|
+
if params.is_a?(Array)
|
100
|
+
@params_meeting_dependency = params.flatten.filter { |param| meets_dependency?(param, request_params) }
|
101
|
+
return @params_meeting_dependency.present?
|
102
|
+
end
|
98
103
|
|
99
104
|
meets_hash_dependency?(params)
|
100
105
|
end
|
@@ -127,7 +132,7 @@ module Grape
|
|
127
132
|
def full_name(name, index: nil)
|
128
133
|
if nested?
|
129
134
|
# Find our containing element's name, and append ours.
|
130
|
-
"#{@parent.full_name(@element)}#{brackets(
|
135
|
+
"#{@parent.full_name(@element)}#{brackets(index || @index)}#{brackets(name)}"
|
131
136
|
elsif lateral?
|
132
137
|
# Find the name of the element as if it was at the same nesting level
|
133
138
|
# as our parent. We need to forward our index upward to achieve this.
|
@@ -49,7 +49,7 @@ module Grape
|
|
49
49
|
next if !@scope.required? && empty_val
|
50
50
|
next unless @scope.meets_dependency?(val, params)
|
51
51
|
|
52
|
-
validate_param!(attr_name, val) if @required ||
|
52
|
+
validate_param!(attr_name, val) if @required || val.try(:key?, attr_name)
|
53
53
|
rescue Grape::Exceptions::Validation => e
|
54
54
|
array_errors << e
|
55
55
|
end
|
@@ -69,7 +69,7 @@ module Grape
|
|
69
69
|
|
70
70
|
def options_key?(key, options = nil)
|
71
71
|
options = instance_variable_get(:@option) if options.nil?
|
72
|
-
options.
|
72
|
+
options.try(:key?, key) && !options[key].nil?
|
73
73
|
end
|
74
74
|
|
75
75
|
def fail_fast?
|
@@ -10,7 +10,7 @@ module Grape
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def validate_param!(attr_name, params)
|
13
|
-
return unless params.
|
13
|
+
return unless params.try(:key?, attr_name)
|
14
14
|
|
15
15
|
excepts = @except.is_a?(Proc) ? @except.call : @except
|
16
16
|
return if excepts.nil?
|
@@ -5,7 +5,7 @@ module Grape
|
|
5
5
|
module Validators
|
6
6
|
class PresenceValidator < Base
|
7
7
|
def validate_param!(attr_name, params)
|
8
|
-
return if params.
|
8
|
+
return if params.try(:key?, attr_name)
|
9
9
|
|
10
10
|
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:presence))
|
11
11
|
end
|
@@ -5,7 +5,7 @@ module Grape
|
|
5
5
|
module Validators
|
6
6
|
class RegexpValidator < Base
|
7
7
|
def validate_param!(attr_name, params)
|
8
|
-
return unless params.
|
8
|
+
return unless params.try(:key?, attr_name)
|
9
9
|
return if Array.wrap(params[attr_name]).all? { |param| param.nil? || param.to_s.scrub.match?((options_key?(:value) ? @option[:value] : @option)) }
|
10
10
|
|
11
11
|
raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: message(:regexp))
|
data/lib/grape/version.rb
CHANGED
data/lib/grape.rb
CHANGED
@@ -20,6 +20,7 @@ require 'active_support/core_ext/hash/slice'
|
|
20
20
|
require 'active_support/core_ext/module/delegation'
|
21
21
|
require 'active_support/core_ext/object/blank'
|
22
22
|
require 'active_support/core_ext/object/deep_dup'
|
23
|
+
require 'active_support/core_ext/object/try'
|
23
24
|
require 'active_support/core_ext/object/duplicable'
|
24
25
|
require 'active_support/core_ext/string/output_safety'
|
25
26
|
require 'active_support/core_ext/string/exclude'
|
@@ -58,12 +59,23 @@ I18n.load_path << File.expand_path('grape/locale/en.yml', __dir__)
|
|
58
59
|
module Grape
|
59
60
|
include ActiveSupport::Configurable
|
60
61
|
|
62
|
+
HTTP_SUPPORTED_METHODS = [
|
63
|
+
Rack::GET,
|
64
|
+
Rack::POST,
|
65
|
+
Rack::PUT,
|
66
|
+
Rack::PATCH,
|
67
|
+
Rack::DELETE,
|
68
|
+
Rack::HEAD,
|
69
|
+
Rack::OPTIONS
|
70
|
+
].freeze
|
71
|
+
|
61
72
|
def self.deprecator
|
62
73
|
@deprecator ||= ActiveSupport::Deprecation.new('2.0', 'Grape')
|
63
74
|
end
|
64
75
|
|
65
76
|
configure do |config|
|
66
|
-
config.param_builder =
|
77
|
+
config.param_builder = :hash_with_indifferent_access
|
78
|
+
config.lint = false
|
67
79
|
config.compile_methods!
|
68
80
|
end
|
69
81
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grape
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Bleigh
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: activesupport
|
@@ -15,14 +15,14 @@ dependencies:
|
|
15
15
|
requirements:
|
16
16
|
- - ">="
|
17
17
|
- !ruby/object:Gem::Version
|
18
|
-
version: '6'
|
18
|
+
version: '6.1'
|
19
19
|
type: :runtime
|
20
20
|
prerelease: false
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
23
|
- - ">="
|
24
24
|
- !ruby/object:Gem::Version
|
25
|
-
version: '6'
|
25
|
+
version: '6.1'
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: dry-types
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
@@ -119,16 +119,17 @@ files:
|
|
119
119
|
- lib/grape/error_formatter.rb
|
120
120
|
- lib/grape/error_formatter/base.rb
|
121
121
|
- lib/grape/error_formatter/json.rb
|
122
|
-
- lib/grape/error_formatter/jsonapi.rb
|
123
122
|
- lib/grape/error_formatter/serializable_hash.rb
|
124
123
|
- lib/grape/error_formatter/txt.rb
|
125
124
|
- lib/grape/error_formatter/xml.rb
|
126
125
|
- lib/grape/exceptions/base.rb
|
126
|
+
- lib/grape/exceptions/conflicting_types.rb
|
127
127
|
- lib/grape/exceptions/empty_message_body.rb
|
128
128
|
- lib/grape/exceptions/incompatible_option_values.rb
|
129
129
|
- lib/grape/exceptions/invalid_accept_header.rb
|
130
130
|
- lib/grape/exceptions/invalid_formatter.rb
|
131
131
|
- lib/grape/exceptions/invalid_message_body.rb
|
132
|
+
- lib/grape/exceptions/invalid_parameters.rb
|
132
133
|
- lib/grape/exceptions/invalid_response.rb
|
133
134
|
- lib/grape/exceptions/invalid_version_header.rb
|
134
135
|
- lib/grape/exceptions/invalid_versioner_option.rb
|
@@ -138,9 +139,12 @@ files:
|
|
138
139
|
- lib/grape/exceptions/missing_mime_type.rb
|
139
140
|
- lib/grape/exceptions/missing_option.rb
|
140
141
|
- lib/grape/exceptions/missing_vendor_option.rb
|
142
|
+
- lib/grape/exceptions/too_deep_parameters.rb
|
141
143
|
- lib/grape/exceptions/too_many_multipart_files.rb
|
144
|
+
- lib/grape/exceptions/unknown_auth_strategy.rb
|
142
145
|
- lib/grape/exceptions/unknown_options.rb
|
143
146
|
- lib/grape/exceptions/unknown_parameter.rb
|
147
|
+
- lib/grape/exceptions/unknown_params_builder.rb
|
144
148
|
- lib/grape/exceptions/unknown_validator.rb
|
145
149
|
- lib/grape/exceptions/unsupported_group_type.rb
|
146
150
|
- lib/grape/exceptions/validation.rb
|
@@ -155,7 +159,6 @@ files:
|
|
155
159
|
- lib/grape/formatter/serializable_hash.rb
|
156
160
|
- lib/grape/formatter/txt.rb
|
157
161
|
- lib/grape/formatter/xml.rb
|
158
|
-
- lib/grape/http/headers.rb
|
159
162
|
- lib/grape/json.rb
|
160
163
|
- lib/grape/locale/en.yml
|
161
164
|
- lib/grape/middleware/auth/base.rb
|
@@ -167,7 +170,6 @@ files:
|
|
167
170
|
- lib/grape/middleware/filter.rb
|
168
171
|
- lib/grape/middleware/formatter.rb
|
169
172
|
- lib/grape/middleware/globals.rb
|
170
|
-
- lib/grape/middleware/helpers.rb
|
171
173
|
- lib/grape/middleware/stack.rb
|
172
174
|
- lib/grape/middleware/versioner.rb
|
173
175
|
- lib/grape/middleware/versioner/accept_version_header.rb
|
@@ -176,10 +178,14 @@ files:
|
|
176
178
|
- lib/grape/middleware/versioner/param.rb
|
177
179
|
- lib/grape/middleware/versioner/path.rb
|
178
180
|
- lib/grape/namespace.rb
|
181
|
+
- lib/grape/params_builder.rb
|
182
|
+
- lib/grape/params_builder/base.rb
|
183
|
+
- lib/grape/params_builder/hash.rb
|
184
|
+
- lib/grape/params_builder/hash_with_indifferent_access.rb
|
185
|
+
- lib/grape/params_builder/hashie_mash.rb
|
179
186
|
- lib/grape/parser.rb
|
180
187
|
- lib/grape/parser/base.rb
|
181
188
|
- lib/grape/parser/json.rb
|
182
|
-
- lib/grape/parser/jsonapi.rb
|
183
189
|
- lib/grape/parser/xml.rb
|
184
190
|
- lib/grape/path.rb
|
185
191
|
- lib/grape/presenters/presenter.rb
|
@@ -201,7 +207,6 @@ files:
|
|
201
207
|
- lib/grape/util/inheritable_setting.rb
|
202
208
|
- lib/grape/util/inheritable_values.rb
|
203
209
|
- lib/grape/util/lazy/block.rb
|
204
|
-
- lib/grape/util/lazy/object.rb
|
205
210
|
- lib/grape/util/lazy/value.rb
|
206
211
|
- lib/grape/util/lazy/value_array.rb
|
207
212
|
- lib/grape/util/lazy/value_enumerable.rb
|
@@ -255,9 +260,9 @@ licenses:
|
|
255
260
|
- MIT
|
256
261
|
metadata:
|
257
262
|
bug_tracker_uri: https://github.com/ruby-grape/grape/issues
|
258
|
-
changelog_uri: https://github.com/ruby-grape/grape/blob/v2.
|
259
|
-
documentation_uri: https://www.rubydoc.info/gems/grape/2.
|
260
|
-
source_code_uri: https://github.com/ruby-grape/grape/tree/v2.
|
263
|
+
changelog_uri: https://github.com/ruby-grape/grape/blob/v2.4.0/CHANGELOG.md
|
264
|
+
documentation_uri: https://www.rubydoc.info/gems/grape/2.4.0
|
265
|
+
source_code_uri: https://github.com/ruby-grape/grape/tree/v2.4.0
|
261
266
|
rubygems_mfa_required: 'true'
|
262
267
|
rdoc_options: []
|
263
268
|
require_paths:
|
@@ -273,7 +278,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
273
278
|
- !ruby/object:Gem::Version
|
274
279
|
version: '0'
|
275
280
|
requirements: []
|
276
|
-
rubygems_version: 3.6.
|
281
|
+
rubygems_version: 3.6.9
|
277
282
|
specification_version: 4
|
278
283
|
summary: A simple Ruby framework for building REST-like APIs.
|
279
284
|
test_files: []
|
data/lib/grape/http/headers.rb
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Grape
|
4
|
-
module Http
|
5
|
-
module Headers
|
6
|
-
HTTP_ACCEPT_VERSION = 'HTTP_ACCEPT_VERSION'
|
7
|
-
HTTP_ACCEPT = 'HTTP_ACCEPT'
|
8
|
-
HTTP_TRANSFER_ENCODING = 'HTTP_TRANSFER_ENCODING'
|
9
|
-
HTTP_VERSION = 'HTTP_VERSION'
|
10
|
-
|
11
|
-
ALLOW = 'Allow'
|
12
|
-
LOCATION = 'Location'
|
13
|
-
X_CASCADE = 'X-Cascade'
|
14
|
-
TRANSFER_ENCODING = 'Transfer-Encoding'
|
15
|
-
|
16
|
-
SUPPORTED_METHODS = [
|
17
|
-
Rack::GET,
|
18
|
-
Rack::POST,
|
19
|
-
Rack::PUT,
|
20
|
-
Rack::PATCH,
|
21
|
-
Rack::DELETE,
|
22
|
-
Rack::HEAD,
|
23
|
-
Rack::OPTIONS
|
24
|
-
].freeze
|
25
|
-
|
26
|
-
SUPPORTED_METHODS_WITHOUT_OPTIONS = (SUPPORTED_METHODS - [Rack::OPTIONS]).freeze
|
27
|
-
|
28
|
-
HTTP_HEADERS = Grape::Util::Lazy::Object.new do
|
29
|
-
common_http_headers = %w[
|
30
|
-
Version
|
31
|
-
Host
|
32
|
-
Connection
|
33
|
-
Cache-Control
|
34
|
-
Dnt
|
35
|
-
Upgrade-Insecure-Requests
|
36
|
-
User-Agent
|
37
|
-
Sec-Fetch-Dest
|
38
|
-
Accept
|
39
|
-
Sec-Fetch-Site
|
40
|
-
Sec-Fetch-Mode
|
41
|
-
Sec-Fetch-User
|
42
|
-
Accept-Encoding
|
43
|
-
Accept-Language
|
44
|
-
Cookie
|
45
|
-
].freeze
|
46
|
-
common_http_headers.each_with_object({}) do |header, response|
|
47
|
-
response["HTTP_#{header.upcase.tr('-', '_')}"] = header
|
48
|
-
end.freeze
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.find_supported_method(route_method)
|
52
|
-
Grape::Http::Headers::SUPPORTED_METHODS.detect { |supported_method| supported_method.casecmp(route_method).zero? }
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
data/lib/grape/parser/jsonapi.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Based on https://github.com/HornsAndHooves/lazy_object
|
4
|
-
|
5
|
-
module Grape
|
6
|
-
module Util
|
7
|
-
module Lazy
|
8
|
-
class Object < BasicObject
|
9
|
-
attr_reader :callable
|
10
|
-
|
11
|
-
def initialize(&callable)
|
12
|
-
@callable = callable
|
13
|
-
end
|
14
|
-
|
15
|
-
def __target_object__
|
16
|
-
@__target_object__ ||= callable.call
|
17
|
-
end
|
18
|
-
|
19
|
-
def ==(other)
|
20
|
-
__target_object__ == other
|
21
|
-
end
|
22
|
-
|
23
|
-
def !=(other)
|
24
|
-
__target_object__ != other
|
25
|
-
end
|
26
|
-
|
27
|
-
def !
|
28
|
-
!__target_object__
|
29
|
-
end
|
30
|
-
|
31
|
-
def method_missing(method_name, *args, &block)
|
32
|
-
if __target_object__.respond_to?(method_name)
|
33
|
-
__target_object__.send(method_name, *args, &block)
|
34
|
-
else
|
35
|
-
super
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def respond_to_missing?(method_name, include_priv = false)
|
40
|
-
__target_object__.respond_to?(method_name, include_priv)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|