hanami 2.0.0.alpha8 → 2.0.0.beta1
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 +442 -241
- data/FEATURES.md +30 -9
- data/README.md +1 -3
- data/hanami.gemspec +21 -11
- data/lib/hanami/app.rb +141 -0
- data/lib/hanami/assets/application_configuration.rb +10 -4
- data/lib/hanami/configuration/actions/content_security_policy.rb +5 -5
- data/lib/hanami/configuration/actions/cookies.rb +2 -2
- data/lib/hanami/configuration/actions.rb +10 -4
- data/lib/hanami/configuration/logger.rb +4 -4
- data/lib/hanami/configuration/router.rb +2 -6
- data/lib/hanami/configuration/sessions.rb +1 -1
- data/lib/hanami/configuration/views.rb +9 -4
- data/lib/hanami/configuration.rb +118 -46
- data/lib/hanami/constants.rb +24 -2
- data/lib/hanami/errors.rb +1 -1
- data/lib/hanami/{application → extensions}/action/slice_configured_action.rb +9 -9
- data/lib/hanami/extensions/action.rb +79 -0
- data/lib/hanami/extensions/view/context.rb +106 -0
- data/lib/hanami/{application → extensions}/view/slice_configured_context.rb +10 -10
- data/lib/hanami/{application → extensions}/view/slice_configured_view.rb +12 -6
- data/lib/hanami/extensions/view.rb +33 -0
- data/lib/hanami/extensions.rb +10 -0
- data/lib/hanami/providers/inflector.rb +13 -0
- data/lib/hanami/providers/logger.rb +13 -0
- data/lib/hanami/providers/rack.rb +27 -0
- data/lib/hanami/providers/routes.rb +33 -0
- data/lib/hanami/providers/settings.rb +23 -0
- data/lib/hanami/rake_tasks.rb +61 -0
- data/lib/hanami/routes.rb +51 -0
- data/lib/hanami/server.rb +1 -1
- data/lib/hanami/settings/dotenv_store.rb +58 -0
- data/lib/hanami/settings.rb +90 -0
- data/lib/hanami/setup.rb +4 -2
- data/lib/hanami/{application → slice}/router.rb +18 -13
- data/lib/hanami/slice/routes_helper.rb +37 -0
- data/lib/hanami/{application → slice}/routing/middleware/stack.rb +43 -5
- data/lib/hanami/slice/routing/resolver.rb +97 -0
- data/lib/hanami/{application → slice}/view_name_inferrer.rb +3 -3
- data/lib/hanami/slice.rb +246 -73
- data/lib/hanami/slice_configurable.rb +4 -17
- data/lib/hanami/slice_name.rb +6 -6
- data/lib/hanami/slice_registrar.rb +119 -0
- data/lib/hanami/version.rb +1 -1
- data/lib/hanami/web/rack_logger.rb +1 -1
- data/lib/hanami.rb +34 -26
- data/spec/integration/application_middleware_stack_spec.rb +84 -0
- data/spec/integration/assets/cdn_spec.rb +48 -0
- data/spec/integration/assets/fingerprint_spec.rb +42 -0
- data/spec/integration/assets/helpers_spec.rb +50 -0
- data/spec/integration/assets/serve_spec.rb +70 -0
- data/spec/integration/assets/subresource_integrity_spec.rb +54 -0
- data/spec/integration/body_parsers_spec.rb +50 -0
- data/spec/integration/cli/assets/precompile_spec.rb +147 -0
- data/spec/integration/cli/assets_spec.rb +14 -0
- data/spec/integration/cli/console_spec.rb +105 -0
- data/spec/integration/cli/db/apply_spec.rb +74 -0
- data/spec/integration/cli/db/console_spec.rb +40 -0
- data/spec/integration/cli/db/create_spec.rb +50 -0
- data/spec/integration/cli/db/drop_spec.rb +54 -0
- data/spec/integration/cli/db/migrate_spec.rb +108 -0
- data/spec/integration/cli/db/prepare_spec.rb +36 -0
- data/spec/integration/cli/db/rollback_spec.rb +96 -0
- data/spec/integration/cli/db/version_spec.rb +38 -0
- data/spec/integration/cli/db_spec.rb +21 -0
- data/spec/integration/cli/destroy/action_spec.rb +143 -0
- data/spec/integration/cli/destroy/app_spec.rb +118 -0
- data/spec/integration/cli/destroy/mailer_spec.rb +74 -0
- data/spec/integration/cli/destroy/migration_spec.rb +70 -0
- data/spec/integration/cli/destroy/model_spec.rb +113 -0
- data/spec/integration/cli/destroy_spec.rb +18 -0
- data/spec/integration/cli/generate/action_spec.rb +469 -0
- data/spec/integration/cli/generate/app_spec.rb +215 -0
- data/spec/integration/cli/generate/mailer_spec.rb +189 -0
- data/spec/integration/cli/generate/migration_spec.rb +72 -0
- data/spec/integration/cli/generate/model_spec.rb +290 -0
- data/spec/integration/cli/generate/secret_spec.rb +56 -0
- data/spec/integration/cli/generate_spec.rb +19 -0
- data/spec/integration/cli/new/database_spec.rb +235 -0
- data/spec/integration/cli/new/hanami_head_spec.rb +27 -0
- data/spec/integration/cli/new/template_spec.rb +118 -0
- data/spec/integration/cli/new/test_spec.rb +274 -0
- data/spec/integration/cli/new_spec.rb +970 -0
- data/spec/integration/cli/plugins_spec.rb +39 -0
- data/spec/integration/cli/routes_spec.rb +49 -0
- data/spec/integration/cli/server_spec.rb +626 -0
- data/spec/integration/cli/version_spec.rb +85 -0
- data/spec/integration/early_hints_spec.rb +35 -0
- data/spec/integration/handle_exceptions_spec.rb +244 -0
- data/spec/integration/head_spec.rb +89 -0
- data/spec/integration/http_headers_spec.rb +29 -0
- data/spec/integration/mailer_spec.rb +32 -0
- data/spec/integration/middleware_spec.rb +81 -0
- data/spec/integration/mount_applications_spec.rb +88 -0
- data/spec/integration/project_initializers_spec.rb +40 -0
- data/spec/integration/rackup_spec.rb +35 -0
- data/spec/integration/rake/with_minitest_spec.rb +67 -0
- data/spec/integration/rake/with_rspec_spec.rb +69 -0
- data/spec/integration/routing_helpers_spec.rb +61 -0
- data/spec/integration/security/content_security_policy_spec.rb +46 -0
- data/spec/integration/security/csrf_protection_spec.rb +42 -0
- data/spec/integration/security/force_ssl_spec.rb +29 -0
- data/spec/integration/security/x_content_type_options_spec.rb +46 -0
- data/spec/integration/security/x_frame_options_spec.rb +46 -0
- data/spec/integration/security/x_xss_protection_spec.rb +46 -0
- data/spec/integration/send_file_spec.rb +51 -0
- data/spec/integration/sessions_spec.rb +247 -0
- data/spec/integration/static_middleware_spec.rb +21 -0
- data/spec/integration/streaming_spec.rb +41 -0
- data/spec/integration/unsafe_send_file_spec.rb +52 -0
- data/spec/isolation/hanami/application/already_configured_spec.rb +19 -0
- data/spec/isolation/hanami/application/inherit_anonymous_class_spec.rb +10 -0
- data/spec/isolation/hanami/application/inherit_concrete_class_spec.rb +14 -0
- data/spec/isolation/hanami/application/not_configured_spec.rb +9 -0
- data/spec/isolation/hanami/application/routes/configured_spec.rb +44 -0
- data/spec/isolation/hanami/application/routes/not_configured_spec.rb +16 -0
- data/spec/isolation/hanami/boot/success_spec.rb +50 -0
- data/spec/new_integration/action/configuration_spec.rb +26 -0
- data/spec/new_integration/action/cookies_spec.rb +58 -0
- data/spec/new_integration/action/csrf_protection_spec.rb +54 -0
- data/spec/new_integration/action/routes_spec.rb +73 -0
- data/spec/new_integration/action/sessions_spec.rb +50 -0
- data/spec/new_integration/action/view_integration_spec.rb +165 -0
- data/spec/new_integration/action/view_rendering/automatic_rendering_spec.rb +247 -0
- data/spec/new_integration/action/view_rendering/paired_view_inference_spec.rb +115 -0
- data/spec/new_integration/action/view_rendering_spec.rb +107 -0
- data/spec/new_integration/code_loading/loading_from_app_spec.rb +152 -0
- data/spec/new_integration/code_loading/loading_from_slice_spec.rb +165 -0
- data/spec/new_integration/container/application_routes_helper_spec.rb +48 -0
- data/spec/new_integration/container/auto_injection_spec.rb +53 -0
- data/spec/new_integration/container/auto_registration_spec.rb +86 -0
- data/spec/new_integration/container/autoloader_spec.rb +80 -0
- data/spec/new_integration/container/imports_spec.rb +253 -0
- data/spec/new_integration/container/prepare_container_spec.rb +123 -0
- data/spec/new_integration/container/shutdown_spec.rb +91 -0
- data/spec/new_integration/container/standard_bootable_components_spec.rb +124 -0
- data/spec/new_integration/rack_app/middleware_spec.rb +215 -0
- data/spec/new_integration/rack_app/non_booted_rack_app_spec.rb +105 -0
- data/spec/new_integration/rack_app/rack_app_spec.rb +524 -0
- data/spec/new_integration/settings_spec.rb +115 -0
- data/spec/new_integration/slices/external_slice_spec.rb +92 -0
- data/spec/new_integration/slices/slice_configuration_spec.rb +40 -0
- data/spec/new_integration/slices/slice_routing_spec.rb +226 -0
- data/spec/new_integration/slices/slice_settings_spec.rb +141 -0
- data/spec/new_integration/slices_spec.rb +101 -0
- data/spec/new_integration/view/configuration_spec.rb +49 -0
- data/spec/new_integration/view/context/assets_spec.rb +67 -0
- data/spec/new_integration/view/context/inflector_spec.rb +48 -0
- data/spec/new_integration/view/context/request_spec.rb +61 -0
- data/spec/new_integration/view/context/routes_spec.rb +86 -0
- data/spec/new_integration/view/context/settings_spec.rb +50 -0
- data/spec/new_integration/view/inflector_spec.rb +57 -0
- data/spec/new_integration/view/part_namespace_spec.rb +96 -0
- data/spec/new_integration/view/path_spec.rb +56 -0
- data/spec/new_integration/view/template_spec.rb +68 -0
- data/spec/new_integration/view/views_spec.rb +103 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/support/app_integration.rb +91 -0
- data/spec/support/coverage.rb +1 -0
- data/spec/support/fixtures/hanami-plugin/Gemfile +8 -0
- data/spec/support/fixtures/hanami-plugin/README.md +35 -0
- data/spec/support/fixtures/hanami-plugin/Rakefile +4 -0
- data/spec/support/fixtures/hanami-plugin/bin/console +15 -0
- data/spec/support/fixtures/hanami-plugin/bin/setup +8 -0
- data/spec/support/fixtures/hanami-plugin/hanami-plugin.gemspec +28 -0
- data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin/cli.rb +19 -0
- data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin/version.rb +7 -0
- data/spec/support/fixtures/hanami-plugin/lib/hanami/plugin.rb +8 -0
- data/spec/support/rspec.rb +27 -0
- data/spec/support/shared_examples/cli/generate/app.rb +494 -0
- data/spec/support/shared_examples/cli/generate/migration.rb +32 -0
- data/spec/support/shared_examples/cli/generate/model.rb +81 -0
- data/spec/support/shared_examples/cli/new.rb +97 -0
- data/spec/unit/hanami/configuration/actions/content_security_policy_spec.rb +102 -0
- data/spec/unit/hanami/configuration/actions/cookies_spec.rb +46 -0
- data/spec/unit/hanami/configuration/actions/csrf_protection_spec.rb +57 -0
- data/spec/unit/hanami/configuration/actions/default_values_spec.rb +52 -0
- data/spec/unit/hanami/configuration/actions/sessions_spec.rb +50 -0
- data/spec/unit/hanami/configuration/actions_spec.rb +78 -0
- data/spec/unit/hanami/configuration/base_url_spec.rb +25 -0
- data/spec/unit/hanami/configuration/inflector_spec.rb +35 -0
- data/spec/unit/hanami/configuration/logger_spec.rb +203 -0
- data/spec/unit/hanami/configuration/views_spec.rb +120 -0
- data/spec/unit/hanami/configuration_spec.rb +43 -0
- data/spec/unit/hanami/env_spec.rb +54 -0
- data/spec/unit/hanami/routes_spec.rb +25 -0
- data/spec/unit/hanami/settings/dotenv_store_spec.rb +119 -0
- data/spec/unit/hanami/settings_spec.rb +56 -0
- data/spec/unit/hanami/slice_configurable_spec.rb +104 -0
- data/spec/unit/hanami/slice_name_spec.rb +47 -0
- data/spec/unit/hanami/slice_spec.rb +17 -0
- data/spec/unit/hanami/version_spec.rb +7 -0
- data/spec/unit/hanami/web/rack_logger_spec.rb +78 -0
- metadata +353 -57
- data/lib/hanami/application/action.rb +0 -72
- data/lib/hanami/application/container/providers/inflector.rb +0 -7
- data/lib/hanami/application/container/providers/logger.rb +0 -7
- data/lib/hanami/application/container/providers/rack_logger.rb +0 -15
- data/lib/hanami/application/container/providers/rack_monitor.rb +0 -12
- data/lib/hanami/application/container/providers/routes_helper.rb +0 -9
- data/lib/hanami/application/container/providers/settings.rb +0 -7
- data/lib/hanami/application/routes.rb +0 -55
- data/lib/hanami/application/routes_helper.rb +0 -34
- data/lib/hanami/application/routing/resolver/node.rb +0 -50
- data/lib/hanami/application/routing/resolver/trie.rb +0 -59
- data/lib/hanami/application/routing/resolver.rb +0 -87
- data/lib/hanami/application/routing/router.rb +0 -36
- data/lib/hanami/application/settings/dotenv_store.rb +0 -60
- data/lib/hanami/application/settings.rb +0 -93
- data/lib/hanami/application/slice_registrar.rb +0 -106
- data/lib/hanami/application/view/context.rb +0 -95
- data/lib/hanami/application/view.rb +0 -24
- data/lib/hanami/application.rb +0 -273
- data/lib/hanami/cli/application/cli.rb +0 -40
- data/lib/hanami/cli/application/command.rb +0 -47
- data/lib/hanami/cli/application/commands/console.rb +0 -81
- data/lib/hanami/cli/application/commands.rb +0 -16
- data/lib/hanami/cli/base_command.rb +0 -48
- data/lib/hanami/cli/commands/command.rb +0 -171
- data/lib/hanami/cli/commands/server.rb +0 -88
- data/lib/hanami/cli/commands.rb +0 -65
- data/lib/hanami/configuration/middleware.rb +0 -20
- data/lib/hanami/configuration/source_dirs.rb +0 -42
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require_relative "../constants"
|
|
4
4
|
|
|
5
5
|
module Hanami
|
|
6
|
-
class
|
|
6
|
+
class Slice
|
|
7
7
|
# Infers a view name for automatically rendering within actions.
|
|
8
8
|
#
|
|
9
9
|
# @api private
|
|
@@ -28,8 +28,8 @@ module Hanami
|
|
|
28
28
|
#
|
|
29
29
|
# @return [Array<string>] array of paired view container keys
|
|
30
30
|
def call(action_class_name:, slice:)
|
|
31
|
-
action_key_base = slice.
|
|
32
|
-
view_key_base = slice.
|
|
31
|
+
action_key_base = slice.config.actions.name_inference_base
|
|
32
|
+
view_key_base = slice.config.actions.view_name_inference_base
|
|
33
33
|
|
|
34
34
|
action_name_key = action_name_key(action_class_name, slice, action_key_base)
|
|
35
35
|
|
data/lib/hanami/slice.rb
CHANGED
|
@@ -1,29 +1,68 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "dry/system/container"
|
|
4
|
-
require "hanami/errors"
|
|
5
|
-
require "pathname"
|
|
6
4
|
require_relative "constants"
|
|
5
|
+
require_relative "errors"
|
|
7
6
|
require_relative "slice_name"
|
|
7
|
+
require_relative "slice_registrar"
|
|
8
8
|
|
|
9
9
|
module Hanami
|
|
10
|
-
#
|
|
10
|
+
# A slice represents any distinct area of concern within an Hanami app.
|
|
11
|
+
#
|
|
12
|
+
# For smaller apps, a slice may encompass the whole app itself (see
|
|
13
|
+
# {Hanami::App}), whereas larger apps may consist of many slices.
|
|
14
|
+
#
|
|
15
|
+
# Each slice corresponds a single module namespace and a single root directory of source
|
|
16
|
+
# files for loading as components into its container.
|
|
17
|
+
#
|
|
18
|
+
# Each slice has its own configuration, and may optionally have its own settings,
|
|
19
|
+
# routes, as well as other nested slices.
|
|
20
|
+
#
|
|
21
|
+
# Slices expect an Hanami app to be defined (which itself is a slice). They will
|
|
22
|
+
# initialize their configuration as a copy of the app's, and will also configure
|
|
23
|
+
# certain components
|
|
24
|
+
#
|
|
25
|
+
# Slices must be _prepared_ and optionally _booted_ before they can be used (see
|
|
26
|
+
# {ClassMethods.prepare} and {ClassMethods.boot}). A prepared slice will lazily load its
|
|
27
|
+
# components and nested slices (useful for minimising initial load time), whereas a
|
|
28
|
+
# booted slice will eagerly load all its components and nested slices, then freeze its
|
|
29
|
+
# container.
|
|
11
30
|
#
|
|
12
31
|
# @since 2.0.0
|
|
13
32
|
class Slice
|
|
33
|
+
@_mutex = Mutex.new
|
|
34
|
+
|
|
14
35
|
def self.inherited(subclass)
|
|
15
36
|
super
|
|
16
37
|
|
|
17
38
|
subclass.extend(ClassMethods)
|
|
18
39
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
40
|
+
@_mutex.synchronize do
|
|
41
|
+
subclass.class_eval do
|
|
42
|
+
@_mutex = Mutex.new
|
|
43
|
+
@container = Class.new(Dry::System::Container)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
22
46
|
end
|
|
23
47
|
|
|
24
48
|
# rubocop:disable Metrics/ModuleLength
|
|
25
49
|
module ClassMethods
|
|
26
|
-
attr_reader :
|
|
50
|
+
attr_reader :parent, :container
|
|
51
|
+
|
|
52
|
+
def app
|
|
53
|
+
Hanami.app
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# A slice's configuration is copied from the app configuration at time of
|
|
57
|
+
# first access. The app should have its configuration completed before
|
|
58
|
+
# slices are loaded.
|
|
59
|
+
def configuration
|
|
60
|
+
@configuration ||= app.configuration.dup.tap do |config|
|
|
61
|
+
# Remove specific values from app that will not apply to this slice
|
|
62
|
+
config.root = nil
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
alias_method :config, :configuration
|
|
27
66
|
|
|
28
67
|
def slice_name
|
|
29
68
|
@slice_name ||= SliceName.new(self, inflector: method(:inflector))
|
|
@@ -34,25 +73,20 @@ module Hanami
|
|
|
34
73
|
end
|
|
35
74
|
|
|
36
75
|
def root
|
|
37
|
-
|
|
76
|
+
configuration.root
|
|
38
77
|
end
|
|
39
78
|
|
|
40
79
|
def inflector
|
|
41
|
-
|
|
80
|
+
configuration.inflector
|
|
42
81
|
end
|
|
43
82
|
|
|
44
83
|
def prepare(provider_name = nil)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
prepare_all
|
|
53
|
-
|
|
54
|
-
@prepared = true
|
|
55
|
-
self
|
|
84
|
+
if provider_name
|
|
85
|
+
container.prepare(provider_name)
|
|
86
|
+
self
|
|
87
|
+
else
|
|
88
|
+
prepare_slice
|
|
89
|
+
end
|
|
56
90
|
end
|
|
57
91
|
|
|
58
92
|
def prepare_container(&block)
|
|
@@ -62,7 +96,10 @@ module Hanami
|
|
|
62
96
|
def boot
|
|
63
97
|
return self if booted?
|
|
64
98
|
|
|
99
|
+
prepare
|
|
100
|
+
|
|
65
101
|
container.finalize!
|
|
102
|
+
slices.each(&:boot)
|
|
66
103
|
|
|
67
104
|
@booted = true
|
|
68
105
|
|
|
@@ -70,6 +107,7 @@ module Hanami
|
|
|
70
107
|
end
|
|
71
108
|
|
|
72
109
|
def shutdown
|
|
110
|
+
slices.each(&:shutdown)
|
|
73
111
|
container.shutdown!
|
|
74
112
|
self
|
|
75
113
|
end
|
|
@@ -82,6 +120,14 @@ module Hanami
|
|
|
82
120
|
!!@booted
|
|
83
121
|
end
|
|
84
122
|
|
|
123
|
+
def slices
|
|
124
|
+
@slices ||= SliceRegistrar.new(self)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def register_slice(...)
|
|
128
|
+
slices.register(...)
|
|
129
|
+
end
|
|
130
|
+
|
|
85
131
|
def register(...)
|
|
86
132
|
container.register(...)
|
|
87
133
|
end
|
|
@@ -118,12 +164,12 @@ module Hanami
|
|
|
118
164
|
# TODO: This should be handled via dry-system (see dry-rb/dry-system#228)
|
|
119
165
|
raise "Cannot import after booting" if booted?
|
|
120
166
|
|
|
121
|
-
|
|
167
|
+
slice = self
|
|
122
168
|
|
|
123
169
|
container.after(:configure) do
|
|
124
170
|
if from.is_a?(Symbol) || from.is_a?(String)
|
|
125
171
|
slice_name = from
|
|
126
|
-
from =
|
|
172
|
+
from = slice.parent.slices[from.to_sym].container
|
|
127
173
|
end
|
|
128
174
|
|
|
129
175
|
as = kwargs[:as] || slice_name
|
|
@@ -132,8 +178,55 @@ module Hanami
|
|
|
132
178
|
end
|
|
133
179
|
end
|
|
134
180
|
|
|
181
|
+
def settings
|
|
182
|
+
@settings ||= load_settings
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def routes
|
|
186
|
+
@routes ||= load_routes
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def router(inspector: nil)
|
|
190
|
+
raise SliceLoadError, "#{self} must be prepared before loading the router" unless prepared?
|
|
191
|
+
|
|
192
|
+
@_mutex.synchronize do
|
|
193
|
+
@_router ||= load_router(inspector: inspector)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def rack_app
|
|
198
|
+
return unless router
|
|
199
|
+
|
|
200
|
+
@rack_app ||= router.to_rack_app
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def call(...)
|
|
204
|
+
rack_app.call(...)
|
|
205
|
+
end
|
|
206
|
+
|
|
135
207
|
private
|
|
136
208
|
|
|
209
|
+
# rubocop:disable Metrics/AbcSize
|
|
210
|
+
|
|
211
|
+
def prepare_slice
|
|
212
|
+
return self if prepared?
|
|
213
|
+
|
|
214
|
+
configuration.finalize!
|
|
215
|
+
|
|
216
|
+
ensure_slice_name
|
|
217
|
+
ensure_slice_consts
|
|
218
|
+
ensure_root
|
|
219
|
+
|
|
220
|
+
prepare_all
|
|
221
|
+
|
|
222
|
+
instance_exec(container, &@prepare_container_block) if @prepare_container_block
|
|
223
|
+
container.configured!
|
|
224
|
+
|
|
225
|
+
@prepared = true
|
|
226
|
+
|
|
227
|
+
self
|
|
228
|
+
end
|
|
229
|
+
|
|
137
230
|
def ensure_slice_name
|
|
138
231
|
unless name
|
|
139
232
|
raise SliceLoadError, "Slice must have a class name before it can be prepared"
|
|
@@ -149,15 +242,25 @@ module Hanami
|
|
|
149
242
|
end
|
|
150
243
|
end
|
|
151
244
|
|
|
245
|
+
def ensure_root
|
|
246
|
+
unless configuration.root
|
|
247
|
+
raise SliceLoadError, "Slice must have a `config.root` before it can be prepared"
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
152
251
|
def prepare_all
|
|
252
|
+
# Load settings first, to fail early in case of missing/unexpected values
|
|
253
|
+
settings
|
|
254
|
+
|
|
255
|
+
prepare_container_consts
|
|
153
256
|
prepare_container_plugins
|
|
154
257
|
prepare_container_base_config
|
|
155
258
|
prepare_container_component_dirs
|
|
156
|
-
prepare_autoloader
|
|
157
259
|
prepare_container_imports
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
260
|
+
prepare_container_providers
|
|
261
|
+
prepare_autoloader
|
|
262
|
+
|
|
263
|
+
prepare_slices
|
|
161
264
|
end
|
|
162
265
|
|
|
163
266
|
def prepare_container_plugins
|
|
@@ -165,7 +268,7 @@ module Hanami
|
|
|
165
268
|
|
|
166
269
|
container.use(
|
|
167
270
|
:zeitwerk,
|
|
168
|
-
loader:
|
|
271
|
+
loader: app.autoloader,
|
|
169
272
|
run_setup: false,
|
|
170
273
|
eager_load: false
|
|
171
274
|
)
|
|
@@ -176,74 +279,144 @@ module Hanami
|
|
|
176
279
|
container.config.root = root
|
|
177
280
|
container.config.provider_dirs = [File.join("config", "providers")]
|
|
178
281
|
|
|
179
|
-
container.config.env =
|
|
180
|
-
container.config.inflector =
|
|
282
|
+
container.config.env = configuration.env
|
|
283
|
+
container.config.inflector = configuration.inflector
|
|
181
284
|
end
|
|
182
285
|
|
|
183
|
-
def prepare_container_component_dirs
|
|
184
|
-
return unless root
|
|
286
|
+
def prepare_container_component_dirs
|
|
287
|
+
return unless root.directory?
|
|
185
288
|
|
|
186
|
-
#
|
|
187
|
-
|
|
188
|
-
next unless root.join(component_dir.path).directory?
|
|
289
|
+
# Component files in both the root and `lib/` define classes in the slice's
|
|
290
|
+
# namespace
|
|
189
291
|
|
|
190
|
-
|
|
292
|
+
if root.join(LIB_DIR)&.directory?
|
|
293
|
+
container.config.component_dirs.add(LIB_DIR) do |dir|
|
|
294
|
+
dir.namespaces.add_root(key: nil, const: slice_name.name)
|
|
295
|
+
end
|
|
296
|
+
end
|
|
191
297
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
298
|
+
# When auto-registering components in the root, ignore files in `config/` (this is
|
|
299
|
+
# for framework config only), `lib/` (these will be auto-registered as above), as
|
|
300
|
+
# well as the configured no_auto_register_paths
|
|
301
|
+
no_auto_register_paths = ([LIB_DIR, CONFIG_DIR] + configuration.no_auto_register_paths)
|
|
302
|
+
.map { |path|
|
|
303
|
+
path.end_with?(File::SEPARATOR) ? path : "#{path}#{File::SEPARATOR}"
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
# TODO: Change `""` (signifying the root) once dry-rb/dry-system#238 is resolved
|
|
307
|
+
container.config.component_dirs.add("") do |dir|
|
|
308
|
+
dir.namespaces.add_root(key: nil, const: slice_name.name)
|
|
309
|
+
dir.auto_register = -> component {
|
|
310
|
+
relative_path = component.file_path.relative_path_from(root).to_s
|
|
311
|
+
!relative_path.start_with?(*no_auto_register_paths)
|
|
312
|
+
|
|
313
|
+
}
|
|
314
|
+
end
|
|
315
|
+
end
|
|
206
316
|
|
|
207
|
-
|
|
317
|
+
def prepare_container_imports
|
|
318
|
+
import(
|
|
319
|
+
keys: config.slices.shared_component_keys,
|
|
320
|
+
from: app.container,
|
|
321
|
+
as: nil
|
|
322
|
+
)
|
|
323
|
+
end
|
|
208
324
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
325
|
+
def prepare_container_providers
|
|
326
|
+
# Check here for the `routes` definition only, not `router` itself, because the
|
|
327
|
+
# `router` requires the slice to be prepared before it can be loaded, and at this
|
|
328
|
+
# point we're still in the process of preparing.
|
|
329
|
+
if routes
|
|
330
|
+
require_relative "providers/routes"
|
|
331
|
+
register_provider(:routes, source: Hanami::Providers::Routes.for_slice(self))
|
|
332
|
+
end
|
|
212
333
|
|
|
213
|
-
|
|
334
|
+
if settings
|
|
335
|
+
require_relative "providers/settings"
|
|
336
|
+
register_provider(:settings, source: Hanami::Providers::Settings.for_slice(self))
|
|
214
337
|
end
|
|
215
338
|
end
|
|
216
339
|
|
|
217
|
-
def prepare_autoloader
|
|
218
|
-
|
|
340
|
+
def prepare_autoloader
|
|
341
|
+
# Everything in the slice directory can be autoloaded _except_ `config/`, which is
|
|
342
|
+
# where we keep files loaded specially by the framework as part of slice setup.
|
|
343
|
+
if root.join(CONFIG_DIR)&.directory?
|
|
344
|
+
container.config.autoloader.ignore(root.join(CONFIG_DIR))
|
|
345
|
+
end
|
|
346
|
+
end
|
|
219
347
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
348
|
+
def prepare_container_consts
|
|
349
|
+
namespace.const_set :Container, container
|
|
350
|
+
namespace.const_set :Deps, container.injector
|
|
351
|
+
end
|
|
223
352
|
|
|
224
|
-
|
|
353
|
+
def prepare_slices
|
|
354
|
+
slices.load_slices.each(&:prepare)
|
|
355
|
+
slices.freeze
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def load_settings
|
|
359
|
+
if root.directory?
|
|
360
|
+
settings_require_path = File.join(root, SETTINGS_PATH)
|
|
225
361
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
362
|
+
begin
|
|
363
|
+
require_relative "./settings"
|
|
364
|
+
require settings_require_path
|
|
365
|
+
rescue LoadError => e
|
|
366
|
+
raise e unless e.path == settings_require_path
|
|
230
367
|
end
|
|
368
|
+
end
|
|
231
369
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
370
|
+
begin
|
|
371
|
+
settings_class = namespace.const_get(SETTINGS_CLASS_NAME)
|
|
372
|
+
settings_class.new(configuration.settings_store)
|
|
373
|
+
rescue NameError => e
|
|
374
|
+
raise e unless e.name == SETTINGS_CLASS_NAME.to_sym
|
|
236
375
|
end
|
|
237
376
|
end
|
|
238
377
|
|
|
239
|
-
def
|
|
240
|
-
|
|
378
|
+
def load_routes
|
|
379
|
+
if root.directory?
|
|
380
|
+
routes_require_path = File.join(root, ROUTES_PATH)
|
|
381
|
+
|
|
382
|
+
begin
|
|
383
|
+
require_relative "./routes"
|
|
384
|
+
require routes_require_path
|
|
385
|
+
rescue LoadError => e
|
|
386
|
+
raise e unless e.path == routes_require_path
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
begin
|
|
391
|
+
routes_class = namespace.const_get(ROUTES_CLASS_NAME)
|
|
392
|
+
routes_class.routes
|
|
393
|
+
rescue NameError => e
|
|
394
|
+
raise e unless e.name == ROUTES_CLASS_NAME.to_sym
|
|
395
|
+
end
|
|
241
396
|
end
|
|
242
397
|
|
|
243
|
-
def
|
|
244
|
-
|
|
245
|
-
|
|
398
|
+
def load_router(inspector:)
|
|
399
|
+
return unless routes
|
|
400
|
+
|
|
401
|
+
require_relative "slice/router"
|
|
402
|
+
|
|
403
|
+
config = configuration
|
|
404
|
+
rack_monitor = self["rack.monitor"]
|
|
405
|
+
|
|
406
|
+
Slice::Router.new(
|
|
407
|
+
inspector: inspector,
|
|
408
|
+
routes: routes,
|
|
409
|
+
resolver: configuration.router.resolver.new(slice: self),
|
|
410
|
+
**configuration.router.options
|
|
411
|
+
) do
|
|
412
|
+
use(rack_monitor)
|
|
413
|
+
use(*config.sessions.middleware) if config.sessions.enabled?
|
|
414
|
+
|
|
415
|
+
middleware_stack.update(config.middleware_stack)
|
|
416
|
+
end
|
|
246
417
|
end
|
|
418
|
+
|
|
419
|
+
# rubocop:enable Metrics/AbcSize
|
|
247
420
|
end
|
|
248
421
|
# rubocop:enable Metrics/ModuleLength
|
|
249
422
|
end
|
|
@@ -30,21 +30,16 @@ module Hanami
|
|
|
30
30
|
|
|
31
31
|
inherited_mod = Module.new do
|
|
32
32
|
define_method(:inherited) do |subclass|
|
|
33
|
-
unless Hanami.
|
|
34
|
-
raise ComponentLoadError, "Class #{klass} must be defined within an Hanami
|
|
33
|
+
unless Hanami.app?
|
|
34
|
+
raise ComponentLoadError, "Class #{klass} must be defined within an Hanami app"
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
super(subclass)
|
|
38
38
|
|
|
39
|
-
subclass.instance_variable_set(:@configured_for_slices, configured_for_slices.dup)
|
|
40
|
-
|
|
41
39
|
slice = slice_for.(subclass)
|
|
42
40
|
return unless slice
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
subclass.configure_for_slice(slice)
|
|
46
|
-
subclass.configured_for_slices << slice
|
|
47
|
-
end
|
|
42
|
+
subclass.configure_for_slice(slice)
|
|
48
43
|
end
|
|
49
44
|
end
|
|
50
45
|
|
|
@@ -56,20 +51,12 @@ module Hanami
|
|
|
56
51
|
def slice_for(klass)
|
|
57
52
|
return unless klass.name
|
|
58
53
|
|
|
59
|
-
slices = Hanami.
|
|
54
|
+
slices = Hanami.app.slices.to_a + [Hanami.app]
|
|
60
55
|
|
|
61
56
|
slices.detect { |slice| klass.name.include?(slice.namespace.to_s) }
|
|
62
57
|
end
|
|
63
58
|
end
|
|
64
59
|
|
|
65
60
|
def configure_for_slice(slice); end
|
|
66
|
-
|
|
67
|
-
def configured_for_slice?(slice)
|
|
68
|
-
configured_for_slices.include?(slice)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def configured_for_slices
|
|
72
|
-
@configured_for_slices ||= []
|
|
73
|
-
end
|
|
74
61
|
end
|
|
75
62
|
end
|
data/lib/hanami/slice_name.rb
CHANGED
|
@@ -3,24 +3,24 @@
|
|
|
3
3
|
require_relative "constants"
|
|
4
4
|
|
|
5
5
|
module Hanami
|
|
6
|
-
# Represents the name of an {
|
|
6
|
+
# Represents the name of an {App} or {Slice}.
|
|
7
7
|
#
|
|
8
|
-
# @see Application::ClassMethods#application_name
|
|
9
8
|
# @see Slice::ClassMethods#slice_name
|
|
9
|
+
# @see App::ClassMethods#app_name
|
|
10
10
|
#
|
|
11
11
|
# @api public
|
|
12
12
|
# @since 2.0.0
|
|
13
13
|
class SliceName
|
|
14
|
-
# Returns a new SliceName for the slice or
|
|
14
|
+
# Returns a new SliceName for the slice or app.
|
|
15
15
|
#
|
|
16
16
|
# You must provide an inflector for the manipulation of the name into various formats.
|
|
17
17
|
# This should be given in the form of a Proc that returns the inflector when called.
|
|
18
18
|
# The reason for this is that the inflector may be replaced by the user during the
|
|
19
|
-
#
|
|
19
|
+
# app configuration phase, so the proc should ensure that the current instance
|
|
20
20
|
# of the inflector is returned whenever needed.
|
|
21
21
|
#
|
|
22
|
-
# @param slice [#name] the slice or
|
|
23
|
-
# @param inflector [Proc] Proc returning the
|
|
22
|
+
# @param slice [#name] the slice or app object
|
|
23
|
+
# @param inflector [Proc] Proc returning the app's inflector when called
|
|
24
24
|
#
|
|
25
25
|
# @api private
|
|
26
26
|
def initialize(slice, inflector:)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "constants"
|
|
4
|
+
require_relative "slice"
|
|
5
|
+
|
|
6
|
+
module Hanami
|
|
7
|
+
# @api private
|
|
8
|
+
class SliceRegistrar
|
|
9
|
+
attr_reader :parent, :slices
|
|
10
|
+
private :parent, :slices
|
|
11
|
+
|
|
12
|
+
def initialize(parent)
|
|
13
|
+
@parent = parent
|
|
14
|
+
@slices = {}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def register(name, slice_class = nil, &block)
|
|
18
|
+
if slices.key?(name.to_sym)
|
|
19
|
+
raise SliceLoadError, "Slice '#{name}' is already registered"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# TODO: raise error unless name meets format (i.e. single level depth only)
|
|
23
|
+
|
|
24
|
+
slice = slice_class || build_slice(name, &block)
|
|
25
|
+
|
|
26
|
+
configure_slice(name, slice)
|
|
27
|
+
|
|
28
|
+
slices[name.to_sym] = slice
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def [](name)
|
|
32
|
+
slices.fetch(name) do
|
|
33
|
+
raise SliceLoadError, "Slice '#{name}' not found"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def freeze
|
|
38
|
+
slices.freeze
|
|
39
|
+
super
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def load_slices
|
|
43
|
+
return self unless root
|
|
44
|
+
|
|
45
|
+
slice_configs = Dir[root.join(CONFIG_DIR, SLICES_DIR, "*#{RB_EXT}")]
|
|
46
|
+
.map { |file| File.basename(file, RB_EXT) }
|
|
47
|
+
|
|
48
|
+
slice_dirs = Dir[File.join(root, SLICES_DIR, "*")]
|
|
49
|
+
.select { |path| File.directory?(path) }
|
|
50
|
+
.map { |path| File.basename(path) }
|
|
51
|
+
|
|
52
|
+
(slice_dirs + slice_configs).uniq.sort.each do |slice_name|
|
|
53
|
+
load_slice(slice_name)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
self
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def each(&block)
|
|
60
|
+
slices.each_value(&block)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def to_a
|
|
64
|
+
slices.values
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
# Runs when a slice file has been found at `config/slices/[slice_name].rb`, or a slice
|
|
70
|
+
# directory at `slices/[slice_name]`. Attempts to require the slice class, if defined,
|
|
71
|
+
# or generates a new slice class for the given slice name.
|
|
72
|
+
def load_slice(slice_name)
|
|
73
|
+
slice_const_name = inflector.camelize(slice_name)
|
|
74
|
+
slice_require_path = root.join(CONFIG_DIR, SLICES_DIR, slice_name).to_s
|
|
75
|
+
|
|
76
|
+
begin
|
|
77
|
+
require(slice_require_path)
|
|
78
|
+
rescue LoadError => e
|
|
79
|
+
raise e unless e.path == slice_require_path
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
slice_class =
|
|
83
|
+
begin
|
|
84
|
+
inflector.constantize("#{slice_const_name}::Slice")
|
|
85
|
+
rescue NameError => e
|
|
86
|
+
raise e unless e.name.to_s == slice_const_name || e.name.to_s == :Slice
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
register(slice_name, slice_class)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def build_slice(slice_name, &block)
|
|
93
|
+
slice_module =
|
|
94
|
+
begin
|
|
95
|
+
slice_module_name = inflector.camelize(slice_name.to_s)
|
|
96
|
+
inflector.constantize(slice_module_name)
|
|
97
|
+
rescue NameError
|
|
98
|
+
Object.const_set(inflector.camelize(slice_module_name), Module.new)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
slice_module.const_set(:Slice, Class.new(Hanami::Slice, &block))
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def configure_slice(slice_name, slice)
|
|
105
|
+
slice.instance_variable_set(:@parent, parent)
|
|
106
|
+
|
|
107
|
+
# Slices require a root, so provide a sensible default based on the slice's parent
|
|
108
|
+
slice.config.root ||= root.join(SLICES_DIR, slice_name.to_s)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def root
|
|
112
|
+
parent.root
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def inflector
|
|
116
|
+
parent.inflector
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
data/lib/hanami/version.rb
CHANGED