hanami 2.0.0.alpha7.1 → 2.0.0.beta1.1
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 +457 -237
- 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 +118 -0
- data/lib/hanami/configuration/actions/cookies.rb +29 -0
- data/lib/hanami/configuration/actions/sessions.rb +46 -0
- data/lib/hanami/configuration/actions.rb +23 -12
- data/lib/hanami/configuration/logger.rb +13 -10
- data/lib/hanami/configuration/router.rb +2 -6
- data/lib/hanami/configuration/sessions.rb +1 -1
- data/lib/hanami/configuration/views.rb +86 -0
- data/lib/hanami/configuration.rb +139 -82
- data/lib/hanami/constants.rb +30 -2
- data/lib/hanami/errors.rb +4 -1
- data/lib/hanami/extensions/action/slice_configured_action.rb +103 -0
- data/lib/hanami/extensions/action.rb +79 -0
- data/lib/hanami/extensions/view/context.rb +106 -0
- data/lib/hanami/extensions/view/slice_configured_context.rb +71 -0
- data/lib/hanami/extensions/view/slice_configured_view.rb +107 -0
- 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/slice/view_name_inferrer.rb +63 -0
- data/lib/hanami/slice.rb +252 -82
- data/lib/hanami/slice_configurable.rb +62 -0
- data/lib/hanami/slice_name.rb +111 -0
- 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 +363 -55
- 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.rb +0 -299
- data/lib/hanami/boot/source_dirs.rb +0 -44
- 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
data/lib/hanami/slice.rb
CHANGED
|
@@ -1,61 +1,92 @@
|
|
|
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"
|
|
6
|
+
require_relative "slice_name"
|
|
7
|
+
require_relative "slice_registrar"
|
|
7
8
|
|
|
8
9
|
module Hanami
|
|
9
|
-
#
|
|
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.
|
|
10
30
|
#
|
|
11
31
|
# @since 2.0.0
|
|
12
32
|
class Slice
|
|
13
|
-
|
|
33
|
+
@_mutex = Mutex.new
|
|
34
|
+
|
|
35
|
+
def self.inherited(subclass)
|
|
14
36
|
super
|
|
15
37
|
|
|
16
|
-
|
|
38
|
+
subclass.extend(ClassMethods)
|
|
17
39
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
40
|
+
@_mutex.synchronize do
|
|
41
|
+
subclass.class_eval do
|
|
42
|
+
@_mutex = Mutex.new
|
|
43
|
+
@container = Class.new(Dry::System::Container)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
21
46
|
end
|
|
22
47
|
|
|
23
48
|
# rubocop:disable Metrics/ModuleLength
|
|
24
49
|
module ClassMethods
|
|
25
|
-
attr_reader :
|
|
50
|
+
attr_reader :parent, :container
|
|
26
51
|
|
|
27
|
-
def
|
|
28
|
-
|
|
52
|
+
def app
|
|
53
|
+
Hanami.app
|
|
29
54
|
end
|
|
30
55
|
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
33
64
|
end
|
|
65
|
+
alias_method :config, :configuration
|
|
34
66
|
|
|
35
|
-
def
|
|
36
|
-
|
|
67
|
+
def slice_name
|
|
68
|
+
@slice_name ||= SliceName.new(self, inflector: method(:inflector))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def namespace
|
|
72
|
+
slice_name.namespace
|
|
37
73
|
end
|
|
38
74
|
|
|
39
75
|
def root
|
|
40
|
-
|
|
76
|
+
configuration.root
|
|
41
77
|
end
|
|
42
78
|
|
|
43
79
|
def inflector
|
|
44
|
-
|
|
80
|
+
configuration.inflector
|
|
45
81
|
end
|
|
46
82
|
|
|
47
83
|
def prepare(provider_name = nil)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
prepare_all
|
|
56
|
-
|
|
57
|
-
@prepared = true
|
|
58
|
-
self
|
|
84
|
+
if provider_name
|
|
85
|
+
container.prepare(provider_name)
|
|
86
|
+
self
|
|
87
|
+
else
|
|
88
|
+
prepare_slice
|
|
89
|
+
end
|
|
59
90
|
end
|
|
60
91
|
|
|
61
92
|
def prepare_container(&block)
|
|
@@ -65,7 +96,10 @@ module Hanami
|
|
|
65
96
|
def boot
|
|
66
97
|
return self if booted?
|
|
67
98
|
|
|
99
|
+
prepare
|
|
100
|
+
|
|
68
101
|
container.finalize!
|
|
102
|
+
slices.each(&:boot)
|
|
69
103
|
|
|
70
104
|
@booted = true
|
|
71
105
|
|
|
@@ -73,6 +107,7 @@ module Hanami
|
|
|
73
107
|
end
|
|
74
108
|
|
|
75
109
|
def shutdown
|
|
110
|
+
slices.each(&:shutdown)
|
|
76
111
|
container.shutdown!
|
|
77
112
|
self
|
|
78
113
|
end
|
|
@@ -85,6 +120,14 @@ module Hanami
|
|
|
85
120
|
!!@booted
|
|
86
121
|
end
|
|
87
122
|
|
|
123
|
+
def slices
|
|
124
|
+
@slices ||= SliceRegistrar.new(self)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def register_slice(...)
|
|
128
|
+
slices.register(...)
|
|
129
|
+
end
|
|
130
|
+
|
|
88
131
|
def register(...)
|
|
89
132
|
container.register(...)
|
|
90
133
|
end
|
|
@@ -121,12 +164,12 @@ module Hanami
|
|
|
121
164
|
# TODO: This should be handled via dry-system (see dry-rb/dry-system#228)
|
|
122
165
|
raise "Cannot import after booting" if booted?
|
|
123
166
|
|
|
124
|
-
|
|
167
|
+
slice = self
|
|
125
168
|
|
|
126
169
|
container.after(:configure) do
|
|
127
170
|
if from.is_a?(Symbol) || from.is_a?(String)
|
|
128
171
|
slice_name = from
|
|
129
|
-
from =
|
|
172
|
+
from = slice.parent.slices[from.to_sym].container
|
|
130
173
|
end
|
|
131
174
|
|
|
132
175
|
as = kwargs[:as] || slice_name
|
|
@@ -135,8 +178,55 @@ module Hanami
|
|
|
135
178
|
end
|
|
136
179
|
end
|
|
137
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
|
+
|
|
138
207
|
private
|
|
139
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
|
+
|
|
140
230
|
def ensure_slice_name
|
|
141
231
|
unless name
|
|
142
232
|
raise SliceLoadError, "Slice must have a class name before it can be prepared"
|
|
@@ -152,15 +242,25 @@ module Hanami
|
|
|
152
242
|
end
|
|
153
243
|
end
|
|
154
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
|
+
|
|
155
251
|
def prepare_all
|
|
252
|
+
# Load settings first, to fail early in case of missing/unexpected values
|
|
253
|
+
settings
|
|
254
|
+
|
|
255
|
+
prepare_container_consts
|
|
156
256
|
prepare_container_plugins
|
|
157
257
|
prepare_container_base_config
|
|
158
258
|
prepare_container_component_dirs
|
|
159
|
-
prepare_autoloader
|
|
160
259
|
prepare_container_imports
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
260
|
+
prepare_container_providers
|
|
261
|
+
prepare_autoloader
|
|
262
|
+
|
|
263
|
+
prepare_slices
|
|
164
264
|
end
|
|
165
265
|
|
|
166
266
|
def prepare_container_plugins
|
|
@@ -168,85 +268,155 @@ module Hanami
|
|
|
168
268
|
|
|
169
269
|
container.use(
|
|
170
270
|
:zeitwerk,
|
|
171
|
-
loader:
|
|
271
|
+
loader: app.autoloader,
|
|
172
272
|
run_setup: false,
|
|
173
273
|
eager_load: false
|
|
174
274
|
)
|
|
175
275
|
end
|
|
176
276
|
|
|
177
277
|
def prepare_container_base_config
|
|
178
|
-
container.config.name = slice_name
|
|
278
|
+
container.config.name = slice_name.to_sym
|
|
179
279
|
container.config.root = root
|
|
180
280
|
container.config.provider_dirs = [File.join("config", "providers")]
|
|
181
281
|
|
|
182
|
-
container.config.env =
|
|
183
|
-
container.config.inflector =
|
|
282
|
+
container.config.env = configuration.env
|
|
283
|
+
container.config.inflector = configuration.inflector
|
|
184
284
|
end
|
|
185
285
|
|
|
186
|
-
def prepare_container_component_dirs
|
|
187
|
-
return unless root
|
|
286
|
+
def prepare_container_component_dirs
|
|
287
|
+
return unless root.directory?
|
|
188
288
|
|
|
189
|
-
#
|
|
190
|
-
|
|
191
|
-
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
|
|
192
291
|
|
|
193
|
-
|
|
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
|
|
194
297
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
|
209
316
|
|
|
210
|
-
|
|
317
|
+
def prepare_container_imports
|
|
318
|
+
import(
|
|
319
|
+
keys: config.slices.shared_component_keys,
|
|
320
|
+
from: app.container,
|
|
321
|
+
as: nil
|
|
322
|
+
)
|
|
323
|
+
end
|
|
211
324
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
|
215
333
|
|
|
216
|
-
|
|
334
|
+
if settings
|
|
335
|
+
require_relative "providers/settings"
|
|
336
|
+
register_provider(:settings, source: Hanami::Providers::Settings.for_slice(self))
|
|
217
337
|
end
|
|
218
338
|
end
|
|
219
339
|
|
|
220
|
-
def prepare_autoloader
|
|
221
|
-
|
|
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
|
|
222
347
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
348
|
+
def prepare_container_consts
|
|
349
|
+
namespace.const_set :Container, container
|
|
350
|
+
namespace.const_set :Deps, container.injector
|
|
351
|
+
end
|
|
226
352
|
|
|
227
|
-
|
|
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)
|
|
228
361
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
362
|
+
begin
|
|
363
|
+
require_relative "./settings"
|
|
364
|
+
require settings_require_path
|
|
365
|
+
rescue LoadError => e
|
|
366
|
+
raise e unless e.path == settings_require_path
|
|
233
367
|
end
|
|
368
|
+
end
|
|
234
369
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
|
239
375
|
end
|
|
240
376
|
end
|
|
241
377
|
|
|
242
|
-
def
|
|
243
|
-
|
|
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
|
|
244
396
|
end
|
|
245
397
|
|
|
246
|
-
def
|
|
247
|
-
|
|
248
|
-
|
|
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
|
|
249
417
|
end
|
|
418
|
+
|
|
419
|
+
# rubocop:enable Metrics/AbcSize
|
|
250
420
|
end
|
|
251
421
|
# rubocop:enable Metrics/ModuleLength
|
|
252
422
|
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "errors"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
# Calls `configure_for_slice(slice)` on the extended class whenever it is first
|
|
7
|
+
# subclassed within a module namespace corresponding to a slice.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# class BaseClass
|
|
11
|
+
# extend Hanami::SliceConfigurable
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# # slices/main/lib/my_class.rb
|
|
15
|
+
# module Main
|
|
16
|
+
# class MyClass < BaseClass
|
|
17
|
+
# # Will be called with `Main::Slice`
|
|
18
|
+
# def self.configure_for_slice(slice)
|
|
19
|
+
# # ...
|
|
20
|
+
# end
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# @api private
|
|
25
|
+
# @since 2.0.0
|
|
26
|
+
module SliceConfigurable
|
|
27
|
+
class << self
|
|
28
|
+
def extended(klass)
|
|
29
|
+
slice_for = method(:slice_for)
|
|
30
|
+
|
|
31
|
+
inherited_mod = Module.new do
|
|
32
|
+
define_method(:inherited) do |subclass|
|
|
33
|
+
unless Hanami.app?
|
|
34
|
+
raise ComponentLoadError, "Class #{klass} must be defined within an Hanami app"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
super(subclass)
|
|
38
|
+
|
|
39
|
+
slice = slice_for.(subclass)
|
|
40
|
+
return unless slice
|
|
41
|
+
|
|
42
|
+
subclass.configure_for_slice(slice)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
klass.singleton_class.prepend(inherited_mod)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def slice_for(klass)
|
|
52
|
+
return unless klass.name
|
|
53
|
+
|
|
54
|
+
slices = Hanami.app.slices.to_a + [Hanami.app]
|
|
55
|
+
|
|
56
|
+
slices.detect { |slice| klass.name.include?(slice.namespace.to_s) }
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def configure_for_slice(slice); end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "constants"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
# Represents the name of an {App} or {Slice}.
|
|
7
|
+
#
|
|
8
|
+
# @see Slice::ClassMethods#slice_name
|
|
9
|
+
# @see App::ClassMethods#app_name
|
|
10
|
+
#
|
|
11
|
+
# @api public
|
|
12
|
+
# @since 2.0.0
|
|
13
|
+
class SliceName
|
|
14
|
+
# Returns a new SliceName for the slice or app.
|
|
15
|
+
#
|
|
16
|
+
# You must provide an inflector for the manipulation of the name into various formats.
|
|
17
|
+
# This should be given in the form of a Proc that returns the inflector when called.
|
|
18
|
+
# The reason for this is that the inflector may be replaced by the user during the
|
|
19
|
+
# app configuration phase, so the proc should ensure that the current instance
|
|
20
|
+
# of the inflector is returned whenever needed.
|
|
21
|
+
#
|
|
22
|
+
# @param slice [#name] the slice or app object
|
|
23
|
+
# @param inflector [Proc] Proc returning the app's inflector when called
|
|
24
|
+
#
|
|
25
|
+
# @api private
|
|
26
|
+
def initialize(slice, inflector:)
|
|
27
|
+
@slice = slice
|
|
28
|
+
@inflector = inflector
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Returns the name of the slice as a downcased, underscored string.
|
|
32
|
+
#
|
|
33
|
+
# This is considered the canonical name of the slice.
|
|
34
|
+
#
|
|
35
|
+
# @example
|
|
36
|
+
# slice_name.name # => "main"
|
|
37
|
+
#
|
|
38
|
+
# @return [String] the slice name
|
|
39
|
+
#
|
|
40
|
+
# @api public
|
|
41
|
+
# @since 2.0.0
|
|
42
|
+
def name
|
|
43
|
+
inflector.underscore(namespace_name)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @api public
|
|
47
|
+
# @since 2.0.0
|
|
48
|
+
alias_method :path, :name
|
|
49
|
+
|
|
50
|
+
# Returns the name of the slice's module namespace.
|
|
51
|
+
#
|
|
52
|
+
# @example
|
|
53
|
+
# slice_name.namespace_name # => "Main"
|
|
54
|
+
#
|
|
55
|
+
# @return [String] the namespace name
|
|
56
|
+
#
|
|
57
|
+
# @api public
|
|
58
|
+
# @since 2.0.0
|
|
59
|
+
def namespace_name
|
|
60
|
+
slice_name.split(MODULE_DELIMITER)[0..-2].join(MODULE_DELIMITER)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Returns the constant for the slice's module namespace.
|
|
64
|
+
#
|
|
65
|
+
# @example
|
|
66
|
+
# slice_name.namespace_const # => Main
|
|
67
|
+
#
|
|
68
|
+
# @return [Module] the namespace module constant
|
|
69
|
+
#
|
|
70
|
+
# @api public
|
|
71
|
+
# @since 2.0.0
|
|
72
|
+
def namespace_const
|
|
73
|
+
inflector.constantize(namespace_name)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @api public
|
|
77
|
+
# @since 2.0.0
|
|
78
|
+
alias_method :namespace, :namespace_const
|
|
79
|
+
|
|
80
|
+
# @api public
|
|
81
|
+
# @since 2.0.0
|
|
82
|
+
alias_method :to_s, :name
|
|
83
|
+
|
|
84
|
+
# Returns the name of a slice as a downcased, underscored symbol.
|
|
85
|
+
#
|
|
86
|
+
# @example
|
|
87
|
+
# slice_name.name # => :main
|
|
88
|
+
#
|
|
89
|
+
# @return [Symbol] the slice name
|
|
90
|
+
#
|
|
91
|
+
# @see name, to_s
|
|
92
|
+
#
|
|
93
|
+
# @api public
|
|
94
|
+
# @since 2.0.0
|
|
95
|
+
def to_sym
|
|
96
|
+
name.to_sym
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
def slice_name
|
|
102
|
+
@slice.name
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# The inflector is callable to allow for it to be configured/replaced after this
|
|
106
|
+
# object has been initialized
|
|
107
|
+
def inflector
|
|
108
|
+
@inflector.()
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|