hanami 2.3.2 → 3.0.0.rc1
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 +55 -19
- data/LICENSE +20 -0
- data/README.md +18 -35
- data/hanami.gemspec +36 -37
- 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/slice_configured_action.rb +1 -1
- data/lib/hanami/extensions/action.rb +2 -2
- data/lib/hanami/extensions/mailer/slice_configured_mailer.rb +120 -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 +176 -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 +369 -0
- data/lib/hanami/providers/i18n/locale/en.yml +57 -0
- data/lib/hanami/providers/i18n.rb +114 -0
- 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 +71 -11
- 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 +28 -276
- 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
data/lib/hanami/config.rb
CHANGED
|
@@ -6,7 +6,7 @@ require "dry/configurable"
|
|
|
6
6
|
require "dry/inflector"
|
|
7
7
|
|
|
8
8
|
require_relative "constants"
|
|
9
|
-
require_relative "
|
|
9
|
+
require_relative "universal_logger"
|
|
10
10
|
|
|
11
11
|
module Hanami
|
|
12
12
|
# Hanami app config
|
|
@@ -105,7 +105,9 @@ module Hanami
|
|
|
105
105
|
# @!attribute [rw] no_auto_register_paths
|
|
106
106
|
# Sets the paths to skip from container auto-registration.
|
|
107
107
|
#
|
|
108
|
-
#
|
|
108
|
+
# Paths are relative to `/app` or a slice root.
|
|
109
|
+
#
|
|
110
|
+
# Defaults to `["db", "entities", "relations", "structs"]`.
|
|
109
111
|
#
|
|
110
112
|
# @return [Array<String>] array of relative paths
|
|
111
113
|
#
|
|
@@ -118,6 +120,36 @@ module Hanami
|
|
|
118
120
|
"structs"
|
|
119
121
|
]
|
|
120
122
|
|
|
123
|
+
# @!attribute [rw] no_memoize
|
|
124
|
+
# Sets the components that should not be memoized in the container.
|
|
125
|
+
#
|
|
126
|
+
# All auto-registered components are memoized by default, meaning each component is resolved
|
|
127
|
+
# only once, with the same instance returned on every subsequent resolution.
|
|
128
|
+
#
|
|
129
|
+
# Use this setting to opt specific components out of memoization. It accepts an array of key
|
|
130
|
+
# prefixes (strings) for the simple case, or a proc for full control. The proc receives a
|
|
131
|
+
# `Dry::System::Component` and should return `true` for components that should _not_ be
|
|
132
|
+
# memoized.
|
|
133
|
+
#
|
|
134
|
+
# Individual components can also opt out by adding a `# memoize: false` magic comment at the
|
|
135
|
+
# top of their source file.
|
|
136
|
+
#
|
|
137
|
+
# Defaults to `[]` (all components memoized).
|
|
138
|
+
#
|
|
139
|
+
# @example Opt out by key prefix
|
|
140
|
+
# config.no_memoize = ["workers", "jobs"]
|
|
141
|
+
#
|
|
142
|
+
# @example Opt out with a proc
|
|
143
|
+
# config.no_memoize = ->(component) {
|
|
144
|
+
# component.key.start_with?("workers")
|
|
145
|
+
# }
|
|
146
|
+
#
|
|
147
|
+
# @return [Array<String>, Proc]
|
|
148
|
+
#
|
|
149
|
+
# @api public
|
|
150
|
+
# @since 2.3.0
|
|
151
|
+
setting :no_memoize, default: []
|
|
152
|
+
|
|
121
153
|
# @!attribute [rw] base_url
|
|
122
154
|
# Sets the base URL for app's web server.
|
|
123
155
|
#
|
|
@@ -182,7 +214,7 @@ module Hanami
|
|
|
182
214
|
# @since 2.1.0
|
|
183
215
|
setting :render_error_responses, default: Hash.new(:internal_server_error).merge!(
|
|
184
216
|
"Hanami::Router::NotAllowedError" => :not_found,
|
|
185
|
-
"Hanami::Router::NotFoundError" => :not_found
|
|
217
|
+
"Hanami::Router::NotFoundError" => :not_found
|
|
186
218
|
)
|
|
187
219
|
|
|
188
220
|
# @!attribute [rw] console
|
|
@@ -220,12 +252,12 @@ module Hanami
|
|
|
220
252
|
# @since 2.0.0
|
|
221
253
|
attr_reader :env
|
|
222
254
|
|
|
223
|
-
# Returns the app's actions config, or a null config if hanami-
|
|
255
|
+
# Returns the app's actions config, or a null config if hanami-action is not bundled.
|
|
224
256
|
#
|
|
225
|
-
# @example When hanami-
|
|
257
|
+
# @example When hanami-action is bundled
|
|
226
258
|
# config.actions.default_request_format # => :html
|
|
227
259
|
#
|
|
228
|
-
# @example When hanami-
|
|
260
|
+
# @example When hanami-action is not bundled
|
|
229
261
|
# config.actions.default_request_format # => NoMethodError
|
|
230
262
|
#
|
|
231
263
|
# @return [Hanami::Config::Actions, Hanami::Config::NullConfig]
|
|
@@ -248,6 +280,20 @@ module Hanami
|
|
|
248
280
|
# @since 2.2.0
|
|
249
281
|
attr_reader :db
|
|
250
282
|
|
|
283
|
+
# Returns the app's i18n config, or a null config if i18n is not bundled.
|
|
284
|
+
#
|
|
285
|
+
# @example When i18n is bundled
|
|
286
|
+
# config.i18n.default_locale # => :en
|
|
287
|
+
#
|
|
288
|
+
# @example When i18n is not bundled
|
|
289
|
+
# config.i18n.default_locale # => NoMethodError
|
|
290
|
+
#
|
|
291
|
+
# @return [Hanami::Config::I18n, Hanami::Config::NullConfig]
|
|
292
|
+
#
|
|
293
|
+
# @api public
|
|
294
|
+
# @since 2.2.0
|
|
295
|
+
attr_reader :i18n
|
|
296
|
+
|
|
251
297
|
# Returns the app's middleware stack, or nil if hanami-router is not bundled.
|
|
252
298
|
#
|
|
253
299
|
# Use this to configure middleware that should apply to all routes.
|
|
@@ -320,7 +366,7 @@ module Hanami
|
|
|
320
366
|
self.render_detailed_errors = (env == :development)
|
|
321
367
|
load_from_env
|
|
322
368
|
|
|
323
|
-
@actions = load_dependent_config("hanami-
|
|
369
|
+
@actions = load_dependent_config("hanami-action") {
|
|
324
370
|
require_relative "config/actions"
|
|
325
371
|
Actions.new
|
|
326
372
|
}
|
|
@@ -332,6 +378,11 @@ module Hanami
|
|
|
332
378
|
|
|
333
379
|
@db = load_dependent_config("hanami-db") { DB.new }
|
|
334
380
|
|
|
381
|
+
@i18n = load_dependent_config("i18n") {
|
|
382
|
+
require_relative "config/i18n"
|
|
383
|
+
I18n.new
|
|
384
|
+
}
|
|
385
|
+
|
|
335
386
|
@logger = Config::Logger.new(env: env, app_name: app_name)
|
|
336
387
|
|
|
337
388
|
@middleware = load_dependent_config("hanami-router") {
|
|
@@ -362,6 +413,7 @@ module Hanami
|
|
|
362
413
|
@actions = source.actions.dup
|
|
363
414
|
@assets = source.assets.dup
|
|
364
415
|
@db = source.db.dup
|
|
416
|
+
@i18n = source.i18n.dup
|
|
365
417
|
@logger = source.logger.dup
|
|
366
418
|
@middleware = source.middleware.dup
|
|
367
419
|
@router = source.router.dup.tap do |router|
|
|
@@ -385,8 +437,6 @@ module Hanami
|
|
|
385
437
|
logger.finalize!
|
|
386
438
|
router.finalize!
|
|
387
439
|
|
|
388
|
-
use_body_parser_middleware
|
|
389
|
-
|
|
390
440
|
super
|
|
391
441
|
end
|
|
392
442
|
|
|
@@ -434,13 +484,19 @@ module Hanami
|
|
|
434
484
|
# Sets the app's logger instance.
|
|
435
485
|
#
|
|
436
486
|
# This entirely replaces the default `Dry::Logger::Dispatcher` instance that would have been
|
|
487
|
+
# configured via {#logger}.
|
|
488
|
+
#
|
|
489
|
+
# The provided logger will be wrapped with {Hanami::UniversalLogger} if it doesn't support both
|
|
490
|
+
# keyword arguments and `#tagged` logging, ensuring compatibility with Hanami's logging
|
|
491
|
+
# interface.
|
|
437
492
|
#
|
|
438
493
|
# @see #logger_instance
|
|
494
|
+
# @see Hanami::UniversalLogger
|
|
439
495
|
#
|
|
440
496
|
# @api public
|
|
441
497
|
# @since 2.0.0
|
|
442
498
|
def logger=(logger_instance)
|
|
443
|
-
@logger_instance = logger_instance
|
|
499
|
+
@logger_instance = Hanami::UniversalLogger[logger_instance]
|
|
444
500
|
end
|
|
445
501
|
|
|
446
502
|
# rubocop:enable Style/TrivialAccessors
|
|
@@ -484,18 +540,6 @@ module Hanami
|
|
|
484
540
|
self.slices = ENV["HANAMI_SLICES"]&.split(",")&.map(&:strip)
|
|
485
541
|
end
|
|
486
542
|
|
|
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
|
|
492
|
-
|
|
493
|
-
def use_body_parser_middleware
|
|
494
|
-
return unless Hanami.bundled?("hanami-router") && Hanami.bundled?("hanami-controller")
|
|
495
|
-
|
|
496
|
-
middleware.use(:body_parser, [DEFAULT_MIDDLEWARE_PARSERS])
|
|
497
|
-
end
|
|
498
|
-
|
|
499
543
|
def load_dependent_config(gem_name)
|
|
500
544
|
if Hanami.bundled?(gem_name)
|
|
501
545
|
yield
|
data/lib/hanami/errors.rb
CHANGED
|
@@ -25,6 +25,12 @@ module Hanami
|
|
|
25
25
|
# @since 2.0.0
|
|
26
26
|
ComponentLoadError = Class.new(Error)
|
|
27
27
|
|
|
28
|
+
# Error raised when there are no routes defined.
|
|
29
|
+
#
|
|
30
|
+
# @api public
|
|
31
|
+
# @since 2.4.0
|
|
32
|
+
NoRoutesDefinedError = Class.new(Error)
|
|
33
|
+
|
|
28
34
|
# Error raised when unsupported middleware configuration is given.
|
|
29
35
|
#
|
|
30
36
|
# @see Hanami::Slice::Routing::Middleware::Stack#use
|
|
@@ -9,7 +9,7 @@ module Hanami
|
|
|
9
9
|
# Integrated behavior for `Hanami::Action` classes within Hanami apps.
|
|
10
10
|
#
|
|
11
11
|
# @see InstanceMethods
|
|
12
|
-
# @see https://github.com/hanami/
|
|
12
|
+
# @see https://github.com/hanami/hanami-action
|
|
13
13
|
#
|
|
14
14
|
# @api public
|
|
15
15
|
# @since 2.0.0
|
|
@@ -109,7 +109,7 @@ module Hanami
|
|
|
109
109
|
end
|
|
110
110
|
|
|
111
111
|
# @api private
|
|
112
|
-
def view_context_options(request, response)
|
|
112
|
+
def view_context_options(request, response)
|
|
113
113
|
{request: request}
|
|
114
114
|
end
|
|
115
115
|
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hanami
|
|
4
|
+
module Extensions
|
|
5
|
+
module Mailer
|
|
6
|
+
# Provides slice-specific configuration and behavior for any mailer class defined within a
|
|
7
|
+
# slice's module namespace.
|
|
8
|
+
#
|
|
9
|
+
# This injects the slice's `"mailers.delivery_method"` component into mailer instances, and
|
|
10
|
+
# points the mailer's view at a slice-configured view class so that mailer templates behave
|
|
11
|
+
# like regular Hanami view templates — sharing the slice's view context, parts, scopes and
|
|
12
|
+
# helpers (including i18n). The only behavior not available to mailer views is
|
|
13
|
+
# request-related state (`request`/`session`/`flash`/`csrf_token`), since mailers are not
|
|
14
|
+
# rendered from a request.
|
|
15
|
+
#
|
|
16
|
+
# @api private
|
|
17
|
+
class SliceConfiguredMailer < Module
|
|
18
|
+
attr_reader :slice
|
|
19
|
+
|
|
20
|
+
def initialize(slice)
|
|
21
|
+
super()
|
|
22
|
+
@slice = slice
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def extended(mailer_class)
|
|
26
|
+
configure_mailer(mailer_class)
|
|
27
|
+
define_new
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def inspect
|
|
31
|
+
"#<#{self.class.name}[#{slice.name}]>"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def define_new
|
|
37
|
+
resolve_delivery_method = method(:resolve_delivery_method)
|
|
38
|
+
|
|
39
|
+
define_method(:new) do |**kwargs|
|
|
40
|
+
super(
|
|
41
|
+
delivery_method: kwargs.fetch(:delivery_method) { resolve_delivery_method.() },
|
|
42
|
+
**kwargs
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def resolve_delivery_method
|
|
48
|
+
slice["mailers.delivery_method"] if slice.key?("mailers.delivery_method")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def configure_mailer(mailer_class)
|
|
52
|
+
return unless Hanami.bundled?("hanami-view")
|
|
53
|
+
|
|
54
|
+
# Build the mailer's view from a slice-configured view class, so it inherits the slice's
|
|
55
|
+
# context, parts, scopes, paths and helpers. The mailer only needs to supply its own
|
|
56
|
+
# template name.
|
|
57
|
+
mailer_class.config.view_class = mailer_view_class
|
|
58
|
+
|
|
59
|
+
if (template = template_name(mailer_class))
|
|
60
|
+
mailer_class.config.template = template
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Returns the view class that mailer views are built from, defining a `Mailers::View` within
|
|
65
|
+
# the slice if one is not already present.
|
|
66
|
+
#
|
|
67
|
+
# This mirrors how the view extension defines a `Views::Context`: because the class is
|
|
68
|
+
# defined within the slice's namespace, it is configured automatically (paths, context,
|
|
69
|
+
# parts, scopes, helpers) just like any other view in the slice. A user may define their own
|
|
70
|
+
# `<Slice>::Mailers::View` to customize mailer rendering; it is used when present.
|
|
71
|
+
def mailer_view_class
|
|
72
|
+
namespace = mailers_namespace
|
|
73
|
+
|
|
74
|
+
if namespace.const_defined?(:View, _inherit = false)
|
|
75
|
+
namespace.const_get(:View)
|
|
76
|
+
else
|
|
77
|
+
namespace.const_set(:View, define_mailer_view)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Defines and configures a view class for the slice's mailers, inheriting from the slice's
|
|
82
|
+
# own base view class if present, otherwise from the plain `Hanami::View`.
|
|
83
|
+
def define_mailer_view
|
|
84
|
+
superclass =
|
|
85
|
+
begin
|
|
86
|
+
slice.inflector.constantize("#{slice.namespace.name}::View")
|
|
87
|
+
rescue NameError
|
|
88
|
+
Hanami::View
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
Class.new(superclass).tap { |klass|
|
|
92
|
+
# Call configure_for_slice explicitly, since this is an anonymous class at this point,
|
|
93
|
+
# so the slice cannot be inferred from its name.
|
|
94
|
+
klass.configure_for_slice(slice)
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def mailers_namespace
|
|
99
|
+
if slice.namespace.const_defined?(:Mailers, _inherit = false)
|
|
100
|
+
slice.namespace.const_get(:Mailers)
|
|
101
|
+
else
|
|
102
|
+
slice.namespace.const_set(:Mailers, Module.new)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Returns the template name for the mailer, retaining the `mailers/` segment from the
|
|
107
|
+
# mailer's namespace so templates resolve under `templates/mailers/` within the slice.
|
|
108
|
+
#
|
|
109
|
+
# For example, `App::Mailers::Welcome` will use template `"mailers/welcome"`.
|
|
110
|
+
def template_name(mailer_class)
|
|
111
|
+
return unless mailer_class.name
|
|
112
|
+
|
|
113
|
+
slice.inflector
|
|
114
|
+
.underscore(mailer_class.name)
|
|
115
|
+
.sub(/^#{slice.slice_name.path}\//, "")
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/mailer"
|
|
4
|
+
require_relative "mailer/slice_configured_mailer"
|
|
5
|
+
|
|
6
|
+
module Hanami
|
|
7
|
+
module Extensions
|
|
8
|
+
# Integrated behavior for `Hanami::Mailer` classes within Hanami apps.
|
|
9
|
+
#
|
|
10
|
+
# @api private
|
|
11
|
+
module Mailer
|
|
12
|
+
def self.included(mailer_class)
|
|
13
|
+
super
|
|
14
|
+
|
|
15
|
+
mailer_class.extend(Hanami::SliceConfigurable)
|
|
16
|
+
mailer_class.extend(ClassMethods)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module ClassMethods
|
|
20
|
+
def configure_for_slice(slice)
|
|
21
|
+
extend SliceConfiguredMailer.new(slice)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Hanami::Mailer.include(Hanami::Extensions::Mailer)
|
|
@@ -25,6 +25,8 @@ module Hanami
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
views_namespace.const_set(:Context, Class.new(context_superclass(slice)).tap { |klass|
|
|
28
|
+
# Call configure_for_slice explicitly, since this is an anonymous class at this point,
|
|
29
|
+
# so the slice cannot be inferred from its name.
|
|
28
30
|
klass.configure_for_slice(slice)
|
|
29
31
|
})
|
|
30
32
|
end
|
|
@@ -40,8 +42,8 @@ module Hanami
|
|
|
40
42
|
slice.inflector.constantize(
|
|
41
43
|
slice.inflector.camelize("#{slice.app.slice_name.name}/views/context")
|
|
42
44
|
)
|
|
43
|
-
rescue NameError =>
|
|
44
|
-
raise
|
|
45
|
+
rescue NameError => exception
|
|
46
|
+
raise exception unless %i[Views Context].include?(exception.name)
|
|
45
47
|
|
|
46
48
|
Hanami::View::Context
|
|
47
49
|
end
|
|
@@ -95,17 +97,19 @@ module Hanami
|
|
|
95
97
|
#
|
|
96
98
|
# @api private
|
|
97
99
|
# @since 2.1.0
|
|
98
|
-
def initialize(
|
|
100
|
+
def initialize(
|
|
99
101
|
inflector: nil,
|
|
100
102
|
routes: nil,
|
|
101
103
|
assets: nil,
|
|
102
104
|
request: nil,
|
|
105
|
+
i18n: nil,
|
|
103
106
|
**args
|
|
104
107
|
)
|
|
105
108
|
@inflector = inflector
|
|
106
109
|
@routes = routes
|
|
107
110
|
@assets = assets
|
|
108
111
|
@request = request
|
|
112
|
+
@i18n = i18n
|
|
109
113
|
|
|
110
114
|
@content_for = {}
|
|
111
115
|
|
|
@@ -157,7 +161,9 @@ module Hanami
|
|
|
157
161
|
# @since 2.1.0
|
|
158
162
|
def request
|
|
159
163
|
unless @request
|
|
160
|
-
raise Hanami::ComponentLoadError,
|
|
164
|
+
raise Hanami::ComponentLoadError, <<~STR
|
|
165
|
+
Request not available. Only views rendered from Hanami::Action instances have a request.
|
|
166
|
+
STR
|
|
161
167
|
end
|
|
162
168
|
|
|
163
169
|
@request
|
|
@@ -190,6 +196,22 @@ module Hanami
|
|
|
190
196
|
@routes
|
|
191
197
|
end
|
|
192
198
|
|
|
199
|
+
# Returns the slice's i18n backend.
|
|
200
|
+
#
|
|
201
|
+
# @return [Hanami::Providers::I18n::Backend] the i18n backend
|
|
202
|
+
#
|
|
203
|
+
# @raise [Hanami::ComponentLoadError] if the i18n gem is not bundled
|
|
204
|
+
#
|
|
205
|
+
# @api public
|
|
206
|
+
# @since x.x.x
|
|
207
|
+
def i18n
|
|
208
|
+
unless @i18n
|
|
209
|
+
raise Hanami::ComponentLoadError, "the i18n gem is required to access translations"
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
@i18n
|
|
213
|
+
end
|
|
214
|
+
|
|
193
215
|
# @overload content_for(key, value = nil, &block)
|
|
194
216
|
# Stores a string or block of template markup for later use.
|
|
195
217
|
#
|
|
@@ -27,6 +27,8 @@ module Hanami
|
|
|
27
27
|
extend SliceConfiguredPart.new(slice)
|
|
28
28
|
|
|
29
29
|
const_set :PartHelpers, Class.new(PartHelpers) { |klass|
|
|
30
|
+
# Call configure_for_slice explicitly, since this is an anonymous class at this point,
|
|
31
|
+
# so the slice cannot be inferred from its name.
|
|
30
32
|
klass.configure_for_slice(slice)
|
|
31
33
|
}
|
|
32
34
|
end
|
|
@@ -39,15 +39,18 @@ module Hanami
|
|
|
39
39
|
# - the configured inflector as `inflector`
|
|
40
40
|
# - "routes" from the app container as `routes`
|
|
41
41
|
# - "assets" from the app container as `assets`
|
|
42
|
+
# - "i18n" from the slice container as `i18n`
|
|
42
43
|
def define_new
|
|
43
44
|
inflector = slice.inflector
|
|
44
45
|
resolve_routes = method(:resolve_routes)
|
|
45
46
|
resolve_assets = method(:resolve_assets)
|
|
47
|
+
resolve_i18n = method(:resolve_i18n)
|
|
46
48
|
|
|
47
49
|
define_method :new do |**kwargs|
|
|
48
50
|
kwargs[:inflector] ||= inflector
|
|
49
51
|
kwargs[:routes] ||= resolve_routes.()
|
|
50
52
|
kwargs[:assets] ||= resolve_assets.()
|
|
53
|
+
kwargs[:i18n] ||= resolve_i18n.()
|
|
51
54
|
|
|
52
55
|
super(**kwargs)
|
|
53
56
|
end
|
|
@@ -60,6 +63,10 @@ module Hanami
|
|
|
60
63
|
def resolve_assets
|
|
61
64
|
slice["assets"] if slice.key?("assets")
|
|
62
65
|
end
|
|
66
|
+
|
|
67
|
+
def resolve_i18n
|
|
68
|
+
slice["i18n"] if slice.key?("i18n")
|
|
69
|
+
end
|
|
63
70
|
end
|
|
64
71
|
end
|
|
65
72
|
end
|
|
@@ -57,6 +57,8 @@ module Hanami
|
|
|
57
57
|
define_method(:new) do |**args|
|
|
58
58
|
return super(**args) if args.key?(:rendering)
|
|
59
59
|
|
|
60
|
+
# Call configure_for_slice explicitly, since this is an anonymous class at this point,
|
|
61
|
+
# so the slice cannot be inferred from its name.
|
|
60
62
|
slice_rendering = Class.new(Hanami::View)
|
|
61
63
|
.configure_for_slice(slice)
|
|
62
64
|
.new
|
|
@@ -49,12 +49,12 @@ module Hanami
|
|
|
49
49
|
|
|
50
50
|
begin
|
|
51
51
|
slice.app.namespace.const_get(:View, false)
|
|
52
|
-
rescue NameError =>
|
|
53
|
-
raise unless
|
|
52
|
+
rescue NameError => exception
|
|
53
|
+
raise unless exception.name == :View
|
|
54
54
|
end
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
# rubocop:disable Metrics/AbcSize
|
|
57
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
58
58
|
def configure_view(view_class)
|
|
59
59
|
view_class.settings.each do |setting|
|
|
60
60
|
next unless slice.config.views.respond_to?(setting.name)
|
|
@@ -134,7 +134,7 @@ module Hanami
|
|
|
134
134
|
view_class.config.scope_namespace = scope_namespace
|
|
135
135
|
end
|
|
136
136
|
end
|
|
137
|
-
# rubocop:enable Metrics/AbcSize
|
|
137
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
138
138
|
|
|
139
139
|
def define_inherited
|
|
140
140
|
template_name = method(:template_name)
|
|
@@ -185,8 +185,8 @@ module Hanami
|
|
|
185
185
|
views_namespace.const_get(:Part)
|
|
186
186
|
else
|
|
187
187
|
views_namespace.const_set(:Part, Class.new(part_superclass).tap { |klass|
|
|
188
|
-
#
|
|
189
|
-
#
|
|
188
|
+
# Call configure_for_slice explicitly, since this is an anonymous class at this point,
|
|
189
|
+
# so the slice cannot be inferred from its name.
|
|
190
190
|
klass.configure_for_slice(slice)
|
|
191
191
|
})
|
|
192
192
|
end
|
|
@@ -208,8 +208,8 @@ module Hanami
|
|
|
208
208
|
views_namespace.const_get(:Scope)
|
|
209
209
|
else
|
|
210
210
|
views_namespace.const_set(:Scope, Class.new(scope_superclass).tap { |klass|
|
|
211
|
-
#
|
|
212
|
-
#
|
|
211
|
+
# Call configure_for_slice explicitly, since this is an anonymous class at this point,
|
|
212
|
+
# so the slice cannot be inferred from its name.
|
|
213
213
|
klass.configure_for_slice(slice)
|
|
214
214
|
})
|
|
215
215
|
end
|
data/lib/hanami/extensions.rb
CHANGED
|
@@ -4,7 +4,7 @@ if Hanami.bundled?("hanami-db")
|
|
|
4
4
|
require_relative "extensions/db/repo"
|
|
5
5
|
end
|
|
6
6
|
|
|
7
|
-
if Hanami.bundled?("hanami-
|
|
7
|
+
if Hanami.bundled?("hanami-action")
|
|
8
8
|
require_relative "extensions/action"
|
|
9
9
|
end
|
|
10
10
|
|
|
@@ -20,6 +20,11 @@ if Hanami.bundled?("hanami-router")
|
|
|
20
20
|
require_relative "extensions/router/errors"
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
if Hanami.bundled?("hanami-mailer")
|
|
24
|
+
require "hanami/mailer"
|
|
25
|
+
require_relative "extensions/mailer"
|
|
26
|
+
end
|
|
27
|
+
|
|
23
28
|
if Hanami.bundled?("dry-operation")
|
|
24
29
|
require_relative "extensions/operation"
|
|
25
30
|
end
|
|
@@ -4,8 +4,6 @@ require "uri"
|
|
|
4
4
|
require "hanami/view"
|
|
5
5
|
require_relative "../constants"
|
|
6
6
|
|
|
7
|
-
# rubocop:disable Metrics/ModuleLength
|
|
8
|
-
|
|
9
7
|
module Hanami
|
|
10
8
|
module Helpers
|
|
11
9
|
# HTML assets helpers
|
|
@@ -804,5 +802,3 @@ module Hanami
|
|
|
804
802
|
end
|
|
805
803
|
end
|
|
806
804
|
end
|
|
807
|
-
|
|
808
|
-
# rubocop:enable Metrics/ModuleLength
|