hanami 2.3.0.beta2 → 2.3.0
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 +64 -1
- data/README.md +1 -3
- data/hanami.gemspec +3 -3
- data/lib/hanami/config/actions/content_security_policy.rb +2 -2
- data/lib/hanami/config/actions.rb +3 -2
- data/lib/hanami/config/router.rb +1 -0
- data/lib/hanami/config.rb +8 -16
- data/lib/hanami/extensions/action.rb +1 -0
- data/lib/hanami/extensions/operation/slice_configured_db_operation.rb +88 -0
- data/lib/hanami/extensions/operation.rb +8 -23
- data/lib/hanami/extensions/view/context.rb +2 -1
- data/lib/hanami/extensions/view/part.rb +3 -0
- data/lib/hanami/extensions/view/scope.rb +3 -0
- data/lib/hanami/extensions/view.rb +1 -0
- data/lib/hanami/helpers/assets_helper.rb +2 -2
- data/lib/hanami/middleware/content_security_policy_nonce.rb +3 -3
- data/lib/hanami/routes.rb +1 -0
- data/lib/hanami/slice/router.rb +201 -12
- data/lib/hanami/slice.rb +1 -0
- data/lib/hanami/slice_configurable.rb +1 -3
- data/lib/hanami/version.rb +1 -1
- data/lib/hanami/web/rack_logger.rb +25 -8
- data/lib/hanami.rb +15 -1
- data/spec/integration/action/format_config_spec.rb +0 -68
- data/spec/integration/logging/request_logging_spec.rb +16 -0
- data/spec/integration/operations/extension_spec.rb +63 -0
- data/spec/integration/rack_app/body_parser_spec.rb +1 -2
- data/spec/integration/router/resource_routes_spec.rb +281 -0
- metadata +14 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 38d55453c1d105a2082e3fb7b6f3ae75318261b02cf496db56832f7223fbb5d2
|
|
4
|
+
data.tar.gz: fe85b8b2cb513066219d58556d981e9afbad8c824415ef303d56134de19c1754
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 363fb71d90a7c35bc55a8eb45ab050314be92e3ef0eb074a5da75d57b8f034e653a3e640c2e49548f0e3dcdf7adaa8cb013fda940befbaf33066e71fd4deded4
|
|
7
|
+
data.tar.gz: 8de0ec686d58238d32397950322e90411158dc9089cbf134d32983a03d0ba5518b3a0338097ee0e8a46a35bc12688c348a498ada16cfa41201ce3824b7892ed9
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Hanami
|
|
2
2
|
|
|
3
|
+
A flexible framework for maintainable Ruby apps.
|
|
4
|
+
|
|
5
|
+
A complete Hanami app is composed of multiple gems. For a complete overview of changes, see also these CHANGELOGs:
|
|
6
|
+
|
|
7
|
+
[Assets][assets], [Assets JS][assets-js], [CLI][cli],
|
|
8
|
+
[Controller][controller], [DB][db], [RSpec][rspec],
|
|
9
|
+
[Reloader][reloader], [Router][router], [Utils][utils],
|
|
10
|
+
[Validations][validations], [View][view], [Webconsole][webconsole]
|
|
11
|
+
|
|
12
|
+
[assets]: https://github.com/hanami/hanami-assets/blob/main/CHANGELOG.md
|
|
13
|
+
[assets-js]: https://github.com/hanami/hanami-assets-js/blob/main/CHANGELOG.md
|
|
14
|
+
[cli]: https://github.com/hanami/hanami-cli/blob/main/CHANGELOG.md
|
|
15
|
+
[controller]: https://github.com/hanami/hanami-controller/blob/main/CHANGELOG.md
|
|
16
|
+
[db]: https://github.com/hanami/hanami-db/blob/main/CHANGELOG.md
|
|
17
|
+
[rspec]: https://github.com/hanami/hanami-rspec/blob/main/CHANGELOG.md
|
|
18
|
+
[reloader]: https://github.com/hanami/hanami-reloader/blob/main/CHANGELOG.md
|
|
19
|
+
[router]: https://github.com/hanami/hanami-router/blob/main/CHANGELOG.md
|
|
20
|
+
[utils]: https://github.com/hanami/hanami-utils/blob/main/CHANGELOG.md
|
|
21
|
+
[validations]: https://github.com/hanami/hanami-validations/blob/main/CHANGELOG.md
|
|
22
|
+
[view]: https://github.com/hanami/hanami-view/blob/main/CHANGELOG.md
|
|
23
|
+
[webconsole]: https://github.com/hanami/hanami-webconsole/blob/main/CHANGELOG.md
|
|
24
|
+
|
|
3
25
|
## [Unreleased]
|
|
4
26
|
|
|
5
27
|
### Added
|
|
@@ -14,6 +36,46 @@
|
|
|
14
36
|
|
|
15
37
|
### Security
|
|
16
38
|
|
|
39
|
+
## [v2.3.0] - 2025-11-12
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- Add `resources` and `resource` methods to the routes DSL. (@afomera and @timriley in #1542 and #1548)
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
resources :users
|
|
47
|
+
# Generates:
|
|
48
|
+
# GET /users users.index
|
|
49
|
+
# GET /users/new users.new
|
|
50
|
+
# POST /users users.create
|
|
51
|
+
# GET /users/:id users.show
|
|
52
|
+
# GET /users/:id/edit users.edit
|
|
53
|
+
# PATCH /users/:id users.update
|
|
54
|
+
# DELETE /users/:id users.destroy
|
|
55
|
+
|
|
56
|
+
resource :profile
|
|
57
|
+
# Generates (singular, no index):
|
|
58
|
+
# GET /profile/new profile.new
|
|
59
|
+
# POST /profile profile.create
|
|
60
|
+
# GET /profile profile.show
|
|
61
|
+
# GET /profile/edit profile.edit
|
|
62
|
+
# PATCH /profile profile.update
|
|
63
|
+
# DELETE /profile profile.destroy
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
- Enable body parsing middleware by default. (@timriley in #1549)
|
|
69
|
+
|
|
70
|
+
This will parse multipart form upload bodies in requests with content type of `multipart/form-data`, and JSON bodies in requests with content types of `application/json` or `application/vnd.api+json`.
|
|
71
|
+
- For standard logging, use block-based API to provide log payloads, so that log payloads are not generated if the log severity is less than the configured log level. (@p8 in #1550)
|
|
72
|
+
|
|
73
|
+
### Fixed
|
|
74
|
+
|
|
75
|
+
- Avoid errors when eagerloading Zeitwerk (or when running `bin/rails zeitwerk:check` if your Hanami app is embedded within Rails) by skipping autoloading for extension files that may depend on non-installed gems. (@timriley in #1498)
|
|
76
|
+
- Only extend `Dry::Operation` for Hanami’s database layer when the hanami-db gem is bundled. (@timriley in #1490)
|
|
77
|
+
- Address deprecation warnings from previous usage of `JSON.fast_generate`. (@krzykamil in #1546)
|
|
78
|
+
|
|
17
79
|
## [v2.3.0.beta1] - 2025-10-17
|
|
18
80
|
|
|
19
81
|
### Changed
|
|
@@ -1523,6 +1585,7 @@ end
|
|
|
1523
1585
|
- [Luca Guidi] Official support for MRI 2.0
|
|
1524
1586
|
|
|
1525
1587
|
|
|
1526
|
-
[unreleased]: https://github.com/hanami/hanami/compare/v2.3.0
|
|
1588
|
+
[unreleased]: https://github.com/hanami/hanami/compare/v2.3.0...HEAD
|
|
1589
|
+
[v2.3.0] https://github.com/hanami/hanami/compare/v2.3.0.beta2...v2.3.0
|
|
1527
1590
|
[v2.3.0.beta2] https://github.com/hanami/hanami/compare/v2.3.0.beta1...v2.3.0.beta2
|
|
1528
1591
|
[v2.3.0.beta1] https://github.com/hanami/hanami/compare/v2.2.1...v2.3.0.beta1
|
data/README.md
CHANGED
|
@@ -21,8 +21,6 @@ These components are designed to be used independently or together in a Hanami a
|
|
|
21
21
|
|
|
22
22
|
## Installation
|
|
23
23
|
|
|
24
|
-
Hanami supports Ruby (MRI) 3.1+.
|
|
25
|
-
|
|
26
24
|
```shell
|
|
27
25
|
gem install hanami
|
|
28
26
|
```
|
|
@@ -95,7 +93,7 @@ $ bundle exec rspec path/to/spec.rb
|
|
|
95
93
|
|
|
96
94
|
### Development Requirements
|
|
97
95
|
|
|
98
|
-
* Ruby >= 3.
|
|
96
|
+
* Ruby >= 3.2
|
|
99
97
|
* Bundler
|
|
100
98
|
* Node.js
|
|
101
99
|
|
data/hanami.gemspec
CHANGED
|
@@ -37,9 +37,9 @@ Gem::Specification.new do |spec|
|
|
|
37
37
|
spec.add_dependency "dry-inflector", "~> 1.0", ">= 1.1.0", "< 2"
|
|
38
38
|
spec.add_dependency "dry-monitor", "~> 1.0", ">= 1.0.1", "< 2"
|
|
39
39
|
spec.add_dependency "dry-system", "~> 1.1"
|
|
40
|
-
spec.add_dependency "dry-logger", "~> 1.
|
|
41
|
-
spec.add_dependency "hanami-cli", "
|
|
42
|
-
spec.add_dependency "hanami-utils", "
|
|
40
|
+
spec.add_dependency "dry-logger", "~> 1.2", "< 2"
|
|
41
|
+
spec.add_dependency "hanami-cli", ">= 2.3.0"
|
|
42
|
+
spec.add_dependency "hanami-utils", ">= 2.3.0"
|
|
43
43
|
spec.add_dependency "json", ">= 2.7.2"
|
|
44
44
|
spec.add_dependency "zeitwerk", "~> 2.6"
|
|
45
45
|
spec.add_dependency "rack-session"
|
|
@@ -101,7 +101,7 @@ module Hanami
|
|
|
101
101
|
# @return [Boolean]
|
|
102
102
|
#
|
|
103
103
|
# @api public
|
|
104
|
-
# @since
|
|
104
|
+
# @since 2.3.0
|
|
105
105
|
def nonce?
|
|
106
106
|
@policy.any? { _2.match?(/'nonce'/) }
|
|
107
107
|
end
|
|
@@ -112,7 +112,7 @@ module Hanami
|
|
|
112
112
|
# @return [Array<(Symbol, Array)>]
|
|
113
113
|
#
|
|
114
114
|
# @api public
|
|
115
|
-
# @since
|
|
115
|
+
# @since 2.3.0
|
|
116
116
|
def middleware
|
|
117
117
|
return [] unless nonce?
|
|
118
118
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "dry/configurable"
|
|
4
|
+
require "hanami/action"
|
|
4
5
|
|
|
5
6
|
module Hanami
|
|
6
7
|
class Config
|
|
@@ -88,7 +89,7 @@ module Hanami
|
|
|
88
89
|
# @return [Proc]
|
|
89
90
|
#
|
|
90
91
|
# @api public
|
|
91
|
-
# @since
|
|
92
|
+
# @since 2.3.0
|
|
92
93
|
setting :content_security_policy_nonce_generator, default: -> { SecureRandom.urlsafe_base64(16) }
|
|
93
94
|
|
|
94
95
|
# @!attribute [rw] method_override
|
|
@@ -156,7 +157,7 @@ module Hanami
|
|
|
156
157
|
end
|
|
157
158
|
|
|
158
159
|
# @api public
|
|
159
|
-
# @since
|
|
160
|
+
# @since 2.3.0
|
|
160
161
|
def content_security_policy? = !!@content_security_policy
|
|
161
162
|
|
|
162
163
|
private
|
data/lib/hanami/config/router.rb
CHANGED
data/lib/hanami/config.rb
CHANGED
|
@@ -320,7 +320,6 @@ module Hanami
|
|
|
320
320
|
self.render_detailed_errors = (env == :development)
|
|
321
321
|
load_from_env
|
|
322
322
|
|
|
323
|
-
|
|
324
323
|
@actions = load_dependent_config("hanami-controller") {
|
|
325
324
|
require_relative "config/actions"
|
|
326
325
|
Actions.new
|
|
@@ -336,6 +335,7 @@ module Hanami
|
|
|
336
335
|
@logger = Config::Logger.new(env: env, app_name: app_name)
|
|
337
336
|
|
|
338
337
|
@middleware = load_dependent_config("hanami-router") {
|
|
338
|
+
require_relative "slice/routing/middleware/stack"
|
|
339
339
|
Slice::Routing::Middleware::Stack.new
|
|
340
340
|
}
|
|
341
341
|
|
|
@@ -484,24 +484,16 @@ module Hanami
|
|
|
484
484
|
self.slices = ENV["HANAMI_SLICES"]&.split(",")&.map(&:strip)
|
|
485
485
|
end
|
|
486
486
|
|
|
487
|
-
|
|
488
|
-
|
|
487
|
+
DEFAULT_MIDDLEWARE_PARSERS = {
|
|
488
|
+
form: ["multipart/form-data"],
|
|
489
|
+
json: ["application/json", "application/vnd.api+json"]
|
|
490
|
+
}.freeze
|
|
491
|
+
private_constant :DEFAULT_MIDDLEWARE_PARSERS
|
|
489
492
|
|
|
490
493
|
def use_body_parser_middleware
|
|
491
|
-
return unless Hanami.bundled?("hanami-controller")
|
|
492
|
-
|
|
493
|
-
return if actions.formats.empty?
|
|
494
|
-
return if middleware.stack["/"].map(&:first).any? { |klass| klass == "Hanami::Middleware::BodyParser" }
|
|
495
|
-
|
|
496
|
-
parsers = SUPPORTED_MIDDLEWARE_PARSERS & actions.formats.accepted
|
|
497
|
-
return if parsers.empty?
|
|
494
|
+
return unless Hanami.bundled?("hanami-router") && Hanami.bundled?("hanami-controller")
|
|
498
495
|
|
|
499
|
-
middleware.use(
|
|
500
|
-
:body_parser,
|
|
501
|
-
[parsers.to_h { |parser_format|
|
|
502
|
-
[parser_format, actions.formats.mime_types_for(parser_format)]
|
|
503
|
-
}]
|
|
504
|
-
)
|
|
496
|
+
middleware.use(:body_parser, [DEFAULT_MIDDLEWARE_PARSERS])
|
|
505
497
|
end
|
|
506
498
|
|
|
507
499
|
def load_dependent_config(gem_name)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hanami
|
|
4
|
+
module Extensions
|
|
5
|
+
module Operation
|
|
6
|
+
# Extends operations to support the database.
|
|
7
|
+
#
|
|
8
|
+
# Add an initializer accepting a `rom:` dependency, which is supplied automatically from the
|
|
9
|
+
# `"db.rom"` component in the operation's slice.
|
|
10
|
+
#
|
|
11
|
+
# @api private
|
|
12
|
+
class SliceConfiguredDBOperation < Module
|
|
13
|
+
attr_reader :slice
|
|
14
|
+
|
|
15
|
+
def initialize(slice)
|
|
16
|
+
super()
|
|
17
|
+
@slice = slice
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def extended(operation_class)
|
|
21
|
+
require "dry/operation/extensions/rom"
|
|
22
|
+
operation_class.include Dry::Operation::Extensions::ROM
|
|
23
|
+
|
|
24
|
+
operation_class.include InstanceMethods
|
|
25
|
+
operation_class.include SliceInstanceMethods.new(slice)
|
|
26
|
+
define_new
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def inspect
|
|
30
|
+
"#<#{self.class.name}[#{slice.name}>"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def define_new
|
|
36
|
+
resolve_rom = method(:resolve_rom)
|
|
37
|
+
|
|
38
|
+
define_method(:new) do |**kwargs|
|
|
39
|
+
super(
|
|
40
|
+
**kwargs,
|
|
41
|
+
rom: kwargs.fetch(:rom) { resolve_rom.() },
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def resolve_rom
|
|
47
|
+
slice["db.rom"] if slice.key?("db.rom")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
module InstanceMethods
|
|
51
|
+
attr_reader :rom
|
|
52
|
+
|
|
53
|
+
def initialize(rom:, **)
|
|
54
|
+
@rom = rom
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class SliceInstanceMethods < Module
|
|
59
|
+
attr_reader :slice
|
|
60
|
+
|
|
61
|
+
def initialize(slice)
|
|
62
|
+
@slice = slice
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def included(operation_class)
|
|
66
|
+
define_transaction
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def define_transaction
|
|
72
|
+
slice = self.slice
|
|
73
|
+
define_method(:transaction) do |**kwargs, &block|
|
|
74
|
+
unless rom
|
|
75
|
+
msg = <<~TEXT.strip
|
|
76
|
+
A configured db for #{slice} is required to run transactions.
|
|
77
|
+
TEXT
|
|
78
|
+
raise Hanami::ComponentLoadError, msg
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
super(**kwargs, &block)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -11,38 +11,23 @@ module Hanami
|
|
|
11
11
|
# @api public
|
|
12
12
|
# @since 2.2.0
|
|
13
13
|
module Operation
|
|
14
|
+
require_relative "operation/slice_configured_db_operation"
|
|
15
|
+
|
|
14
16
|
# @api private
|
|
15
|
-
|
|
16
|
-
def self.included(operation_class)
|
|
17
|
+
def self.extended(operation_class)
|
|
17
18
|
super
|
|
18
19
|
|
|
19
20
|
operation_class.extend(Hanami::SliceConfigurable)
|
|
20
|
-
operation_class.extend(ClassMethods)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
#
|
|
24
|
-
# @since 2.2.0
|
|
25
|
-
module ClassMethods
|
|
26
|
-
# @api private
|
|
27
|
-
# @since 2.2.0
|
|
28
|
-
def configure_for_slice(slice)
|
|
29
|
-
include slice.namespace::Deps["db.rom"]
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# @api private
|
|
33
|
-
# @since 2.2.0
|
|
34
|
-
def inherited(subclass)
|
|
35
|
-
super
|
|
23
|
+
# private
|
|
36
24
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
require "dry/operation/extensions/rom"
|
|
41
|
-
subclass.include Dry::Operation::Extensions::ROM
|
|
42
|
-
end
|
|
25
|
+
# @api private
|
|
26
|
+
def configure_for_slice(slice)
|
|
27
|
+
extend SliceConfiguredDBOperation.new(slice) if Hanami.bundled?("hanami-db")
|
|
43
28
|
end
|
|
44
29
|
end
|
|
45
30
|
end
|
|
46
31
|
end
|
|
47
32
|
|
|
48
|
-
Dry::Operation.
|
|
33
|
+
Dry::Operation.extend(Hanami::Extensions::Operation)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../../errors"
|
|
4
|
+
require_relative "slice_configured_context"
|
|
4
5
|
|
|
5
6
|
module Hanami
|
|
6
7
|
module Extensions
|
|
@@ -167,7 +168,7 @@ module Hanami
|
|
|
167
168
|
# @return [Boolean]
|
|
168
169
|
#
|
|
169
170
|
# @api public
|
|
170
|
-
# @since
|
|
171
|
+
# @since 2.3.0
|
|
171
172
|
def request?
|
|
172
173
|
!!@request
|
|
173
174
|
end
|
|
@@ -710,7 +710,7 @@ module Hanami
|
|
|
710
710
|
#
|
|
711
711
|
# @return [String, nil] nonce value of the current request
|
|
712
712
|
#
|
|
713
|
-
# @since
|
|
713
|
+
# @since 2.3.0
|
|
714
714
|
#
|
|
715
715
|
# @example App configuration
|
|
716
716
|
#
|
|
@@ -766,7 +766,7 @@ module Hanami
|
|
|
766
766
|
_context.assets.crossorigin?(source)
|
|
767
767
|
end
|
|
768
768
|
|
|
769
|
-
# @since
|
|
769
|
+
# @since 2.3.0
|
|
770
770
|
# @api private
|
|
771
771
|
def _nonce(source, nonce_option)
|
|
772
772
|
if nonce_option == false
|
|
@@ -14,16 +14,16 @@ module Hanami
|
|
|
14
14
|
# @see Hanami::Middleware::ContentSecurityPolicyNonce
|
|
15
15
|
#
|
|
16
16
|
# @api private
|
|
17
|
-
# @since
|
|
17
|
+
# @since 2.3.0
|
|
18
18
|
class ContentSecurityPolicyNonce
|
|
19
19
|
# @api private
|
|
20
|
-
# @since
|
|
20
|
+
# @since 2.3.0
|
|
21
21
|
def initialize(app)
|
|
22
22
|
@app = app
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
# @api private
|
|
26
|
-
# @since
|
|
26
|
+
# @since 2.3.0
|
|
27
27
|
def call(env)
|
|
28
28
|
return @app.call(env) unless Hanami.app.config.actions.content_security_policy?
|
|
29
29
|
|