lennarb 0.1.5 → 0.1.6
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 +165 -0
- data/lib/lenna/application.rb +53 -0
- data/lib/lenna/middleware/app.rb +107 -92
- data/lib/lenna/middleware/default/error_handler.rb +184 -179
- data/lib/lenna/middleware/default/logging.rb +79 -81
- data/lib/lenna/router/builder.rb +111 -86
- data/lib/lenna/router/cache.rb +44 -30
- data/lib/lenna/router/namespace_stack.rb +66 -62
- data/lib/lenna/router/request.rb +125 -101
- data/lib/lenna/router/response.rb +505 -375
- data/lib/lenna/router/route_matcher.rb +56 -57
- data/lib/lenna/router.rb +186 -154
- data/lib/lennarb/array_extensions.rb +25 -11
- data/lib/lennarb/version.rb +5 -2
- data/lib/lennarb.rb +8 -1
- data/license.md +21 -0
- data/readme.md +31 -0
- metadata +64 -56
- data/CHANGELOG.md +0 -62
- data/LICENCE +0 -24
- data/README.md +0 -229
- data/lib/lenna/base.rb +0 -52
@@ -1,379 +1,509 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2023, by Aristóteles Coutinho.
|
5
|
+
|
3
6
|
module Lenna
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
7
|
+
class Router
|
8
|
+
# The Response class is responsible for managing the response.
|
9
|
+
#
|
10
|
+
# @attr headers [Hash] the response headers
|
11
|
+
# @attr body [Array(String)] the response body
|
12
|
+
# @attr status [Integer] the response status
|
13
|
+
# @attr params [Hash] the response params
|
14
|
+
#
|
15
|
+
# @public `Since v0.1.0`
|
16
|
+
#
|
17
|
+
class Response
|
18
|
+
# The status of the response.
|
19
|
+
#
|
20
|
+
# @return [Integer] the status of the Response
|
21
|
+
#
|
22
|
+
# @public
|
23
|
+
#
|
24
|
+
public attr_accessor :status
|
25
|
+
|
26
|
+
# The body of the response.
|
27
|
+
#
|
28
|
+
# @return [Array(String)] the body of the Response
|
29
|
+
#
|
30
|
+
# @public
|
31
|
+
#
|
32
|
+
public attr_reader :body
|
33
|
+
|
34
|
+
# The headers of the response.
|
35
|
+
#
|
36
|
+
# @return [Hash] the headers of the Response
|
37
|
+
#
|
38
|
+
# @public
|
39
|
+
#
|
40
|
+
public attr_reader :headers
|
41
|
+
|
42
|
+
# The params of the response.
|
43
|
+
#
|
44
|
+
# @return [Hash] the params of the Response
|
45
|
+
#
|
46
|
+
# @public
|
47
|
+
#
|
48
|
+
public attr_reader :params
|
49
|
+
|
50
|
+
# The length of the response.
|
51
|
+
#
|
52
|
+
# @return [Integer] the length of the Response
|
53
|
+
#
|
54
|
+
# @public
|
55
|
+
#
|
56
|
+
public attr_reader :length
|
57
|
+
|
58
|
+
# This method will initialize the response.
|
59
|
+
#
|
60
|
+
# @parameter headers [Hash] the response headers, default is {}
|
61
|
+
# @parameter status [Integer] the response status, default is 200
|
62
|
+
# @parameter body [Array(String)] the response body, default is []
|
63
|
+
# @parameter params [Hash] the response params, default is {}
|
64
|
+
# @length [Integer] the response length, default is 0
|
65
|
+
#
|
66
|
+
# @return [void]
|
67
|
+
#
|
68
|
+
def initialize(headers = {}, status = 200, body = [])
|
69
|
+
@params = {}
|
70
|
+
@body = body
|
71
|
+
@status = status
|
72
|
+
@headers = headers
|
73
|
+
@length = 0
|
74
|
+
end
|
75
|
+
|
76
|
+
# This method will set the params.
|
77
|
+
#
|
78
|
+
# @parameter params [Hash] the params to be used
|
79
|
+
#
|
80
|
+
# @return [void]
|
81
|
+
#
|
82
|
+
# @public
|
83
|
+
#
|
84
|
+
# ex.
|
85
|
+
# response.params = { 'name' => 'John' }
|
86
|
+
# # => { 'name' => 'John' }
|
87
|
+
#
|
88
|
+
def assign_params(params)
|
89
|
+
params => ::Hash
|
90
|
+
|
91
|
+
@params = params
|
92
|
+
rescue ::NoMatchingPatternError
|
93
|
+
raise ::ArgumentError, 'params must be a hash'
|
94
|
+
end
|
95
|
+
alias params= assign_params
|
96
|
+
|
97
|
+
# Returns the response header corresponding to `key`.
|
98
|
+
#
|
99
|
+
# @parameter key [String] the header name
|
100
|
+
#
|
101
|
+
# @return [String] the header value
|
102
|
+
#
|
103
|
+
# @public
|
104
|
+
#
|
105
|
+
# ex.
|
106
|
+
# res["Content-Type"] # => "text/html"
|
107
|
+
# res["Content-Length"] # => "42"
|
108
|
+
#
|
109
|
+
def [](key) = @headers[key]
|
110
|
+
|
111
|
+
# Thi method set the body value.
|
112
|
+
#
|
113
|
+
# @parameter value [Array(String)] the body value
|
114
|
+
#
|
115
|
+
# @return [void]
|
116
|
+
#
|
117
|
+
# @public
|
118
|
+
#
|
119
|
+
def assign_body(value) = put_body(value)
|
120
|
+
alias body= assign_body
|
121
|
+
|
122
|
+
# This method will set the header value.
|
123
|
+
#
|
124
|
+
# @parameter header [String] the header name
|
125
|
+
# @parameter value [String] the header value
|
126
|
+
#
|
127
|
+
# @return [void]
|
128
|
+
#
|
129
|
+
# @public
|
130
|
+
#
|
131
|
+
# ex.
|
132
|
+
# assign_header('X-Request-Id', '123')
|
133
|
+
# # => '123'
|
134
|
+
#
|
135
|
+
# assign_header('X-Request-Id', '456')
|
136
|
+
# # => ['123', '456']
|
137
|
+
#
|
138
|
+
# assign_header('X-Request-Id', ['456', '789'])
|
139
|
+
# # => ['123', '456', '789']
|
140
|
+
#
|
141
|
+
def assign_header(key, value) = put_header(key, value)
|
142
|
+
alias []= assign_header
|
143
|
+
|
144
|
+
# Add multiple headers.
|
145
|
+
#
|
146
|
+
# @parameter headers [Hash] the headers
|
147
|
+
# @return [void]
|
148
|
+
#
|
149
|
+
# ex.
|
150
|
+
# headers = {
|
151
|
+
# 'Content-Type' => 'application/json',
|
152
|
+
# 'X-Request-Id' => '123'
|
153
|
+
# }
|
154
|
+
#
|
155
|
+
def assign_headers(headers)
|
156
|
+
headers => ::Hash
|
157
|
+
|
158
|
+
headers.each { |key, value| put_header(key, value) }
|
159
|
+
end
|
160
|
+
alias headers= assign_headers
|
161
|
+
|
162
|
+
# This method will get the content type.
|
163
|
+
#
|
164
|
+
# @return [String] the content type
|
165
|
+
#
|
166
|
+
# @public
|
167
|
+
#
|
168
|
+
def content_type = @headers['Content-Type']
|
169
|
+
|
170
|
+
# This method will delete the header.
|
171
|
+
#
|
172
|
+
# @parameter header [String] the header name
|
173
|
+
#
|
174
|
+
# @return [void]
|
175
|
+
#
|
176
|
+
# @public
|
177
|
+
#
|
178
|
+
def remove_header(key) = delete_header(key)
|
179
|
+
|
180
|
+
# This method will set the cookie.
|
181
|
+
#
|
182
|
+
# @parameter key [String] the key of the cookie
|
183
|
+
# @return [void]
|
184
|
+
# @parameter value [String] the value of the cookie
|
185
|
+
#
|
186
|
+
#
|
187
|
+
# @public
|
188
|
+
#
|
189
|
+
# ex.
|
190
|
+
# response.assign_cookie('foo', 'bar')
|
191
|
+
# # => 'foo=bar'
|
192
|
+
#
|
193
|
+
# response.header['Set-Cookie']
|
194
|
+
# # => 'foo=bar'
|
195
|
+
#
|
196
|
+
# response.assign_cookie('foo2', 'bar2')
|
197
|
+
# # => 'foo=bar; foo2=bar2'
|
198
|
+
# response.header['Set-Cookie']
|
199
|
+
# # => 'foo=bar; foo2=bar2'
|
200
|
+
#
|
201
|
+
# response.assign_cookie('bar', {
|
202
|
+
# domain: 'example.com',
|
203
|
+
# path: '/',
|
204
|
+
# # expires: Time.now + 24 * 60 * 60,
|
205
|
+
# secure: true,
|
206
|
+
# httponly: true
|
207
|
+
# })
|
208
|
+
#
|
209
|
+
# response.header['Set-Cookie'].split('\n').last
|
210
|
+
# # => 'bar=; domain=example.com; path=/; secure; HttpOnly'
|
211
|
+
#
|
212
|
+
# note:
|
213
|
+
# This method doesn't sign and/or encrypt the cookie.
|
214
|
+
# If you want to sign and/or encrypt the cookie, then you can use
|
215
|
+
# the `Rack::Session::Cookie` middleware.
|
216
|
+
#
|
217
|
+
def assign_cookie(key, value)
|
218
|
+
key => ::String
|
219
|
+
value => ::String
|
220
|
+
|
221
|
+
::Rack::Utils.set_cookie_header!(@headers, key, value)
|
222
|
+
rescue ::NoMatchingPatternError
|
223
|
+
raise ::ArgumentError, 'key must be a string'
|
224
|
+
end
|
225
|
+
|
226
|
+
# This method will get all the cookies.
|
227
|
+
#
|
228
|
+
# @return [Hash] the cookies
|
229
|
+
#
|
230
|
+
# @public
|
231
|
+
#
|
232
|
+
def cookies = @headers['set-cookie']
|
233
|
+
|
234
|
+
# This method will delete the cookie.
|
235
|
+
#
|
236
|
+
# @parameter key [String] the key of the cookie
|
237
|
+
#
|
238
|
+
# @return [void]
|
239
|
+
#
|
240
|
+
# @public
|
241
|
+
#
|
242
|
+
# ex.
|
243
|
+
# response.delete_cookie('foo')
|
244
|
+
# # => 'foo=; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000'
|
245
|
+
#
|
246
|
+
def delete_cookie(key)
|
247
|
+
key => ::String
|
248
|
+
|
249
|
+
::Rack::Utils.delete_cookie_header!(@headers, key)
|
250
|
+
rescue ::NoMatchingPatternError
|
251
|
+
raise ::ArgumentError, 'key must be a string'
|
252
|
+
end
|
253
|
+
|
254
|
+
# This method will set redirect location. The status will be set to 302.
|
255
|
+
#
|
256
|
+
# @parameter location [String] the redirect location
|
257
|
+
# @parameter status [Integer] the redirect status, default is 302.
|
258
|
+
#
|
259
|
+
# @return [void]
|
260
|
+
#
|
261
|
+
# @public
|
262
|
+
#
|
263
|
+
def redirect(location, status: 302)
|
264
|
+
location => ::String
|
265
|
+
|
266
|
+
put_header('Location', location)
|
267
|
+
put_status(status)
|
268
|
+
|
269
|
+
finish!
|
270
|
+
rescue ::NoMatchingPatternError
|
271
|
+
raise ::ArgumentError, 'location must be a string'
|
272
|
+
end
|
273
|
+
|
274
|
+
# This method will finish the response.
|
275
|
+
#
|
276
|
+
# @return [void]
|
277
|
+
#
|
278
|
+
# @public
|
279
|
+
#
|
280
|
+
def finish = finish!
|
281
|
+
|
282
|
+
# This method will set the response content type.
|
283
|
+
#
|
284
|
+
# @parameter type [String] the response content type
|
285
|
+
# @parameter charset [Hash] the response charset
|
286
|
+
#
|
287
|
+
# @return [void]
|
288
|
+
#
|
289
|
+
# @public
|
290
|
+
#
|
291
|
+
def assign_content_type(type, charset: nil)
|
292
|
+
type => ::String
|
293
|
+
|
294
|
+
case charset
|
295
|
+
in ::String then put_header('Content-Type', "#{type}; charset=#{charset}")
|
296
|
+
else put_header('Content-Type', type)
|
297
|
+
end
|
298
|
+
rescue ::NoMatchingPatternError
|
299
|
+
raise ::ArgumentError, 'type must be a string'
|
300
|
+
end
|
301
|
+
|
302
|
+
# This method will set the response data and finish the response.
|
303
|
+
#
|
304
|
+
# @parameter data [Hash | Array] the response data
|
305
|
+
#
|
306
|
+
# @return [void]
|
307
|
+
#
|
308
|
+
# @public
|
309
|
+
#
|
310
|
+
# ex.
|
311
|
+
# response.json({ foo: 'bar' })
|
312
|
+
# # => { foo: 'bar' }
|
313
|
+
#
|
314
|
+
# response.json([{ foo: 'bar' }])
|
315
|
+
# # => [{ foo: 'bar' }]
|
316
|
+
#
|
317
|
+
def json(data = {}, status: 200)
|
318
|
+
data => ::Array | ::Hash
|
319
|
+
|
320
|
+
put_status(status)
|
321
|
+
put_header('Content-Type', 'application/json')
|
322
|
+
put_body(data.to_json)
|
323
|
+
|
324
|
+
finish!
|
325
|
+
end
|
326
|
+
|
327
|
+
# Set the response content type to text/html.
|
328
|
+
#
|
329
|
+
# @parameter str [String] the response body
|
330
|
+
#
|
331
|
+
# @return [void]
|
332
|
+
#
|
333
|
+
# @public
|
334
|
+
#
|
335
|
+
def html(str = nil, status: 200)
|
336
|
+
put_status(status)
|
337
|
+
put_header('Content-Type', 'text/html')
|
338
|
+
put_body(str)
|
339
|
+
|
340
|
+
finish!
|
341
|
+
end
|
342
|
+
|
343
|
+
# This method will render the template.
|
344
|
+
#
|
345
|
+
# @parameter template_nam [String] the template name
|
346
|
+
# @parameter path [String] the template path, default is 'views'
|
347
|
+
# @parameter locals [Hash] the template locals
|
348
|
+
#
|
349
|
+
# @return [void | Exception]
|
350
|
+
#
|
351
|
+
# @public
|
352
|
+
#
|
353
|
+
# ex.
|
354
|
+
# render('index')
|
355
|
+
# # => Render the template `views/index.html.erb`
|
356
|
+
#
|
357
|
+
# render('users/index')
|
358
|
+
# # => Render the template `views/users/index.html.erb`
|
359
|
+
#
|
360
|
+
# render('index', path: 'app/views/users')
|
361
|
+
# # => Render the template `app/views/users/index.html.erb`
|
362
|
+
#
|
363
|
+
# render('index', locals: { name: 'John' })
|
364
|
+
# # => Render the template `views/index.html.erb` with the local
|
365
|
+
# # variable `name` set to 'John'
|
366
|
+
#
|
367
|
+
def render(template_name, path: 'views', locals: {}, status: 200)
|
368
|
+
template_path = ::File.join(path, "#{template_name}.html.erb")
|
369
|
+
|
370
|
+
# Check if the template exists
|
371
|
+
unless File.exist?(template_path)
|
372
|
+
msg = "Template not found: #{template_path} 🤷♂️."
|
373
|
+
|
374
|
+
# Oops! The template doesn't exist or the path is wrong.
|
375
|
+
#
|
376
|
+
# The template exists? 🤔
|
377
|
+
# If you want to render a template from a custom path, then you
|
378
|
+
# can pass the full path though the path: keyword argument instead
|
379
|
+
# of just the name. For example:
|
380
|
+
# render('index', path: 'app/views/users')
|
381
|
+
raise msg
|
382
|
+
end
|
383
|
+
|
384
|
+
::File
|
385
|
+
.read(template_path)
|
386
|
+
.then { |template| ::ERB.new(template).result_with_hash(locals) }
|
387
|
+
.then { |erb_template| html(erb_template, status:) }
|
388
|
+
end
|
389
|
+
|
390
|
+
# Helper methods for the response.
|
391
|
+
#
|
392
|
+
# @return [void]
|
393
|
+
#
|
394
|
+
# @public
|
395
|
+
#
|
396
|
+
def not_found
|
397
|
+
put_body(['Not Found'])
|
398
|
+
put_status(404)
|
399
|
+
|
400
|
+
finish!
|
401
|
+
end
|
402
|
+
|
403
|
+
private
|
404
|
+
|
405
|
+
# This method will get the response status.
|
406
|
+
#
|
407
|
+
# @return [Integer] the response status
|
408
|
+
#
|
409
|
+
def put_status(value)
|
410
|
+
value => ::Integer
|
411
|
+
|
412
|
+
self.status = value
|
413
|
+
rescue ::NoMatchingPatternError
|
414
|
+
raise ::ArgumentError, 'status must be an integer'
|
415
|
+
end
|
416
|
+
|
417
|
+
# This method will set the body.
|
418
|
+
#
|
419
|
+
# @parameter body [Array(String)] the body to be used
|
420
|
+
#
|
421
|
+
# @return [void]
|
422
|
+
#
|
423
|
+
def put_body(value)
|
424
|
+
value => ::String | ::Array
|
425
|
+
|
426
|
+
case value
|
427
|
+
in ::String then @body = [value]
|
428
|
+
in ::Array then @body = value
|
429
|
+
end
|
430
|
+
rescue ::NoMatchingPatternError
|
431
|
+
raise ::ArgumentError, 'body must be a string or an array'
|
432
|
+
end
|
433
|
+
|
434
|
+
# @parameter key [String] the header name
|
435
|
+
# @parameter value [String] the value to be used
|
436
|
+
#
|
437
|
+
# @return [void]
|
438
|
+
#
|
439
|
+
def put_header(key, value)
|
440
|
+
raise ::ArgumentError, 'key must be a string' unless key.is_a?(::String)
|
441
|
+
|
442
|
+
unless value.is_a?(::String) || value.is_a?(::Array)
|
443
|
+
raise ::ArgumentError, 'value must be a string or an array'
|
444
|
+
end
|
445
|
+
|
446
|
+
header_value = @headers[key]
|
447
|
+
|
448
|
+
new_values = ::Array.wrap(value)
|
449
|
+
existing_values = ::Array.wrap(header_value)
|
450
|
+
|
451
|
+
@headers[key] = (existing_values + new_values).uniq.join(', ')
|
452
|
+
end
|
453
|
+
|
454
|
+
# This method will delete a header by key.
|
455
|
+
#
|
456
|
+
# @parameter key [String] the header name
|
457
|
+
#
|
458
|
+
# @return [void]
|
459
|
+
#
|
460
|
+
def delete_header(key) = @headers.delete(key)
|
461
|
+
|
462
|
+
# @parameter value [String] the redirect location
|
463
|
+
#
|
464
|
+
# @return [void]
|
465
|
+
#
|
466
|
+
def location!(value)
|
467
|
+
value => ::String
|
468
|
+
|
469
|
+
put_header('Location', value)
|
470
|
+
end
|
471
|
+
|
472
|
+
# This method will finish the response.
|
473
|
+
#
|
474
|
+
# @return [void]
|
475
|
+
#
|
476
|
+
def finish!
|
477
|
+
default_router_header!
|
478
|
+
default_content_length! unless @headers['Content-Length']
|
479
|
+
default_html_content_type! unless @headers['Content-Type']
|
480
|
+
|
481
|
+
[@status, @headers, @body]
|
482
|
+
end
|
483
|
+
|
484
|
+
# This method will set the response default html content type.
|
485
|
+
#
|
486
|
+
# @return [void]
|
487
|
+
#
|
488
|
+
def default_html_content_type!
|
489
|
+
put_header('Content-Type', 'text/html')
|
490
|
+
end
|
491
|
+
|
492
|
+
# This method will set the response default content length.
|
493
|
+
#
|
494
|
+
# @return [void]
|
495
|
+
#
|
496
|
+
def default_content_length!
|
497
|
+
put_header('Content-Length', @body.join.size.to_s)
|
498
|
+
end
|
499
|
+
|
500
|
+
# This method will set the response default router header.
|
501
|
+
#
|
502
|
+
# @return [void]
|
503
|
+
#
|
504
|
+
def default_router_header!
|
505
|
+
put_header('Server', "Lennarb VERSION #{::Lennarb::VERSION}")
|
506
|
+
end
|
507
|
+
end
|
508
|
+
end
|
379
509
|
end
|