regal 0.1.0 → 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.
- checksums.yaml +4 -4
- data/.yardopts +1 -1
- data/README.md +145 -0
- data/lib/regal/app.rb +395 -137
- data/lib/regal/request.rb +17 -14
- data/lib/regal/response.rb +24 -0
- data/lib/regal/version.rb +2 -1
- data/spec/regal/app_spec.rb +606 -146
- data/spec/regal/request_spec.rb +12 -19
- data/spec/spec_helper.rb +7 -0
- metadata +4 -2
data/lib/regal/app.rb
CHANGED
@@ -2,122 +2,182 @@ require 'rack'
|
|
2
2
|
|
3
3
|
module Regal
|
4
4
|
module App
|
5
|
-
|
6
|
-
|
5
|
+
# Creates a new app described by the given block.
|
6
|
+
#
|
7
|
+
# @yield []
|
8
|
+
# @return [Class<Route>]
|
9
|
+
def self.create(&block)
|
10
|
+
Class.new(Route).create(&block)
|
7
11
|
end
|
8
12
|
|
9
|
-
|
10
|
-
|
13
|
+
# Creates a new app and instantiates it.
|
14
|
+
#
|
15
|
+
# This is the same as `App.create { }.new(attributes)`.
|
16
|
+
#
|
17
|
+
# @param [Hash] attributes
|
18
|
+
# @yield []
|
19
|
+
# @return [Route]
|
20
|
+
def self.new(attributes={}, &block)
|
21
|
+
create(&block).new(attributes)
|
11
22
|
end
|
12
23
|
end
|
13
24
|
|
14
|
-
module
|
15
|
-
|
25
|
+
module RoutesDsl
|
26
|
+
# @private
|
27
|
+
attr_reader :name,
|
28
|
+
:befores,
|
29
|
+
:afters,
|
30
|
+
:rescuers,
|
31
|
+
:handlers
|
16
32
|
|
33
|
+
# @private
|
17
34
|
def create(name=nil, &block)
|
35
|
+
@name = name
|
18
36
|
@mounted_apps = []
|
19
|
-
@
|
20
|
-
@dynamic_route = nil
|
37
|
+
@routes = {}
|
21
38
|
@handlers = {}
|
22
39
|
@befores = []
|
23
40
|
@afters = []
|
24
|
-
@setups = []
|
25
|
-
@middlewares = []
|
26
41
|
@rescuers = []
|
27
|
-
@name = name
|
28
42
|
class_exec(&block)
|
43
|
+
@mounted_apps.freeze
|
44
|
+
@routes.freeze
|
45
|
+
@handlers.freeze
|
46
|
+
@befores.freeze
|
47
|
+
@afters.freeze
|
48
|
+
@rescuers.freeze
|
29
49
|
self
|
30
50
|
end
|
31
51
|
|
32
|
-
|
33
|
-
|
34
|
-
setups + @setups
|
35
|
-
else
|
36
|
-
@setups && @setups.dup
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def befores
|
41
|
-
if superclass.respond_to?(:befores) && (befores = superclass.befores)
|
42
|
-
befores + @befores
|
43
|
-
else
|
44
|
-
@befores && @befores.dup
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def afters
|
49
|
-
if superclass.respond_to?(:afters) && (afters = superclass.afters)
|
50
|
-
afters + @afters
|
51
|
-
else
|
52
|
-
@afters && @afters.dup
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def middlewares
|
57
|
-
if superclass.respond_to?(:middlewares) && (middlewares = superclass.middlewares)
|
58
|
-
middlewares + @middlewares
|
59
|
-
else
|
60
|
-
@middlewares && @middlewares.dup
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def rescuers
|
65
|
-
if superclass.respond_to?(:rescuers) && (rescuers = superclass.rescuers)
|
66
|
-
rescuers + @rescuers
|
67
|
-
else
|
68
|
-
@rescuers && @rescuers.dup
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def create_routes(args)
|
52
|
+
# @private
|
53
|
+
def create_routes(attributes)
|
73
54
|
routes = {}
|
74
|
-
if @dynamic_route
|
75
|
-
routes.default = @dynamic_route.new(*args)
|
76
|
-
end
|
77
55
|
@mounted_apps.each do |app|
|
78
|
-
|
56
|
+
mounted_routes = app.create_routes(attributes)
|
57
|
+
mounted_routes.merge!(mounted_routes) do |name, route, _|
|
58
|
+
MountGraft.new(app, route)
|
59
|
+
end
|
60
|
+
routes.merge!(mounted_routes)
|
79
61
|
end
|
80
|
-
@
|
81
|
-
routes[path] = cls.new(
|
62
|
+
@routes.each do |path, cls|
|
63
|
+
routes[path] = cls.new(attributes)
|
64
|
+
end
|
65
|
+
if @routes.default
|
66
|
+
routes.default = @routes.default.new(attributes)
|
82
67
|
end
|
83
68
|
routes
|
84
69
|
end
|
85
70
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
71
|
+
# Defines a route.
|
72
|
+
#
|
73
|
+
# A route is either static or dynamic. Static routes match request path
|
74
|
+
# components verbatim, whereas dynamic routes captures their value. A
|
75
|
+
# static route is defined by a string and a dynamic route by a symbol.
|
76
|
+
#
|
77
|
+
# When routes are matched during request handling a static route will match
|
78
|
+
# if it is exactly equal to the next path component. A dynamic route will
|
79
|
+
# always match. All static routes are tried before any dynamic route.
|
80
|
+
#
|
81
|
+
# A route can only have a single dynamic child route. If you declare multiple
|
82
|
+
# dynamic routes only the last one is kept.
|
83
|
+
#
|
84
|
+
# @param [String, Symbol] s either a string, which creates a static route
|
85
|
+
# that matches a path component exactly, or a symbol, which captures the
|
86
|
+
# value of the path component.
|
87
|
+
# @yield []
|
88
|
+
# @return [void]
|
90
89
|
def route(s, &block)
|
91
90
|
r = Class.new(self).create(s, &block)
|
92
91
|
if s.is_a?(Symbol)
|
93
|
-
@
|
92
|
+
@routes.default = r
|
94
93
|
else
|
95
|
-
@
|
94
|
+
@routes[s] = r
|
96
95
|
end
|
96
|
+
nil
|
97
97
|
end
|
98
98
|
|
99
|
+
# Mount a child app.
|
100
|
+
#
|
101
|
+
# Mounting a child app makes that app's routes available as if they were
|
102
|
+
# defined in this route.
|
103
|
+
#
|
104
|
+
# @param [Class<Route>] app
|
105
|
+
# @return [void]
|
99
106
|
def mount(app)
|
100
107
|
@mounted_apps << app
|
108
|
+
nil
|
101
109
|
end
|
102
110
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
111
|
+
# Wrap the child routes and handlers in a scope.
|
112
|
+
#
|
113
|
+
# Scopes can have before, after and rescue blocks that are not shared with
|
114
|
+
# sibling scopes. They work more or less like mounting apps, but inline.
|
115
|
+
#
|
116
|
+
# @yield []
|
117
|
+
# @return [void]
|
118
|
+
def scope(&block)
|
119
|
+
mount(Class.new(self).create(&block))
|
109
120
|
end
|
110
121
|
|
122
|
+
# Register a before block for this route.
|
123
|
+
#
|
124
|
+
# Before blocks run before the request handler and have access to the
|
125
|
+
# request and response.
|
126
|
+
#
|
127
|
+
# A route can have any number of before blocks and they will be called
|
128
|
+
# in the order that they are declared with the outermost route's before
|
129
|
+
# blocks being called first, followed by child routes.
|
130
|
+
#
|
131
|
+
# @yield [request, response]
|
132
|
+
# @yieldparam request [Request]
|
133
|
+
# @yieldparam response [Response]
|
134
|
+
# @return [void]
|
111
135
|
def before(&block)
|
112
136
|
@befores << block
|
137
|
+
nil
|
113
138
|
end
|
114
139
|
|
140
|
+
# Register an after block for this route.
|
141
|
+
#
|
142
|
+
# After blocks run after the request handler and have access to the
|
143
|
+
# request and response.
|
144
|
+
#
|
145
|
+
# A route can have any number of after blocks and they will be called
|
146
|
+
# in the order that they are declared with the innermost route's after
|
147
|
+
# blocks being called first, followed by the parent route.
|
148
|
+
#
|
149
|
+
# @yield [request, response]
|
150
|
+
# @yieldparam request [Request]
|
151
|
+
# @yieldparam response [Response]
|
152
|
+
# @return [void]
|
115
153
|
def after(&block)
|
116
154
|
@afters << block
|
155
|
+
nil
|
117
156
|
end
|
118
157
|
|
158
|
+
# Register a rescue block for this route.
|
159
|
+
#
|
160
|
+
# Rescue blocks run when a before or after block, or a handler raises
|
161
|
+
# an error that matches the block's error type (compared using `#===`).
|
162
|
+
#
|
163
|
+
# A route can have any number of rescue blocks, but the order they are
|
164
|
+
# declared in is important. When an error is raised the blocks are searched
|
165
|
+
# in reverse order for a match, so the last declared rescue block with a
|
166
|
+
# matching type will be the one to handle the error.
|
167
|
+
#
|
168
|
+
# Once an error handler has been called the error is assumed to have been
|
169
|
+
# handled. If the error handler raises an error, the next matching handler
|
170
|
+
# will be found, or the error will bubble up outside the app.
|
171
|
+
#
|
172
|
+
# @param [Class<Error>] type
|
173
|
+
# @yield [error, request, response]
|
174
|
+
# @yieldparam error [Error]
|
175
|
+
# @yieldparam request [Request]
|
176
|
+
# @yieldparam response [Response]
|
177
|
+
# @return [void]
|
119
178
|
def rescue_from(type, &block)
|
120
179
|
@rescuers << [type, block]
|
180
|
+
nil
|
121
181
|
end
|
122
182
|
|
123
183
|
[:get, :head, :options, :delete, :post, :put, :patch].each do |name|
|
@@ -127,110 +187,308 @@ module Regal
|
|
127
187
|
end
|
128
188
|
end
|
129
189
|
|
190
|
+
# @!group Handlers
|
191
|
+
|
192
|
+
# Register a handler for `GET` requests to this route
|
193
|
+
#
|
194
|
+
# @!method get
|
195
|
+
# @yield [request, response]
|
196
|
+
# @yieldparam request [Request]
|
197
|
+
# @yieldparam response [Response]
|
198
|
+
# @yieldreturn [Object] the response body
|
199
|
+
# @return [void]
|
200
|
+
|
201
|
+
# Register a handler for `HEAD` requests to this route
|
202
|
+
#
|
203
|
+
# @!method head
|
204
|
+
# @yield [request, response]
|
205
|
+
# @yieldparam request [Request]
|
206
|
+
# @yieldparam response [Response]
|
207
|
+
# @yieldreturn [Object] the response body
|
208
|
+
# @return [void]
|
209
|
+
|
210
|
+
# Register a handler for `OPTIONS` requests to this route
|
211
|
+
#
|
212
|
+
# @!method options
|
213
|
+
# @yield [request, response]
|
214
|
+
# @yieldparam request [Request]
|
215
|
+
# @yieldparam response [Response]
|
216
|
+
# @yieldreturn [Object] the response body
|
217
|
+
# @return [void]
|
218
|
+
|
219
|
+
# Register a handler for `DELETE` requests to this route
|
220
|
+
#
|
221
|
+
# @!method delete
|
222
|
+
# @yield [request, response]
|
223
|
+
# @yieldparam request [Request]
|
224
|
+
# @yieldparam response [Response]
|
225
|
+
# @yieldreturn [Object] the response body
|
226
|
+
# @return [void]
|
227
|
+
|
228
|
+
# Register a handler for `POST` requests to this route
|
229
|
+
#
|
230
|
+
# @!method post
|
231
|
+
# @yield [request, response]
|
232
|
+
# @yieldparam request [Request]
|
233
|
+
# @yieldparam response [Response]
|
234
|
+
# @yieldreturn [Object] the response body
|
235
|
+
# @return [void]
|
236
|
+
|
237
|
+
# Register a handler for `PUT` requests to this route
|
238
|
+
#
|
239
|
+
# @!method put
|
240
|
+
# @yield [request, response]
|
241
|
+
# @yieldparam request [Request]
|
242
|
+
# @yieldparam response [Response]
|
243
|
+
# @yieldreturn [Object] the response body
|
244
|
+
# @return [void]
|
245
|
+
|
246
|
+
# Register a handler for `PATCH` requests to this route
|
247
|
+
#
|
248
|
+
# @!method patch
|
249
|
+
# @yield [request, response]
|
250
|
+
# @yieldparam request [Request]
|
251
|
+
# @yieldparam response [Response]
|
252
|
+
# @yieldreturn [Object] the response body
|
253
|
+
# @return [void]
|
254
|
+
|
255
|
+
# Register a handler for any request method
|
256
|
+
#
|
257
|
+
# `any` handlers are called when there is no specific handler in this route
|
258
|
+
# for the request method.
|
259
|
+
#
|
260
|
+
# @yield [request, response]
|
261
|
+
# @yieldparam request [Request]
|
262
|
+
# @yieldparam response [Response]
|
263
|
+
# @yieldreturn [Object] the response body
|
264
|
+
# @return [void]
|
130
265
|
def any(&block)
|
131
266
|
@handlers.default = block
|
132
267
|
end
|
133
|
-
end
|
134
268
|
|
135
|
-
|
136
|
-
|
269
|
+
# @!endgroup
|
270
|
+
end
|
137
271
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
272
|
+
# @private
|
273
|
+
module Arounds
|
274
|
+
def before(request, response)
|
275
|
+
@befores.each do |before|
|
276
|
+
unless response.finished?
|
277
|
+
@route.instance_exec(request, response, &before)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
146
281
|
|
147
|
-
|
282
|
+
def after(request, response)
|
283
|
+
@afters.each do |after|
|
284
|
+
begin
|
285
|
+
@route.instance_exec(request, response, &after)
|
286
|
+
rescue => e
|
287
|
+
raise unless rescue_error(e, request, response)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
148
291
|
|
149
|
-
def
|
150
|
-
@
|
151
|
-
|
152
|
-
|
292
|
+
def rescue_error(e, request, response)
|
293
|
+
@rescuers.reverse_each do |type, handler|
|
294
|
+
if type === e
|
295
|
+
@route.instance_exec(e, request, response, &handler)
|
296
|
+
return true
|
297
|
+
end
|
153
298
|
end
|
299
|
+
false
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
# A route is an application, or a part of an application
|
304
|
+
class Route
|
305
|
+
extend RoutesDsl
|
306
|
+
include Arounds
|
307
|
+
|
308
|
+
# @private
|
309
|
+
attr_reader :name,
|
310
|
+
:routes
|
311
|
+
|
312
|
+
# Create a new application with this route as its root
|
313
|
+
#
|
314
|
+
# @param [Hash] attributes a copy of this hash will be available
|
315
|
+
# from {Request#attributes} during request processing
|
316
|
+
def initialize(attributes)
|
317
|
+
@attributes = attributes.dup.freeze
|
318
|
+
@name = self.class.name
|
154
319
|
@befores = self.class.befores
|
155
|
-
@afters = self.class.afters
|
320
|
+
@afters = self.class.afters
|
156
321
|
@rescuers = self.class.rescuers
|
157
|
-
@routes = self.class.create_routes(args)
|
158
322
|
@handlers = self.class.handlers
|
159
|
-
@
|
160
|
-
|
161
|
-
@app = self.class.middlewares.reduce(method(:handle)) do |app, (middleware, args, block)|
|
162
|
-
middleware.new(app, *args, &block)
|
163
|
-
end
|
164
|
-
end
|
323
|
+
@routes = self.class.create_routes(attributes)
|
324
|
+
@route = self
|
165
325
|
freeze
|
166
326
|
end
|
167
327
|
|
328
|
+
# Route and handle a request
|
329
|
+
#
|
330
|
+
# @param [Hash] env
|
331
|
+
# @return [Array<(Integer, Hash, Enumerable)>]
|
168
332
|
def call(env)
|
169
|
-
path_components =
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
333
|
+
path_components = path_components = env[PATH_INFO_KEY].split(SLASH).drop(1)
|
334
|
+
parent_routes, path_captures = match_route(path_components)
|
335
|
+
matching_route = parent_routes.last
|
336
|
+
request_method = env[REQUEST_METHOD_KEY]
|
337
|
+
if matching_route && matching_route.can_handle?(request_method)
|
338
|
+
request = Request.new(env, path_captures, @attributes)
|
339
|
+
response = Response.new
|
340
|
+
finishing_route = run_befores(parent_routes, request, response)
|
341
|
+
if finishing_route.nil? && !response.finished?
|
342
|
+
begin
|
343
|
+
result = matching_route.handle(request_method, request, response)
|
344
|
+
unless response.finished?
|
345
|
+
response.body = result
|
346
|
+
end
|
347
|
+
rescue => e
|
348
|
+
finishing_route = handle_error(parent_routes, finishing_route, e, request, response)
|
349
|
+
end
|
176
350
|
end
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
@app.call(env)
|
181
|
-
else
|
182
|
-
handle(env)
|
351
|
+
run_afters(parent_routes, finishing_route, request, response)
|
352
|
+
if no_body_response?(request_method, response)
|
353
|
+
response.no_body
|
183
354
|
end
|
355
|
+
response
|
356
|
+
elsif matching_route
|
357
|
+
METHOD_NOT_ALLOWED_RESPONSE
|
184
358
|
else
|
185
359
|
NOT_FOUND_RESPONSE
|
186
360
|
end
|
187
361
|
end
|
188
362
|
|
363
|
+
# @private
|
364
|
+
def can_handle?(request_method)
|
365
|
+
!!@handlers[request_method]
|
366
|
+
end
|
367
|
+
|
368
|
+
# @private
|
369
|
+
def handle(request_method, request, response)
|
370
|
+
handler = @handlers[request_method]
|
371
|
+
instance_exec(request, response, &handler)
|
372
|
+
end
|
373
|
+
|
189
374
|
private
|
190
375
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
376
|
+
METHOD_NOT_ALLOWED_RESPONSE = [405, {}.freeze, [].freeze].freeze
|
377
|
+
NOT_FOUND_RESPONSE = [404, {}.freeze, [].freeze].freeze
|
378
|
+
SLASH = '/'.freeze
|
379
|
+
PATH_INFO_KEY = 'PATH_INFO'.freeze
|
380
|
+
REQUEST_METHOD_KEY = 'REQUEST_METHOD'.freeze
|
381
|
+
HEAD_METHOD = 'HEAD'.freeze
|
382
|
+
|
383
|
+
def no_body_response?(request_method, response)
|
384
|
+
request_method == HEAD_METHOD || response.status < 200 || response.status == 204 || response.status == 205 || response.status == 304
|
385
|
+
end
|
386
|
+
|
387
|
+
def match_route(path_components)
|
388
|
+
path_captures = {}
|
389
|
+
matching_route = self
|
390
|
+
parent_routes = [self]
|
391
|
+
path_components.each do |path_component|
|
392
|
+
if matching_route
|
393
|
+
wildcard_route = !matching_route.routes.include?(path_component)
|
394
|
+
matching_route = matching_route.routes[path_component]
|
395
|
+
if matching_route && wildcard_route
|
396
|
+
path_captures[matching_route.name] = path_component
|
199
397
|
end
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
398
|
+
end
|
399
|
+
parent_routes << matching_route
|
400
|
+
end
|
401
|
+
[parent_routes, path_captures]
|
402
|
+
end
|
403
|
+
|
404
|
+
def run_befores(parent_routes, request, response)
|
405
|
+
parent_routes.each do |parent_route|
|
406
|
+
begin
|
407
|
+
parent_route.before(request, response)
|
408
|
+
if response.finished?
|
409
|
+
return parent_route
|
207
410
|
end
|
208
411
|
rescue => e
|
209
|
-
handle_error(e, request, response)
|
412
|
+
return handle_error(parent_routes, parent_route, e, request, response)
|
210
413
|
end
|
211
|
-
|
414
|
+
end
|
415
|
+
nil
|
416
|
+
end
|
417
|
+
|
418
|
+
def run_afters(parent_routes, finishing_route, request, response)
|
419
|
+
skip_routes = !finishing_route.nil?
|
420
|
+
parent_routes.reverse_each do |parent_route|
|
421
|
+
if !skip_routes || finishing_route == parent_route
|
422
|
+
skip_routes = false
|
212
423
|
begin
|
213
|
-
|
424
|
+
parent_route.after(request, response)
|
214
425
|
rescue => e
|
215
|
-
|
426
|
+
skip_routes = true
|
427
|
+
finishing_route = handle_error(parent_routes, parent_route, e, request, response)
|
216
428
|
end
|
217
429
|
end
|
218
|
-
response
|
219
|
-
else
|
220
|
-
METHOD_NOT_ALLOWED_RESPONSE
|
221
430
|
end
|
222
431
|
end
|
223
432
|
|
224
|
-
def handle_error(e, request, response)
|
225
|
-
|
226
|
-
|
227
|
-
if
|
228
|
-
|
229
|
-
|
230
|
-
|
433
|
+
def handle_error(parent_routes, finishing_route, e, request, response)
|
434
|
+
skip_routes = !finishing_route.nil?
|
435
|
+
parent_routes.reverse_each do |parent_route|
|
436
|
+
if !skip_routes || finishing_route == parent_route
|
437
|
+
skip_routes = false
|
438
|
+
begin
|
439
|
+
if parent_route.rescue_error(e, request, response)
|
440
|
+
return parent_route
|
441
|
+
end
|
442
|
+
rescue => e
|
443
|
+
if parent_routes.first == parent_route
|
444
|
+
raise e
|
445
|
+
else
|
446
|
+
next_level = parent_routes[parent_routes.index(parent_route) - 1]
|
447
|
+
return handle_error(parent_routes, next_level, e, request, response)
|
448
|
+
end
|
449
|
+
end
|
231
450
|
end
|
232
451
|
end
|
233
|
-
raise
|
452
|
+
raise e
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
# @private
|
457
|
+
class MountGraft
|
458
|
+
include Arounds
|
459
|
+
|
460
|
+
attr_reader :name,
|
461
|
+
:routes
|
462
|
+
|
463
|
+
def initialize(mounted_app, route)
|
464
|
+
@route = route
|
465
|
+
@name = route.name
|
466
|
+
@routes = route.routes
|
467
|
+
@befores = mounted_app.befores
|
468
|
+
@afters = mounted_app.afters
|
469
|
+
@rescuers = mounted_app.rescuers
|
470
|
+
end
|
471
|
+
|
472
|
+
def can_handle?(request_method)
|
473
|
+
@route.can_handle?(request_method)
|
474
|
+
end
|
475
|
+
|
476
|
+
def handle(*args)
|
477
|
+
@route.handle(*args)
|
478
|
+
end
|
479
|
+
|
480
|
+
def before(*args)
|
481
|
+
super
|
482
|
+
@route.before(*args)
|
483
|
+
end
|
484
|
+
|
485
|
+
def after(*args)
|
486
|
+
@route.after(*args)
|
487
|
+
super
|
488
|
+
end
|
489
|
+
|
490
|
+
def rescue_error(e, *args)
|
491
|
+
@route.rescue_error(e, *args) or super
|
234
492
|
end
|
235
493
|
end
|
236
494
|
end
|
data/lib/regal/request.rb
CHANGED
@@ -1,29 +1,31 @@
|
|
1
1
|
module Regal
|
2
2
|
class Request
|
3
|
-
|
3
|
+
# @!attribute [r] env
|
4
|
+
# @return [Hash]
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
@attributes = {}
|
8
|
-
end
|
6
|
+
# @!attribute [r] attributes
|
7
|
+
# @return [Hash]
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
end
|
9
|
+
attr_reader :env,
|
10
|
+
:attributes
|
13
11
|
|
14
|
-
|
15
|
-
|
12
|
+
# @private
|
13
|
+
def initialize(env, path_captures, attributes_prototype={})
|
14
|
+
@env = env
|
15
|
+
@path_captures = path_captures
|
16
|
+
@attributes = attributes_prototype.dup
|
16
17
|
end
|
17
18
|
|
19
|
+
# @return [Hash]
|
18
20
|
def parameters
|
19
21
|
@parameters ||= begin
|
20
|
-
path_captures = @env[Route::PATH_CAPTURES_KEY]
|
21
22
|
query = Rack::Utils.parse_query(@env[QUERY_STRING_KEY])
|
22
|
-
query.merge!(path_captures)
|
23
|
+
query.merge!(@path_captures)
|
23
24
|
query.freeze
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
28
|
+
# @return [Hash]
|
27
29
|
def headers
|
28
30
|
@headers ||= begin
|
29
31
|
headers = @env.each_with_object({}) do |(key, value), headers|
|
@@ -44,10 +46,13 @@ module Regal
|
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
49
|
+
# @return [IO]
|
47
50
|
def body
|
48
51
|
@env[RACK_INPUT_KEY]
|
49
52
|
end
|
50
53
|
|
54
|
+
private
|
55
|
+
|
51
56
|
HEADER_PREFIX = 'HTTP_'.freeze
|
52
57
|
QUERY_STRING_KEY = 'QUERY_STRING'.freeze
|
53
58
|
CONTENT_LENGTH_KEY = 'CONTENT_LENGTH'.freeze
|
@@ -55,7 +60,5 @@ module Regal
|
|
55
60
|
CONTENT_TYPE_KEY = 'CONTENT_TYPE'.freeze
|
56
61
|
CONTENT_TYPE_HEADER = 'Content-Type'.freeze
|
57
62
|
RACK_INPUT_KEY = 'rack.input'.freeze
|
58
|
-
REQUEST_METHOD_KEY = 'REQUEST_METHOD'.freeze
|
59
|
-
HEAD_METHOD = 'HEAD'.freeze
|
60
63
|
end
|
61
64
|
end
|