hanami-controller 2.0.0.beta1 → 2.0.0.beta4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|