hanami-controller 1.3.2 → 2.0.0.alpha3
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 +83 -0
- data/LICENSE.md +1 -1
- data/README.md +299 -537
- data/hanami-controller.gemspec +5 -4
- data/lib/hanami/action/application_action.rb +112 -0
- data/lib/hanami/action/application_configuration/cookies.rb +29 -0
- data/lib/hanami/action/application_configuration/sessions.rb +46 -0
- data/lib/hanami/action/application_configuration.rb +92 -0
- data/lib/hanami/action/base_params.rb +2 -2
- data/lib/hanami/action/cache/cache_control.rb +4 -4
- data/lib/hanami/action/cache/conditional_get.rb +3 -1
- data/lib/hanami/action/cache/directives.rb +1 -1
- data/lib/hanami/action/cache/expires.rb +3 -3
- data/lib/hanami/action/cache.rb +1 -139
- data/lib/hanami/action/configuration.rb +428 -0
- data/lib/hanami/action/cookie_jar.rb +3 -3
- data/lib/hanami/action/cookies.rb +3 -62
- data/lib/hanami/action/csrf_protection.rb +214 -0
- data/lib/hanami/action/flash.rb +102 -207
- data/lib/hanami/action/glue.rb +5 -31
- data/lib/hanami/action/halt.rb +12 -0
- data/lib/hanami/action/mime.rb +78 -485
- data/lib/hanami/action/params.rb +2 -2
- data/lib/hanami/action/rack/file.rb +1 -1
- data/lib/hanami/action/request.rb +30 -20
- data/lib/hanami/action/response.rb +193 -0
- data/lib/hanami/action/session.rb +11 -128
- data/lib/hanami/action/standalone_action.rb +579 -0
- data/lib/hanami/action/validatable.rb +1 -1
- data/lib/hanami/action/view_name_inferrer.rb +46 -0
- data/lib/hanami/action.rb +129 -73
- data/lib/hanami/controller/version.rb +1 -1
- data/lib/hanami/controller.rb +0 -227
- data/lib/hanami/http/status.rb +2 -2
- metadata +45 -27
- data/lib/hanami/action/callable.rb +0 -92
- data/lib/hanami/action/callbacks.rb +0 -214
- data/lib/hanami/action/configurable.rb +0 -50
- data/lib/hanami/action/exposable/guard.rb +0 -104
- data/lib/hanami/action/exposable.rb +0 -126
- data/lib/hanami/action/head.rb +0 -121
- data/lib/hanami/action/rack/callable.rb +0 -47
- data/lib/hanami/action/rack.rb +0 -399
- data/lib/hanami/action/redirect.rb +0 -59
- data/lib/hanami/action/throwable.rb +0 -196
- data/lib/hanami/controller/configuration.rb +0 -763
- data/lib/hanami-controller.rb +0 -1
@@ -1,47 +0,0 @@
|
|
1
|
-
module Hanami
|
2
|
-
module Action
|
3
|
-
module Rack
|
4
|
-
module Callable
|
5
|
-
# Callable module for actions. With this module, actions with middlewares
|
6
|
-
# will be able to work with rack builder.
|
7
|
-
#
|
8
|
-
# @param env [Hash] the full Rack env or the params. This value may vary,
|
9
|
-
# see the examples below.
|
10
|
-
#
|
11
|
-
# @since 0.4.0
|
12
|
-
# @api private
|
13
|
-
#
|
14
|
-
# @see Hanami::Action::Rack::ClassMethods#rack_builder
|
15
|
-
# @see Hanami::Action::Rack::ClassMethods#use
|
16
|
-
#
|
17
|
-
# @example
|
18
|
-
# require 'hanami/controller'
|
19
|
-
#
|
20
|
-
# class MyMiddleware
|
21
|
-
# def initialize(app)
|
22
|
-
# @app = app
|
23
|
-
# end
|
24
|
-
#
|
25
|
-
# def call(env)
|
26
|
-
# #...
|
27
|
-
# end
|
28
|
-
# end
|
29
|
-
#
|
30
|
-
# class Show
|
31
|
-
# include Hanami::Action
|
32
|
-
# use MyMiddleware
|
33
|
-
#
|
34
|
-
# def call(params)
|
35
|
-
# # ...
|
36
|
-
# puts params # => { id: 23 } extracted from Rack env
|
37
|
-
# end
|
38
|
-
# end
|
39
|
-
#
|
40
|
-
# Show.respond_to?(:call) # => true
|
41
|
-
def call(env)
|
42
|
-
rack_builder.call(env)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
data/lib/hanami/action/rack.rb
DELETED
@@ -1,399 +0,0 @@
|
|
1
|
-
require 'securerandom'
|
2
|
-
require 'hanami/action/request'
|
3
|
-
require 'hanami/action/base_params'
|
4
|
-
require 'hanami/action/rack/callable'
|
5
|
-
require 'hanami/action/rack/file'
|
6
|
-
require 'hanami/utils/deprecation'
|
7
|
-
|
8
|
-
module Hanami
|
9
|
-
module Action
|
10
|
-
# Rack integration API
|
11
|
-
#
|
12
|
-
# @since 0.1.0
|
13
|
-
module Rack
|
14
|
-
# Rack SPEC response code
|
15
|
-
#
|
16
|
-
# @since 1.0.0
|
17
|
-
# @api private
|
18
|
-
RESPONSE_CODE = 0
|
19
|
-
|
20
|
-
# Rack SPEC response headers
|
21
|
-
#
|
22
|
-
# @since 1.0.0
|
23
|
-
# @api private
|
24
|
-
RESPONSE_HEADERS = 1
|
25
|
-
|
26
|
-
# Rack SPEC response body
|
27
|
-
#
|
28
|
-
# @since 1.0.0
|
29
|
-
# @api private
|
30
|
-
RESPONSE_BODY = 2
|
31
|
-
|
32
|
-
# The default HTTP response code
|
33
|
-
#
|
34
|
-
# @since 0.1.0
|
35
|
-
# @api private
|
36
|
-
DEFAULT_RESPONSE_CODE = 200
|
37
|
-
|
38
|
-
# Not Found
|
39
|
-
#
|
40
|
-
# @since 1.0.0
|
41
|
-
# @api private
|
42
|
-
NOT_FOUND = 404
|
43
|
-
|
44
|
-
# The default Rack response body
|
45
|
-
#
|
46
|
-
# @since 0.1.0
|
47
|
-
# @api private
|
48
|
-
DEFAULT_RESPONSE_BODY = []
|
49
|
-
|
50
|
-
# The default HTTP Request ID length
|
51
|
-
#
|
52
|
-
# @since 0.3.0
|
53
|
-
# @api private
|
54
|
-
#
|
55
|
-
# @see Hanami::Action::Rack#request_id
|
56
|
-
DEFAULT_REQUEST_ID_LENGTH = 16
|
57
|
-
|
58
|
-
# The request method
|
59
|
-
#
|
60
|
-
# @since 0.3.2
|
61
|
-
# @api private
|
62
|
-
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
63
|
-
|
64
|
-
# The Content-Length HTTP header
|
65
|
-
#
|
66
|
-
# @since 1.0.0
|
67
|
-
# @api private
|
68
|
-
CONTENT_LENGTH = 'Content-Length'.freeze
|
69
|
-
|
70
|
-
# The non-standard HTTP header to pass the control over when a resource
|
71
|
-
# cannot be found by the current endpoint
|
72
|
-
#
|
73
|
-
# @since 1.0.0
|
74
|
-
# @api private
|
75
|
-
X_CASCADE = 'X-Cascade'.freeze
|
76
|
-
|
77
|
-
# HEAD request
|
78
|
-
#
|
79
|
-
# @since 0.3.2
|
80
|
-
# @api private
|
81
|
-
HEAD = 'HEAD'.freeze
|
82
|
-
|
83
|
-
# The key that returns router parsed body from the Rack env
|
84
|
-
ROUTER_PARSED_BODY = 'router.parsed_body'.freeze
|
85
|
-
|
86
|
-
# Override Ruby's hook for modules.
|
87
|
-
# It includes basic Hanami::Action modules to the given class.
|
88
|
-
#
|
89
|
-
# @param base [Class] the target action
|
90
|
-
#
|
91
|
-
# @since 0.1.0
|
92
|
-
# @api private
|
93
|
-
#
|
94
|
-
# @see http://www.ruby-doc.org/core-2.1.2/Module.html#method-i-included
|
95
|
-
def self.included(base)
|
96
|
-
base.class_eval do
|
97
|
-
extend ClassMethods
|
98
|
-
prepend InstanceMethods
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
# @api private
|
103
|
-
module ClassMethods
|
104
|
-
# Build rack builder
|
105
|
-
#
|
106
|
-
# @return [Rack::Builder]
|
107
|
-
# @api private
|
108
|
-
def rack_builder
|
109
|
-
@rack_builder ||= begin
|
110
|
-
extend Hanami::Action::Rack::Callable
|
111
|
-
rack_builder = ::Rack::Builder.new
|
112
|
-
rack_builder.run ->(env) { self.new.call(env) }
|
113
|
-
rack_builder
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
# Use a Rack middleware
|
118
|
-
#
|
119
|
-
# The middleware will be used as it is.
|
120
|
-
#
|
121
|
-
# At the runtime, the middleware be invoked with the raw Rack env.
|
122
|
-
#
|
123
|
-
# Multiple middlewares can be employed, just by using multiple times
|
124
|
-
# this method.
|
125
|
-
#
|
126
|
-
# @param middleware [#call] A Rack middleware
|
127
|
-
# @param args [Array] Array arguments for middleware
|
128
|
-
#
|
129
|
-
# @since 0.2.0
|
130
|
-
#
|
131
|
-
# @see Hanami::Action::Callbacks::ClassMethods#before
|
132
|
-
#
|
133
|
-
# @example Middleware
|
134
|
-
# require 'hanami/controller'
|
135
|
-
#
|
136
|
-
# module Sessions
|
137
|
-
# class Create
|
138
|
-
# include Hanami::Action
|
139
|
-
# use OmniAuth
|
140
|
-
#
|
141
|
-
# def call(params)
|
142
|
-
# # ...
|
143
|
-
# end
|
144
|
-
# end
|
145
|
-
# end
|
146
|
-
def use(middleware, *args, &block)
|
147
|
-
rack_builder.use middleware, *args, &block
|
148
|
-
end
|
149
|
-
|
150
|
-
# Returns the class which defines the params
|
151
|
-
#
|
152
|
-
# Returns the class which has been provided to define the
|
153
|
-
# params. By default this will be Hanami::Action::Params.
|
154
|
-
#
|
155
|
-
# @return [Class] A params class (when whitelisted) or
|
156
|
-
# Hanami::Action::Params
|
157
|
-
#
|
158
|
-
# @api private
|
159
|
-
# @since 0.7.0
|
160
|
-
def params_class
|
161
|
-
@params_class ||= BaseParams
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
# @since 0.7.0
|
166
|
-
# @api private
|
167
|
-
module InstanceMethods
|
168
|
-
# @since 0.7.0
|
169
|
-
# @api private
|
170
|
-
def initialize(*)
|
171
|
-
super
|
172
|
-
@_status = nil
|
173
|
-
@_body = nil
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
protected
|
178
|
-
# Gets the headers from the response
|
179
|
-
#
|
180
|
-
# @return [Hash] the HTTP headers from the response
|
181
|
-
#
|
182
|
-
# @since 0.1.0
|
183
|
-
#
|
184
|
-
# @example
|
185
|
-
# require 'hanami/controller'
|
186
|
-
#
|
187
|
-
# class Show
|
188
|
-
# include Hanami::Action
|
189
|
-
#
|
190
|
-
# def call(params)
|
191
|
-
# # ...
|
192
|
-
# self.headers # => { ... }
|
193
|
-
# self.headers.merge!({'X-Custom' => 'OK'})
|
194
|
-
# end
|
195
|
-
# end
|
196
|
-
def headers
|
197
|
-
@headers
|
198
|
-
end
|
199
|
-
|
200
|
-
# Returns a serialized Rack response (Array), according to the current
|
201
|
-
# status code, headers, and body.
|
202
|
-
#
|
203
|
-
# @return [Array] the serialized response
|
204
|
-
#
|
205
|
-
# @since 0.1.0
|
206
|
-
# @api private
|
207
|
-
#
|
208
|
-
# @see Hanami::Action::Rack::DEFAULT_RESPONSE_CODE
|
209
|
-
# @see Hanami::Action::Rack::DEFAULT_RESPONSE_BODY
|
210
|
-
# @see Hanami::Action::Rack#status=
|
211
|
-
# @see Hanami::Action::Rack#headers
|
212
|
-
# @see Hanami::Action::Rack#body=
|
213
|
-
def response
|
214
|
-
[ @_status || DEFAULT_RESPONSE_CODE, headers, @_body || DEFAULT_RESPONSE_BODY.dup ]
|
215
|
-
end
|
216
|
-
|
217
|
-
# Calculates an unique ID for the current request
|
218
|
-
#
|
219
|
-
# @return [String] The unique ID
|
220
|
-
#
|
221
|
-
# @since 0.3.0
|
222
|
-
def request_id
|
223
|
-
# FIXME make this number configurable and document the probabilities of clashes
|
224
|
-
@request_id ||= SecureRandom.hex(DEFAULT_REQUEST_ID_LENGTH)
|
225
|
-
end
|
226
|
-
|
227
|
-
# Returns a Hanami specialized rack request
|
228
|
-
#
|
229
|
-
# @return [Hanami::Action::Request] The request
|
230
|
-
#
|
231
|
-
# @since 0.3.1
|
232
|
-
#
|
233
|
-
# @example
|
234
|
-
# require 'hanami/controller'
|
235
|
-
#
|
236
|
-
# class Create
|
237
|
-
# include Hanami::Action
|
238
|
-
#
|
239
|
-
# def call(params)
|
240
|
-
# ip = request.ip
|
241
|
-
# secure = request.ssl?
|
242
|
-
# end
|
243
|
-
# end
|
244
|
-
def request
|
245
|
-
@request ||= ::Hanami::Action::Request.new(@_env)
|
246
|
-
end
|
247
|
-
|
248
|
-
# Return parsed request body
|
249
|
-
#
|
250
|
-
# @deprecated
|
251
|
-
def parsed_request_body
|
252
|
-
Hanami::Utils::Deprecation.new('#parsed_request_body is deprecated and it will be removed in future versions')
|
253
|
-
@_env.fetch(ROUTER_PARSED_BODY, nil)
|
254
|
-
end
|
255
|
-
|
256
|
-
private
|
257
|
-
|
258
|
-
# Sets the HTTP status code for the response
|
259
|
-
#
|
260
|
-
# @param status [Fixnum] an HTTP status code
|
261
|
-
# @return [void]
|
262
|
-
#
|
263
|
-
# @since 0.1.0
|
264
|
-
#
|
265
|
-
# @example
|
266
|
-
# require 'hanami/controller'
|
267
|
-
#
|
268
|
-
# class Create
|
269
|
-
# include Hanami::Action
|
270
|
-
#
|
271
|
-
# def call(params)
|
272
|
-
# # ...
|
273
|
-
# self.status = 201
|
274
|
-
# end
|
275
|
-
# end
|
276
|
-
def status=(status)
|
277
|
-
@_status = status
|
278
|
-
end
|
279
|
-
|
280
|
-
# Sets the body of the response
|
281
|
-
#
|
282
|
-
# @param body [String] the body of the response
|
283
|
-
# @return [void]
|
284
|
-
#
|
285
|
-
# @since 0.1.0
|
286
|
-
#
|
287
|
-
# @example
|
288
|
-
# require 'hanami/controller'
|
289
|
-
#
|
290
|
-
# class Show
|
291
|
-
# include Hanami::Action
|
292
|
-
#
|
293
|
-
# def call(params)
|
294
|
-
# # ...
|
295
|
-
# self.body = 'Hi!'
|
296
|
-
# end
|
297
|
-
# end
|
298
|
-
def body=(body)
|
299
|
-
body = Array(body) unless body.respond_to?(:each)
|
300
|
-
@_body = body
|
301
|
-
end
|
302
|
-
|
303
|
-
# Send a file as response.
|
304
|
-
# <tt>This method only sends files from the public directory</tt>
|
305
|
-
#
|
306
|
-
# It automatically handle the following cases:
|
307
|
-
#
|
308
|
-
# * <tt>Content-Type</tt> and <tt>Content-Length</tt>
|
309
|
-
# * File Not found (returns a 404)
|
310
|
-
# * Conditional GET (via <tt>If-Modified-Since</tt> header)
|
311
|
-
# * Range requests (via <tt>Range</tt> header)
|
312
|
-
#
|
313
|
-
# @param path [String, Pathname] the body of the response
|
314
|
-
# @return [void]
|
315
|
-
#
|
316
|
-
# @since 0.4.3
|
317
|
-
#
|
318
|
-
# @example
|
319
|
-
# require 'hanami/controller'
|
320
|
-
#
|
321
|
-
# class Show
|
322
|
-
# include Hanami::Action
|
323
|
-
#
|
324
|
-
# def call(params)
|
325
|
-
# # ...
|
326
|
-
# send_file Pathname.new('path/to/file')
|
327
|
-
# end
|
328
|
-
# end
|
329
|
-
def send_file(path)
|
330
|
-
_send_file(
|
331
|
-
File.new(path, self.class.configuration.public_directory).call(@_env)
|
332
|
-
)
|
333
|
-
end
|
334
|
-
|
335
|
-
# Send a file as response from anywhere in the file system.
|
336
|
-
#
|
337
|
-
# @see Hanami::Action::Rack#send_file
|
338
|
-
#
|
339
|
-
# @param path [String, Pathname] path to the file to be sent
|
340
|
-
# @return [void]
|
341
|
-
#
|
342
|
-
# @since 1.0.0
|
343
|
-
#
|
344
|
-
# @example
|
345
|
-
# require 'hanami/controller'
|
346
|
-
#
|
347
|
-
# class Show
|
348
|
-
# include Hanami::Action
|
349
|
-
#
|
350
|
-
# def call(params)
|
351
|
-
# # ...
|
352
|
-
# unsafe_send_file Pathname.new('/tmp/path/to/file')
|
353
|
-
# end
|
354
|
-
# end
|
355
|
-
def unsafe_send_file(path)
|
356
|
-
directory = self.class.configuration.root_directory if Pathname.new(path).relative?
|
357
|
-
|
358
|
-
_send_file(
|
359
|
-
File.new(path, directory).call(@_env)
|
360
|
-
)
|
361
|
-
end
|
362
|
-
|
363
|
-
# Check if the current request is a HEAD
|
364
|
-
#
|
365
|
-
# @return [TrueClass,FalseClass] the result of the check
|
366
|
-
#
|
367
|
-
# @since 0.3.2
|
368
|
-
def head?
|
369
|
-
request_method == HEAD
|
370
|
-
end
|
371
|
-
|
372
|
-
# NOTE: <tt>Hanami::Action::CSRFProtection</tt> (<tt>hanamirb</tt> gem) depends on this.
|
373
|
-
#
|
374
|
-
# @api private
|
375
|
-
# @since 0.4.4
|
376
|
-
def request_method
|
377
|
-
@_env[REQUEST_METHOD]
|
378
|
-
end
|
379
|
-
|
380
|
-
# @since 1.0.0
|
381
|
-
# @api private
|
382
|
-
def _send_file(response)
|
383
|
-
headers.merge!(response[RESPONSE_HEADERS])
|
384
|
-
|
385
|
-
if response[RESPONSE_CODE] == NOT_FOUND
|
386
|
-
headers.delete(X_CASCADE)
|
387
|
-
headers.delete(CONTENT_LENGTH)
|
388
|
-
halt NOT_FOUND
|
389
|
-
else
|
390
|
-
# FIXME: this is a fix for https://github.com/hanami/controller/issues/240
|
391
|
-
# It's here to maintain the backward compat with 1.1, as we can't remove `#halt`
|
392
|
-
# We should review the workflow for 2.0, because I don't like callbacks to be referenced from here.
|
393
|
-
_run_after_callbacks(params)
|
394
|
-
halt response[RESPONSE_CODE], response[RESPONSE_BODY]
|
395
|
-
end
|
396
|
-
end
|
397
|
-
end
|
398
|
-
end
|
399
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
module Hanami
|
2
|
-
module Action
|
3
|
-
# HTTP redirect API
|
4
|
-
#
|
5
|
-
# @since 0.1.0
|
6
|
-
module Redirect
|
7
|
-
# The HTTP header for redirects
|
8
|
-
#
|
9
|
-
# @since 0.2.0
|
10
|
-
# @api private
|
11
|
-
LOCATION = 'Location'.freeze
|
12
|
-
|
13
|
-
private
|
14
|
-
|
15
|
-
# Redirect to the given URL and halt the request
|
16
|
-
#
|
17
|
-
# @param url [String] the destination URL
|
18
|
-
# @param status [Fixnum] the http code
|
19
|
-
#
|
20
|
-
# @since 0.1.0
|
21
|
-
#
|
22
|
-
# @see Hanami::Action::Throwable#halt
|
23
|
-
#
|
24
|
-
# @example With default status code (302)
|
25
|
-
# require 'hanami/controller'
|
26
|
-
#
|
27
|
-
# class Create
|
28
|
-
# include Hanami::Action
|
29
|
-
#
|
30
|
-
# def call(params)
|
31
|
-
# # ...
|
32
|
-
# redirect_to 'http://example.com/articles/23'
|
33
|
-
# end
|
34
|
-
# end
|
35
|
-
#
|
36
|
-
# action = Create.new
|
37
|
-
# action.call({}) # => [302, {'Location' => '/articles/23'}, '']
|
38
|
-
#
|
39
|
-
# @example With custom status code
|
40
|
-
# require 'hanami/controller'
|
41
|
-
#
|
42
|
-
# class Create
|
43
|
-
# include Hanami::Action
|
44
|
-
#
|
45
|
-
# def call(params)
|
46
|
-
# # ...
|
47
|
-
# redirect_to 'http://example.com/articles/23', status: 301
|
48
|
-
# end
|
49
|
-
# end
|
50
|
-
#
|
51
|
-
# action = Create.new
|
52
|
-
# action.call({}) # => [301, {'Location' => '/articles/23'}, '']
|
53
|
-
def redirect_to(url, status: 302)
|
54
|
-
headers[LOCATION] = ::String.new(url)
|
55
|
-
halt(status)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
@@ -1,196 +0,0 @@
|
|
1
|
-
require 'hanami/utils/class_attribute'
|
2
|
-
require 'hanami/http/status'
|
3
|
-
|
4
|
-
module Hanami
|
5
|
-
module Action
|
6
|
-
# Throw API
|
7
|
-
#
|
8
|
-
# @since 0.1.0
|
9
|
-
#
|
10
|
-
# @see Hanami::Action::Throwable::ClassMethods#handle_exception
|
11
|
-
# @see Hanami::Action::Throwable#halt
|
12
|
-
# @see Hanami::Action::Throwable#status
|
13
|
-
module Throwable
|
14
|
-
# @since 0.2.0
|
15
|
-
# @api private
|
16
|
-
RACK_ERRORS = 'rack.errors'.freeze
|
17
|
-
|
18
|
-
# This isn't part of Rack SPEC
|
19
|
-
#
|
20
|
-
# Exception notifiers use <tt>rack.exception</tt> instead of
|
21
|
-
# <tt>rack.errors</tt>, so we need to support it.
|
22
|
-
#
|
23
|
-
# @since 0.5.0
|
24
|
-
# @api private
|
25
|
-
#
|
26
|
-
# @see Hanami::Action::Throwable::RACK_ERRORS
|
27
|
-
# @see http://www.rubydoc.info/github/rack/rack/file/SPEC#The_Error_Stream
|
28
|
-
# @see https://github.com/hanami/controller/issues/133
|
29
|
-
RACK_EXCEPTION = 'rack.exception'.freeze
|
30
|
-
|
31
|
-
# @since 0.1.0
|
32
|
-
# @api private
|
33
|
-
def self.included(base)
|
34
|
-
base.extend ClassMethods
|
35
|
-
end
|
36
|
-
|
37
|
-
# Throw API class methods
|
38
|
-
#
|
39
|
-
# @since 0.1.0
|
40
|
-
# @api private
|
41
|
-
module ClassMethods
|
42
|
-
private
|
43
|
-
|
44
|
-
# Handle the given exception with an HTTP status code.
|
45
|
-
#
|
46
|
-
# When the exception is raise during #call execution, it will be
|
47
|
-
# translated into the associated HTTP status.
|
48
|
-
#
|
49
|
-
# This is a fine grained control, for a global configuration see
|
50
|
-
# Hanami::Action.handled_exceptions
|
51
|
-
#
|
52
|
-
# @param exception [Hash] the exception class must be the key and the
|
53
|
-
# HTTP status the value of the hash
|
54
|
-
#
|
55
|
-
# @since 0.1.0
|
56
|
-
#
|
57
|
-
# @see Hanami::Action.handled_exceptions
|
58
|
-
#
|
59
|
-
# @example
|
60
|
-
# require 'hanami/controller'
|
61
|
-
#
|
62
|
-
# class Show
|
63
|
-
# include Hanami::Action
|
64
|
-
# handle_exception RecordNotFound => 404
|
65
|
-
#
|
66
|
-
# def call(params)
|
67
|
-
# # ...
|
68
|
-
# raise RecordNotFound.new
|
69
|
-
# end
|
70
|
-
# end
|
71
|
-
#
|
72
|
-
# Show.new.call({id: 1}) # => [404, {}, ['Not Found']]
|
73
|
-
def handle_exception(exception)
|
74
|
-
configuration.handle_exception(exception)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
protected
|
79
|
-
|
80
|
-
# Halt the action execution with the given HTTP status code and message.
|
81
|
-
#
|
82
|
-
# When used, the execution of a callback or of an action is interrupted
|
83
|
-
# and the control returns to the framework, that decides how to handle
|
84
|
-
# the event.
|
85
|
-
#
|
86
|
-
# If a message is provided, it sets the response body with the message.
|
87
|
-
# Otherwise, it sets the response body with the default message associated
|
88
|
-
# to the code (eg 404 will set `"Not Found"`).
|
89
|
-
#
|
90
|
-
# @param code [Fixnum] a valid HTTP status code
|
91
|
-
# @param message [String] the response body
|
92
|
-
#
|
93
|
-
# @since 0.2.0
|
94
|
-
#
|
95
|
-
# @see Hanami::Controller#handled_exceptions
|
96
|
-
# @see Hanami::Action::Throwable#handle_exception
|
97
|
-
# @see Hanami::Http::Status:ALL
|
98
|
-
#
|
99
|
-
# @example Basic usage
|
100
|
-
# require 'hanami/controller'
|
101
|
-
#
|
102
|
-
# class Show
|
103
|
-
# def call(params)
|
104
|
-
# halt 404
|
105
|
-
# end
|
106
|
-
# end
|
107
|
-
#
|
108
|
-
# # => [404, {}, ["Not Found"]]
|
109
|
-
#
|
110
|
-
# @example Custom message
|
111
|
-
# require 'hanami/controller'
|
112
|
-
#
|
113
|
-
# class Show
|
114
|
-
# def call(params)
|
115
|
-
# halt 404, "This is not the droid you're looking for."
|
116
|
-
# end
|
117
|
-
# end
|
118
|
-
#
|
119
|
-
# # => [404, {}, ["This is not the droid you're looking for."]]
|
120
|
-
def halt(code, message = nil)
|
121
|
-
message ||= Http::Status.message_for(code)
|
122
|
-
status(code, message)
|
123
|
-
|
124
|
-
throw :halt
|
125
|
-
end
|
126
|
-
|
127
|
-
# Sets the given code and message for the response
|
128
|
-
#
|
129
|
-
# @param code [Fixnum] a valid HTTP status code
|
130
|
-
# @param message [String] the response body
|
131
|
-
#
|
132
|
-
# @since 0.1.0
|
133
|
-
# @see Hanami::Http::Status:ALL
|
134
|
-
def status(code, message)
|
135
|
-
self.status = code
|
136
|
-
self.body = message
|
137
|
-
end
|
138
|
-
|
139
|
-
private
|
140
|
-
# @since 0.1.0
|
141
|
-
# @api private
|
142
|
-
def _rescue
|
143
|
-
catch :halt do
|
144
|
-
begin
|
145
|
-
yield
|
146
|
-
rescue => exception
|
147
|
-
_reference_in_rack_errors(exception)
|
148
|
-
_handle_exception(exception)
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
# @since 0.2.0
|
154
|
-
# @api private
|
155
|
-
def _reference_in_rack_errors(exception)
|
156
|
-
return if configuration.handled_exception?(exception)
|
157
|
-
|
158
|
-
@_env[RACK_EXCEPTION] = exception
|
159
|
-
|
160
|
-
if errors = @_env[RACK_ERRORS]
|
161
|
-
errors.write(_dump_exception(exception))
|
162
|
-
errors.flush
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
# @since 0.2.0
|
167
|
-
# @api private
|
168
|
-
def _dump_exception(exception)
|
169
|
-
[[exception.class, exception.message].compact.join(": "), *exception.backtrace].join("\n\t")
|
170
|
-
end
|
171
|
-
|
172
|
-
# @since 0.1.0
|
173
|
-
# @api private
|
174
|
-
def _handle_exception(exception)
|
175
|
-
raise unless configuration.handle_exceptions
|
176
|
-
|
177
|
-
instance_exec(
|
178
|
-
exception,
|
179
|
-
&_exception_handler(exception)
|
180
|
-
)
|
181
|
-
end
|
182
|
-
|
183
|
-
# @since 0.3.0
|
184
|
-
# @api private
|
185
|
-
def _exception_handler(exception)
|
186
|
-
handler = configuration.exception_handler(exception)
|
187
|
-
|
188
|
-
if respond_to?(handler.to_s, true)
|
189
|
-
method(handler)
|
190
|
-
else
|
191
|
-
->(ex) { halt handler }
|
192
|
-
end
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|