hanami-controller 2.0.0.beta1 → 2.0.0.beta4
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 +19 -0
- data/README.md +1 -1
- data/lib/hanami/action/config.rb +262 -0
- data/lib/hanami/action/constants.rb +6 -12
- data/lib/hanami/action/csrf_protection.rb +1 -0
- data/lib/hanami/action/error.rb +41 -0
- data/lib/hanami/action/mime.rb +65 -31
- data/lib/hanami/action/params.rb +5 -12
- data/lib/hanami/action/request.rb +21 -2
- data/lib/hanami/action/response.rb +20 -22
- data/lib/hanami/action/session.rb +4 -0
- data/lib/hanami/action.rb +106 -103
- data/lib/hanami/controller/version.rb +1 -1
- metadata +5 -4
- data/lib/hanami/action/configuration.rb +0 -436
@@ -3,7 +3,6 @@
|
|
3
3
|
require "rack"
|
4
4
|
require "rack/response"
|
5
5
|
require "hanami/utils/kernel"
|
6
|
-
require "hanami/action/flash"
|
7
6
|
require "hanami/action/halt"
|
8
7
|
require "hanami/action/cookie_jar"
|
9
8
|
require "hanami/action/cache/cache_control"
|
@@ -27,7 +26,7 @@ module Hanami
|
|
27
26
|
|
28
27
|
# @since 2.0.0
|
29
28
|
# @api private
|
30
|
-
attr_reader :request, :
|
29
|
+
attr_reader :request, :exposures, :format, :env, :view_options
|
31
30
|
|
32
31
|
# @since 2.0.0
|
33
32
|
# @api private
|
@@ -36,7 +35,7 @@ module Hanami
|
|
36
35
|
# @since 2.0.0
|
37
36
|
# @api private
|
38
37
|
def self.build(status, env)
|
39
|
-
new(
|
38
|
+
new(config: nil, content_type: Mime.best_q_match(env[Action::HTTP_ACCEPT]), env: env).tap do |r|
|
40
39
|
r.status = status
|
41
40
|
r.body = Http::Status.message_for(status)
|
42
41
|
r.set_format(Mime.format_for(r.content_type))
|
@@ -45,18 +44,18 @@ module Hanami
|
|
45
44
|
|
46
45
|
# @since 2.0.0
|
47
46
|
# @api private
|
48
|
-
def initialize(request:,
|
47
|
+
def initialize(request:, config:, content_type: nil, env: {}, headers: {}, view_options: nil, sessions_enabled: false) # rubocop:disable Layout/LineLength, Metrics/ParameterLists
|
49
48
|
super([], 200, headers.dup)
|
50
49
|
set_header(Action::CONTENT_TYPE, content_type)
|
51
50
|
|
52
51
|
@request = request
|
53
|
-
@
|
54
|
-
@configuration = configuration
|
52
|
+
@config = config
|
55
53
|
@charset = ::Rack::MediaType.params(content_type).fetch("charset", nil)
|
56
54
|
@exposures = {}
|
57
55
|
@env = env
|
58
56
|
@view_options = view_options || DEFAULT_VIEW_OPTIONS
|
59
57
|
|
58
|
+
@sessions_enabled = sessions_enabled
|
60
59
|
@sending_file = false
|
61
60
|
end
|
62
61
|
|
@@ -103,19 +102,27 @@ module Hanami
|
|
103
102
|
# @since 2.0.0
|
104
103
|
# @api public
|
105
104
|
def session
|
106
|
-
|
105
|
+
unless @sessions_enabled
|
106
|
+
raise Hanami::Action::MissingSessionError.new("Hanami::Action::Response#session")
|
107
|
+
end
|
108
|
+
|
109
|
+
request.session
|
107
110
|
end
|
108
111
|
|
109
112
|
# @since 2.0.0
|
110
113
|
# @api public
|
111
|
-
def
|
112
|
-
|
114
|
+
def flash
|
115
|
+
unless @sessions_enabled
|
116
|
+
raise Hanami::Action::MissingSessionError.new("Hanami::Action::Response#flash")
|
117
|
+
end
|
118
|
+
|
119
|
+
request.flash
|
113
120
|
end
|
114
121
|
|
115
122
|
# @since 2.0.0
|
116
123
|
# @api public
|
117
|
-
def
|
118
|
-
@
|
124
|
+
def cookies
|
125
|
+
@cookies ||= CookieJar.new(env.dup, headers, @config.cookies)
|
119
126
|
end
|
120
127
|
|
121
128
|
# @since 2.0.0
|
@@ -131,7 +138,7 @@ module Hanami
|
|
131
138
|
# @api public
|
132
139
|
def send_file(path)
|
133
140
|
_send_file(
|
134
|
-
Rack::File.new(path, @
|
141
|
+
Rack::File.new(path, @config.public_directory).call(env)
|
135
142
|
)
|
136
143
|
end
|
137
144
|
|
@@ -139,7 +146,7 @@ module Hanami
|
|
139
146
|
# @api public
|
140
147
|
def unsafe_send_file(path)
|
141
148
|
directory = if Pathname.new(path).relative?
|
142
|
-
@
|
149
|
+
@config.root_directory
|
143
150
|
else
|
144
151
|
FILE_SYSTEM_ROOT
|
145
152
|
end
|
@@ -175,15 +182,6 @@ module Hanami
|
|
175
182
|
end
|
176
183
|
end
|
177
184
|
|
178
|
-
# @since 2.0.0
|
179
|
-
# @api private
|
180
|
-
def request_id
|
181
|
-
env.fetch(Action::REQUEST_ID) do
|
182
|
-
# FIXME: raise a meaningful error, by inviting devs to include Hanami::Action::Session
|
183
|
-
raise "Can't find request ID"
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
185
|
# @since 2.0.0
|
188
186
|
# @api public
|
189
187
|
def set_format(value) # rubocop:disable Naming/AccessorMethodName
|
data/lib/hanami/action.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
begin
|
4
|
+
require "dry/core"
|
5
|
+
require "dry/types"
|
4
6
|
require "hanami/validations"
|
5
7
|
require "hanami/action/validatable"
|
6
8
|
rescue LoadError # rubocop:disable Lint/SuppressedException
|
7
9
|
end
|
8
10
|
|
9
|
-
require "
|
11
|
+
require "dry/configurable"
|
10
12
|
require "hanami/utils/callbacks"
|
11
13
|
require "hanami/utils"
|
12
14
|
require "hanami/utils/string"
|
@@ -14,14 +16,15 @@ require "hanami/utils/kernel"
|
|
14
16
|
require "rack"
|
15
17
|
require "rack/utils"
|
16
18
|
|
19
|
+
require_relative "action/config"
|
17
20
|
require_relative "action/constants"
|
18
21
|
require_relative "action/base_params"
|
19
|
-
require_relative "action/configuration"
|
20
22
|
require_relative "action/halt"
|
21
23
|
require_relative "action/mime"
|
22
24
|
require_relative "action/rack/file"
|
23
25
|
require_relative "action/request"
|
24
26
|
require_relative "action/response"
|
27
|
+
require_relative "action/error"
|
25
28
|
|
26
29
|
module Hanami
|
27
30
|
# An HTTP endpoint
|
@@ -36,7 +39,52 @@ module Hanami
|
|
36
39
|
# # ...
|
37
40
|
# end
|
38
41
|
# end
|
42
|
+
#
|
43
|
+
# @api public
|
39
44
|
class Action
|
45
|
+
extend Dry::Configurable(config_class: Config)
|
46
|
+
|
47
|
+
# See {Config} for individual setting accessor API docs
|
48
|
+
setting :handled_exceptions, default: {}
|
49
|
+
setting :formats, default: Config::DEFAULT_FORMATS
|
50
|
+
setting :default_request_format, constructor: -> (format) {
|
51
|
+
Utils::Kernel.Symbol(format) unless format.nil?
|
52
|
+
}
|
53
|
+
setting :default_response_format, constructor: -> (format) {
|
54
|
+
Utils::Kernel.Symbol(format) unless format.nil?
|
55
|
+
}
|
56
|
+
setting :accepted_formats, default: []
|
57
|
+
setting :default_charset
|
58
|
+
setting :default_headers, default: {}, constructor: -> (headers) { headers.compact }
|
59
|
+
setting :cookies, default: {}, constructor: -> (cookie_options) {
|
60
|
+
# Call `to_h` here to permit `ApplicationConfiguration::Cookies` object to be
|
61
|
+
# provided when application actions are configured
|
62
|
+
cookie_options.to_h.compact
|
63
|
+
}
|
64
|
+
setting :root_directory, constructor: -> (dir) {
|
65
|
+
Pathname(File.expand_path(dir || Dir.pwd)).realpath
|
66
|
+
}
|
67
|
+
setting :public_directory, default: Config::DEFAULT_PUBLIC_DIRECTORY
|
68
|
+
setting :before_callbacks, default: Utils::Callbacks::Chain.new, cloneable: true
|
69
|
+
setting :after_callbacks, default: Utils::Callbacks::Chain.new, cloneable: true
|
70
|
+
|
71
|
+
# @!scope class
|
72
|
+
|
73
|
+
# @!method config
|
74
|
+
# Returns the action's config. Use this to configure your action.
|
75
|
+
#
|
76
|
+
# @example Access inside class body
|
77
|
+
# class Show < Hanami::Action
|
78
|
+
# config.default_response_format = :json
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# @return [Config]
|
82
|
+
#
|
83
|
+
# @api public
|
84
|
+
# @since 2.0.0
|
85
|
+
|
86
|
+
# @!scope instance
|
87
|
+
|
40
88
|
# Override Ruby's hook for modules.
|
41
89
|
# It includes basic Hanami::Action modules to the given class.
|
42
90
|
#
|
@@ -49,31 +97,13 @@ module Hanami
|
|
49
97
|
|
50
98
|
if subclass.superclass == Action
|
51
99
|
subclass.class_eval do
|
52
|
-
include Utils::ClassAttribute
|
53
|
-
|
54
|
-
class_attribute :before_callbacks
|
55
|
-
self.before_callbacks = Utils::Callbacks::Chain.new
|
56
|
-
|
57
|
-
class_attribute :after_callbacks
|
58
|
-
self.after_callbacks = Utils::Callbacks::Chain.new
|
59
|
-
|
60
100
|
include Validatable if defined?(Validatable)
|
61
101
|
end
|
62
102
|
end
|
63
103
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
# @since 2.0.0
|
68
|
-
# @api private
|
69
|
-
def self.configuration
|
70
|
-
@configuration ||= Configuration.new
|
71
|
-
end
|
72
|
-
|
73
|
-
class << self
|
74
|
-
# @since 2.0.0
|
75
|
-
# @api private
|
76
|
-
alias_method :config, :configuration
|
104
|
+
if instance_variable_defined?(:@params_class)
|
105
|
+
subclass.instance_variable_set(:@params_class, @params_class)
|
106
|
+
end
|
77
107
|
end
|
78
108
|
|
79
109
|
# Returns the class which defines the params
|
@@ -87,12 +117,7 @@ module Hanami
|
|
87
117
|
# @api private
|
88
118
|
# @since 0.7.0
|
89
119
|
def self.params_class
|
90
|
-
@params_class
|
91
|
-
end
|
92
|
-
|
93
|
-
# FIXME: make this thread-safe
|
94
|
-
def self.accepted_formats
|
95
|
-
@accepted_formats ||= []
|
120
|
+
@params_class || BaseParams
|
96
121
|
end
|
97
122
|
|
98
123
|
# Placeholder implementation for params class method
|
@@ -167,7 +192,7 @@ module Hanami
|
|
167
192
|
# # 2. set the article
|
168
193
|
# # 3. `#handle`
|
169
194
|
def self.append_before(...)
|
170
|
-
before_callbacks.append(...)
|
195
|
+
config.before_callbacks.append(...)
|
171
196
|
end
|
172
197
|
|
173
198
|
class << self
|
@@ -191,7 +216,7 @@ module Hanami
|
|
191
216
|
#
|
192
217
|
# @see Hanami::Action::Callbacks::ClassMethods#append_before
|
193
218
|
def self.append_after(...)
|
194
|
-
after_callbacks.append(...)
|
219
|
+
config.after_callbacks.append(...)
|
195
220
|
end
|
196
221
|
|
197
222
|
class << self
|
@@ -215,7 +240,7 @@ module Hanami
|
|
215
240
|
#
|
216
241
|
# @see Hanami::Action::Callbacks::ClassMethods#prepend_after
|
217
242
|
def self.prepend_before(...)
|
218
|
-
before_callbacks.prepend(...)
|
243
|
+
config.before_callbacks.prepend(...)
|
219
244
|
end
|
220
245
|
|
221
246
|
# Define a callback for an Action.
|
@@ -234,7 +259,7 @@ module Hanami
|
|
234
259
|
#
|
235
260
|
# @see Hanami::Action::Callbacks::ClassMethods#prepend_before
|
236
261
|
def self.prepend_after(...)
|
237
|
-
after_callbacks.prepend(...)
|
262
|
+
config.after_callbacks.prepend(...)
|
238
263
|
end
|
239
264
|
|
240
265
|
# Restrict the access to the specified mime type symbols.
|
@@ -246,7 +271,7 @@ module Hanami
|
|
246
271
|
#
|
247
272
|
# @since 0.1.0
|
248
273
|
#
|
249
|
-
# @see
|
274
|
+
# @see Config#format
|
250
275
|
#
|
251
276
|
# @example
|
252
277
|
# require "hanami/controller"
|
@@ -262,58 +287,28 @@ module Hanami
|
|
262
287
|
# # When called with "*/*" => 200
|
263
288
|
# # When called with "text/html" => 200
|
264
289
|
# # When called with "application/json" => 200
|
265
|
-
# # When called with "application/xml" =>
|
290
|
+
# # When called with "application/xml" => 415
|
266
291
|
def self.accept(*formats)
|
267
|
-
|
268
|
-
before :enforce_accepted_mime_types
|
292
|
+
config.accepted_formats = formats
|
269
293
|
end
|
270
294
|
|
271
|
-
#
|
272
|
-
#
|
273
|
-
# @overload new(**deps, ...)
|
274
|
-
# @param deps [Hash] action dependencies
|
275
|
-
#
|
276
|
-
# @overload new(configuration:, **deps, ...)
|
277
|
-
# @param configuration [Hanami::Action::Configuration] action configuration
|
278
|
-
# @param deps [Hash] action dependencies
|
279
|
-
#
|
280
|
-
# @return [Hanami::Action] Action object
|
295
|
+
# @see Config#handle_exception
|
281
296
|
#
|
282
297
|
# @since 2.0.0
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
obj.instance_variable_set(:@configuration, configuration.dup.finalize!)
|
287
|
-
obj.instance_variable_set(:@accepted_mime_types, Mime.restrict_mime_types(configuration, accepted_formats))
|
288
|
-
obj.send(:initialize, *args, **kwargs, &block)
|
289
|
-
obj.freeze
|
290
|
-
end
|
298
|
+
# @api public
|
299
|
+
def self.handle_exception(...)
|
300
|
+
config.handle_exception(...)
|
291
301
|
end
|
292
302
|
|
303
|
+
# Returns a new action
|
304
|
+
#
|
293
305
|
# @since 2.0.0
|
294
|
-
# @api
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
MODULE_SEPARATOR_TRANSFORMER = [:gsub, "::", "."].freeze
|
299
|
-
|
300
|
-
# @since 2.0.0
|
301
|
-
# @api private
|
302
|
-
def self.call(name)
|
303
|
-
Utils::String.transform(name, MODULE_SEPARATOR_TRANSFORMER, :underscore) unless name.nil?
|
304
|
-
end
|
305
|
-
|
306
|
-
class << self
|
307
|
-
# @since 2.0.0
|
308
|
-
# @api private
|
309
|
-
alias_method :[], :call
|
310
|
-
end
|
306
|
+
# @api public
|
307
|
+
def initialize(config: self.class.config)
|
308
|
+
@config = config
|
309
|
+
freeze
|
311
310
|
end
|
312
311
|
|
313
|
-
# @since 2.0.0
|
314
|
-
# @api private
|
315
|
-
attr_reader :name
|
316
|
-
|
317
312
|
# Implements the Rack/Hanami::Action protocol
|
318
313
|
#
|
319
314
|
# @since 0.1.0
|
@@ -324,16 +319,22 @@ module Hanami
|
|
324
319
|
|
325
320
|
halted = catch :halt do
|
326
321
|
params = self.class.params_class.new(env)
|
327
|
-
request = build_request(
|
322
|
+
request = build_request(
|
323
|
+
env: env,
|
324
|
+
params: params,
|
325
|
+
sessions_enabled: sessions_enabled?
|
326
|
+
)
|
328
327
|
response = build_response(
|
329
328
|
request: request,
|
330
|
-
|
331
|
-
|
332
|
-
content_type: Mime.calculate_content_type_with_charset(configuration, request, accepted_mime_types),
|
329
|
+
config: config,
|
330
|
+
content_type: Mime.calculate_content_type_with_charset(config, request, config.accepted_mime_types),
|
333
331
|
env: env,
|
334
|
-
headers:
|
332
|
+
headers: config.default_headers,
|
333
|
+
sessions_enabled: sessions_enabled?
|
335
334
|
)
|
336
335
|
|
336
|
+
enforce_accepted_mime_types(request)
|
337
|
+
|
337
338
|
_run_before_callbacks(request, response)
|
338
339
|
handle(request, response)
|
339
340
|
_run_after_callbacks(request, response)
|
@@ -344,12 +345,6 @@ module Hanami
|
|
344
345
|
finish(request, response, halted)
|
345
346
|
end
|
346
347
|
|
347
|
-
# @since 2.0.0
|
348
|
-
# @api public
|
349
|
-
def initialize(**deps)
|
350
|
-
@_deps = deps
|
351
|
-
end
|
352
|
-
|
353
348
|
protected
|
354
349
|
|
355
350
|
# Hook for subclasses to apply behavior as part of action invocation
|
@@ -421,36 +416,44 @@ module Hanami
|
|
421
416
|
|
422
417
|
# @since 2.0.0
|
423
418
|
# @api private
|
424
|
-
attr_reader :
|
419
|
+
attr_reader :config
|
425
420
|
|
426
421
|
# @since 2.0.0
|
427
422
|
# @api private
|
428
|
-
def
|
429
|
-
|
430
|
-
end
|
423
|
+
def enforce_accepted_mime_types(request)
|
424
|
+
return if config.accepted_formats.empty?
|
431
425
|
|
432
|
-
|
433
|
-
|
434
|
-
def enforce_accepted_mime_types(req, *)
|
435
|
-
Mime.accepted_mime_type?(req, accepted_mime_types, configuration) or halt 406
|
426
|
+
Mime.enforce_accept(request, config) { return halt 406 }
|
427
|
+
Mime.enforce_content_type(request, config) { return halt 415 }
|
436
428
|
end
|
437
429
|
|
438
430
|
# @since 2.0.0
|
439
431
|
# @api private
|
440
432
|
def exception_handler(exception)
|
441
|
-
|
433
|
+
config.handled_exceptions.each do |exception_class, handler|
|
442
434
|
return handler if exception.is_a?(exception_class)
|
443
435
|
end
|
444
436
|
|
445
437
|
nil
|
446
438
|
end
|
447
439
|
|
440
|
+
# @see Session#sessions_enabled?
|
448
441
|
# @since 2.0.0
|
449
442
|
# @api private
|
450
|
-
def
|
451
|
-
|
443
|
+
def sessions_enabled?
|
444
|
+
false
|
452
445
|
end
|
453
446
|
|
447
|
+
# Hook to be overridden by `Hanami::Extensions::Action` for integrated actions
|
448
|
+
#
|
449
|
+
# @since 2.0.0
|
450
|
+
# @api private
|
451
|
+
def build_request(**options)
|
452
|
+
Request.new(**options)
|
453
|
+
end
|
454
|
+
|
455
|
+
# Hook to be overridden by `Hanami::Extensions::Action` for integrated actions
|
456
|
+
#
|
454
457
|
# @since 2.0.0
|
455
458
|
# @api private
|
456
459
|
def build_response(**options)
|
@@ -507,14 +510,14 @@ module Hanami
|
|
507
510
|
# @since 0.1.0
|
508
511
|
# @api private
|
509
512
|
def _run_before_callbacks(*args)
|
510
|
-
|
513
|
+
config.before_callbacks.run(self, *args)
|
511
514
|
nil
|
512
515
|
end
|
513
516
|
|
514
517
|
# @since 0.1.0
|
515
518
|
# @api private
|
516
519
|
def _run_after_callbacks(*args)
|
517
|
-
|
520
|
+
config.after_callbacks.run(self, *args)
|
518
521
|
nil
|
519
522
|
end
|
520
523
|
|
@@ -589,9 +592,9 @@ module Hanami
|
|
589
592
|
case value
|
590
593
|
when Symbol
|
591
594
|
format = Utils::Kernel.Symbol(value)
|
592
|
-
[format, Action::Mime.format_to_mime_type(format,
|
595
|
+
[format, Action::Mime.format_to_mime_type(format, config)]
|
593
596
|
when String
|
594
|
-
[Action::Mime.detect_format(value,
|
597
|
+
[Action::Mime.detect_format(value, config), value]
|
595
598
|
else
|
596
599
|
raise Hanami::Controller::UnknownFormatError.new(value)
|
597
600
|
end
|
@@ -614,7 +617,7 @@ module Hanami
|
|
614
617
|
_empty_headers(res) if _requires_empty_headers?(res)
|
615
618
|
_empty_body(res) if res.head?
|
616
619
|
|
617
|
-
res.set_format(Action::Mime.detect_format(res.content_type,
|
620
|
+
res.set_format(Action::Mime.detect_format(res.content_type, config))
|
618
621
|
res[:params] = req.params
|
619
622
|
res[:format] = res.format
|
620
623
|
res
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hanami-controller
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.
|
4
|
+
version: 2.0.0.beta4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luca Guidi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -152,11 +152,12 @@ files:
|
|
152
152
|
- lib/hanami/action/cache/conditional_get.rb
|
153
153
|
- lib/hanami/action/cache/directives.rb
|
154
154
|
- lib/hanami/action/cache/expires.rb
|
155
|
-
- lib/hanami/action/
|
155
|
+
- lib/hanami/action/config.rb
|
156
156
|
- lib/hanami/action/constants.rb
|
157
157
|
- lib/hanami/action/cookie_jar.rb
|
158
158
|
- lib/hanami/action/cookies.rb
|
159
159
|
- lib/hanami/action/csrf_protection.rb
|
160
|
+
- lib/hanami/action/error.rb
|
160
161
|
- lib/hanami/action/flash.rb
|
161
162
|
- lib/hanami/action/halt.rb
|
162
163
|
- lib/hanami/action/mime.rb
|
@@ -191,7 +192,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
191
192
|
- !ruby/object:Gem::Version
|
192
193
|
version: 1.3.1
|
193
194
|
requirements: []
|
194
|
-
rubygems_version: 3.3.
|
195
|
+
rubygems_version: 3.3.7
|
195
196
|
signing_key:
|
196
197
|
specification_version: 4
|
197
198
|
summary: Complete, fast and testable actions for Rack and Hanami
|