hanami 2.3.2 → 3.0.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 +103 -19
- data/LICENSE +20 -0
- data/README.md +18 -35
- data/hanami.gemspec +36 -37
- data/lib/hanami/app.rb +12 -2
- data/lib/hanami/config/db.rb +2 -0
- data/lib/hanami/config/i18n.rb +138 -0
- data/lib/hanami/config/logger.rb +15 -7
- data/lib/hanami/config/null_config.rb +1 -1
- data/lib/hanami/config/views.rb +17 -0
- data/lib/hanami/config.rb +66 -22
- data/lib/hanami/errors.rb +6 -0
- data/lib/hanami/extensions/action/i18n_helper.rb +64 -0
- data/lib/hanami/extensions/action/name_inferrer.rb +34 -0
- data/lib/hanami/extensions/action/slice_configured_action.rb +31 -2
- data/lib/hanami/extensions/action.rb +20 -7
- data/lib/hanami/extensions/mailer/slice_configured_mailer.rb +134 -0
- data/lib/hanami/extensions/mailer.rb +28 -0
- data/lib/hanami/extensions/operation/slice_configured_db_operation.rb +2 -0
- data/lib/hanami/extensions/view/context.rb +26 -4
- data/lib/hanami/extensions/view/part.rb +2 -0
- data/lib/hanami/extensions/view/slice_configured_context.rb +7 -0
- data/lib/hanami/extensions/view/slice_configured_part.rb +2 -0
- data/lib/hanami/extensions/view/slice_configured_view.rb +8 -8
- data/lib/hanami/extensions/view/standard_helpers.rb +4 -0
- data/lib/hanami/extensions.rb +6 -1
- data/lib/hanami/helpers/assets_helper.rb +0 -4
- data/lib/hanami/helpers/form_helper.rb +1 -1
- data/lib/hanami/helpers/i18n_helper.rb +241 -0
- data/lib/hanami/logger/rack_formatter.rb +73 -0
- data/lib/hanami/logger/sql_formatter.rb +80 -0
- data/lib/hanami/logger/sql_logger.rb +48 -0
- data/lib/hanami/middleware/render_errors.rb +2 -2
- data/lib/hanami/providers/db.rb +7 -2
- data/lib/hanami/providers/db_logging.rb +4 -7
- data/lib/hanami/providers/i18n/backend.rb +373 -0
- data/lib/hanami/providers/i18n/locale/en.yml +57 -0
- data/lib/hanami/providers/i18n.rb +114 -0
- data/lib/hanami/providers/logger.rb +23 -1
- data/lib/hanami/providers/mailers.rb +101 -0
- data/lib/hanami/routes.rb +1 -0
- data/lib/hanami/settings/composite_store.rb +53 -0
- data/lib/hanami/settings.rb +4 -4
- data/lib/hanami/slice/router.rb +15 -10
- data/lib/hanami/slice.rb +102 -11
- data/lib/hanami/slice_configurable.rb +1 -3
- data/lib/hanami/slice_registrar.rb +2 -2
- data/lib/hanami/universal_logger.rb +250 -0
- data/lib/hanami/version.rb +1 -1
- data/lib/hanami/web/rack_logger.rb +2 -80
- data/lib/hanami/web/welcome.html.erb +443 -58
- data/lib/hanami.rb +4 -2
- metadata +34 -280
- data/CODE_OF_CONDUCT.md +0 -74
- data/FEATURES.md +0 -269
- data/LICENSE.md +0 -22
- data/spec/integration/action/cookies_spec.rb +0 -58
- data/spec/integration/action/csrf_protection_spec.rb +0 -54
- data/spec/integration/action/format_config_spec.rb +0 -129
- data/spec/integration/action/routes_spec.rb +0 -71
- data/spec/integration/action/sessions_spec.rb +0 -50
- data/spec/integration/action/slice_configuration_spec.rb +0 -284
- data/spec/integration/action/view_rendering/automatic_rendering_spec.rb +0 -247
- data/spec/integration/action/view_rendering/paired_view_inference_spec.rb +0 -115
- data/spec/integration/action/view_rendering/view_context_spec.rb +0 -221
- data/spec/integration/action/view_rendering_spec.rb +0 -89
- data/spec/integration/assets/assets_spec.rb +0 -155
- data/spec/integration/assets/cross_slice_assets_helpers_spec.rb +0 -129
- data/spec/integration/assets/serve_static_assets_spec.rb +0 -152
- data/spec/integration/code_loading/loading_from_app_spec.rb +0 -152
- data/spec/integration/code_loading/loading_from_lib_spec.rb +0 -242
- data/spec/integration/code_loading/loading_from_slice_spec.rb +0 -165
- data/spec/integration/container/application_routes_helper_spec.rb +0 -48
- data/spec/integration/container/auto_injection_spec.rb +0 -53
- data/spec/integration/container/auto_registration_spec.rb +0 -86
- data/spec/integration/container/autoloader_spec.rb +0 -82
- data/spec/integration/container/imports_spec.rb +0 -253
- data/spec/integration/container/prepare_container_spec.rb +0 -125
- data/spec/integration/container/provider_environment_spec.rb +0 -52
- data/spec/integration/container/provider_lifecycle_spec.rb +0 -61
- data/spec/integration/container/shutdown_spec.rb +0 -91
- data/spec/integration/container/standard_providers/rack_provider_spec.rb +0 -44
- data/spec/integration/container/standard_providers_spec.rb +0 -124
- data/spec/integration/db/auto_registration_spec.rb +0 -39
- data/spec/integration/db/commands_spec.rb +0 -80
- data/spec/integration/db/db_inflector_spec.rb +0 -57
- data/spec/integration/db/db_slices_spec.rb +0 -398
- data/spec/integration/db/db_spec.rb +0 -245
- data/spec/integration/db/gateways_spec.rb +0 -361
- data/spec/integration/db/logging_spec.rb +0 -301
- data/spec/integration/db/mappers_spec.rb +0 -84
- data/spec/integration/db/provider_config_spec.rb +0 -88
- data/spec/integration/db/provider_spec.rb +0 -35
- data/spec/integration/db/relations_spec.rb +0 -60
- data/spec/integration/db/repo_spec.rb +0 -300
- data/spec/integration/db/slices_importing_from_parent.rb +0 -130
- data/spec/integration/dotenv_loading_spec.rb +0 -138
- data/spec/integration/logging/exception_logging_spec.rb +0 -120
- data/spec/integration/logging/notifications_spec.rb +0 -68
- data/spec/integration/logging/request_logging_spec.rb +0 -202
- data/spec/integration/operations/extension_spec.rb +0 -122
- data/spec/integration/rack_app/body_parser_spec.rb +0 -108
- data/spec/integration/rack_app/method_override_spec.rb +0 -97
- data/spec/integration/rack_app/middleware_spec.rb +0 -720
- data/spec/integration/rack_app/non_booted_rack_app_spec.rb +0 -104
- data/spec/integration/rack_app/rack_app_spec.rb +0 -442
- data/spec/integration/rake_tasks_spec.rb +0 -107
- data/spec/integration/router/resource_routes_spec.rb +0 -281
- data/spec/integration/settings/access_in_slice_class_body_spec.rb +0 -83
- data/spec/integration/settings/access_to_constants_spec.rb +0 -46
- data/spec/integration/settings/loading_from_env_spec.rb +0 -188
- data/spec/integration/settings/settings_component_loading_spec.rb +0 -113
- data/spec/integration/settings/slice_registration_spec.rb +0 -145
- data/spec/integration/settings/using_types_spec.rb +0 -80
- data/spec/integration/setup_spec.rb +0 -165
- data/spec/integration/slices/external_slice_spec.rb +0 -91
- data/spec/integration/slices/slice_configuration_spec.rb +0 -42
- data/spec/integration/slices/slice_loading_spec.rb +0 -171
- data/spec/integration/slices/slice_registrations_spec.rb +0 -80
- data/spec/integration/slices/slice_routing_spec.rb +0 -219
- data/spec/integration/slices_spec.rb +0 -471
- data/spec/integration/view/config/default_context_spec.rb +0 -149
- data/spec/integration/view/config/inflector_spec.rb +0 -57
- data/spec/integration/view/config/part_class_spec.rb +0 -147
- data/spec/integration/view/config/part_namespace_spec.rb +0 -103
- data/spec/integration/view/config/paths_spec.rb +0 -119
- data/spec/integration/view/config/scope_class_spec.rb +0 -147
- data/spec/integration/view/config/scope_namespace_spec.rb +0 -103
- data/spec/integration/view/config/template_spec.rb +0 -38
- data/spec/integration/view/context/assets_spec.rb +0 -79
- data/spec/integration/view/context/inflector_spec.rb +0 -40
- data/spec/integration/view/context/request_spec.rb +0 -57
- data/spec/integration/view/context/routes_spec.rb +0 -84
- data/spec/integration/view/helpers/form_helper_spec.rb +0 -174
- data/spec/integration/view/helpers/part_helpers_spec.rb +0 -124
- data/spec/integration/view/helpers/scope_helpers_spec.rb +0 -84
- data/spec/integration/view/helpers/user_defined_helpers/part_helpers_spec.rb +0 -162
- data/spec/integration/view/helpers/user_defined_helpers/scope_helpers_spec.rb +0 -119
- data/spec/integration/view/parts/default_rendering_spec.rb +0 -138
- data/spec/integration/view/slice_configuration_spec.rb +0 -289
- data/spec/integration/view/views_spec.rb +0 -103
- data/spec/integration/web/content_security_policy_nonce_spec.rb +0 -251
- data/spec/integration/web/render_detailed_errors_spec.rb +0 -107
- data/spec/integration/web/render_errors_spec.rb +0 -242
- data/spec/integration/web/welcome_view_spec.rb +0 -84
- data/spec/spec_helper.rb +0 -28
- data/spec/support/app_integration.rb +0 -157
- data/spec/support/coverage.rb +0 -1
- data/spec/support/matchers.rb +0 -32
- data/spec/support/rspec.rb +0 -27
- data/spec/unit/hanami/config/actions/content_security_policy_spec.rb +0 -96
- data/spec/unit/hanami/config/actions/cookies_spec.rb +0 -46
- data/spec/unit/hanami/config/actions/csrf_protection_spec.rb +0 -58
- data/spec/unit/hanami/config/actions/default_values_spec.rb +0 -43
- data/spec/unit/hanami/config/actions/sessions_spec.rb +0 -48
- data/spec/unit/hanami/config/actions_spec.rb +0 -52
- data/spec/unit/hanami/config/base_url_spec.rb +0 -25
- data/spec/unit/hanami/config/console_spec.rb +0 -22
- data/spec/unit/hanami/config/db_spec.rb +0 -38
- data/spec/unit/hanami/config/inflector_spec.rb +0 -35
- data/spec/unit/hanami/config/logger_spec.rb +0 -195
- data/spec/unit/hanami/config/render_detailed_errors_spec.rb +0 -25
- data/spec/unit/hanami/config/render_errors_spec.rb +0 -25
- data/spec/unit/hanami/config/router_spec.rb +0 -44
- data/spec/unit/hanami/config/slices_spec.rb +0 -34
- data/spec/unit/hanami/config/views_spec.rb +0 -80
- data/spec/unit/hanami/env_spec.rb +0 -37
- data/spec/unit/hanami/extensions/view/context_spec.rb +0 -59
- data/spec/unit/hanami/helpers/assets_helper/asset_url_spec.rb +0 -120
- data/spec/unit/hanami/helpers/assets_helper/audio_tag_spec.rb +0 -132
- data/spec/unit/hanami/helpers/assets_helper/favicon_tag_spec.rb +0 -87
- data/spec/unit/hanami/helpers/assets_helper/image_tag_spec.rb +0 -92
- data/spec/unit/hanami/helpers/assets_helper/javascript_tag_spec.rb +0 -143
- data/spec/unit/hanami/helpers/assets_helper/stylesheet_tag_spec.rb +0 -126
- data/spec/unit/hanami/helpers/assets_helper/video_tag_spec.rb +0 -136
- data/spec/unit/hanami/helpers/form_helper_spec.rb +0 -2857
- data/spec/unit/hanami/port_spec.rb +0 -117
- data/spec/unit/hanami/providers/db/config/default_config_spec.rb +0 -100
- data/spec/unit/hanami/providers/db/config/gateway_spec.rb +0 -73
- data/spec/unit/hanami/providers/db/config_spec.rb +0 -143
- data/spec/unit/hanami/router/errors/not_allowed_error_spec.rb +0 -27
- data/spec/unit/hanami/router/errors/not_found_error_spec.rb +0 -22
- data/spec/unit/hanami/settings/env_store_spec.rb +0 -52
- data/spec/unit/hanami/settings_spec.rb +0 -111
- data/spec/unit/hanami/slice_configurable_spec.rb +0 -141
- data/spec/unit/hanami/slice_name_spec.rb +0 -47
- data/spec/unit/hanami/slice_spec.rb +0 -99
- data/spec/unit/hanami/web/rack_logger_spec.rb +0 -99
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hanami
|
|
4
|
+
module Providers
|
|
5
|
+
# Registers the `"mailers.delivery_method"` component. This is an SMTP delivery method built
|
|
6
|
+
# from SMTP environment variables when present, otherwise the test delivery method.
|
|
7
|
+
#
|
|
8
|
+
# SMTP env vars may take a per-slice prefix derived from the slice name (e.g. an "admin" slice
|
|
9
|
+
# reads `ADMIN__SMTP_ADDRESS`, falling back to `SMTP_ADDRESS`). Register your own `:mailers`
|
|
10
|
+
# provider if you need to use another delivery method or different setup logic.
|
|
11
|
+
#
|
|
12
|
+
# In the test env, environment variables are ignored, and the test delivery method is always
|
|
13
|
+
# used, so the test suite can never send real email.
|
|
14
|
+
#
|
|
15
|
+
# In the production env, warns noisily when there is no SMTP configuration, before falling back
|
|
16
|
+
# to the test delivery method. This ensures an app whose mail setup is a work in progress can
|
|
17
|
+
# still boot.
|
|
18
|
+
#
|
|
19
|
+
# @api private
|
|
20
|
+
class Mailers < Hanami::Provider::Source
|
|
21
|
+
# Maps SMTP environment variable names to their compatible delivery option keys.
|
|
22
|
+
#
|
|
23
|
+
# Example values:
|
|
24
|
+
#
|
|
25
|
+
# SMTP_ADDRESS=smtp.example.com
|
|
26
|
+
# SMTP_PORT=587
|
|
27
|
+
# SMTP_USERNAME=postmaster@example.com
|
|
28
|
+
# SMTP_PASSWORD=s3cr3t
|
|
29
|
+
# SMTP_AUTHENTICATION=plain
|
|
30
|
+
SMTP_ENV_VARS = {
|
|
31
|
+
"SMTP_ADDRESS" => :address,
|
|
32
|
+
"SMTP_PORT" => :port,
|
|
33
|
+
"SMTP_USERNAME" => :user_name,
|
|
34
|
+
"SMTP_PASSWORD" => :password,
|
|
35
|
+
"SMTP_AUTHENTICATION" => :authentication
|
|
36
|
+
}.freeze
|
|
37
|
+
|
|
38
|
+
# Coercions applied to SMTP env var values, keyed by option. Options not listed here are
|
|
39
|
+
# passed through unchanged (as strings).
|
|
40
|
+
SMTP_COERCIONS = Hash.new(:itself.to_proc).update(
|
|
41
|
+
port: ->(value) { Integer(value) },
|
|
42
|
+
authentication: ->(value) { value.to_sym }
|
|
43
|
+
).freeze
|
|
44
|
+
|
|
45
|
+
def start
|
|
46
|
+
require "hanami/mailer"
|
|
47
|
+
|
|
48
|
+
register "delivery_method", build_delivery_method
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def build_delivery_method
|
|
54
|
+
return Hanami::Mailer::Delivery::Test.new if Hanami.env?(:test)
|
|
55
|
+
|
|
56
|
+
smtp_options = smtp_options_from_env
|
|
57
|
+
|
|
58
|
+
return Hanami::Mailer::Delivery::SMTP.new(**smtp_options) if smtp_options.key?(:address)
|
|
59
|
+
|
|
60
|
+
warn_missing_smtp if Hanami.env?(:production)
|
|
61
|
+
|
|
62
|
+
Hanami::Mailer::Delivery::Test.new
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def smtp_options_from_env
|
|
66
|
+
SMTP_ENV_VARS.each_with_object({}) do |(var, option), options|
|
|
67
|
+
value = env_value(var)
|
|
68
|
+
next if value.nil?
|
|
69
|
+
|
|
70
|
+
coercion = SMTP_COERCIONS[option]
|
|
71
|
+
options[option] = coercion ? coercion.call(value) : value
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Reads an SMTP env var, preferring a per-slice prefixed name (e.g. `ADMIN__SMTP_ADDRESS`)
|
|
76
|
+
# and falling back to the unprefixed name shared across slices.
|
|
77
|
+
def env_value(var)
|
|
78
|
+
return ENV[var] if slice.app?
|
|
79
|
+
|
|
80
|
+
slice_prefixed_var = "#{slice.slice_name.name.gsub("/", "__").upcase}__#{var}"
|
|
81
|
+
ENV[slice_prefixed_var] || ENV[var]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def warn_missing_smtp
|
|
85
|
+
message = \
|
|
86
|
+
"No SMTP configuration found for #{slice.slice_name.name} in production; " \
|
|
87
|
+
"falling back to the test delivery method — mail will NOT be sent. " \
|
|
88
|
+
"Set SMTP_ADDRESS (and related SMTP_* variables), or register a custom :mailers provider."
|
|
89
|
+
|
|
90
|
+
slice.app["logger"].warn(message)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
Dry::System.register_provider_source(
|
|
95
|
+
:mailers,
|
|
96
|
+
source: Mailers,
|
|
97
|
+
group: :hanami,
|
|
98
|
+
provider_options: {namespace: true}
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
end
|
data/lib/hanami/routes.rb
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/core/constants"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
class Settings
|
|
7
|
+
# A settings store that chains multiple stores with fallback resolution.
|
|
8
|
+
#
|
|
9
|
+
# Each store is tried in order. The first store to return a value wins.
|
|
10
|
+
# Stores must implement `#fetch` with the same signature as `Hash#fetch`.
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# # config/app.rb
|
|
14
|
+
# config.settings_store = Hanami::Settings::CompositeStore.new(
|
|
15
|
+
# Hanami::Settings::EnvStore.new,
|
|
16
|
+
# MyCustomStore.new
|
|
17
|
+
# )
|
|
18
|
+
#
|
|
19
|
+
# @api public
|
|
20
|
+
# @since 3.0.0
|
|
21
|
+
class CompositeStore
|
|
22
|
+
# @api private
|
|
23
|
+
Undefined = Dry::Core::Constants::Undefined
|
|
24
|
+
|
|
25
|
+
# @param stores [Array<#fetch>] ordered list of stores to query
|
|
26
|
+
def initialize(*stores)
|
|
27
|
+
@stores = stores
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Fetches a value by trying each store in order.
|
|
31
|
+
#
|
|
32
|
+
# @param name [String, Symbol] the setting name
|
|
33
|
+
# @param args [Array] optional default value
|
|
34
|
+
# @yield [name] optional block for default value
|
|
35
|
+
# @return [Object] the setting value
|
|
36
|
+
# @raise [KeyError] if no store has the key and no default is given
|
|
37
|
+
#
|
|
38
|
+
# @api public
|
|
39
|
+
# @since 3.0.0
|
|
40
|
+
def fetch(name, *args, &block)
|
|
41
|
+
@stores.each do |store|
|
|
42
|
+
value = store.fetch(name, Undefined)
|
|
43
|
+
return value unless value.equal?(Undefined)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
return args.first unless args.empty?
|
|
47
|
+
return yield(name) if block
|
|
48
|
+
|
|
49
|
+
raise KeyError, "key not found: #{name.inspect}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
data/lib/hanami/settings.rb
CHANGED
|
@@ -140,8 +140,8 @@ module Hanami
|
|
|
140
140
|
|
|
141
141
|
begin
|
|
142
142
|
require slice_settings_require_path
|
|
143
|
-
rescue LoadError =>
|
|
144
|
-
raise
|
|
143
|
+
rescue LoadError => exception
|
|
144
|
+
raise exception unless exception.path == slice_settings_require_path
|
|
145
145
|
end
|
|
146
146
|
end
|
|
147
147
|
end
|
|
@@ -167,8 +167,8 @@ module Hanami
|
|
|
167
167
|
else
|
|
168
168
|
public_send("#{name}=", value)
|
|
169
169
|
end
|
|
170
|
-
rescue =>
|
|
171
|
-
errs[name] =
|
|
170
|
+
rescue => exception # rubocop:disable Style/RescueStandardError
|
|
171
|
+
errs[name] = exception
|
|
172
172
|
end
|
|
173
173
|
|
|
174
174
|
raise InvalidSettingsError, errors if errors.any?
|
data/lib/hanami/slice/router.rb
CHANGED
|
@@ -23,7 +23,14 @@ module Hanami
|
|
|
23
23
|
attr_reader :path_prefix
|
|
24
24
|
|
|
25
25
|
# @api private
|
|
26
|
-
def initialize(
|
|
26
|
+
def initialize(
|
|
27
|
+
routes:,
|
|
28
|
+
inflector:,
|
|
29
|
+
middleware_stack: Routing::Middleware::Stack.new,
|
|
30
|
+
prefix: ::Hanami::Router::DEFAULT_PREFIX,
|
|
31
|
+
**kwargs,
|
|
32
|
+
&blk
|
|
33
|
+
)
|
|
27
34
|
@path_prefix = Hanami::Router::Prefix.new(prefix)
|
|
28
35
|
@inflector = inflector
|
|
29
36
|
@middleware_stack = middleware_stack
|
|
@@ -233,22 +240,20 @@ module Hanami
|
|
|
233
240
|
|
|
234
241
|
def route_suffix(suffix)
|
|
235
242
|
return suffix.sub(LEADING_ID_REGEX, "") if suffix && singular?
|
|
243
|
+
|
|
236
244
|
suffix
|
|
237
245
|
end
|
|
238
246
|
LEADING_ID_REGEX = %r{\A/:id}
|
|
239
247
|
|
|
240
248
|
def key_path_base
|
|
241
|
-
@key_path_base ||=
|
|
242
|
-
if @
|
|
243
|
-
@
|
|
249
|
+
@key_path_base ||= @options[:to] || begin
|
|
250
|
+
if @resource_scope.any?
|
|
251
|
+
prefix = @resource_scope.join(CONTAINER_KEY_DELIMITER)
|
|
252
|
+
"#{prefix}#{CONTAINER_KEY_DELIMITER}#{@name}"
|
|
244
253
|
else
|
|
245
|
-
@name.to_s
|
|
246
|
-
next name unless @resource_scope.any?
|
|
247
|
-
|
|
248
|
-
prefix = @resource_scope.join(CONTAINER_KEY_DELIMITER)
|
|
249
|
-
"#{prefix}#{CONTAINER_KEY_DELIMITER}#{name}"
|
|
250
|
-
}
|
|
254
|
+
@name.to_s
|
|
251
255
|
end
|
|
256
|
+
end
|
|
252
257
|
end
|
|
253
258
|
|
|
254
259
|
def route_name(action, prefix)
|
data/lib/hanami/slice.rb
CHANGED
|
@@ -46,7 +46,6 @@ module Hanami
|
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
# rubocop:disable Metrics/ModuleLength
|
|
50
49
|
module ClassMethods
|
|
51
50
|
# Returns the slice's parent.
|
|
52
51
|
#
|
|
@@ -395,6 +394,26 @@ module Hanami
|
|
|
395
394
|
@slices ||= SliceRegistrar.new(self)
|
|
396
395
|
end
|
|
397
396
|
|
|
397
|
+
# Returns an array of this slice and all of its nested slices.
|
|
398
|
+
#
|
|
399
|
+
# The nested slices are returned first (with their more specific namespaces ahead of their
|
|
400
|
+
# parents'), and this slice is returned last. This ordering means a class can be matched to
|
|
401
|
+
# its most specific slice by detecting the first slice whose namespace it belongs to. It is
|
|
402
|
+
# useful when you need to operate across every container, such as in test support code.
|
|
403
|
+
#
|
|
404
|
+
# @example
|
|
405
|
+
# Hanami.app.with_slices # => [Main::Nested::Slice, Main::Slice, Admin::Slice, MyApp::App]
|
|
406
|
+
#
|
|
407
|
+
# @return [Array<Hanami::Slice>] this slice and all of its (nested) slices, this slice last
|
|
408
|
+
#
|
|
409
|
+
# @see #slices
|
|
410
|
+
#
|
|
411
|
+
# @api public
|
|
412
|
+
# @since 3.0.0
|
|
413
|
+
def with_slices
|
|
414
|
+
slices.with_nested + [self]
|
|
415
|
+
end
|
|
416
|
+
|
|
398
417
|
# @overload register_slice(name, &block)
|
|
399
418
|
# Registers a nested slice with the given name.
|
|
400
419
|
#
|
|
@@ -797,12 +816,24 @@ module Hanami
|
|
|
797
816
|
#
|
|
798
817
|
# @return [Array] the three-element Rack response array
|
|
799
818
|
#
|
|
819
|
+
# @raise [Hanami::NoRoutesDefinedError] if the hanami-router gem is missing or
|
|
820
|
+
# no routes are defined.
|
|
821
|
+
#
|
|
800
822
|
# @see #rack_app
|
|
801
823
|
#
|
|
802
824
|
# @api public
|
|
803
825
|
# @since 2.0.0
|
|
804
826
|
def call(...)
|
|
805
|
-
rack_app
|
|
827
|
+
if rack_app
|
|
828
|
+
rack_app.call(...)
|
|
829
|
+
else
|
|
830
|
+
error_message = if Hanami.bundled?("hanami-router")
|
|
831
|
+
"Could not handle this rack request because no routes are defined"
|
|
832
|
+
else
|
|
833
|
+
"Could not handle this rack request because the hanami router gem is missing, please add it"
|
|
834
|
+
end
|
|
835
|
+
raise NoRoutesDefinedError, error_message
|
|
836
|
+
end
|
|
806
837
|
end
|
|
807
838
|
|
|
808
839
|
private
|
|
@@ -893,10 +924,27 @@ module Hanami
|
|
|
893
924
|
container.config.provider_dirs = [File.join("config", "providers")]
|
|
894
925
|
container.config.registrations_dir = File.join("config", "registrations")
|
|
895
926
|
|
|
927
|
+
container.config.component_dirs.memoize = memoize_policy
|
|
928
|
+
|
|
896
929
|
container.config.env = config.env
|
|
897
930
|
container.config.inflector = config.inflector
|
|
898
931
|
end
|
|
899
932
|
|
|
933
|
+
def memoize_policy
|
|
934
|
+
# Do not memoize components in the test env, so they can be stubbed if required.
|
|
935
|
+
return false if config.env == :test
|
|
936
|
+
|
|
937
|
+
no_memoize = config.no_memoize
|
|
938
|
+
|
|
939
|
+
if no_memoize.respond_to?(:call)
|
|
940
|
+
->(component) { !no_memoize.call(component) }
|
|
941
|
+
elsif no_memoize.is_a?(Array) && no_memoize.any?
|
|
942
|
+
->(component) { !component.key.start_with?(*no_memoize) }
|
|
943
|
+
else
|
|
944
|
+
true
|
|
945
|
+
end
|
|
946
|
+
end
|
|
947
|
+
|
|
900
948
|
def prepare_container_component_dirs
|
|
901
949
|
return unless root.directory?
|
|
902
950
|
|
|
@@ -935,6 +983,7 @@ module Hanami
|
|
|
935
983
|
)
|
|
936
984
|
end
|
|
937
985
|
|
|
986
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
938
987
|
def prepare_container_providers
|
|
939
988
|
# Check here for the `routes` definition only, not `router` itself, because the
|
|
940
989
|
# `router` requires the slice to be prepared before it can be loaded, and at this
|
|
@@ -956,19 +1005,39 @@ module Hanami
|
|
|
956
1005
|
|
|
957
1006
|
if register_db_provider?
|
|
958
1007
|
# Only register providers if the user hasn't provided their own
|
|
959
|
-
|
|
1008
|
+
unless container.providers[:db]
|
|
960
1009
|
register_provider(:db, namespace: true, source: Providers::DB)
|
|
961
1010
|
end
|
|
962
1011
|
|
|
963
|
-
|
|
1012
|
+
unless container.providers[:relations]
|
|
964
1013
|
register_provider(:relations, namespace: true, source: Providers::Relations)
|
|
965
1014
|
end
|
|
966
1015
|
end
|
|
967
1016
|
end
|
|
1017
|
+
|
|
1018
|
+
if Hanami.bundled?("i18n")
|
|
1019
|
+
require_relative "providers/i18n"
|
|
1020
|
+
|
|
1021
|
+
if register_i18n_provider? && !container.providers[:i18n]
|
|
1022
|
+
register_provider(:i18n, source: Providers::I18n)
|
|
1023
|
+
end
|
|
1024
|
+
end
|
|
1025
|
+
|
|
1026
|
+
if Hanami.bundled?("hanami-mailer")
|
|
1027
|
+
# Explicit require here to ensure the provider source registers itself, to allow the
|
|
1028
|
+
# user to configure it within their own concrete provider file.
|
|
1029
|
+
require_relative "providers/mailers"
|
|
1030
|
+
|
|
1031
|
+
# Only register the provider if the user hasn't provided their own.
|
|
1032
|
+
if register_mailers_provider? && !container.providers[:mailers]
|
|
1033
|
+
register_provider(:mailers, namespace: true, source: Providers::Mailers)
|
|
1034
|
+
end
|
|
1035
|
+
end
|
|
968
1036
|
end
|
|
1037
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
969
1038
|
|
|
970
1039
|
def prepare_autoloader
|
|
971
|
-
autoloader.tag = "hanami.slices.#{slice_name
|
|
1040
|
+
autoloader.tag = "hanami.slices.#{slice_name}"
|
|
972
1041
|
|
|
973
1042
|
# Component dirs are automatically pushed to the autoloader by dry-system's zeitwerk plugin.
|
|
974
1043
|
# This method adds other dirs that are not otherwise configured as component dirs.
|
|
@@ -1009,19 +1078,20 @@ module Hanami
|
|
|
1009
1078
|
begin
|
|
1010
1079
|
require_relative "./routes"
|
|
1011
1080
|
require routes_require_path
|
|
1012
|
-
rescue LoadError =>
|
|
1013
|
-
raise
|
|
1081
|
+
rescue LoadError => exception
|
|
1082
|
+
raise exception unless exception.path == routes_require_path
|
|
1014
1083
|
end
|
|
1015
1084
|
end
|
|
1016
1085
|
|
|
1017
1086
|
begin
|
|
1018
1087
|
routes_class = namespace.const_get(ROUTES_CLASS_NAME)
|
|
1019
1088
|
routes_class.routes
|
|
1020
|
-
rescue NameError =>
|
|
1021
|
-
raise
|
|
1089
|
+
rescue NameError => exception
|
|
1090
|
+
raise exception unless exception.name == ROUTES_CLASS_NAME.to_sym
|
|
1022
1091
|
end
|
|
1023
1092
|
end
|
|
1024
1093
|
|
|
1094
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
1025
1095
|
def load_router(inspector:)
|
|
1026
1096
|
return unless routes
|
|
1027
1097
|
|
|
@@ -1065,7 +1135,7 @@ module Hanami
|
|
|
1065
1135
|
use(Hanami::Webconsole::Middleware, config)
|
|
1066
1136
|
end
|
|
1067
1137
|
|
|
1068
|
-
if Hanami.bundled?("hanami-
|
|
1138
|
+
if Hanami.bundled?("hanami-action")
|
|
1069
1139
|
if config.actions.method_override
|
|
1070
1140
|
require "rack/method_override"
|
|
1071
1141
|
use(Rack::MethodOverride)
|
|
@@ -1088,6 +1158,7 @@ module Hanami
|
|
|
1088
1158
|
middleware_stack.update(config.middleware_stack)
|
|
1089
1159
|
end
|
|
1090
1160
|
end
|
|
1161
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
1091
1162
|
|
|
1092
1163
|
def render_errors?
|
|
1093
1164
|
config.render_errors
|
|
@@ -1111,6 +1182,27 @@ module Hanami
|
|
|
1111
1182
|
source_path.join("assets").directory?
|
|
1112
1183
|
end
|
|
1113
1184
|
|
|
1185
|
+
# Ensures an i18n provider is available in every slice.
|
|
1186
|
+
#
|
|
1187
|
+
# For the app, this will always be a standalone provider. For slices, this will be a
|
|
1188
|
+
# standalone provider unless the slice is configured to share the app's "i18n" component.
|
|
1189
|
+
def register_i18n_provider?
|
|
1190
|
+
return true if self == app
|
|
1191
|
+
|
|
1192
|
+
!config.shared_app_component_keys.include?("i18n")
|
|
1193
|
+
end
|
|
1194
|
+
|
|
1195
|
+
# Ensures a mailers provider is available in every slice.
|
|
1196
|
+
#
|
|
1197
|
+
# For the app, this will always be a standalone provider. For slices, this will be a
|
|
1198
|
+
# standalone provider unless the slice is configured to share the app's
|
|
1199
|
+
# "mailers.delivery_method" component.
|
|
1200
|
+
def register_mailers_provider?
|
|
1201
|
+
return true if self == app
|
|
1202
|
+
|
|
1203
|
+
!config.shared_app_component_keys.include?("mailers.delivery_method")
|
|
1204
|
+
end
|
|
1205
|
+
|
|
1114
1206
|
def register_db_provider?
|
|
1115
1207
|
concrete_db_provider? ||
|
|
1116
1208
|
db_config_dir? ||
|
|
@@ -1143,6 +1235,5 @@ module Hanami
|
|
|
1143
1235
|
|
|
1144
1236
|
# rubocop:enable Metrics/AbcSize
|
|
1145
1237
|
end
|
|
1146
|
-
# rubocop:enable Metrics/ModuleLength
|
|
1147
1238
|
end
|
|
1148
1239
|
end
|
|
@@ -57,9 +57,7 @@ module Hanami
|
|
|
57
57
|
def slice_for(klass)
|
|
58
58
|
return unless klass.name
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
slices.detect { |slice| klass.name.start_with?("#{slice.namespace}#{MODULE_DELIMITER}") }
|
|
60
|
+
Hanami.app.with_slices.detect { |slice| klass.name.start_with?("#{slice.namespace}#{MODULE_DELIMITER}") }
|
|
63
61
|
end
|
|
64
62
|
end
|
|
65
63
|
|
|
@@ -108,8 +108,8 @@ module Hanami
|
|
|
108
108
|
slice_class =
|
|
109
109
|
begin
|
|
110
110
|
inflector.constantize("#{slice_module_name(slice_name)}#{MODULE_DELIMITER}Slice")
|
|
111
|
-
rescue NameError =>
|
|
112
|
-
raise
|
|
111
|
+
rescue NameError => exception
|
|
112
|
+
raise exception unless exception.name.to_s == inflector.camelize(slice_name) || exception.name.to_s == :Slice
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
register(slice_name, slice_class)
|