hanami-controller 1.3.0 → 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 +83 -0
- data/LICENSE.md +1 -1
- data/README.md +297 -538
- data/hanami-controller.gemspec +6 -5
- data/lib/hanami/action.rb +129 -73
- 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/base_params.rb +2 -2
- data/lib/hanami/action/cache.rb +1 -139
- data/lib/hanami/action/cache/cache_control.rb +4 -4
- data/lib/hanami/action/cache/conditional_get.rb +7 -2
- data/lib/hanami/action/cache/directives.rb +1 -1
- data/lib/hanami/action/cache/expires.rb +3 -3
- data/lib/hanami/action/configuration.rb +430 -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 +3 -3
- 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 +581 -0
- data/lib/hanami/action/validatable.rb +2 -2
- data/lib/hanami/action/view_name_inferrer.rb +46 -0
- data/lib/hanami/controller.rb +0 -227
- data/lib/hanami/controller/version.rb +1 -1
- data/lib/hanami/http/status.rb +2 -2
- metadata +47 -30
- data/lib/hanami-controller.rb +0 -1
- 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.rb +0 -126
- data/lib/hanami/action/exposable/guard.rb +0 -104
- data/lib/hanami/action/head.rb +0 -121
- data/lib/hanami/action/rack.rb +0 -399
- data/lib/hanami/action/rack/callable.rb +0 -47
- 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/action/glue.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Hanami
|
2
|
-
|
2
|
+
class Action
|
3
3
|
# Glue code for full stack Hanami applications
|
4
4
|
#
|
5
5
|
# This includes missing rendering logic that it makes sense to include
|
@@ -14,43 +14,17 @@ module Hanami
|
|
14
14
|
# @since 0.3.0
|
15
15
|
ENV_KEY = 'hanami.action'.freeze
|
16
16
|
|
17
|
-
# @api private
|
18
|
-
# @since 0.3.2
|
19
|
-
ADDITIONAL_HTTP_STATUSES_WITHOUT_BODY = Set.new([301, 302]).freeze
|
20
|
-
|
21
|
-
# Override Ruby's Module#included
|
22
|
-
#
|
23
|
-
# @api private
|
24
|
-
# @since 0.3.0
|
25
|
-
def self.included(base)
|
26
|
-
base.class_eval { _expose(:format) if respond_to?(:_expose) }
|
27
|
-
end
|
28
|
-
|
29
|
-
# Check if the current HTTP request is renderable.
|
30
|
-
#
|
31
|
-
# It verifies if the verb isn't HEAD, if the status demands to omit
|
32
|
-
# the body and if it isn't sending a file.
|
33
|
-
#
|
34
|
-
# @return [TrueClass,FalseClass] the result of the check
|
35
|
-
#
|
36
|
-
# @api private
|
37
|
-
# @since 0.3.2
|
38
|
-
def renderable?
|
39
|
-
!_requires_no_body? &&
|
40
|
-
!sending_file? &&
|
41
|
-
!ADDITIONAL_HTTP_STATUSES_WITHOUT_BODY.include?(@_status)
|
42
|
-
end
|
43
|
-
|
44
17
|
protected
|
18
|
+
|
45
19
|
# Put the current instance into the Rack environment
|
46
20
|
#
|
47
21
|
# @api private
|
48
22
|
# @since 0.3.0
|
49
23
|
#
|
50
24
|
# @see Hanami::Action#finish
|
51
|
-
def finish
|
25
|
+
def finish(req, *)
|
26
|
+
req.env[ENV_KEY] = self
|
52
27
|
super
|
53
|
-
@_env[ENV_KEY] = self
|
54
28
|
end
|
55
29
|
|
56
30
|
# Check if the request's body is a file
|
@@ -59,7 +33,7 @@ module Hanami
|
|
59
33
|
#
|
60
34
|
# @since 0.4.3
|
61
35
|
def sending_file?
|
62
|
-
|
36
|
+
response.body.is_a?(::Rack::File::Iterator)
|
63
37
|
end
|
64
38
|
end
|
65
39
|
end
|
data/lib/hanami/action/mime.rb
CHANGED
@@ -1,24 +1,16 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require "hanami/utils"
|
2
|
+
require "rack/utils"
|
3
|
+
require "rack/mime"
|
4
4
|
|
5
5
|
module Hanami
|
6
|
-
|
7
|
-
# Mime type API
|
8
|
-
#
|
9
|
-
# @since 0.1.0
|
10
|
-
#
|
11
|
-
# @see Hanami::Action::Mime::ClassMethods#accept
|
6
|
+
class Action
|
12
7
|
module Mime
|
13
|
-
|
14
|
-
|
15
|
-
# @since 0.1.0
|
16
|
-
# @api private
|
17
|
-
HTTP_ACCEPT = 'HTTP_ACCEPT'.freeze
|
8
|
+
DEFAULT_CONTENT_TYPE = 'application/octet-stream'.freeze
|
9
|
+
DEFAULT_CHARSET = 'utf-8'.freeze
|
18
10
|
|
19
11
|
# The key that returns content mime type from the Rack env
|
20
12
|
#
|
21
|
-
# @since
|
13
|
+
# @since 2.0.0
|
22
14
|
# @api private
|
23
15
|
HTTP_CONTENT_TYPE = 'CONTENT_TYPE'.freeze
|
24
16
|
|
@@ -28,29 +20,11 @@ module Hanami
|
|
28
20
|
# @api private
|
29
21
|
CONTENT_TYPE = 'Content-Type'.freeze
|
30
22
|
|
31
|
-
# The default mime type for an incoming HTTP request
|
32
|
-
#
|
33
|
-
# @since 0.1.0
|
34
|
-
# @api private
|
35
|
-
DEFAULT_ACCEPT = '*/*'.freeze
|
36
|
-
|
37
|
-
# The default mime type that is returned in the response
|
38
|
-
#
|
39
|
-
# @since 0.1.0
|
40
|
-
# @api private
|
41
|
-
DEFAULT_CONTENT_TYPE = 'application/octet-stream'.freeze
|
42
|
-
|
43
|
-
# The default charset that is returned in the response
|
44
|
-
#
|
45
|
-
# @since 0.3.0
|
46
|
-
# @api private
|
47
|
-
DEFAULT_CHARSET = 'utf-8'.freeze
|
48
|
-
|
49
23
|
# Most commom MIME Types used for responses
|
50
24
|
#
|
51
25
|
# @since 1.0.0
|
52
26
|
# @api private
|
53
|
-
|
27
|
+
TYPES = {
|
54
28
|
txt: 'text/plain',
|
55
29
|
html: 'text/html',
|
56
30
|
json: 'application/json',
|
@@ -102,496 +76,115 @@ module Hanami
|
|
102
76
|
xml: 'application/xml',
|
103
77
|
xslt: 'application/xslt+xml',
|
104
78
|
yml: 'text/yaml',
|
105
|
-
zip: 'application/zip'
|
79
|
+
zip: 'application/zip'
|
80
|
+
}.freeze
|
106
81
|
|
107
|
-
|
108
|
-
|
109
|
-
#
|
110
|
-
# @param base [Class] the target action
|
111
|
-
#
|
112
|
-
# @since 0.1.0
|
113
|
-
# @api private
|
114
|
-
#
|
115
|
-
# @see http://www.ruby-doc.org/core-2.1.2/Module.html#method-i-included
|
116
|
-
def self.included(base)
|
117
|
-
base.class_eval do
|
118
|
-
extend ClassMethods
|
119
|
-
prepend InstanceMethods
|
120
|
-
end
|
82
|
+
def self.content_type_with_charset(content_type, charset)
|
83
|
+
"#{content_type}; charset=#{charset}"
|
121
84
|
end
|
122
85
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
#
|
136
|
-
# @param formats[Array<Symbol>] one or more symbols representing mime type(s)
|
137
|
-
#
|
138
|
-
# @raise [Hanami::Controller::UnknownFormatError] if the symbol cannot
|
139
|
-
# be converted into a mime type
|
140
|
-
#
|
141
|
-
# @since 0.1.0
|
142
|
-
#
|
143
|
-
# @see Hanami::Controller::Configuration#format
|
144
|
-
#
|
145
|
-
# @example
|
146
|
-
# require 'hanami/controller'
|
147
|
-
#
|
148
|
-
# class Show
|
149
|
-
# include Hanami::Action
|
150
|
-
# accept :html, :json
|
151
|
-
#
|
152
|
-
# def call(params)
|
153
|
-
# # ...
|
154
|
-
# end
|
155
|
-
# end
|
156
|
-
#
|
157
|
-
# # When called with "*/*" => 200
|
158
|
-
# # When called with "text/html" => 200
|
159
|
-
# # When called with "application/json" => 200
|
160
|
-
# # When called with "application/xml" => 406
|
161
|
-
def accept(*formats)
|
162
|
-
mime_types = formats.map do |format|
|
163
|
-
format_to_mime_type(format)
|
164
|
-
end
|
165
|
-
|
166
|
-
configuration.restrict_mime_types!(mime_types)
|
167
|
-
|
168
|
-
before do
|
169
|
-
unless mime_types.find {|mt| accept?(mt) }
|
170
|
-
halt 406
|
171
|
-
end
|
172
|
-
end
|
86
|
+
# Use for setting Content-Type
|
87
|
+
# If the request has the ACCEPT header it will try to return the best Content-Type based
|
88
|
+
# on the content of the ACCEPT header taking in consideration the weights
|
89
|
+
#
|
90
|
+
# If no ACCEPT header it will check the default response_format, then the default request format and
|
91
|
+
# lastly it will fallback to DEFAULT_CONTENT_TYPE
|
92
|
+
#
|
93
|
+
# @return [String]
|
94
|
+
def self.content_type(configuration, request, accepted_mime_types)
|
95
|
+
if request.accept_header?
|
96
|
+
type = best_q_match(request.accept, accepted_mime_types)
|
97
|
+
return type if type
|
173
98
|
end
|
174
99
|
|
175
|
-
|
176
|
-
#
|
177
|
-
# @param formats[Array<Symbol>] one or more symbols representing mime type(s)
|
178
|
-
#
|
179
|
-
# @since 1.2.0
|
180
|
-
#
|
181
|
-
# @example
|
182
|
-
# require 'hanami/controller'
|
183
|
-
#
|
184
|
-
# class Upload
|
185
|
-
# include Hanami::Action
|
186
|
-
# content_type :json
|
187
|
-
#
|
188
|
-
# def call(params)
|
189
|
-
# # ...
|
190
|
-
# end
|
191
|
-
# end
|
192
|
-
#
|
193
|
-
# # When called with "text/html" => 415
|
194
|
-
# # When called with "application/json" => 200
|
195
|
-
def content_type(*formats)
|
196
|
-
mime_types = formats.map { |format| format_to_mime_type(format) }
|
197
|
-
|
198
|
-
before do
|
199
|
-
mime_type = @_env[HTTP_CONTENT_TYPE] || default_content_type || DEFAULT_CONTENT_TYPE
|
200
|
-
|
201
|
-
if mime_types.none? {|mt| ::Rack::Mime.match?(mime_type, mt) }
|
202
|
-
halt 415
|
203
|
-
end
|
204
|
-
end
|
205
|
-
end
|
100
|
+
default_response_type(configuration) || default_content_type(configuration) || DEFAULT_CONTENT_TYPE
|
206
101
|
end
|
207
102
|
|
208
|
-
|
209
|
-
|
210
|
-
module InstanceMethods
|
211
|
-
# @since 0.7.0
|
212
|
-
# @api private
|
213
|
-
def initialize(*)
|
214
|
-
super
|
215
|
-
@content_type = nil
|
216
|
-
@charset = nil
|
217
|
-
end
|
103
|
+
def self.charset(default_charset)
|
104
|
+
default_charset || DEFAULT_CHARSET
|
218
105
|
end
|
219
106
|
|
220
|
-
|
221
|
-
|
222
|
-
# The framework automatically detects the request mime type, and returns
|
223
|
-
# the corresponding format.
|
224
|
-
#
|
225
|
-
# However, if this value was explicitly set by `#format=`, it will return
|
226
|
-
# that value
|
227
|
-
#
|
228
|
-
# @return [Symbol] a symbol that corresponds to the content type
|
229
|
-
#
|
230
|
-
# @since 0.2.0
|
231
|
-
#
|
232
|
-
# @see Hanami::Action::Mime#format=
|
233
|
-
# @see Hanami::Action::Mime#content_type
|
234
|
-
#
|
235
|
-
# @example Default scenario
|
236
|
-
# require 'hanami/controller'
|
237
|
-
#
|
238
|
-
# class Show
|
239
|
-
# include Hanami::Action
|
240
|
-
#
|
241
|
-
# def call(params)
|
242
|
-
# end
|
243
|
-
# end
|
244
|
-
#
|
245
|
-
# action = Show.new
|
246
|
-
#
|
247
|
-
# _, headers, _ = action.call({ 'HTTP_ACCEPT' => 'text/html' })
|
248
|
-
# headers['Content-Type'] # => 'text/html'
|
249
|
-
# action.format # => :html
|
250
|
-
#
|
251
|
-
# @example Set value
|
252
|
-
# require 'hanami/controller'
|
253
|
-
#
|
254
|
-
# class Show
|
255
|
-
# include Hanami::Action
|
256
|
-
#
|
257
|
-
# def call(params)
|
258
|
-
# self.format = :xml
|
259
|
-
# end
|
260
|
-
# end
|
261
|
-
#
|
262
|
-
# action = Show.new
|
263
|
-
#
|
264
|
-
# _, headers, _ = action.call({ 'HTTP_ACCEPT' => 'text/html' })
|
265
|
-
# headers['Content-Type'] # => 'application/xml'
|
266
|
-
# action.format # => :xml
|
267
|
-
def format
|
268
|
-
@format ||= detect_format
|
107
|
+
def self.default_response_type(configuration)
|
108
|
+
format_to_mime_type(configuration.default_response_format, configuration)
|
269
109
|
end
|
270
110
|
|
271
|
-
|
272
|
-
|
273
|
-
# It prefers, in order:
|
274
|
-
# * Explicit set value (see Hanami::Action::Mime#format=)
|
275
|
-
# * Weighted value from Accept header based on all known MIME Types:
|
276
|
-
# - Custom registered MIME Types (see Hanami::Controller::Configuration#format)
|
277
|
-
# * Configured default content type (see Hanami::Controller::Configuration#default_response_format)
|
278
|
-
# * Hard-coded default content type (see Hanami::Action::Mime::DEFAULT_CONTENT_TYPE)
|
279
|
-
#
|
280
|
-
# To override the value, use <tt>#format=</tt>
|
281
|
-
#
|
282
|
-
# @return [String] the content type from the request.
|
283
|
-
#
|
284
|
-
# @since 0.1.0
|
285
|
-
#
|
286
|
-
# @see Hanami::Action::Mime#format=
|
287
|
-
# @see Hanami::Configuration#default_request_format
|
288
|
-
# @see Hanami::Action::Mime#default_content_type
|
289
|
-
# @see Hanami::Action::Mime#DEFAULT_CONTENT_TYPE
|
290
|
-
# @see Hanami::Controller::Configuration#format
|
291
|
-
# @see Hanami::Controller::Configuration#default_response_format
|
292
|
-
#
|
293
|
-
# @example
|
294
|
-
# require 'hanami/controller'
|
295
|
-
#
|
296
|
-
# class Show
|
297
|
-
# include Hanami::Action
|
298
|
-
#
|
299
|
-
# def call(params)
|
300
|
-
# # ...
|
301
|
-
# content_type # => 'text/html'
|
302
|
-
# end
|
303
|
-
# end
|
304
|
-
def content_type
|
305
|
-
return @content_type unless @content_type.nil?
|
306
|
-
@content_type = content_type_from_accept_header if accept_header?
|
307
|
-
@content_type || default_response_type || default_content_type || DEFAULT_CONTENT_TYPE
|
111
|
+
def self.default_content_type(configuration)
|
112
|
+
format_to_mime_type(configuration.default_request_format, configuration)
|
308
113
|
end
|
309
114
|
|
310
|
-
|
311
|
-
|
312
|
-
# @return [String] the charset of the request.
|
313
|
-
#
|
314
|
-
# @since 0.3.0
|
315
|
-
#
|
316
|
-
# @example
|
317
|
-
# require 'hanami/controller'
|
318
|
-
#
|
319
|
-
# class Show
|
320
|
-
# include Hanami::Action
|
321
|
-
#
|
322
|
-
# def call(params)
|
323
|
-
# # ...
|
324
|
-
# self.charset = 'koi8-r'
|
325
|
-
# end
|
326
|
-
# end
|
327
|
-
def charset=(value)
|
328
|
-
@charset = value
|
329
|
-
end
|
115
|
+
def self.format_to_mime_type(format, configuration)
|
116
|
+
return if format.nil?
|
330
117
|
|
331
|
-
|
332
|
-
|
333
|
-
# It prefers, in order:
|
334
|
-
# * Explicit set value (see #charset=)
|
335
|
-
# * Default configuration charset
|
336
|
-
# * Default content type
|
337
|
-
#
|
338
|
-
# To override the value, use <tt>#charset=</tt>
|
339
|
-
#
|
340
|
-
# @return [String] the charset of the request.
|
341
|
-
#
|
342
|
-
# @since 0.3.0
|
343
|
-
#
|
344
|
-
# @see Hanami::Action::Mime#charset=
|
345
|
-
# @see Hanami::Configuration#default_charset
|
346
|
-
# @see Hanami::Action::Mime#default_charset
|
347
|
-
# @see Hanami::Action::Mime#DEFAULT_CHARSET
|
348
|
-
#
|
349
|
-
# @example
|
350
|
-
# require 'hanami/controller'
|
351
|
-
#
|
352
|
-
# class Show
|
353
|
-
# include Hanami::Action
|
354
|
-
#
|
355
|
-
# def call(params)
|
356
|
-
# # ...
|
357
|
-
# charset # => 'text/html'
|
358
|
-
# end
|
359
|
-
# end
|
360
|
-
def charset
|
361
|
-
@charset || default_charset || DEFAULT_CHARSET
|
118
|
+
configuration.mime_type_for(format) ||
|
119
|
+
TYPES.fetch(format) { raise Hanami::Controller::UnknownFormatError.new(format) }
|
362
120
|
end
|
363
121
|
|
364
|
-
|
365
|
-
|
366
|
-
# Finalize the response by setting the current content type
|
122
|
+
# Transforms MIME Types to symbol
|
123
|
+
# Used for setting the format of the response
|
367
124
|
#
|
368
|
-
# @
|
369
|
-
# @
|
125
|
+
# @see Hanami::Action::Mime#finish
|
126
|
+
# @example
|
127
|
+
# detect_format("text/html; charset=utf-8", configuration) #=> :html
|
370
128
|
#
|
371
|
-
# @
|
372
|
-
def
|
373
|
-
|
374
|
-
|
129
|
+
# @return [Symbol, nil]
|
130
|
+
def self.detect_format(content_type, configuration)
|
131
|
+
return if content_type.nil?
|
132
|
+
ct = content_type.split(";").first
|
133
|
+
configuration.format_for(ct) || format_for(ct)
|
375
134
|
end
|
376
135
|
|
377
|
-
|
378
|
-
|
379
|
-
# The framework detects the `HTTP_ACCEPT` header of the request and sets
|
380
|
-
# the proper `Content-Type` header in the response.
|
381
|
-
# Within this default scenario, `#format` returns a symbol that
|
382
|
-
# corresponds to `#content_type`.
|
383
|
-
# For instance, if a client sends an `HTTP_ACCEPT` with `text/html`,
|
384
|
-
# `#content_type` will return `text/html` and `#format` `:html`.
|
385
|
-
#
|
386
|
-
# However, it's possible to override what the framework have detected.
|
387
|
-
# If a client asks for an `HTTP_ACCEPT` `*/*`, but we want to force the
|
388
|
-
# response to be a `text/html` we can use this method.
|
389
|
-
#
|
390
|
-
# When the format is set, the framework searches for a corresponding mime
|
391
|
-
# type to be set as the `Content-Type` header of the response.
|
392
|
-
# This lookup is performed first in the configuration, and then in
|
393
|
-
# `Hanami::Action::Mime::MIME_TYPES`. If the lookup fails, it raises an error.
|
394
|
-
#
|
395
|
-
# PERFORMANCE: Because `Hanami::Controller::Configuration#formats` is
|
396
|
-
# smaller and looked up first than `Hanami::Action::Mime::MIME_TYPES`,
|
397
|
-
# we suggest to configure the most common mime types used by your
|
398
|
-
# application, **even if they are already present in that Rack constant**.
|
399
|
-
#
|
400
|
-
# @param format [#to_sym] the format
|
401
|
-
#
|
402
|
-
# @return [void]
|
403
|
-
#
|
404
|
-
# @raise [TypeError] if the format cannot be coerced into a Symbol
|
405
|
-
# @raise [Hanami::Controller::UnknownFormatError] if the format doesn't
|
406
|
-
# have a corresponding mime type
|
407
|
-
#
|
408
|
-
# @since 0.2.0
|
409
|
-
#
|
410
|
-
# @see Hanami::Action::Mime#format
|
411
|
-
# @see Hanami::Action::Mime#content_type
|
412
|
-
# @see Hanami::Controller::Configuration#format
|
413
|
-
#
|
414
|
-
# @example Default scenario
|
415
|
-
# require 'hanami/controller'
|
416
|
-
#
|
417
|
-
# class Show
|
418
|
-
# include Hanami::Action
|
419
|
-
#
|
420
|
-
# def call(params)
|
421
|
-
# end
|
422
|
-
# end
|
423
|
-
#
|
424
|
-
# action = Show.new
|
425
|
-
#
|
426
|
-
# _, headers, _ = action.call({ 'HTTP_ACCEPT' => '*/*' })
|
427
|
-
# headers['Content-Type'] # => 'application/octet-stream'
|
428
|
-
# action.format # => :all
|
429
|
-
#
|
430
|
-
# _, headers, _ = action.call({ 'HTTP_ACCEPT' => 'text/html' })
|
431
|
-
# headers['Content-Type'] # => 'text/html'
|
432
|
-
# action.format # => :html
|
433
|
-
#
|
434
|
-
# @example Simple usage
|
435
|
-
# require 'hanami/controller'
|
436
|
-
#
|
437
|
-
# class Show
|
438
|
-
# include Hanami::Action
|
439
|
-
#
|
440
|
-
# def call(params)
|
441
|
-
# # ...
|
442
|
-
# self.format = :json
|
443
|
-
# end
|
444
|
-
# end
|
445
|
-
#
|
446
|
-
# action = Show.new
|
447
|
-
#
|
448
|
-
# _, headers, _ = action.call({ 'HTTP_ACCEPT' => '*/*' })
|
449
|
-
# headers['Content-Type'] # => 'application/json'
|
450
|
-
# action.format # => :json
|
451
|
-
#
|
452
|
-
# @example Unknown format
|
453
|
-
# require 'hanami/controller'
|
454
|
-
#
|
455
|
-
# class Show
|
456
|
-
# include Hanami::Action
|
457
|
-
#
|
458
|
-
# def call(params)
|
459
|
-
# # ...
|
460
|
-
# self.format = :unknown
|
461
|
-
# end
|
462
|
-
# end
|
463
|
-
#
|
464
|
-
# action = Show.new
|
465
|
-
# action.call({ 'HTTP_ACCEPT' => '*/*' })
|
466
|
-
# # => raise Hanami::Controller::UnknownFormatError
|
467
|
-
#
|
468
|
-
# @example Custom mime type/format
|
469
|
-
# require 'hanami/controller'
|
470
|
-
#
|
471
|
-
# Hanami::Controller.configure do
|
472
|
-
# format :custom, 'application/custom'
|
473
|
-
# end
|
474
|
-
#
|
475
|
-
# class Show
|
476
|
-
# include Hanami::Action
|
477
|
-
#
|
478
|
-
# def call(params)
|
479
|
-
# # ...
|
480
|
-
# self.format = :custom
|
481
|
-
# end
|
482
|
-
# end
|
483
|
-
#
|
484
|
-
# _, headers, _ = action.call({ 'HTTP_ACCEPT' => '*/*' })
|
485
|
-
# headers['Content-Type'] # => 'application/custom'
|
486
|
-
# action.format # => :custom
|
487
|
-
def format=(format)
|
488
|
-
@format = Utils::Kernel.Symbol(format)
|
489
|
-
@content_type = self.class.format_to_mime_type(@format)
|
136
|
+
def self.format_for(content_type)
|
137
|
+
TYPES.key(content_type)
|
490
138
|
end
|
491
139
|
|
492
|
-
#
|
493
|
-
#
|
494
|
-
# @return [Boolean] true if the given mime type matches Accept
|
495
|
-
#
|
496
|
-
# @since 0.1.0
|
497
|
-
#
|
140
|
+
# Transforms symbols to MIME Types
|
498
141
|
# @example
|
499
|
-
#
|
500
|
-
#
|
501
|
-
# class Show
|
502
|
-
# include Hanami::Action
|
503
|
-
#
|
504
|
-
# def call(params)
|
505
|
-
# # ...
|
506
|
-
# # @_env['HTTP_ACCEPT'] # => 'text/html,application/xhtml+xml,application/xml;q=0.9'
|
507
|
-
#
|
508
|
-
# accept?('text/html') # => true
|
509
|
-
# accept?('application/xml') # => true
|
510
|
-
# accept?('application/json') # => false
|
511
|
-
#
|
512
|
-
#
|
142
|
+
# restrict_mime_types(configuration, [:json]) #=> ["application/json"]
|
513
143
|
#
|
514
|
-
#
|
144
|
+
# @return [Array<String>, nil]
|
515
145
|
#
|
516
|
-
#
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
!!::Rack::Utils.q_values(accept).find do |mime, _|
|
523
|
-
::Rack::Mime.match?(mime_type, mime)
|
146
|
+
# @raise [Hanami::Controller::UnknownFormatError] if the format is invalid
|
147
|
+
def self.restrict_mime_types(configuration, accepted_formats)
|
148
|
+
return if accepted_formats.empty?
|
149
|
+
|
150
|
+
mime_types = accepted_formats.map do |format|
|
151
|
+
format_to_mime_type(format, configuration)
|
524
152
|
end
|
525
|
-
end
|
526
153
|
|
527
|
-
|
528
|
-
# @api private
|
529
|
-
def accept
|
530
|
-
@accept ||= @_env[HTTP_ACCEPT] || DEFAULT_ACCEPT
|
531
|
-
end
|
154
|
+
accepted_mime_types = mime_types & configuration.mime_types
|
532
155
|
|
533
|
-
|
534
|
-
|
535
|
-
# @return [TrueClass,FalseClass] the result of the check
|
536
|
-
#
|
537
|
-
# @since 0.8.0
|
538
|
-
# @api private
|
539
|
-
def accept_header?
|
540
|
-
accept != DEFAULT_ACCEPT
|
156
|
+
return if accepted_mime_types.empty?
|
157
|
+
accepted_mime_types
|
541
158
|
end
|
542
159
|
|
543
|
-
#
|
544
|
-
#
|
545
|
-
# or the custom registered ones (see Hanami::Controller::Configuration#format).
|
160
|
+
# Use for checking the Content-Type header to make sure is valid based
|
161
|
+
# on the accepted_mime_types
|
546
162
|
#
|
547
|
-
#
|
163
|
+
# If no Content-Type is sent in the request it will check the default_request_format
|
548
164
|
#
|
549
|
-
# @
|
550
|
-
|
551
|
-
|
552
|
-
# @see Hanami::Action::Mime#MIME_TYPES
|
553
|
-
# @see Hanami::Controller::Configuration#format
|
554
|
-
#
|
555
|
-
# @api private
|
556
|
-
def content_type_from_accept_header
|
557
|
-
best_q_match(accept, configuration.mime_types)
|
558
|
-
end
|
165
|
+
# @return [TrueClass, FalseClass]
|
166
|
+
def self.accepted_mime_type?(request, accepted_mime_types, configuration)
|
167
|
+
mime_type = request.env[HTTP_CONTENT_TYPE] || default_content_type(configuration) || DEFAULT_CONTENT_TYPE
|
559
168
|
|
560
|
-
|
561
|
-
# @api private
|
562
|
-
def default_response_type
|
563
|
-
self.class.format_to_mime_type(configuration.default_response_format) if configuration.default_response_format
|
169
|
+
!accepted_mime_types.find { |mt| ::Rack::Mime.match?(mt, mime_type) }.nil?
|
564
170
|
end
|
565
171
|
|
566
|
-
#
|
567
|
-
#
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
# @api private
|
576
|
-
def detect_format
|
577
|
-
configuration.format_for(content_type) || MIME_TYPES.key(content_type)
|
578
|
-
end
|
579
|
-
|
580
|
-
# @since 0.3.0
|
581
|
-
# @api private
|
582
|
-
def default_charset
|
583
|
-
configuration.default_charset
|
172
|
+
# Use for setting the content_type and charset if the response
|
173
|
+
#
|
174
|
+
# @see Hanami::Action::Mime#call
|
175
|
+
#
|
176
|
+
# @return [String]
|
177
|
+
def self.calculate_content_type_with_charset(configuration, request, accepted_mime_types)
|
178
|
+
charset = self.charset(configuration.default_charset)
|
179
|
+
content_type = self.content_type(configuration, request, accepted_mime_types)
|
180
|
+
content_type_with_charset(content_type, charset)
|
584
181
|
end
|
585
182
|
|
586
|
-
#
|
587
|
-
# @api private
|
588
|
-
def content_type_with_charset
|
589
|
-
"#{content_type}; charset=#{charset}"
|
590
|
-
end
|
183
|
+
# private
|
591
184
|
|
592
185
|
# Patched version of <tt>Rack::Utils.best_q_match</tt>.
|
593
186
|
#
|
594
|
-
# @since 0.
|
187
|
+
# @since 2.0.0
|
595
188
|
# @api private
|
596
189
|
#
|
597
190
|
# @see http://www.rubydoc.info/gems/rack/Rack/Utils#best_q_match-class_method
|
@@ -599,7 +192,7 @@ module Hanami
|
|
599
192
|
# @see https://github.com/hanami/controller/issues/59
|
600
193
|
# @see https://github.com/hanami/controller/issues/104
|
601
194
|
# @see https://github.com/hanami/controller/issues/275
|
602
|
-
def best_q_match(q_value_header, available_mimes)
|
195
|
+
def self.best_q_match(q_value_header, available_mimes = TYPES.values)
|
603
196
|
::Rack::Utils.q_values(q_value_header).each_with_index.map do |(req_mime, quality), index|
|
604
197
|
match = available_mimes.find { |am| ::Rack::Mime.match?(am, req_mime) }
|
605
198
|
next unless match
|