batch_api 0.1.1 → 0.2.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.
- data/changelog.md +17 -0
- data/lib/batch_api.rb +6 -1
- data/lib/batch_api/batch_error.rb +41 -0
- data/lib/batch_api/configuration.rb +31 -21
- data/lib/batch_api/error_wrapper.rb +44 -0
- data/lib/batch_api/internal_middleware.rb +87 -0
- data/lib/batch_api/internal_middleware/decode_json_body.rb +24 -0
- data/lib/batch_api/internal_middleware/response_filter.rb +27 -0
- data/lib/batch_api/operation/rack.rb +4 -5
- data/lib/batch_api/processor.rb +22 -20
- data/lib/batch_api/processor/executor.rb +18 -0
- data/lib/batch_api/processor/sequential.rb +29 -0
- data/lib/batch_api/{middleware.rb → rack_middleware.rb} +2 -2
- data/lib/batch_api/response.rb +10 -8
- data/lib/batch_api/version.rb +1 -1
- data/readme.md +179 -106
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +15 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/javascripts/endpoints.js +2 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/assets/stylesheets/endpoints.css +4 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/endpoints_controller.rb +36 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/helpers/endpoints_helper.rb +2 -0
- data/spec/dummy/app/views/endpoints/get.html.erb +2 -0
- data/spec/dummy/app/views/endpoints/post.html.erb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +63 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +64 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +1742 -0
- data/spec/dummy/log/test.log +48237 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/test/functional/endpoints_controller_test.rb +14 -0
- data/spec/dummy/test/unit/helpers/endpoints_helper_test.rb +4 -0
- data/spec/integration/rails_spec.rb +10 -0
- data/spec/integration/shared_examples.rb +256 -0
- data/spec/integration/sinatra_integration_spec.rb +14 -0
- data/spec/lib/batch_api_spec.rb +20 -0
- data/spec/lib/batch_error_spec.rb +23 -0
- data/spec/lib/configuration_spec.rb +30 -0
- data/spec/lib/error_wrapper_spec.rb +68 -0
- data/spec/lib/internal_middleware/decode_json_body_spec.rb +37 -0
- data/spec/lib/internal_middleware/response_filter_spec.rb +61 -0
- data/spec/lib/internal_middleware_spec.rb +91 -0
- data/spec/lib/operation/rack_spec.rb +243 -0
- data/spec/lib/operation/rails_spec.rb +100 -0
- data/spec/lib/processor/executor_spec.rb +22 -0
- data/spec/lib/processor/sequential_spec.rb +39 -0
- data/spec/lib/processor_spec.rb +134 -0
- data/spec/lib/rack_middleware_spec.rb +103 -0
- data/spec/lib/response_spec.rb +53 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/support/sinatra_app.rb +54 -0
- metadata +148 -12
- data/lib/batch_api/error.rb +0 -3
- data/lib/batch_api/errors/base.rb +0 -45
- data/lib/batch_api/errors/operation.rb +0 -7
- data/lib/batch_api/errors/request.rb +0 -26
- data/lib/batch_api/processor/strategies/sequential.rb +0 -18
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Configure Rails Environment
|
2
|
+
ENV["RAILS_ENV"] = "test"
|
3
|
+
|
4
|
+
require File.expand_path("../dummy/config/environment.rb", __FILE__)
|
5
|
+
require "rails/test_help"
|
6
|
+
require 'rspec/rails'
|
7
|
+
require 'faker'
|
8
|
+
require 'timecop'
|
9
|
+
|
10
|
+
Rails.backtrace_cleaner.remove_silencers!
|
11
|
+
|
12
|
+
# Load support files
|
13
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
14
|
+
|
15
|
+
# Load fixtures from the engine
|
16
|
+
if ActiveSupport::TestCase.method_defined?(:fixture_path=)
|
17
|
+
ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
|
18
|
+
end
|
19
|
+
|
20
|
+
RSpec.configure do |config|
|
21
|
+
config.before :each do
|
22
|
+
BatchApi.config.limit = 20
|
23
|
+
BatchApi.config.endpoint = "/batch"
|
24
|
+
BatchApi.config.verb = :post
|
25
|
+
|
26
|
+
BatchApi.stub(:rails?).and_return(false)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'rack/contrib'
|
3
|
+
|
4
|
+
class SinatraApp < Sinatra::Base
|
5
|
+
use Rack::PostBodyContentTypeParser
|
6
|
+
use BatchApi::RackMiddleware
|
7
|
+
|
8
|
+
get "/endpoint" do
|
9
|
+
headers["GET"] = "hello"
|
10
|
+
# including this in the body would mess the body up
|
11
|
+
# due to the other headers inserted
|
12
|
+
headers["REQUEST_HEADERS"] = header_output
|
13
|
+
content_type :json
|
14
|
+
|
15
|
+
status 422
|
16
|
+
{
|
17
|
+
result: "GET OK",
|
18
|
+
params: params.except(:endpoint)
|
19
|
+
}.to_json
|
20
|
+
end
|
21
|
+
|
22
|
+
get "/endpoint/capture/:captured" do
|
23
|
+
content_type :json
|
24
|
+
{result: params[:captured]}.to_json
|
25
|
+
end
|
26
|
+
|
27
|
+
post "/endpoint" do
|
28
|
+
headers["POST"] = "guten tag"
|
29
|
+
headers["REQUEST_HEADERS"] = header_output
|
30
|
+
content_type :json
|
31
|
+
status 203
|
32
|
+
{
|
33
|
+
result: "POST OK",
|
34
|
+
params: params.except(:endpoint)
|
35
|
+
}.to_json
|
36
|
+
end
|
37
|
+
|
38
|
+
get "/endpoint/error" do
|
39
|
+
raise StandardError
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def header_output
|
45
|
+
# we only want the headers that were sent by the client
|
46
|
+
# headers in sinatra are just read directly from env
|
47
|
+
# env has a ton of additional information we don't want
|
48
|
+
# and that reference the request itself, causing an infinite loop
|
49
|
+
env.inject({}) do |h, (k, v)|
|
50
|
+
h.tap {|hash| hash[k.to_s] = v.to_s if k =~ /HTTP_/}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: batch_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,24 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-10-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: middleware
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
14
30
|
- !ruby/object:Gem::Dependency
|
15
31
|
name: rails
|
16
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -115,17 +131,19 @@ executables: []
|
|
115
131
|
extensions: []
|
116
132
|
extra_rdoc_files: []
|
117
133
|
files:
|
134
|
+
- lib/batch_api/batch_error.rb
|
118
135
|
- lib/batch_api/configuration.rb
|
119
|
-
- lib/batch_api/
|
120
|
-
- lib/batch_api/
|
121
|
-
- lib/batch_api/
|
122
|
-
- lib/batch_api/
|
123
|
-
- lib/batch_api/middleware.rb
|
136
|
+
- lib/batch_api/error_wrapper.rb
|
137
|
+
- lib/batch_api/internal_middleware/decode_json_body.rb
|
138
|
+
- lib/batch_api/internal_middleware/response_filter.rb
|
139
|
+
- lib/batch_api/internal_middleware.rb
|
124
140
|
- lib/batch_api/operation/rack.rb
|
125
141
|
- lib/batch_api/operation/rails.rb
|
126
142
|
- lib/batch_api/operation.rb
|
127
|
-
- lib/batch_api/processor/
|
143
|
+
- lib/batch_api/processor/executor.rb
|
144
|
+
- lib/batch_api/processor/sequential.rb
|
128
145
|
- lib/batch_api/processor.rb
|
146
|
+
- lib/batch_api/rack_middleware.rb
|
129
147
|
- lib/batch_api/response.rb
|
130
148
|
- lib/batch_api/utils.rb
|
131
149
|
- lib/batch_api/version.rb
|
@@ -135,6 +153,65 @@ files:
|
|
135
153
|
- Rakefile
|
136
154
|
- changelog.md
|
137
155
|
- readme.md
|
156
|
+
- spec/dummy/app/assets/javascripts/application.js
|
157
|
+
- spec/dummy/app/assets/javascripts/endpoints.js
|
158
|
+
- spec/dummy/app/assets/stylesheets/application.css
|
159
|
+
- spec/dummy/app/assets/stylesheets/endpoints.css
|
160
|
+
- spec/dummy/app/controllers/application_controller.rb
|
161
|
+
- spec/dummy/app/controllers/endpoints_controller.rb
|
162
|
+
- spec/dummy/app/helpers/application_helper.rb
|
163
|
+
- spec/dummy/app/helpers/endpoints_helper.rb
|
164
|
+
- spec/dummy/app/views/endpoints/get.html.erb
|
165
|
+
- spec/dummy/app/views/endpoints/post.html.erb
|
166
|
+
- spec/dummy/app/views/layouts/application.html.erb
|
167
|
+
- spec/dummy/config/application.rb
|
168
|
+
- spec/dummy/config/boot.rb
|
169
|
+
- spec/dummy/config/database.yml
|
170
|
+
- spec/dummy/config/environment.rb
|
171
|
+
- spec/dummy/config/environments/development.rb
|
172
|
+
- spec/dummy/config/environments/production.rb
|
173
|
+
- spec/dummy/config/environments/test.rb
|
174
|
+
- spec/dummy/config/initializers/backtrace_silencers.rb
|
175
|
+
- spec/dummy/config/initializers/inflections.rb
|
176
|
+
- spec/dummy/config/initializers/mime_types.rb
|
177
|
+
- spec/dummy/config/initializers/secret_token.rb
|
178
|
+
- spec/dummy/config/initializers/session_store.rb
|
179
|
+
- spec/dummy/config/initializers/wrap_parameters.rb
|
180
|
+
- spec/dummy/config/locales/en.yml
|
181
|
+
- spec/dummy/config/routes.rb
|
182
|
+
- spec/dummy/config.ru
|
183
|
+
- spec/dummy/db/development.sqlite3
|
184
|
+
- spec/dummy/db/test.sqlite3
|
185
|
+
- spec/dummy/log/development.log
|
186
|
+
- spec/dummy/log/test.log
|
187
|
+
- spec/dummy/public/404.html
|
188
|
+
- spec/dummy/public/422.html
|
189
|
+
- spec/dummy/public/500.html
|
190
|
+
- spec/dummy/public/favicon.ico
|
191
|
+
- spec/dummy/Rakefile
|
192
|
+
- spec/dummy/README.rdoc
|
193
|
+
- spec/dummy/script/rails
|
194
|
+
- spec/dummy/test/functional/endpoints_controller_test.rb
|
195
|
+
- spec/dummy/test/unit/helpers/endpoints_helper_test.rb
|
196
|
+
- spec/integration/rails_spec.rb
|
197
|
+
- spec/integration/shared_examples.rb
|
198
|
+
- spec/integration/sinatra_integration_spec.rb
|
199
|
+
- spec/lib/batch_api_spec.rb
|
200
|
+
- spec/lib/batch_error_spec.rb
|
201
|
+
- spec/lib/configuration_spec.rb
|
202
|
+
- spec/lib/error_wrapper_spec.rb
|
203
|
+
- spec/lib/internal_middleware/decode_json_body_spec.rb
|
204
|
+
- spec/lib/internal_middleware/response_filter_spec.rb
|
205
|
+
- spec/lib/internal_middleware_spec.rb
|
206
|
+
- spec/lib/operation/rack_spec.rb
|
207
|
+
- spec/lib/operation/rails_spec.rb
|
208
|
+
- spec/lib/processor/executor_spec.rb
|
209
|
+
- spec/lib/processor/sequential_spec.rb
|
210
|
+
- spec/lib/processor_spec.rb
|
211
|
+
- spec/lib/rack_middleware_spec.rb
|
212
|
+
- spec/lib/response_spec.rb
|
213
|
+
- spec/spec_helper.rb
|
214
|
+
- spec/support/sinatra_app.rb
|
138
215
|
homepage: http://github.com/arsduo/batch_api
|
139
216
|
licenses: []
|
140
217
|
post_install_message:
|
@@ -149,7 +226,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
149
226
|
version: '0'
|
150
227
|
segments:
|
151
228
|
- 0
|
152
|
-
hash:
|
229
|
+
hash: 1297509053132292607
|
153
230
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
231
|
none: false
|
155
232
|
requirements:
|
@@ -158,11 +235,70 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
158
235
|
version: '0'
|
159
236
|
segments:
|
160
237
|
- 0
|
161
|
-
hash:
|
238
|
+
hash: 1297509053132292607
|
162
239
|
requirements: []
|
163
240
|
rubyforge_project:
|
164
|
-
rubygems_version: 1.8.
|
241
|
+
rubygems_version: 1.8.24
|
165
242
|
signing_key:
|
166
243
|
specification_version: 3
|
167
244
|
summary: A RESTful Batch API for Rails
|
168
|
-
test_files:
|
245
|
+
test_files:
|
246
|
+
- spec/dummy/app/assets/javascripts/application.js
|
247
|
+
- spec/dummy/app/assets/javascripts/endpoints.js
|
248
|
+
- spec/dummy/app/assets/stylesheets/application.css
|
249
|
+
- spec/dummy/app/assets/stylesheets/endpoints.css
|
250
|
+
- spec/dummy/app/controllers/application_controller.rb
|
251
|
+
- spec/dummy/app/controllers/endpoints_controller.rb
|
252
|
+
- spec/dummy/app/helpers/application_helper.rb
|
253
|
+
- spec/dummy/app/helpers/endpoints_helper.rb
|
254
|
+
- spec/dummy/app/views/endpoints/get.html.erb
|
255
|
+
- spec/dummy/app/views/endpoints/post.html.erb
|
256
|
+
- spec/dummy/app/views/layouts/application.html.erb
|
257
|
+
- spec/dummy/config/application.rb
|
258
|
+
- spec/dummy/config/boot.rb
|
259
|
+
- spec/dummy/config/database.yml
|
260
|
+
- spec/dummy/config/environment.rb
|
261
|
+
- spec/dummy/config/environments/development.rb
|
262
|
+
- spec/dummy/config/environments/production.rb
|
263
|
+
- spec/dummy/config/environments/test.rb
|
264
|
+
- spec/dummy/config/initializers/backtrace_silencers.rb
|
265
|
+
- spec/dummy/config/initializers/inflections.rb
|
266
|
+
- spec/dummy/config/initializers/mime_types.rb
|
267
|
+
- spec/dummy/config/initializers/secret_token.rb
|
268
|
+
- spec/dummy/config/initializers/session_store.rb
|
269
|
+
- spec/dummy/config/initializers/wrap_parameters.rb
|
270
|
+
- spec/dummy/config/locales/en.yml
|
271
|
+
- spec/dummy/config/routes.rb
|
272
|
+
- spec/dummy/config.ru
|
273
|
+
- spec/dummy/db/development.sqlite3
|
274
|
+
- spec/dummy/db/test.sqlite3
|
275
|
+
- spec/dummy/log/development.log
|
276
|
+
- spec/dummy/log/test.log
|
277
|
+
- spec/dummy/public/404.html
|
278
|
+
- spec/dummy/public/422.html
|
279
|
+
- spec/dummy/public/500.html
|
280
|
+
- spec/dummy/public/favicon.ico
|
281
|
+
- spec/dummy/Rakefile
|
282
|
+
- spec/dummy/README.rdoc
|
283
|
+
- spec/dummy/script/rails
|
284
|
+
- spec/dummy/test/functional/endpoints_controller_test.rb
|
285
|
+
- spec/dummy/test/unit/helpers/endpoints_helper_test.rb
|
286
|
+
- spec/integration/rails_spec.rb
|
287
|
+
- spec/integration/shared_examples.rb
|
288
|
+
- spec/integration/sinatra_integration_spec.rb
|
289
|
+
- spec/lib/batch_api_spec.rb
|
290
|
+
- spec/lib/batch_error_spec.rb
|
291
|
+
- spec/lib/configuration_spec.rb
|
292
|
+
- spec/lib/error_wrapper_spec.rb
|
293
|
+
- spec/lib/internal_middleware/decode_json_body_spec.rb
|
294
|
+
- spec/lib/internal_middleware/response_filter_spec.rb
|
295
|
+
- spec/lib/internal_middleware_spec.rb
|
296
|
+
- spec/lib/operation/rack_spec.rb
|
297
|
+
- spec/lib/operation/rails_spec.rb
|
298
|
+
- spec/lib/processor/executor_spec.rb
|
299
|
+
- spec/lib/processor/sequential_spec.rb
|
300
|
+
- spec/lib/processor_spec.rb
|
301
|
+
- spec/lib/rack_middleware_spec.rb
|
302
|
+
- spec/lib/response_spec.rb
|
303
|
+
- spec/spec_helper.rb
|
304
|
+
- spec/support/sinatra_app.rb
|
data/lib/batch_api/error.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
module BatchApi
|
2
|
-
# Public: an error thrown during a batch operation.
|
3
|
-
# This has a body class and a cookies accessor and can
|
4
|
-
# function in place of a regular BatchResponse object.
|
5
|
-
module Errors
|
6
|
-
class Base
|
7
|
-
# Public: create a new BatchError from a Rails error.
|
8
|
-
def initialize(error)
|
9
|
-
@error = error
|
10
|
-
end
|
11
|
-
|
12
|
-
# Public: the error details as a hash, which can be returned
|
13
|
-
# to clients as JSON.
|
14
|
-
def body
|
15
|
-
message = if self.class.expose_backtrace?
|
16
|
-
{
|
17
|
-
message: @error.message,
|
18
|
-
backtrace: @error.backtrace
|
19
|
-
}
|
20
|
-
else
|
21
|
-
{ message: @error.message }
|
22
|
-
end
|
23
|
-
{ error: message }
|
24
|
-
end
|
25
|
-
|
26
|
-
# Public: turn the error body into a Rack-compatible body component.
|
27
|
-
#
|
28
|
-
# Returns: an Array with the error body represented as JSON.
|
29
|
-
def render
|
30
|
-
[status_code, Middleware.content_type, [MultiJson.dump(body)]]
|
31
|
-
end
|
32
|
-
|
33
|
-
# Public: the status code to return for the given error.
|
34
|
-
def status_code
|
35
|
-
500
|
36
|
-
end
|
37
|
-
|
38
|
-
# Internal: whether the backtrace should be exposed in the response.
|
39
|
-
# Currently Rails-specific, needs to be generalized (to ENV["RACK_ENV"])?
|
40
|
-
def self.expose_backtrace?
|
41
|
-
!Rails.env.production?
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
require 'batch_api/errors/base'
|
2
|
-
|
3
|
-
module BatchApi
|
4
|
-
module Errors
|
5
|
-
# Public: This class encapsulates errors that occur at a request level.
|
6
|
-
# For instance, it returns proper error codes for BadOptionErrors or other
|
7
|
-
# identifiable problems. (For actual code errors, it returns a 500
|
8
|
-
# response.)
|
9
|
-
class Request < BatchApi::Errors::Base
|
10
|
-
# Public: return the appropriate status code for the error. For
|
11
|
-
# errors from bad Batch API input, raise a 422, otherwise, a 500.
|
12
|
-
def status_code
|
13
|
-
case @error
|
14
|
-
when BatchApi::Processor::BadOptionError,
|
15
|
-
BatchApi::Processor::OperationLimitExceeded,
|
16
|
-
BatchApi::Processor::NoOperationsError,
|
17
|
-
BatchApi::Operation::MalformedOperationError
|
18
|
-
422
|
19
|
-
else
|
20
|
-
500
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
@@ -1,18 +0,0 @@
|
|
1
|
-
module BatchApi
|
2
|
-
class Processor
|
3
|
-
module Strategies
|
4
|
-
module Sequential
|
5
|
-
# Public: execute all operations sequentially.
|
6
|
-
#
|
7
|
-
# ops - a set of BatchApi::Operations
|
8
|
-
# options - a set of options
|
9
|
-
#
|
10
|
-
# Returns an array of BatchApi::Response objects.
|
11
|
-
def self.execute!(ops, options = {})
|
12
|
-
ops.map(&:execute)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|