hanami-controller 2.0.0.alpha1 → 2.0.0.alpha2
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/LICENSE.md +1 -1
- data/README.md +5 -5
- data/hanami-controller.gemspec +4 -3
- data/lib/hanami/action.rb +14 -573
- data/lib/hanami/action/application_action.rb +111 -0
- data/lib/hanami/action/application_configuration.rb +92 -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/cache/conditional_get.rb +4 -1
- data/lib/hanami/action/configuration.rb +430 -0
- data/lib/hanami/action/csrf_protection.rb +214 -0
- data/lib/hanami/action/flash.rb +101 -206
- data/lib/hanami/action/mime.rb +8 -1
- data/lib/hanami/action/params.rb +1 -1
- data/lib/hanami/action/response.rb +43 -24
- data/lib/hanami/action/session.rb +6 -14
- data/lib/hanami/action/standalone_action.rb +581 -0
- data/lib/hanami/action/validatable.rb +1 -1
- data/lib/hanami/action/view_name_inferrer.rb +46 -0
- data/lib/hanami/controller.rb +0 -17
- data/lib/hanami/controller/version.rb +1 -1
- data/lib/hanami/http/status.rb +2 -2
- metadata +33 -13
- data/lib/hanami-controller.rb +0 -1
- data/lib/hanami/controller/configuration.rb +0 -308
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d159c89cbe6b24762a475776ae1585e76925c8f26501c6334c908893624587e5
|
4
|
+
data.tar.gz: b5f4b394c5337ae9b46f2482f86f69aaf151dbaee9cd2ac95be15f69a12ce65a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aac599272b21e8ca56b2c519b93a47593dc126d442be727d87b4e8884e1c2da3d349d480399e1ed781560acb9e450e24b1985fbcd7083d65d79fb8573c3db606
|
7
|
+
data.tar.gz: 75beecc46dce543367e7869887cda80937c2d838a65996f9bd831e56dfade6fa1ccccc03c7c0a0ace5aad833e26db10638e6c51e869b70cb6dc95abd351b7000
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,25 @@
|
|
1
1
|
# Hanami::Controller
|
2
2
|
Complete, fast and testable actions for Rack
|
3
3
|
|
4
|
+
## v2.0.0.alpha2 - 2021-05-04
|
5
|
+
### Added
|
6
|
+
- [Luca Guidi] Official support for Ruby: MRI 3.0
|
7
|
+
- [Tim Riley] Introduced `Hanami::Action::ApplicationAction`
|
8
|
+
- [Tim Riley] Introduced `Hanami::Action::Configuration`
|
9
|
+
- [Tim Riley] Introduced `Hanami::Action::ApplicationConfiguration`
|
10
|
+
- [Tim Riley] Auto-inject a paired view into any `Hanami::Action::ApplicationAction` instance
|
11
|
+
- [Tim Riley] Auto-render `Hanami::Action::ApplicationAction` subclasses that don't implement `#handle`
|
12
|
+
- [Tim Riley] Enable CSRF protection automatically when HTTP sessions are enabled
|
13
|
+
|
14
|
+
### Fixed
|
15
|
+
- [Luca Guidi] Ensure `Hanami::Action::Response#renderable?` to return `false` when body is set
|
16
|
+
- [Andrew Croome] Ensure `Hanami::Action.accept` to use Rack `CONTENT_TYPE` for the _before callback_ check
|
17
|
+
|
18
|
+
### Changed
|
19
|
+
- [Luca Guidi] Drop support for Ruby: MRI 2.5.
|
20
|
+
- [Tim Riley] Removed `Hanami::Action.handle_exception` in favor of `Hanami::Action.config.handle_exception`
|
21
|
+
- [Tim Riley] Rewritten `Hanami::Action::Flash`, based on Roda's `FlashHash`
|
22
|
+
|
4
23
|
## v2.0.0.alpha1 - 2019-01-30
|
5
24
|
### Added
|
6
25
|
- [Luca Guidi] `Hanami::Action::Request#session` to access the HTTP session as it was originally sent
|
@@ -50,6 +69,16 @@ Complete, fast and testable actions for Rack
|
|
50
69
|
- [Luca Guidi] When an exception is raised, it won't be caught, unless it's handled
|
51
70
|
- [Luca Guidi] `Hanami::Action` exception handlers now accept `Hanami::Action::Request`, `Hanami::Action::Response`, and exception arguments
|
52
71
|
|
72
|
+
## v1.3.3 - 2020-01-14
|
73
|
+
### Added
|
74
|
+
- [Luca Guidi] Official support for Ruby: MRI 2.7
|
75
|
+
- [Luca Guidi] Support `rack` 2.1
|
76
|
+
- [Luca Guidi] Support for both `hanami-validations` 1 and 2
|
77
|
+
|
78
|
+
## v1.3.2 - 2019-06-28
|
79
|
+
### Fixed
|
80
|
+
- [Ian Ker-Seymer] Ensure `Etag` to work when `If-Modified-Since` is sent from browser and upstream proxy sets `Last-Modified` automatically.
|
81
|
+
|
53
82
|
## v1.3.1 - 2019-01-18
|
54
83
|
### Added
|
55
84
|
- [Luca Guidi] Official support for Ruby: MRI 2.6
|
data/LICENSE.md
CHANGED
data/README.md
CHANGED
@@ -5,20 +5,20 @@ Complete, fast and testable actions for Rack and [Hanami](http://hanamirb.org)
|
|
5
5
|
## Status
|
6
6
|
|
7
7
|
[](https://badge.fury.io/rb/hanami-controller)
|
8
|
-
[](https://codecov.io/gh/hanami/controller)
|
8
|
+
[](https://github.com/hanami/controller/actions?query=workflow%3Aci+branch%3Aunstable)
|
9
|
+
[](https://codecov.io/gh/hanami/controller)
|
11
10
|
[](https://depfu.com/github/hanami/controller?project=Bundler)
|
12
11
|
[](http://inch-ci.org/github/hanami/controller)
|
13
12
|
|
14
13
|
## Contact
|
15
14
|
|
16
15
|
* Home page: http://hanamirb.org
|
16
|
+
* Community: http://hanamirb.org/community
|
17
|
+
* Guides: https://guides.hanamirb.org
|
17
18
|
* Mailing List: http://hanamirb.org/mailing-list
|
18
19
|
* API Doc: http://rdoc.info/gems/hanami-controller
|
19
20
|
* Bugs/Issues: https://github.com/hanami/controller/issues
|
20
21
|
* Chat: http://chat.hanamirb.org
|
21
|
-
* Chat: https://gitter.im/hanami/chat
|
22
22
|
|
23
23
|
## Rubies
|
24
24
|
|
@@ -963,6 +963,6 @@ __Hanami::Controller__ uses [Semantic Versioning 2.0.0](http://semver.org)
|
|
963
963
|
|
964
964
|
## Copyright
|
965
965
|
|
966
|
-
Copyright © 2014-
|
966
|
+
Copyright © 2014-2021 Luca Guidi – Released under MIT License
|
967
967
|
|
968
968
|
This project was formerly known as Lotus (`lotus-controller`).
|
data/hanami-controller.gemspec
CHANGED
@@ -17,13 +17,14 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.executables = []
|
18
18
|
spec.test_files = spec.files.grep(%r{^(spec)/})
|
19
19
|
spec.require_paths = ['lib']
|
20
|
-
spec.required_ruby_version = '>= 2.
|
20
|
+
spec.required_ruby_version = '>= 2.6.0'
|
21
21
|
|
22
22
|
spec.add_dependency 'rack', '~> 2.0'
|
23
23
|
spec.add_dependency 'hanami-utils', '~> 2.0.alpha'
|
24
|
+
spec.add_dependency 'dry-configurable', '~> 0.12'
|
24
25
|
|
25
26
|
spec.add_development_dependency 'bundler', '>= 1.6', '< 3'
|
26
27
|
spec.add_development_dependency 'rack-test', '~> 1.0'
|
27
|
-
spec.add_development_dependency 'rake', '~>
|
28
|
-
spec.add_development_dependency 'rspec', '~> 3.
|
28
|
+
spec.add_development_dependency 'rake', '~> 13'
|
29
|
+
spec.add_development_dependency 'rspec', '~> 3.9'
|
29
30
|
end
|
data/lib/hanami/action.rb
CHANGED
@@ -1,21 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'hanami/action/validatable'
|
4
|
-
rescue LoadError
|
5
|
-
end
|
6
|
-
|
7
|
-
require 'hanami/action/request'
|
8
|
-
require 'hanami/action/response'
|
9
|
-
require 'hanami/action/base_params'
|
10
|
-
require 'hanami/action/rack/file'
|
11
|
-
|
12
|
-
require 'rack/utils'
|
13
|
-
require 'hanami/utils'
|
14
|
-
require 'hanami/utils/kernel'
|
15
|
-
|
16
|
-
require 'hanami/utils/class_attribute'
|
17
|
-
|
18
|
-
require 'hanami/utils/callbacks'
|
1
|
+
require_relative 'action/application_action'
|
2
|
+
require_relative 'action/standalone_action'
|
19
3
|
|
20
4
|
module Hanami
|
21
5
|
# An HTTP endpoint
|
@@ -33,9 +17,6 @@ module Hanami
|
|
33
17
|
# end
|
34
18
|
# end
|
35
19
|
class Action
|
36
|
-
require "hanami/action/mime"
|
37
|
-
require "hanami/action/halt"
|
38
|
-
|
39
20
|
# Rack SPEC response code
|
40
21
|
#
|
41
22
|
# @since 1.0.0
|
@@ -163,564 +144,24 @@ module Hanami
|
|
163
144
|
# @api private
|
164
145
|
LOCATION = 'Location'.freeze
|
165
146
|
|
166
|
-
|
167
|
-
# It includes basic Hanami::Action modules to the given class.
|
168
|
-
#
|
169
|
-
# @param base [Class] the target action
|
170
|
-
#
|
171
|
-
# @since 0.1.0
|
172
|
-
# @api private
|
173
|
-
def self.inherited(base)
|
174
|
-
if base.superclass == Hanami::Action
|
175
|
-
base.class_eval do
|
176
|
-
include Utils::ClassAttribute
|
177
|
-
|
178
|
-
class_attribute :before_callbacks
|
179
|
-
self.before_callbacks = Utils::Callbacks::Chain.new
|
147
|
+
include StandaloneAction
|
180
148
|
|
181
|
-
|
182
|
-
|
149
|
+
def self.inherited(subclass)
|
150
|
+
super
|
183
151
|
|
184
|
-
|
185
|
-
|
152
|
+
# When inheriting within an Hanami app, and the application provider has
|
153
|
+
# changed from the superclass, (re-)configure the action for the provider,
|
154
|
+
# i.e. for the slice and/or the application itself
|
155
|
+
if (provider = application_provider(subclass)) && provider != application_provider(subclass.superclass)
|
156
|
+
subclass.include ApplicationAction.new(provider)
|
186
157
|
end
|
187
158
|
end
|
188
159
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
# params. By default this will be Hanami::Action::Params.
|
193
|
-
#
|
194
|
-
# @return [Class] A params class (when whitelisted) or
|
195
|
-
# Hanami::Action::Params
|
196
|
-
#
|
197
|
-
# @api private
|
198
|
-
# @since 0.7.0
|
199
|
-
def self.params_class
|
200
|
-
@params_class ||= BaseParams
|
201
|
-
end
|
202
|
-
|
203
|
-
# FIXME: make this thread-safe
|
204
|
-
def self.accepted_formats
|
205
|
-
@accepted_formats ||= []
|
206
|
-
end
|
207
|
-
|
208
|
-
# FIXME: make this thread-safe
|
209
|
-
def self.handled_exceptions
|
210
|
-
@handled_exceptions ||= {}
|
211
|
-
end
|
212
|
-
|
213
|
-
# Define a callback for an Action.
|
214
|
-
# The callback will be executed **before** the action is called, in the
|
215
|
-
# order they are added.
|
216
|
-
#
|
217
|
-
# @param callbacks [Symbol, Array<Symbol>] a single or multiple symbol(s)
|
218
|
-
# each of them is representing a name of a method available in the
|
219
|
-
# context of the Action.
|
220
|
-
#
|
221
|
-
# @param blk [Proc] an anonymous function to be executed
|
222
|
-
#
|
223
|
-
# @return [void]
|
224
|
-
#
|
225
|
-
# @since 0.3.2
|
226
|
-
#
|
227
|
-
# @see Hanami::Action::Callbacks::ClassMethods#append_after
|
228
|
-
#
|
229
|
-
# @example Method names (symbols)
|
230
|
-
# require 'hanami/controller'
|
231
|
-
#
|
232
|
-
# class Show
|
233
|
-
# include Hanami::Action
|
234
|
-
#
|
235
|
-
# before :authenticate, :set_article
|
236
|
-
#
|
237
|
-
# def call(params)
|
238
|
-
# end
|
239
|
-
#
|
240
|
-
# private
|
241
|
-
# def authenticate
|
242
|
-
# # ...
|
243
|
-
# end
|
244
|
-
#
|
245
|
-
# # `params` in the method signature is optional
|
246
|
-
# def set_article(params)
|
247
|
-
# @article = Article.find params[:id]
|
248
|
-
# end
|
249
|
-
# end
|
250
|
-
#
|
251
|
-
# # The order of execution will be:
|
252
|
-
# #
|
253
|
-
# # 1. #authenticate
|
254
|
-
# # 2. #set_article
|
255
|
-
# # 3. #call
|
256
|
-
#
|
257
|
-
# @example Anonymous functions (Procs)
|
258
|
-
# require 'hanami/controller'
|
259
|
-
#
|
260
|
-
# class Show
|
261
|
-
# include Hanami::Action
|
262
|
-
#
|
263
|
-
# before { ... } # 1 do some authentication stuff
|
264
|
-
# before {|params| @article = Article.find params[:id] } # 2
|
265
|
-
#
|
266
|
-
# def call(params)
|
267
|
-
# end
|
268
|
-
# end
|
269
|
-
#
|
270
|
-
# # The order of execution will be:
|
271
|
-
# #
|
272
|
-
# # 1. authentication
|
273
|
-
# # 2. set the article
|
274
|
-
# # 3. #call
|
275
|
-
def self.append_before(*callbacks, &blk)
|
276
|
-
before_callbacks.append(*callbacks, &blk)
|
277
|
-
end
|
278
|
-
|
279
|
-
class << self
|
280
|
-
# @since 0.1.0
|
281
|
-
alias before append_before
|
282
|
-
end
|
283
|
-
|
284
|
-
# Define a callback for an Action.
|
285
|
-
# The callback will be executed **after** the action is called, in the
|
286
|
-
# order they are added.
|
287
|
-
#
|
288
|
-
# @param callbacks [Symbol, Array<Symbol>] a single or multiple symbol(s)
|
289
|
-
# each of them is representing a name of a method available in the
|
290
|
-
# context of the Action.
|
291
|
-
#
|
292
|
-
# @param blk [Proc] an anonymous function to be executed
|
293
|
-
#
|
294
|
-
# @return [void]
|
295
|
-
#
|
296
|
-
# @since 0.3.2
|
297
|
-
#
|
298
|
-
# @see Hanami::Action::Callbacks::ClassMethods#append_before
|
299
|
-
def self.append_after(*callbacks, &blk)
|
300
|
-
after_callbacks.append(*callbacks, &blk)
|
301
|
-
end
|
302
|
-
|
303
|
-
class << self
|
304
|
-
# @since 0.1.0
|
305
|
-
alias after append_after
|
306
|
-
end
|
307
|
-
|
308
|
-
# Define a callback for an Action.
|
309
|
-
# The callback will be executed **before** the action is called.
|
310
|
-
# It will add the callback at the beginning of the callbacks' chain.
|
311
|
-
#
|
312
|
-
# @param callbacks [Symbol, Array<Symbol>] a single or multiple symbol(s)
|
313
|
-
# each of them is representing a name of a method available in the
|
314
|
-
# context of the Action.
|
315
|
-
#
|
316
|
-
# @param blk [Proc] an anonymous function to be executed
|
317
|
-
#
|
318
|
-
# @return [void]
|
319
|
-
#
|
320
|
-
# @since 0.3.2
|
321
|
-
#
|
322
|
-
# @see Hanami::Action::Callbacks::ClassMethods#prepend_after
|
323
|
-
def self.prepend_before(*callbacks, &blk)
|
324
|
-
before_callbacks.prepend(*callbacks, &blk)
|
325
|
-
end
|
326
|
-
|
327
|
-
# Define a callback for an Action.
|
328
|
-
# The callback will be executed **after** the action is called.
|
329
|
-
# It will add the callback at the beginning of the callbacks' chain.
|
330
|
-
#
|
331
|
-
# @param callbacks [Symbol, Array<Symbol>] a single or multiple symbol(s)
|
332
|
-
# each of them is representing a name of a method available in the
|
333
|
-
# context of the Action.
|
334
|
-
#
|
335
|
-
# @param blk [Proc] an anonymous function to be executed
|
336
|
-
#
|
337
|
-
# @return [void]
|
338
|
-
#
|
339
|
-
# @since 0.3.2
|
340
|
-
#
|
341
|
-
# @see Hanami::Action::Callbacks::ClassMethods#prepend_before
|
342
|
-
def self.prepend_after(*callbacks, &blk)
|
343
|
-
after_callbacks.prepend(*callbacks, &blk)
|
344
|
-
end
|
345
|
-
|
346
|
-
# Restrict the access to the specified mime type symbols.
|
347
|
-
#
|
348
|
-
# @param formats[Array<Symbol>] one or more symbols representing mime type(s)
|
349
|
-
#
|
350
|
-
# @raise [Hanami::Controller::UnknownFormatError] if the symbol cannot
|
351
|
-
# be converted into a mime type
|
352
|
-
#
|
353
|
-
# @since 0.1.0
|
354
|
-
#
|
355
|
-
# @see Hanami::Controller::Configuration#format
|
356
|
-
#
|
357
|
-
# @example
|
358
|
-
# require 'hanami/controller'
|
359
|
-
#
|
360
|
-
# class Show
|
361
|
-
# include Hanami::Action
|
362
|
-
# accept :html, :json
|
363
|
-
#
|
364
|
-
# def call(params)
|
365
|
-
# # ...
|
366
|
-
# end
|
367
|
-
# end
|
368
|
-
#
|
369
|
-
# # When called with "*/*" => 200
|
370
|
-
# # When called with "text/html" => 200
|
371
|
-
# # When called with "application/json" => 200
|
372
|
-
# # When called with "application/xml" => 406
|
373
|
-
def self.accept(*formats)
|
374
|
-
@accepted_formats = *formats
|
375
|
-
before :enforce_accepted_mime_types
|
376
|
-
end
|
377
|
-
|
378
|
-
# Handle the given exception with an HTTP status code.
|
379
|
-
#
|
380
|
-
# When the exception is raise during #call execution, it will be
|
381
|
-
# translated into the associated HTTP status.
|
382
|
-
#
|
383
|
-
# This is a fine grained control, for a global configuration see
|
384
|
-
# Hanami::Action.handled_exceptions
|
385
|
-
#
|
386
|
-
# @param exception [Hash] the exception class must be the key and the
|
387
|
-
# HTTP status the value of the hash
|
388
|
-
#
|
389
|
-
# @since 0.1.0
|
390
|
-
#
|
391
|
-
# @see Hanami::Action.handled_exceptions
|
392
|
-
#
|
393
|
-
# @example
|
394
|
-
# require 'hanami/controller'
|
395
|
-
#
|
396
|
-
# class Show
|
397
|
-
# include Hanami::Action
|
398
|
-
# handle_exception RecordNotFound => 404
|
399
|
-
#
|
400
|
-
# def call(params)
|
401
|
-
# # ...
|
402
|
-
# raise RecordNotFound.new
|
403
|
-
# end
|
404
|
-
# end
|
405
|
-
#
|
406
|
-
# Show.new.call({id: 1}) # => [404, {}, ['Not Found']]
|
407
|
-
def self.handle_exception(exception)
|
408
|
-
handled_exceptions.merge!(exception)
|
409
|
-
end
|
410
|
-
|
411
|
-
# Callbacks API instance methods
|
412
|
-
#
|
413
|
-
# @since 0.1.0
|
414
|
-
# @api private
|
415
|
-
def self.new(configuration:, **args)
|
416
|
-
allocate.tap do |obj|
|
417
|
-
obj.instance_variable_set(:@configuration, configuration)
|
418
|
-
obj.instance_variable_set(:@accepted_mime_types, Mime.restrict_mime_types(configuration, accepted_formats))
|
419
|
-
obj.instance_variable_set(
|
420
|
-
:@handled_exceptions,
|
421
|
-
::Hash[
|
422
|
-
configuration
|
423
|
-
.handled_exceptions
|
424
|
-
.merge(handled_exceptions)
|
425
|
-
.sort{ |(ex1,_),(ex2,_)| ex1.ancestors.include?(ex2) ? -1 : 1 }
|
426
|
-
]
|
427
|
-
)
|
428
|
-
obj.send(:initialize, **args)
|
429
|
-
obj.freeze
|
160
|
+
def self.application_provider(subclass)
|
161
|
+
if Hanami.respond_to?(:application?) && Hanami.application?
|
162
|
+
Hanami.application.component_provider(subclass)
|
430
163
|
end
|
431
164
|
end
|
432
|
-
|
433
|
-
# Implements the Rack/Hanami::Action protocol
|
434
|
-
#
|
435
|
-
# @since 0.1.0
|
436
|
-
# @api private
|
437
|
-
def call(env)
|
438
|
-
request = nil
|
439
|
-
response = nil
|
440
|
-
|
441
|
-
halted = catch :halt do
|
442
|
-
begin
|
443
|
-
params = self.class.params_class.new(env)
|
444
|
-
request = Hanami::Action::Request.new(env, params)
|
445
|
-
response = Hanami::Action::Response.new(action: self.class.name, configuration: configuration, content_type: Mime.calculate_content_type_with_charset(configuration, request, accepted_mime_types), env: env, header: configuration.default_headers)
|
446
|
-
_run_before_callbacks(request, response)
|
447
|
-
handle(request, response)
|
448
|
-
_run_after_callbacks(request, response)
|
449
|
-
rescue => exception
|
450
|
-
_handle_exception(request, response, exception)
|
451
|
-
end
|
452
|
-
end
|
453
|
-
|
454
|
-
finish(request, response, halted)
|
455
|
-
end
|
456
|
-
|
457
|
-
def initialize(**)
|
458
|
-
end
|
459
|
-
|
460
|
-
protected
|
461
|
-
|
462
|
-
def handle(request, response)
|
463
|
-
end
|
464
|
-
|
465
|
-
# Halt the action execution with the given HTTP status code and message.
|
466
|
-
#
|
467
|
-
# When used, the execution of a callback or of an action is interrupted
|
468
|
-
# and the control returns to the framework, that decides how to handle
|
469
|
-
# the event.
|
470
|
-
#
|
471
|
-
# If a message is provided, it sets the response body with the message.
|
472
|
-
# Otherwise, it sets the response body with the default message associated
|
473
|
-
# to the code (eg 404 will set `"Not Found"`).
|
474
|
-
#
|
475
|
-
# @param status [Fixnum] a valid HTTP status code
|
476
|
-
# @param body [String] the response body
|
477
|
-
#
|
478
|
-
# @raise [StandardError] if the code isn't valid
|
479
|
-
#
|
480
|
-
# @since 0.2.0
|
481
|
-
#
|
482
|
-
# @see Hanami::Controller#handled_exceptions
|
483
|
-
# @see Hanami::Action::Throwable#handle_exception
|
484
|
-
# @see Hanami::Http::Status:ALL
|
485
|
-
#
|
486
|
-
# @example Basic usage
|
487
|
-
# require 'hanami/controller'
|
488
|
-
#
|
489
|
-
# class Show
|
490
|
-
# def call(params)
|
491
|
-
# halt 404
|
492
|
-
# end
|
493
|
-
# end
|
494
|
-
#
|
495
|
-
# # => [404, {}, ["Not Found"]]
|
496
|
-
#
|
497
|
-
# @example Custom message
|
498
|
-
# require 'hanami/controller'
|
499
|
-
#
|
500
|
-
# class Show
|
501
|
-
# def call(params)
|
502
|
-
# halt 404, "This is not the droid you're looking for."
|
503
|
-
# end
|
504
|
-
# end
|
505
|
-
#
|
506
|
-
# # => [404, {}, ["This is not the droid you're looking for."]]
|
507
|
-
def halt(status, body = nil)
|
508
|
-
Halt.call(status, body)
|
509
|
-
end
|
510
|
-
|
511
|
-
# @since 0.3.2
|
512
|
-
# @api private
|
513
|
-
def _requires_no_body?(res)
|
514
|
-
HTTP_STATUSES_WITHOUT_BODY.include?(res.status)
|
515
|
-
end
|
516
|
-
|
517
|
-
# @since 2.0.0
|
518
|
-
# @api private
|
519
|
-
def _requires_empty_headers?(res)
|
520
|
-
_requires_no_body?(res) || res.head?
|
521
|
-
end
|
522
|
-
|
523
|
-
private
|
524
|
-
|
525
|
-
attr_reader :configuration
|
526
|
-
|
527
|
-
def accepted_mime_types
|
528
|
-
@accepted_mime_types || configuration.mime_types
|
529
|
-
end
|
530
|
-
|
531
|
-
def enforce_accepted_mime_types(req, *)
|
532
|
-
Mime.accepted_mime_type?(req, accepted_mime_types, configuration) or halt 406
|
533
|
-
end
|
534
|
-
|
535
|
-
attr_reader :handled_exceptions
|
536
|
-
|
537
|
-
def exception_handler(exception)
|
538
|
-
handled_exceptions.each do |exception_class, handler|
|
539
|
-
return handler if exception.kind_of?(exception_class)
|
540
|
-
end
|
541
|
-
|
542
|
-
nil
|
543
|
-
end
|
544
|
-
|
545
|
-
# @since 0.2.0
|
546
|
-
# @api private
|
547
|
-
def _reference_in_rack_errors(req, exception)
|
548
|
-
req.env[RACK_EXCEPTION] = exception
|
549
|
-
|
550
|
-
if errors = req.env[RACK_ERRORS]
|
551
|
-
errors.write(_dump_exception(exception))
|
552
|
-
errors.flush
|
553
|
-
end
|
554
|
-
end
|
555
|
-
|
556
|
-
# @since 0.2.0
|
557
|
-
# @api private
|
558
|
-
def _dump_exception(exception)
|
559
|
-
[[exception.class, exception.message].compact.join(": "), *exception.backtrace].join("\n\t")
|
560
|
-
end
|
561
|
-
|
562
|
-
# @since 0.1.0
|
563
|
-
# @api private
|
564
|
-
def _handle_exception(req, res, exception)
|
565
|
-
handler = exception_handler(exception)
|
566
|
-
|
567
|
-
if handler.nil?
|
568
|
-
_reference_in_rack_errors(req, exception)
|
569
|
-
raise exception
|
570
|
-
end
|
571
|
-
|
572
|
-
instance_exec(
|
573
|
-
req,
|
574
|
-
res,
|
575
|
-
exception,
|
576
|
-
&_exception_handler(handler)
|
577
|
-
)
|
578
|
-
|
579
|
-
nil
|
580
|
-
end
|
581
|
-
|
582
|
-
# @since 0.3.0
|
583
|
-
# @api private
|
584
|
-
def _exception_handler(handler)
|
585
|
-
if respond_to?(handler.to_s, true)
|
586
|
-
method(handler)
|
587
|
-
else
|
588
|
-
->(*) { halt handler }
|
589
|
-
end
|
590
|
-
end
|
591
|
-
|
592
|
-
# @since 0.1.0
|
593
|
-
# @api private
|
594
|
-
def _run_before_callbacks(*args)
|
595
|
-
self.class.before_callbacks.run(self, *args)
|
596
|
-
nil
|
597
|
-
end
|
598
|
-
|
599
|
-
# @since 0.1.0
|
600
|
-
# @api private
|
601
|
-
def _run_after_callbacks(*args)
|
602
|
-
self.class.after_callbacks.run(self, *args)
|
603
|
-
nil
|
604
|
-
end
|
605
|
-
|
606
|
-
# According to RFC 2616, when a response MUST have an empty body, it only
|
607
|
-
# allows Entity Headers.
|
608
|
-
#
|
609
|
-
# For instance, a <tt>204</tt> doesn't allow <tt>Content-Type</tt> or any
|
610
|
-
# other custom header.
|
611
|
-
#
|
612
|
-
# This restriction is enforced by <tt>Hanami::Action#_requires_no_body?</tt>.
|
613
|
-
#
|
614
|
-
# However, there are cases that demand to bypass this rule to set meta
|
615
|
-
# informations via headers.
|
616
|
-
#
|
617
|
-
# An example is a <tt>DELETE</tt> request for a JSON API application.
|
618
|
-
# It returns a <tt>204</tt> but still wants to specify the rate limit
|
619
|
-
# quota via <tt>X-Rate-Limit</tt>.
|
620
|
-
#
|
621
|
-
# @since 0.5.0
|
622
|
-
#
|
623
|
-
# @see Hanami::Action#_requires_no_body?
|
624
|
-
#
|
625
|
-
# @example
|
626
|
-
# require 'hanami/controller'
|
627
|
-
#
|
628
|
-
# module Books
|
629
|
-
# class Destroy
|
630
|
-
# include Hanami::Action
|
631
|
-
#
|
632
|
-
# def call(params)
|
633
|
-
# # ...
|
634
|
-
# self.headers.merge!(
|
635
|
-
# 'Last-Modified' => 'Fri, 27 Nov 2015 13:32:36 GMT',
|
636
|
-
# 'X-Rate-Limit' => '4000',
|
637
|
-
# 'Content-Type' => 'application/json',
|
638
|
-
# 'X-No-Pass' => 'true'
|
639
|
-
# )
|
640
|
-
#
|
641
|
-
# self.status = 204
|
642
|
-
# end
|
643
|
-
#
|
644
|
-
# private
|
645
|
-
#
|
646
|
-
# def keep_response_header?(header)
|
647
|
-
# super || header == 'X-Rate-Limit'
|
648
|
-
# end
|
649
|
-
# end
|
650
|
-
# end
|
651
|
-
#
|
652
|
-
# # Only the following headers will be sent:
|
653
|
-
# # * Last-Modified - because we used `super' in the method that respects the HTTP RFC
|
654
|
-
# # * X-Rate-Limit - because we explicitely allow it
|
655
|
-
#
|
656
|
-
# # Both Content-Type and X-No-Pass are removed because they're not allowed
|
657
|
-
def keep_response_header?(header)
|
658
|
-
ENTITY_HEADERS.include?(header)
|
659
|
-
end
|
660
|
-
|
661
|
-
# @since 2.0.0
|
662
|
-
# @api private
|
663
|
-
def _empty_headers(res)
|
664
|
-
res.headers.select! { |header, _| keep_response_header?(header) }
|
665
|
-
end
|
666
|
-
|
667
|
-
def format(value)
|
668
|
-
case value
|
669
|
-
when Symbol
|
670
|
-
format = Utils::Kernel.Symbol(value)
|
671
|
-
[format, Action::Mime.format_to_mime_type(format, configuration)]
|
672
|
-
when String
|
673
|
-
[Action::Mime.detect_format(value, configuration), value]
|
674
|
-
else
|
675
|
-
raise Hanami::Controller::UnknownFormatError.new(value)
|
676
|
-
end
|
677
|
-
end
|
678
|
-
|
679
|
-
# Raise error when `Hanami::Action::Session` isn't included.
|
680
|
-
#
|
681
|
-
# To use `session`, include `Hanami::Action::Session`.
|
682
|
-
#
|
683
|
-
# @raise [Hanami::Controller::MissingSessionError]
|
684
|
-
#
|
685
|
-
# @since 1.2.0
|
686
|
-
def session
|
687
|
-
raise Hanami::Controller::MissingSessionError.new(:session)
|
688
|
-
end
|
689
|
-
|
690
|
-
# Raise error when `Hanami::Action::Session` isn't included.
|
691
|
-
#
|
692
|
-
# To use `flash`, include `Hanami::Action::Session`.
|
693
|
-
#
|
694
|
-
# @raise [Hanami::Controller::MissingSessionError]
|
695
|
-
#
|
696
|
-
# @since 1.2.0
|
697
|
-
def flash
|
698
|
-
raise Hanami::Controller::MissingSessionError.new(:flash)
|
699
|
-
end
|
700
|
-
|
701
|
-
# Finalize the response
|
702
|
-
#
|
703
|
-
# This method is abstract and COULD be implemented by included modules in
|
704
|
-
# order to prepare their data before the response will be returned to the
|
705
|
-
# webserver.
|
706
|
-
#
|
707
|
-
# @since 0.1.0
|
708
|
-
# @api private
|
709
|
-
# @abstract
|
710
|
-
#
|
711
|
-
# @see Hanami::Action::Callable#finish
|
712
|
-
# @see Hanami::Action::Session#finish
|
713
|
-
# @see Hanami::Action::Cookies#finish
|
714
|
-
# @see Hanami::Action::Cache#finish
|
715
|
-
def finish(req, res, halted)
|
716
|
-
res.status, res.body = *halted unless halted.nil?
|
717
|
-
|
718
|
-
_empty_headers(res) if _requires_empty_headers?(res)
|
719
|
-
|
720
|
-
res.set_format(Action::Mime.detect_format(res.content_type, configuration))
|
721
|
-
res[:params] = req.params
|
722
|
-
res[:format] = res.format
|
723
|
-
res
|
724
|
-
end
|
165
|
+
private_class_method :application_provider
|
725
166
|
end
|
726
167
|
end
|