hanami 2.0.0.beta3 → 2.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -0
- data/hanami.gemspec +9 -8
- data/lib/hanami/app.rb +50 -39
- data/lib/hanami/assets/app_config.rb +61 -0
- data/lib/hanami/assets/{configuration.rb → config.rb} +9 -10
- data/lib/hanami/{configuration → config}/actions/content_security_policy.rb +3 -3
- data/lib/hanami/config/actions/cookies.rb +57 -0
- data/lib/hanami/config/actions/sessions.rb +83 -0
- data/lib/hanami/config/actions.rb +164 -0
- data/lib/hanami/config/logger.rb +176 -0
- data/lib/hanami/config/null_config.rb +14 -0
- data/lib/hanami/{configuration → config}/router.rb +8 -9
- data/lib/hanami/{configuration → config}/views.rb +16 -20
- data/lib/hanami/config.rb +396 -0
- data/lib/hanami/constants.rb +4 -0
- data/lib/hanami/errors.rb +20 -0
- data/lib/hanami/extensions/action/slice_configured_action.rb +10 -6
- data/lib/hanami/extensions/action.rb +59 -7
- data/lib/hanami/extensions/view/context.rb +3 -4
- data/lib/hanami/extensions/view/slice_configured_view.rb +4 -4
- data/lib/hanami/extensions/view.rb +7 -5
- data/lib/hanami/providers/inflector.rb +6 -2
- data/lib/hanami/providers/logger.rb +9 -3
- data/lib/hanami/providers/rack.rb +12 -2
- data/lib/hanami/providers/routes.rb +14 -6
- data/lib/hanami/routes.rb +36 -1
- data/lib/hanami/settings/env_store.rb +4 -4
- data/lib/hanami/settings.rb +169 -21
- data/lib/hanami/slice/router.rb +38 -16
- data/lib/hanami/slice/routing/middleware/stack.rb +108 -40
- data/lib/hanami/slice/routing/resolver.rb +10 -17
- data/lib/hanami/slice/view_name_inferrer.rb +1 -1
- data/lib/hanami/slice.rb +605 -51
- data/lib/hanami/slice_configurable.rb +1 -1
- data/lib/hanami/slice_registrar.rb +25 -14
- data/lib/hanami/version.rb +2 -3
- data/lib/hanami/web/rack_logger.rb +14 -4
- data/lib/hanami.rb +122 -24
- data/spec/integration/action/csrf_protection_spec.rb +1 -1
- data/spec/integration/container/application_routes_helper_spec.rb +3 -1
- data/spec/integration/container/prepare_container_spec.rb +2 -0
- data/spec/integration/container/provider_lifecycle_spec.rb +61 -0
- data/spec/integration/container/standard_providers/rack_provider_spec.rb +44 -0
- data/spec/integration/container/{standard_bootable_components_spec.rb → standard_providers_spec.rb} +3 -3
- data/spec/integration/rack_app/body_parser_spec.rb +111 -0
- data/spec/integration/rack_app/middleware_spec.rb +455 -3
- data/spec/integration/rack_app/non_booted_rack_app_spec.rb +2 -1
- data/spec/integration/rack_app/rack_app_spec.rb +39 -11
- data/spec/integration/settings/access_in_slice_class_body_spec.rb +82 -0
- data/spec/integration/settings/access_to_constants_spec.rb +23 -146
- data/spec/integration/{slices/slice_settings_spec.rb → settings/slice_registration_spec.rb} +5 -1
- data/spec/integration/settings/using_types_spec.rb +4 -11
- data/spec/integration/setup_spec.rb +4 -4
- data/spec/integration/slices/external_slice_spec.rb +2 -1
- data/spec/integration/slices/slice_configuration_spec.rb +3 -1
- data/spec/integration/slices/slice_loading_spec.rb +4 -4
- data/spec/integration/slices/slice_routing_spec.rb +4 -3
- data/spec/integration/slices_spec.rb +100 -0
- data/spec/isolation/hanami/boot/success_spec.rb +1 -1
- data/spec/support/app_integration.rb +10 -15
- data/spec/unit/hanami/{configuration → config}/actions/content_security_policy_spec.rb +16 -16
- data/spec/unit/hanami/{configuration → config}/actions/cookies_spec.rb +6 -6
- data/spec/unit/hanami/{configuration → config}/actions/csrf_protection_spec.rb +12 -12
- data/spec/unit/hanami/config/actions/default_values_spec.rb +54 -0
- data/spec/unit/hanami/{configuration → config}/actions/sessions_spec.rb +6 -8
- data/spec/unit/hanami/{configuration → config}/actions_spec.rb +8 -20
- data/spec/unit/hanami/{configuration → config}/base_url_spec.rb +2 -2
- data/spec/unit/hanami/{configuration → config}/inflector_spec.rb +2 -2
- data/spec/unit/hanami/{configuration → config}/logger_spec.rb +42 -59
- data/spec/unit/hanami/{configuration → config}/router_spec.rb +7 -8
- data/spec/unit/hanami/{configuration → config}/slices_spec.rb +2 -2
- data/spec/unit/hanami/{configuration → config}/views_spec.rb +13 -24
- data/spec/unit/hanami/settings_spec.rb +65 -10
- data/spec/unit/hanami/slice_configurable_spec.rb +21 -2
- data/spec/unit/hanami/slice_spec.rb +32 -0
- data/spec/unit/hanami/version_spec.rb +1 -1
- data/spec/unit/hanami/web/rack_logger_spec.rb +13 -2
- metadata +100 -76
- data/lib/hanami/assets/app_configuration.rb +0 -69
- data/lib/hanami/configuration/actions/cookies.rb +0 -29
- data/lib/hanami/configuration/actions/sessions.rb +0 -46
- data/lib/hanami/configuration/actions.rb +0 -101
- data/lib/hanami/configuration/logger.rb +0 -87
- data/lib/hanami/configuration/null_configuration.rb +0 -14
- data/lib/hanami/configuration/sessions.rb +0 -50
- data/lib/hanami/configuration.rb +0 -234
- data/lib/hanami/providers/settings.rb +0 -98
- data/spec/unit/hanami/configuration/actions/default_values_spec.rb +0 -52
- data/spec/unit/hanami/configuration_spec.rb +0 -43
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6a1a430f511f0bda44248e88d4fb2c87d10dc3d8fbabf092b895ff2a3bbb78db
|
4
|
+
data.tar.gz: 75c6e767584d34103505eb3637fc0fb72dde6118b35fa5f4a76a186681031e7a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 306e3261fb72d114ddcb9128de3a3c9a499bff57f1b2c51fd3cee54e644248c3bd981f54b626d7e5549e1fa9c467d86156a7c2c3e4c14c869501e698d07f6423
|
7
|
+
data.tar.gz: a59bf43bf07700e282abe6e83919fd53a5e277b7efa5706b743a25537006eef76b49409bedf0af49cae8122bd39d5c396f705f1e148a01367565e1accb46d9a6
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,45 @@
|
|
2
2
|
|
3
3
|
The web, with simplicity.
|
4
4
|
|
5
|
+
## v2.0.0.rc1 - 2022-11-08
|
6
|
+
|
7
|
+
### Added
|
8
|
+
|
9
|
+
- [Piotr Solnica] Use Zeitwerk to auto-load Hanami
|
10
|
+
- [Tim Riley] Introduce `Hanami::Slice.stop` to properly shutdown all the application slices
|
11
|
+
|
12
|
+
### Fixed
|
13
|
+
|
14
|
+
- [Luca Guidi] Ensure to properly mount Rack middleware in routing scope and slice
|
15
|
+
- [Tim Riley] Simplify and clarify usage of `Hanami::Config#enviroment`
|
16
|
+
- [Tim Riley] Improve error message for missing action class
|
17
|
+
- [Tim Riley] Expect nested slices to use parent’s namespace
|
18
|
+
|
19
|
+
### Changed
|
20
|
+
|
21
|
+
- [Piotr Solnica] Replace `Hanami::Logger` with `Dry::Logger`
|
22
|
+
- [Tim Riley] Remove duplicated configuration `config.session` and keep `config.actions.sessions`
|
23
|
+
|
24
|
+
## v2.0.0.beta4 - 2022-10-24
|
25
|
+
|
26
|
+
### Added
|
27
|
+
|
28
|
+
- [Peter Solnica] Improve middleware config by avoiding class names and requires: symbols instead of classes can be passed as middleware identifiers (`config.middleware.use(:body_parser, :json)`); constant namespaces can be configured for resolving symbols into class names (`config.middleware.namespaces = [Hanami::Middleware, MyStuff::Middlewares]`), with the first match winning; first-party middleware classes are required automatically. (#1215)
|
29
|
+
|
30
|
+
### Fixed
|
31
|
+
|
32
|
+
- [Luca Guidi] Do not attempt to load routes if hanami-router is not bundled (#1214)
|
33
|
+
- [Marc Busqué] Ensure nested slices are considered when auto-configuring application components using `Hanami::SliceConfigurable` (#1212)
|
34
|
+
- [Tim Riley] Ensure errors are raised during settings loading for any settings that are missing entirely from ENV (#1222)
|
35
|
+
|
36
|
+
### Changed
|
37
|
+
|
38
|
+
- [Tim Riley] Rename `Hanami::App.configuration` to `Hanami::App.config` and `Hanami::Configuration` to `Hanami::Config` (#1218)
|
39
|
+
- [Tim Riley] Make `Hanami::App.settings` available as a class method again (#1213)
|
40
|
+
- [Tim Riley] `config/settings.rb` in apps no longer has access to autoloaded constants (changed back to behavior pre-beta3) (#1213)
|
41
|
+
- [Tim Riley] Auto-generate a nested `Types` module in `Hanami::Settings` subclasses if dry-types is available, for added convenience in type checking settings (#1213)
|
42
|
+
- [Marc Busqué] Hide values from `Hanami::Settings#inspect` (values may contain sensitive data). Values can be inspected via `#inspect_values` (#1217)
|
43
|
+
|
5
44
|
## v2.0.0.beta3 - 2022-09-21
|
6
45
|
|
7
46
|
### Changed
|
data/hanami.gemspec
CHANGED
@@ -32,14 +32,15 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
33
33
|
|
34
34
|
spec.add_dependency "bundler", ">= 1.16", "< 3"
|
35
|
-
spec.add_dependency "dry-configurable", "~> 0
|
36
|
-
spec.add_dependency "dry-core", "~> 0
|
37
|
-
spec.add_dependency "dry-inflector", "~> 0
|
38
|
-
spec.add_dependency "dry-
|
39
|
-
spec.add_dependency "dry-
|
40
|
-
spec.add_dependency "
|
41
|
-
spec.add_dependency "hanami-
|
42
|
-
spec.add_dependency "
|
35
|
+
spec.add_dependency "dry-configurable", "~> 1.0", "< 2"
|
36
|
+
spec.add_dependency "dry-core", "~> 1.0", "< 2"
|
37
|
+
spec.add_dependency "dry-inflector", "~> 1.0", "< 2"
|
38
|
+
spec.add_dependency "dry-monitor", "~> 1.0", "< 2"
|
39
|
+
spec.add_dependency "dry-system", "~> 1.0.rc"
|
40
|
+
spec.add_dependency "dry-logger", "~> 1.0.rc"
|
41
|
+
spec.add_dependency "hanami-cli", "~> 2.0.rc"
|
42
|
+
spec.add_dependency "hanami-utils", "~> 2.0.rc"
|
43
|
+
spec.add_dependency "zeitwerk", "~> 2.6"
|
43
44
|
|
44
45
|
spec.add_development_dependency "rspec", "~> 3.8"
|
45
46
|
spec.add_development_dependency "rack-test", "~> 1.1"
|
data/lib/hanami/app.rb
CHANGED
@@ -1,17 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "configuration"
|
4
3
|
require_relative "constants"
|
5
|
-
require_relative "slice"
|
6
|
-
require_relative "slice_name"
|
7
4
|
|
8
5
|
module Hanami
|
9
|
-
# The Hanami app is a singular slice tasked with managing the core components of
|
10
|
-
#
|
6
|
+
# The Hanami app is a singular slice tasked with managing the core components of the app and
|
7
|
+
# coordinating overall app boot.
|
11
8
|
#
|
12
|
-
# For smaller apps, the app may be the only slice present, whereas larger apps
|
13
|
-
#
|
14
|
-
# of shared components only.
|
9
|
+
# For smaller apps, the app may be the only slice present, whereas larger apps may consist of many
|
10
|
+
# slices, with the app reserved for holding a small number of shared components only.
|
15
11
|
#
|
16
12
|
# @see Slice
|
17
13
|
#
|
@@ -20,6 +16,8 @@ module Hanami
|
|
20
16
|
class App < Slice
|
21
17
|
@_mutex = Mutex.new
|
22
18
|
|
19
|
+
# @api private
|
20
|
+
# @since 2.0.0
|
23
21
|
def self.inherited(subclass)
|
24
22
|
super
|
25
23
|
|
@@ -29,11 +27,11 @@ module Hanami
|
|
29
27
|
|
30
28
|
@_mutex.synchronize do
|
31
29
|
subclass.class_eval do
|
32
|
-
@
|
30
|
+
@config = Hanami::Config.new(app_name: slice_name, env: Hanami.env)
|
33
31
|
|
34
|
-
# Prepare the load path (based on the default root of `Dir.pwd`) as early as
|
35
|
-
#
|
36
|
-
#
|
32
|
+
# Prepare the load path (based on the default root of `Dir.pwd`) as early as possible, so
|
33
|
+
# you can make a `require` inside the body of an `App` subclass, which may be useful for
|
34
|
+
# certain kinds of app configuration.
|
37
35
|
prepare_load_path
|
38
36
|
|
39
37
|
load_dotenv
|
@@ -43,29 +41,41 @@ module Hanami
|
|
43
41
|
|
44
42
|
# App class interface
|
45
43
|
module ClassMethods
|
46
|
-
|
44
|
+
# Returns the app's config.
|
45
|
+
#
|
46
|
+
# @return [Hanami::Config]
|
47
|
+
#
|
48
|
+
# @api public
|
49
|
+
# @since 2.0.0
|
50
|
+
attr_reader :config
|
47
51
|
|
52
|
+
# Returns the app's {SliceName}.
|
53
|
+
#
|
54
|
+
# @return [Hanami::SliceName]
|
55
|
+
#
|
56
|
+
# @see Slice::ClassMethods#slice_name
|
57
|
+
#
|
58
|
+
# @api public
|
59
|
+
# @since 2.0.0
|
48
60
|
def app_name
|
49
61
|
slice_name
|
50
62
|
end
|
51
63
|
|
52
|
-
# Prepares the $LOAD_PATH based on the app's configured root, prepending the `lib/`
|
53
|
-
#
|
54
|
-
# nothing.
|
64
|
+
# Prepares the $LOAD_PATH based on the app's configured root, prepending the `lib/` directory
|
65
|
+
# if it exists. If the lib directory is already added, this will do nothing.
|
55
66
|
#
|
56
|
-
# In ordinary circumstances, you should never have to call this method: this method
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
# certain app configuration steps.
|
67
|
+
# In ordinary circumstances, you should never have to call this method: this method is called
|
68
|
+
# immediately upon subclassing {Hanami::App}, as a convenicence to put lib/ (under the default
|
69
|
+
# root of `Dir.pwd`) on the load path automatically. This is helpful if you need to require
|
70
|
+
# files inside the subclass body for performing certain app configuration steps.
|
61
71
|
#
|
62
|
-
# If you change your app's `config.root` and you need to require files from its
|
63
|
-
#
|
64
|
-
#
|
72
|
+
# If you change your app's `config.root` and you need to require files from its `lib/`
|
73
|
+
# directory within your {App} subclass body, you should call {.prepare_load_path} explicitly
|
74
|
+
# after setting the new root.
|
65
75
|
#
|
66
|
-
# Otherwise, this method is called again as part of the app {.prepare} step, so if
|
67
|
-
#
|
68
|
-
#
|
76
|
+
# Otherwise, this method is called again as part of the app {.prepare} step, so if you've
|
77
|
+
# changed your app's root and do _not_ need to require files within your {App} subclass body,
|
78
|
+
# then you don't need to call this method.
|
69
79
|
#
|
70
80
|
# @example
|
71
81
|
# module MyApp
|
@@ -124,12 +134,12 @@ module Hanami
|
|
124
134
|
# Make app-wide notifications available as early as possible
|
125
135
|
container.use(:notifications)
|
126
136
|
|
127
|
-
# Ensure all basic slice preparation is complete before we make adjustments below
|
128
|
-
#
|
137
|
+
# Ensure all basic slice preparation is complete before we make adjustments below (which
|
138
|
+
# rely on the basic prepare steps having already run)
|
129
139
|
super
|
130
140
|
|
131
|
-
# Run specific prepare steps for the app slice. Note also that some
|
132
|
-
#
|
141
|
+
# Run specific prepare steps for the app slice. Note also that some standard steps have been
|
142
|
+
# skipped via the empty method overrides below.
|
133
143
|
prepare_app_component_dirs
|
134
144
|
prepare_app_providers
|
135
145
|
end
|
@@ -150,9 +160,9 @@ module Hanami
|
|
150
160
|
end
|
151
161
|
end
|
152
162
|
|
153
|
-
# When auto-registering components in app/, ignore files in `app/lib/` (these will
|
154
|
-
#
|
155
|
-
no_auto_register_paths = ([LIB_DIR] +
|
163
|
+
# When auto-registering components in app/, ignore files in `app/lib/` (these will be
|
164
|
+
# auto-registered as above), as well as the configured no_auto_register_paths
|
165
|
+
no_auto_register_paths = ([LIB_DIR] + config.no_auto_register_paths)
|
156
166
|
.map { |path|
|
157
167
|
path.end_with?(File::SEPARATOR) ? path : "#{path}#{File::SEPARATOR}"
|
158
168
|
}
|
@@ -178,14 +188,15 @@ module Hanami
|
|
178
188
|
register_provider(:logger, source: Hanami::Providers::Logger)
|
179
189
|
end
|
180
190
|
|
181
|
-
|
182
|
-
|
191
|
+
if Hanami.bundled?("rack")
|
192
|
+
require_relative "providers/rack"
|
193
|
+
register_provider(:rack, source: Hanami::Providers::Rack, namespace: true)
|
194
|
+
end
|
183
195
|
end
|
184
196
|
|
185
197
|
def prepare_autoloader
|
186
|
-
# Component dirs are automatically pushed to the autoloader by dry-system's
|
187
|
-
#
|
188
|
-
# as component dirs.
|
198
|
+
# Component dirs are automatically pushed to the autoloader by dry-system's zeitwerk plugin.
|
199
|
+
# This method adds other dirs that are not otherwise configured as component dirs.
|
189
200
|
|
190
201
|
# Autoload classes from `lib/[app_namespace]/`
|
191
202
|
if root.join(LIB_DIR, app_name.name).directory?
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/configurable"
|
4
|
+
|
5
|
+
module Hanami
|
6
|
+
# @api private
|
7
|
+
module Assets
|
8
|
+
# App config for assets.
|
9
|
+
#
|
10
|
+
# This is NOT RELEASED as of 2.0.0.
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
class AppConfig
|
14
|
+
include Dry::Configurable
|
15
|
+
|
16
|
+
attr_reader :base_config
|
17
|
+
protected :base_config
|
18
|
+
|
19
|
+
setting :server_url, default: "http://localhost:8080"
|
20
|
+
|
21
|
+
def initialize(*)
|
22
|
+
super
|
23
|
+
|
24
|
+
@base_config = Assets::Config.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize_copy(source)
|
28
|
+
super
|
29
|
+
@base_config = source.base_config.dup
|
30
|
+
end
|
31
|
+
|
32
|
+
def finalize!
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the list of available settings
|
36
|
+
#
|
37
|
+
# @return [Set]
|
38
|
+
#
|
39
|
+
# @api private
|
40
|
+
def settings
|
41
|
+
base_config.settings + self.class.settings
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def method_missing(name, *args, &block)
|
47
|
+
if config.respond_to?(name)
|
48
|
+
config.public_send(name, *args, &block)
|
49
|
+
elsif base_config.respond_to?(name)
|
50
|
+
base_config.public_send(name, *args, &block)
|
51
|
+
else
|
52
|
+
super
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def respond_to_missing?(name, _incude_all = false)
|
57
|
+
config.respond_to?(name) || base_config.respond_to?(name) || super
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -4,16 +4,19 @@ require "dry/configurable"
|
|
4
4
|
|
5
5
|
module Hanami
|
6
6
|
module Assets
|
7
|
-
#
|
8
|
-
#
|
9
|
-
|
7
|
+
# App config for assets.
|
8
|
+
#
|
9
|
+
# This is NOT RELEASED as of 2.0.0.
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
class Config
|
10
13
|
include Dry::Configurable
|
11
14
|
|
12
|
-
# Initialize the
|
15
|
+
# Initialize the Config
|
13
16
|
#
|
14
|
-
# @yield [config] the
|
17
|
+
# @yield [config] the config object
|
15
18
|
#
|
16
|
-
# @return [
|
19
|
+
# @return [Config]
|
17
20
|
#
|
18
21
|
# @since 2.0.0
|
19
22
|
# @api private
|
@@ -34,8 +37,6 @@ module Hanami
|
|
34
37
|
|
35
38
|
private
|
36
39
|
|
37
|
-
# @since 2.0.0
|
38
|
-
# @api private
|
39
40
|
def method_missing(name, *args, &block)
|
40
41
|
if config.respond_to?(name)
|
41
42
|
config.public_send(name, *args, &block)
|
@@ -44,8 +45,6 @@ module Hanami
|
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
47
|
-
# @since 2.0.0
|
48
|
-
# @api private
|
49
48
|
def respond_to_missing?(name, _incude_all = false)
|
50
49
|
config.respond_to?(name) || super
|
51
50
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Hanami
|
4
|
-
class
|
4
|
+
class Config
|
5
5
|
class Actions
|
6
|
-
#
|
6
|
+
# Config for Content Security Policy in Hanami apps
|
7
7
|
#
|
8
8
|
# @since 2.0.0
|
9
9
|
class ContentSecurityPolicy
|
@@ -99,7 +99,7 @@ module Hanami
|
|
99
99
|
|
100
100
|
# @since 2.0.0
|
101
101
|
# @api private
|
102
|
-
def
|
102
|
+
def to_s
|
103
103
|
@policy.map do |key, value|
|
104
104
|
"#{dasherize(key)} #{value}"
|
105
105
|
end.join(";\n")
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
class Config
|
5
|
+
class Actions
|
6
|
+
# Wrapper for app-level config of HTTP cookies for Hanami actions.
|
7
|
+
#
|
8
|
+
# This decorates the hash of cookie options that is otherwise directly configurable on
|
9
|
+
# actions, and adds the `enabled?` method to allow app base action to determine whether to
|
10
|
+
# include the `Action::Cookies` module.
|
11
|
+
#
|
12
|
+
# @api public
|
13
|
+
# @since 2.0.0
|
14
|
+
class Cookies
|
15
|
+
# Returns the cookie options.
|
16
|
+
#
|
17
|
+
# @return [Hash]
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
# @since 2.0.0
|
21
|
+
attr_reader :options
|
22
|
+
|
23
|
+
# Returns a new `Cookies`.
|
24
|
+
#
|
25
|
+
# You should not need to initialize this class directly. Instead use
|
26
|
+
# {Hanami::Config::Actions#cookies}.
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
# @since 2.0.0
|
30
|
+
def initialize(options)
|
31
|
+
@options = options
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns true if any cookie options have been provided.
|
35
|
+
#
|
36
|
+
# @return [Boolean]
|
37
|
+
#
|
38
|
+
# @api public
|
39
|
+
# @since 2.0.0
|
40
|
+
def enabled?
|
41
|
+
!options.nil?
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the cookie options.
|
45
|
+
#
|
46
|
+
# If no options have been provided, returns an empty hash.
|
47
|
+
#
|
48
|
+
# @return [Hash]
|
49
|
+
#
|
50
|
+
# @api public
|
51
|
+
def to_h
|
52
|
+
options.to_h
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "hanami/utils/string"
|
4
|
+
require "hanami/utils/class"
|
5
|
+
|
6
|
+
module Hanami
|
7
|
+
class Config
|
8
|
+
class Actions
|
9
|
+
# Config for HTTP session middleware in Hanami actions.
|
10
|
+
#
|
11
|
+
# @api public
|
12
|
+
# @since 2.0.0
|
13
|
+
class Sessions
|
14
|
+
# Returns the configured session storage
|
15
|
+
#
|
16
|
+
# @return [Symbol]
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
# @since 2.0.0
|
20
|
+
attr_reader :storage
|
21
|
+
|
22
|
+
# Returns the configured session storage options
|
23
|
+
#
|
24
|
+
# @return [Array]
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
# @since 2.0.0
|
28
|
+
attr_reader :options
|
29
|
+
|
30
|
+
# Returns a new `Sessions`.
|
31
|
+
#
|
32
|
+
# You should not need to initialize this class directly. Instead use
|
33
|
+
# {Hanami::Config::Actions#sessions=}.
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# config.actions.sessions = :cookie, {secret: "xyz"}
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
# @since 2.0.0
|
40
|
+
def initialize(storage = nil, *options)
|
41
|
+
@storage = storage
|
42
|
+
@options = options
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns true if sessions have been enabled.
|
46
|
+
#
|
47
|
+
# @return [Boolean]
|
48
|
+
#
|
49
|
+
# @api public
|
50
|
+
# @since 2.0.0
|
51
|
+
def enabled?
|
52
|
+
!storage.nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns an array of the session storage middleware name and its options, or an empty array
|
56
|
+
# if sessions have not been enabled.
|
57
|
+
#
|
58
|
+
# @return [Array<(Symbol, Array)>]
|
59
|
+
#
|
60
|
+
# @api public
|
61
|
+
# @since 2.0.0
|
62
|
+
def middleware
|
63
|
+
return [] unless enabled?
|
64
|
+
|
65
|
+
[storage_middleware, options].flatten(1)
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def storage_middleware
|
71
|
+
require_storage
|
72
|
+
|
73
|
+
name = Utils::String.classify(storage)
|
74
|
+
Utils::Class.load!(name, ::Rack::Session)
|
75
|
+
end
|
76
|
+
|
77
|
+
def require_storage
|
78
|
+
require "rack/session/#{storage}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/configurable"
|
4
|
+
|
5
|
+
module Hanami
|
6
|
+
class Config
|
7
|
+
# Hanami actions config
|
8
|
+
#
|
9
|
+
# This exposes all the settings from the standalone `Hanami::Action` class, pre-configured with
|
10
|
+
# sensible defaults for actions within a full Hanami app. It also provides additional settings
|
11
|
+
# for further integration of actions with other full stack app components.
|
12
|
+
#
|
13
|
+
# @since 2.0.0
|
14
|
+
# @api public
|
15
|
+
class Actions
|
16
|
+
include Dry::Configurable
|
17
|
+
|
18
|
+
# @!attribute [rw] cookies
|
19
|
+
# Sets or returns a hash of cookie options for actions.
|
20
|
+
#
|
21
|
+
# The hash is wrapped by {Hanami::Config::Actions::Cookies}, which also provides an
|
22
|
+
# `enabled?` method, returning true in the case of any options provided.
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# config.actions.cookies = {max_age: 300}
|
26
|
+
#
|
27
|
+
# @return [Hanami::Config::Actions::Cookies]
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
# @since 2.0.0
|
31
|
+
setting :cookies, default: {}, constructor: -> options { Cookies.new(options) }
|
32
|
+
|
33
|
+
# @!attribute [rw] sessions
|
34
|
+
# Sets or returns the session store (and its options) for actions.
|
35
|
+
#
|
36
|
+
# The given values are taken as an argument list to be passed to {Config::Sessions#initialize}.
|
37
|
+
#
|
38
|
+
# The configured session store is used when setting up the app or slice
|
39
|
+
# {Slice::ClassMethods#router router}.
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# config.actions.sessions = :cookie, {secret: "xyz"}
|
43
|
+
#
|
44
|
+
# @return [Config::Sessions]
|
45
|
+
#
|
46
|
+
# @see Config::Sessions
|
47
|
+
# @see Slice::ClassMethods#router
|
48
|
+
#
|
49
|
+
# @api public
|
50
|
+
# @since 2.0.0
|
51
|
+
setting :sessions, constructor: proc { |storage, *options| Sessions.new(storage, *options) }
|
52
|
+
|
53
|
+
# @!attribute [rw] csrf_protection
|
54
|
+
# Sets or returns whether CSRF protection should be enabled for action classes.
|
55
|
+
#
|
56
|
+
# Defaults to true if {#sessions} is enabled. You can override this by explicitly setting a
|
57
|
+
# true or false value.
|
58
|
+
#
|
59
|
+
# When true, this will include `Hanami::Action::CSRFProtection` in all action classes.
|
60
|
+
#
|
61
|
+
# @return [Boolean]
|
62
|
+
#
|
63
|
+
# @api public
|
64
|
+
# @since 2.0.0
|
65
|
+
setting :csrf_protection
|
66
|
+
|
67
|
+
# Returns the Content Security Policy config for actions.
|
68
|
+
#
|
69
|
+
# The resulting policy is set as a default `"Content-Security-Policy"` response header.
|
70
|
+
#
|
71
|
+
# @return [Hanami::Config::Actions::ContentSecurityPolicy]
|
72
|
+
#
|
73
|
+
# @api public
|
74
|
+
# @since 2.0.0
|
75
|
+
attr_accessor :content_security_policy
|
76
|
+
|
77
|
+
# The following settings are for view and assets integration with actions, and are NOT
|
78
|
+
# publicly released as of 2.0.0. We'll make full documentation available when these become
|
79
|
+
# public in a subsequent release.
|
80
|
+
|
81
|
+
# @!attribute [rw] name_inference_base
|
82
|
+
# @api private
|
83
|
+
setting :name_inference_base, default: "actions"
|
84
|
+
|
85
|
+
# @!attribute [rw] view_context_identifier
|
86
|
+
# @api private
|
87
|
+
setting :view_context_identifier, default: "views.context"
|
88
|
+
|
89
|
+
# @!attribute [rw] view_name_inferrer
|
90
|
+
# @api private
|
91
|
+
setting :view_name_inferrer, default: Slice::ViewNameInferrer
|
92
|
+
|
93
|
+
# @!attribute [rw] view_name_inference_base
|
94
|
+
# @api private
|
95
|
+
setting :view_name_inference_base, default: "views"
|
96
|
+
|
97
|
+
# @api private
|
98
|
+
attr_reader :base_config
|
99
|
+
protected :base_config
|
100
|
+
|
101
|
+
# @api private
|
102
|
+
def initialize(*, **options)
|
103
|
+
super()
|
104
|
+
|
105
|
+
@base_config = Hanami::Action.config.dup
|
106
|
+
@content_security_policy = ContentSecurityPolicy.new do |csp|
|
107
|
+
if assets_server_url = options[:assets_server_url]
|
108
|
+
csp[:script_src] += " #{assets_server_url}"
|
109
|
+
csp[:style_src] += " #{assets_server_url}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
configure_defaults
|
114
|
+
end
|
115
|
+
|
116
|
+
# @api private
|
117
|
+
def initialize_copy(source)
|
118
|
+
super
|
119
|
+
@base_config = source.base_config.dup
|
120
|
+
@content_security_policy = source.content_security_policy.dup
|
121
|
+
end
|
122
|
+
private :initialize_copy
|
123
|
+
|
124
|
+
# @api private
|
125
|
+
def finalize!
|
126
|
+
# A nil value for `csrf_protection` means it has not been explicitly configured
|
127
|
+
# (neither true nor false), so we can default it to whether sessions are enabled
|
128
|
+
self.csrf_protection = sessions.enabled? if csrf_protection.nil?
|
129
|
+
|
130
|
+
if content_security_policy
|
131
|
+
default_headers["Content-Security-Policy"] = content_security_policy.to_s
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
# Apply defaults for base config
|
138
|
+
def configure_defaults
|
139
|
+
self.default_request_format = :html
|
140
|
+
self.default_response_format = :html
|
141
|
+
|
142
|
+
self.default_headers = {
|
143
|
+
"X-Frame-Options" => "DENY",
|
144
|
+
"X-Content-Type-Options" => "nosniff",
|
145
|
+
"X-XSS-Protection" => "1; mode=block"
|
146
|
+
}
|
147
|
+
end
|
148
|
+
|
149
|
+
def method_missing(name, *args, &block)
|
150
|
+
if config.respond_to?(name)
|
151
|
+
config.public_send(name, *args, &block)
|
152
|
+
elsif base_config.respond_to?(name)
|
153
|
+
base_config.public_send(name, *args, &block)
|
154
|
+
else
|
155
|
+
super
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def respond_to_missing?(name, _incude_all = false)
|
160
|
+
config.respond_to?(name) || base_config.respond_to?(name) || super
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|