piko-lite-box 0.0.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.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/hanami-3.0.1/CHANGELOG.md +1701 -0
  3. data/hanami-3.0.1/LICENSE +20 -0
  4. data/hanami-3.0.1/README.md +89 -0
  5. data/hanami-3.0.1/hanami.gemspec +49 -0
  6. data/hanami-3.0.1/lib/hanami/app.rb +197 -0
  7. data/hanami-3.0.1/lib/hanami/boot.rb +5 -0
  8. data/hanami-3.0.1/lib/hanami/config/actions/content_security_policy.rb +140 -0
  9. data/hanami-3.0.1/lib/hanami/config/actions/cookies.rb +57 -0
  10. data/hanami-3.0.1/lib/hanami/config/actions/sessions.rb +83 -0
  11. data/hanami-3.0.1/lib/hanami/config/actions.rb +189 -0
  12. data/hanami-3.0.1/lib/hanami/config/assets.rb +84 -0
  13. data/hanami-3.0.1/lib/hanami/config/console.rb +79 -0
  14. data/hanami-3.0.1/lib/hanami/config/db.rb +35 -0
  15. data/hanami-3.0.1/lib/hanami/config/i18n.rb +138 -0
  16. data/hanami-3.0.1/lib/hanami/config/logger.rb +207 -0
  17. data/hanami-3.0.1/lib/hanami/config/null_config.rb +17 -0
  18. data/hanami-3.0.1/lib/hanami/config/router.rb +48 -0
  19. data/hanami-3.0.1/lib/hanami/config/views.rb +103 -0
  20. data/hanami-3.0.1/lib/hanami/config.rb +564 -0
  21. data/hanami-3.0.1/lib/hanami/constants.rb +62 -0
  22. data/hanami-3.0.1/lib/hanami/env.rb +52 -0
  23. data/hanami-3.0.1/lib/hanami/errors.rb +41 -0
  24. data/hanami-3.0.1/lib/hanami/extensions/action/i18n_helper.rb +64 -0
  25. data/hanami-3.0.1/lib/hanami/extensions/action/name_inferrer.rb +34 -0
  26. data/hanami-3.0.1/lib/hanami/extensions/action/slice_configured_action.rb +190 -0
  27. data/hanami-3.0.1/lib/hanami/extensions/action.rb +147 -0
  28. data/hanami-3.0.1/lib/hanami/extensions/db/repo.rb +108 -0
  29. data/hanami-3.0.1/lib/hanami/extensions/mailer/slice_configured_mailer.rb +134 -0
  30. data/hanami-3.0.1/lib/hanami/extensions/mailer.rb +28 -0
  31. data/hanami-3.0.1/lib/hanami/extensions/operation/slice_configured_db_operation.rb +90 -0
  32. data/hanami-3.0.1/lib/hanami/extensions/operation.rb +33 -0
  33. data/hanami-3.0.1/lib/hanami/extensions/router/errors.rb +58 -0
  34. data/hanami-3.0.1/lib/hanami/extensions/view/context.rb +298 -0
  35. data/hanami-3.0.1/lib/hanami/extensions/view/part.rb +92 -0
  36. data/hanami-3.0.1/lib/hanami/extensions/view/scope.rb +36 -0
  37. data/hanami-3.0.1/lib/hanami/extensions/view/slice_configured_context.rb +73 -0
  38. data/hanami-3.0.1/lib/hanami/extensions/view/slice_configured_helpers.rb +55 -0
  39. data/hanami-3.0.1/lib/hanami/extensions/view/slice_configured_part.rb +73 -0
  40. data/hanami-3.0.1/lib/hanami/extensions/view/slice_configured_view.rb +243 -0
  41. data/hanami-3.0.1/lib/hanami/extensions/view/standard_helpers.rb +26 -0
  42. data/hanami-3.0.1/lib/hanami/extensions/view.rb +38 -0
  43. data/hanami-3.0.1/lib/hanami/extensions.rb +30 -0
  44. data/hanami-3.0.1/lib/hanami/helpers/assets_helper.rb +804 -0
  45. data/hanami-3.0.1/lib/hanami/helpers/form_helper/form_builder.rb +1389 -0
  46. data/hanami-3.0.1/lib/hanami/helpers/form_helper/values.rb +75 -0
  47. data/hanami-3.0.1/lib/hanami/helpers/form_helper.rb +213 -0
  48. data/hanami-3.0.1/lib/hanami/helpers/i18n_helper.rb +241 -0
  49. data/hanami-3.0.1/lib/hanami/logger/rack_formatter.rb +73 -0
  50. data/hanami-3.0.1/lib/hanami/logger/sql_formatter.rb +80 -0
  51. data/hanami-3.0.1/lib/hanami/logger/sql_logger.rb +48 -0
  52. data/hanami-3.0.1/lib/hanami/middleware/assets.rb +21 -0
  53. data/hanami-3.0.1/lib/hanami/middleware/content_security_policy_nonce.rb +53 -0
  54. data/hanami-3.0.1/lib/hanami/middleware/public_errors_app.rb +75 -0
  55. data/hanami-3.0.1/lib/hanami/middleware/render_errors.rb +90 -0
  56. data/hanami-3.0.1/lib/hanami/port.rb +45 -0
  57. data/hanami-3.0.1/lib/hanami/prepare.rb +5 -0
  58. data/hanami-3.0.1/lib/hanami/provider/source.rb +16 -0
  59. data/hanami-3.0.1/lib/hanami/provider_registrar.rb +28 -0
  60. data/hanami-3.0.1/lib/hanami/providers/assets.rb +28 -0
  61. data/hanami-3.0.1/lib/hanami/providers/db/adapter.rb +75 -0
  62. data/hanami-3.0.1/lib/hanami/providers/db/adapters.rb +66 -0
  63. data/hanami-3.0.1/lib/hanami/providers/db/config.rb +44 -0
  64. data/hanami-3.0.1/lib/hanami/providers/db/gateway.rb +87 -0
  65. data/hanami-3.0.1/lib/hanami/providers/db/sql_adapter.rb +100 -0
  66. data/hanami-3.0.1/lib/hanami/providers/db.rb +325 -0
  67. data/hanami-3.0.1/lib/hanami/providers/db_logging.rb +19 -0
  68. data/hanami-3.0.1/lib/hanami/providers/i18n/backend.rb +373 -0
  69. data/hanami-3.0.1/lib/hanami/providers/i18n/locale/en.yml +57 -0
  70. data/hanami-3.0.1/lib/hanami/providers/i18n.rb +114 -0
  71. data/hanami-3.0.1/lib/hanami/providers/inflector.rb +17 -0
  72. data/hanami-3.0.1/lib/hanami/providers/logger.rb +41 -0
  73. data/hanami-3.0.1/lib/hanami/providers/mailers.rb +101 -0
  74. data/hanami-3.0.1/lib/hanami/providers/rack.rb +47 -0
  75. data/hanami-3.0.1/lib/hanami/providers/relations.rb +31 -0
  76. data/hanami-3.0.1/lib/hanami/providers/routes.rb +29 -0
  77. data/hanami-3.0.1/lib/hanami/rake_tasks.rb +54 -0
  78. data/hanami-3.0.1/lib/hanami/routes.rb +146 -0
  79. data/hanami-3.0.1/lib/hanami/settings/composite_store.rb +53 -0
  80. data/hanami-3.0.1/lib/hanami/settings/env_store.rb +32 -0
  81. data/hanami-3.0.1/lib/hanami/settings.rb +234 -0
  82. data/hanami-3.0.1/lib/hanami/setup.rb +6 -0
  83. data/hanami-3.0.1/lib/hanami/slice/router.rb +280 -0
  84. data/hanami-3.0.1/lib/hanami/slice/routes_helper.rb +37 -0
  85. data/hanami-3.0.1/lib/hanami/slice/routing/middleware/stack.rb +216 -0
  86. data/hanami-3.0.1/lib/hanami/slice/routing/resolver.rb +88 -0
  87. data/hanami-3.0.1/lib/hanami/slice/view_name_inferrer.rb +63 -0
  88. data/hanami-3.0.1/lib/hanami/slice.rb +1239 -0
  89. data/hanami-3.0.1/lib/hanami/slice_configurable.rb +72 -0
  90. data/hanami-3.0.1/lib/hanami/slice_name.rb +111 -0
  91. data/hanami-3.0.1/lib/hanami/slice_registrar.rb +215 -0
  92. data/hanami-3.0.1/lib/hanami/universal_logger.rb +250 -0
  93. data/hanami-3.0.1/lib/hanami/version.rb +45 -0
  94. data/hanami-3.0.1/lib/hanami/web/rack_logger.rb +126 -0
  95. data/hanami-3.0.1/lib/hanami/web/welcome.html.erb +584 -0
  96. data/hanami-3.0.1/lib/hanami/web/welcome.rb +46 -0
  97. data/hanami-3.0.1/lib/hanami.rb +276 -0
  98. data/piko-lite-box.gemspec +11 -0
  99. metadata +137 -0
@@ -0,0 +1,1239 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zeitwerk"
4
+ require "dry/system"
5
+
6
+ require_relative "constants"
7
+ require_relative "errors"
8
+
9
+ module Hanami
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 config, and may optionally have its own settings, routes, as well as
19
+ # other nested slices.
20
+ #
21
+ # Slices expect an Hanami app to be defined (which itself is a slice). They will initialize their
22
+ # config as a copy of the app's, and will also configure certain components
23
+ #
24
+ # Slices must be _prepared_ and optionally _booted_ before they can be used (see
25
+ # {ClassMethods.prepare} and {ClassMethods.boot}). A prepared slice will lazily load its
26
+ # components and nested slices (useful for minimising initial load time), whereas a
27
+ # booted slice will eagerly load all its components and nested slices, then freeze its
28
+ # container.
29
+ #
30
+ # @since 2.0.0
31
+ class Slice
32
+ @_mutex = Mutex.new
33
+
34
+ # @api private
35
+ def self.inherited(subclass)
36
+ super
37
+
38
+ subclass.extend(ClassMethods)
39
+
40
+ @_mutex.synchronize do
41
+ subclass.class_eval do
42
+ @_mutex = Mutex.new
43
+ @autoloader = Zeitwerk::Loader.new
44
+ @container = Class.new(Dry::System::Container)
45
+ end
46
+ end
47
+ end
48
+
49
+ module ClassMethods
50
+ # Returns the slice's parent.
51
+ #
52
+ # For top-level slices defined in `slices/` or `config/slices/`, this will be the Hanami app
53
+ # itself (`Hanami.app`). For nested slices, this will be the slice in which they were
54
+ # registered.
55
+ #
56
+ # @return [Hanami::Slice]
57
+ #
58
+ # @see #register_slice
59
+ #
60
+ # @api public
61
+ # @since 2.0.0
62
+ attr_reader :parent
63
+
64
+ # Returns the slice's autoloader.
65
+ #
66
+ # Each slice has its own `Zeitwerk::Loader` autoloader instance, which is setup when the slice
67
+ # is {#prepare prepared}.
68
+ #
69
+ # @return [Zeitwerk::Loader]
70
+ #
71
+ # @see https://github.com/fxn/zeitwerk
72
+ #
73
+ # @api public
74
+ # @since 2.0.0
75
+ attr_reader :autoloader
76
+
77
+ # Returns the slice's container.
78
+ #
79
+ # This is a `Dry::System::Container` that is already configured for the slice.
80
+ #
81
+ # In ordinary usage, you shouldn't need direct access the container at all, since the slice
82
+ # provides its own methods for interacting with the container (such as {#[]}, {#keys}, {#key?}
83
+ # {#register}, {#register_provider}, {#prepare}, {#start}, {#stop}).
84
+ #
85
+ # If you need to configure the container directly, use {#prepare_container}.
86
+ #
87
+ # @see https://dry-rb.org/gems/dry-system
88
+ #
89
+ # @api public
90
+ # @since 2.0.0
91
+ attr_reader :container
92
+
93
+ # Returns the Hanami app.
94
+ #
95
+ # @return [Hanami::App]
96
+ #
97
+ # @api public
98
+ # @since 2.0.0
99
+ def app
100
+ Hanami.app
101
+ end
102
+
103
+ # Returns true if the slice is Hanami.app
104
+ #
105
+ # @return [Boolean]
106
+ #
107
+ # @api public
108
+ # @since 2.2.0
109
+ def app?
110
+ eql?(app)
111
+ end
112
+
113
+ # Returns the slice's config.
114
+ #
115
+ # A slice's config is copied from the app config at time of first access.
116
+ #
117
+ # @return [Hanami::Config]
118
+ #
119
+ # @see App::ClassMethods.config
120
+ #
121
+ # @api public
122
+ # @since 2.0.0
123
+ def config
124
+ @config ||= app.config.dup.tap do |slice_config|
125
+ # Unset config from app that does not apply to ordinary slices
126
+ slice_config.root = nil
127
+ end
128
+ end
129
+
130
+ # Evaluates the block for a given app environment only.
131
+ #
132
+ # If the given `env_name` matches {Hanami.env}, then the block will be evaluated in the
133
+ # context of `self` (the slice) via `instance_eval`. The slice is also passed as the block's
134
+ # optional argument.
135
+ #
136
+ # If the env does not match, then the block is not evaluated at all.
137
+ #
138
+ # @example
139
+ # module MySlice
140
+ # class Slice < Hanami::Slice
141
+ # environment(:test) do
142
+ # config.logger.level = :info
143
+ # end
144
+ # end
145
+ # end
146
+ #
147
+ # @overload environment(env_name)
148
+ # @param env_name [Symbol] the environment name
149
+ #
150
+ # @overload environment(env_name)
151
+ # @param env_name [Symbol] the environment name
152
+ # @yieldparam slice [self] the slice
153
+ #
154
+ # @return [self]
155
+ #
156
+ # @see Hanami.env
157
+ #
158
+ # @api public
159
+ # @since 2.0.0
160
+ def environment(env_name, &block)
161
+ instance_eval(&block) if env_name == config.env
162
+ self
163
+ end
164
+
165
+ # Returns a {SliceName} for the slice, an object with methods returning the name of the slice
166
+ # in various formats.
167
+ #
168
+ # @return [SliceName]
169
+ #
170
+ # @api public
171
+ # @since 2.0.0
172
+ def slice_name
173
+ @slice_name ||= SliceName.new(self, inflector: method(:inflector))
174
+ end
175
+
176
+ # Returns the constant for the slice's module namespace.
177
+ #
178
+ # @example
179
+ # MySlice::Slice.namespace # => MySlice
180
+ #
181
+ # @return [Module] the namespace module constant
182
+ #
183
+ # @see SliceName#namespace
184
+ #
185
+ # @api public
186
+ # @since 2.0.0
187
+ def namespace
188
+ slice_name.namespace
189
+ end
190
+
191
+ # Returns the slice's root, either the root as explicitly configured, or a default fallback of
192
+ # the slice's name within the app's `slices/` dir.
193
+ #
194
+ # @return [Pathname]
195
+ #
196
+ # @see Config#root
197
+ #
198
+ # @api public
199
+ # @since 2.0.0
200
+ def root
201
+ # Provides a best guess for a root when it is not yet configured.
202
+ #
203
+ # This is particularly useful for user-defined slice classes that access `settings` inside
204
+ # the class body (since the root needed to find the settings file). In this case,
205
+ # `configuration.root` may be nil when `settings` is called, since the root is configured by
206
+ # `SliceRegistrar#configure_slice` _after_ the class is loaded.
207
+ #
208
+ # In common cases, this best guess will be correct since most Hanami slices will be expected
209
+ # to live in the app SLICES_DIR. For advanced cases, the correct slice root should be
210
+ # explicitly configured at the beginning of the slice class body, before any calls to
211
+ # `settings`.
212
+ config.root || app.root.join(SLICES_DIR, slice_name.to_s)
213
+ end
214
+
215
+ # Returns the slice's root component directory, accounting for App as a special case.
216
+ #
217
+ # @return [Pathname]
218
+ #
219
+ # @api public
220
+ # @since 2.2.0
221
+ def source_path
222
+ app? ? root.join(APP_DIR) : root
223
+ end
224
+
225
+ # Returns the slice's root component directory, as a path relative to the app's root.
226
+ #
227
+ # @return [Pathname]
228
+ #
229
+ # @see #source_path
230
+ #
231
+ # @api public
232
+ # @since 2.3.0
233
+ def relative_source_path
234
+ source_path.relative_path_from(app.root)
235
+ end
236
+
237
+ # Returns the slice's configured inflector.
238
+ #
239
+ # Unless explicitly re-configured for the slice, this will be the app's inflector.
240
+ #
241
+ # @return [Dry::Inflector]
242
+ #
243
+ # @see Config#inflector
244
+ # @see Config#inflections
245
+ #
246
+ # @api public
247
+ # @since 2.0.0
248
+ def inflector
249
+ config.inflector
250
+ end
251
+
252
+ # @overload prepare
253
+ # Prepares the slice.
254
+ #
255
+ # This will define the slice's `Slice` and `Deps` constants, make all Ruby source files
256
+ # inside the slice's root dir autoloadable, as well as lazily loadable as container
257
+ # components.
258
+ #
259
+ # Call `prepare` when you want to access particular components within the slice while still
260
+ # minimizing load time. Preparing slices is the approach taken when loading the Hanami
261
+ # console or when running tests.
262
+ #
263
+ # @return [self]
264
+ #
265
+ # @see #boot
266
+ #
267
+ # @api public
268
+ # @since 2.0.0
269
+ #
270
+ # @overload prepare(provider_name)
271
+ # Prepares a provider.
272
+ #
273
+ # This triggers the provider's `prepare` lifecycle step.
274
+ #
275
+ # @param provider_name [Symbol] the name of the provider to start
276
+ #
277
+ # @return [self]
278
+ #
279
+ # @api public
280
+ # @since 2.0.0
281
+ def prepare(provider_name = nil)
282
+ if provider_name
283
+ container.prepare(provider_name)
284
+ else
285
+ prepare_slice
286
+ end
287
+
288
+ self
289
+ end
290
+
291
+ # Captures the given block to be called with the slice's container during the slice's
292
+ # `prepare` step, after the slice has already configured the container.
293
+ #
294
+ # This is intended for advanced usage only and should not be needed for ordinary slice
295
+ # configuration and usage.
296
+ #
297
+ # @example
298
+ # module MySlice
299
+ # class Slice < Hanami::Slice
300
+ # prepare_container do |container|
301
+ # # ...
302
+ # end
303
+ # end
304
+ # end
305
+ #
306
+ # @yieldparam container [Dry::System::Container] the slice's container
307
+ #
308
+ # @return [self]
309
+ #
310
+ # @see #prepare
311
+ #
312
+ # @api public
313
+ # @since 2.0.0
314
+ def prepare_container(&block)
315
+ @prepare_container_block = block
316
+ self
317
+ end
318
+
319
+ # Boots the slice.
320
+ #
321
+ # This will prepare the slice (if not already prepared), start each of its providers, register
322
+ # all the slice's components from its Ruby source files, and import components from any other
323
+ # slices. It will also boot any of the slice's own registered nested slices. It will then
324
+ # freeze its container so no further components can be registered.
325
+ #
326
+ # Call `boot` if you want to fully load a slice and incur all load time up front, such as when
327
+ # preparing an app to serve web requests. Booting slices is the approach taken when running
328
+ # Hanami's standard Puma setup (see `config.ru`).
329
+ #
330
+ # @return [self]
331
+ #
332
+ # @see #prepare
333
+ #
334
+ # @api public
335
+ # @since 2.0.0
336
+ def boot
337
+ return self if booted?
338
+
339
+ prepare
340
+
341
+ container.finalize!
342
+ slices.each(&:boot)
343
+
344
+ @booted = true
345
+
346
+ self
347
+ end
348
+
349
+ # Shuts down the slice's providers, as well as the providers in any nested slices.
350
+ #
351
+ # @return [self]
352
+ #
353
+ # @api public
354
+ # @since 2.0.0
355
+ def shutdown
356
+ slices.each(&:shutdown)
357
+ container.shutdown!
358
+ self
359
+ end
360
+
361
+ # Returns true if the slice has been prepared.
362
+ #
363
+ # @return [Boolean]
364
+ #
365
+ # @see #prepare
366
+ #
367
+ # @api public
368
+ # @since 2.0.0
369
+ def prepared?
370
+ !!@prepared
371
+ end
372
+
373
+ # Returns true if the slice has been booted.
374
+ #
375
+ # @return [Boolean]
376
+ #
377
+ # @see #boot
378
+ #
379
+ # @api public
380
+ # @since 2.0.0
381
+ def booted?
382
+ !!@booted
383
+ end
384
+
385
+ # Returns the slice's collection of nested slices.
386
+ #
387
+ # @return [SliceRegistrar]
388
+ #
389
+ # @see #register_slice
390
+ #
391
+ # @api public
392
+ # @since 2.0.0
393
+ def slices
394
+ @slices ||= SliceRegistrar.new(self)
395
+ end
396
+
397
+ # Returns an array of this slice and all of its nested slices.
398
+ #
399
+ # The nested slices are returned first (with their more specific namespaces ahead of their
400
+ # parents'), and this slice is returned last. This ordering means a class can be matched to
401
+ # its most specific slice by detecting the first slice whose namespace it belongs to. It is
402
+ # useful when you need to operate across every container, such as in test support code.
403
+ #
404
+ # @example
405
+ # Hanami.app.with_slices # => [Main::Nested::Slice, Main::Slice, Admin::Slice, MyApp::App]
406
+ #
407
+ # @return [Array<Hanami::Slice>] this slice and all of its (nested) slices, this slice last
408
+ #
409
+ # @see #slices
410
+ #
411
+ # @api public
412
+ # @since 3.0.0
413
+ def with_slices
414
+ slices.with_nested + [self]
415
+ end
416
+
417
+ # @overload register_slice(name, &block)
418
+ # Registers a nested slice with the given name.
419
+ #
420
+ # This will define a new {Slice} subclass for the slice. If a block is given, it is passed
421
+ # the class object, and will be evaluated in the context of the class like `class_eval`.
422
+ #
423
+ # @example
424
+ # MySlice::Slice.register_slice do
425
+ # # Configure the slice or do other class-level things here
426
+ # end
427
+ #
428
+ # @param name [Symbol] the identifier for the slice to be registered
429
+ # @yieldparam slice [Hanami::Slice] the newly defined slice class
430
+ #
431
+ # @overload register_slice(name, slice_class)
432
+ # Registers a nested slice with the given name.
433
+ #
434
+ # The given `slice_class` will be registered as the slice. It must be a subclass of {Slice}.
435
+ #
436
+ # @param name [Symbol] the identifier for the slice to be registered
437
+ # @param slice_class [Hanami::Slice]
438
+ #
439
+ # @return [slices]
440
+ #
441
+ # @see SliceRegistrar#register
442
+ #
443
+ # @api public
444
+ # @since 2.0.0
445
+ def register_slice(...)
446
+ slices.register(...)
447
+ end
448
+
449
+ # Registers a component in the slice's container.
450
+ #
451
+ # @overload register(key, object)
452
+ # Registers the given object as the component. This same object will be returned whenever
453
+ # the component is resolved.
454
+ #
455
+ # @param key [String] the component's key
456
+ # @param object [Object] the object to register as the component
457
+ #
458
+ # @overload register(key, memoize: false, &block)
459
+ # Registers the given block as the component. When the component is resolved, the return
460
+ # value of the block will be returned.
461
+ #
462
+ # Since the block is not called until resolution-time, this is a useful way to register
463
+ # components that have dependencies on other components in the container, which as yet may
464
+ # be unavailable at the time of registration.
465
+ #
466
+ # All auto-registered components are registered in block form.
467
+ #
468
+ # When `memoize` is true, the component will be memoized upon first resolution and the same
469
+ # object returned on all subsequent resolutions, meaning the block is only called once.
470
+ # Otherwise, the block will be called and a new object returned on every resolution.
471
+ #
472
+ # @param key [String] the component's key
473
+ # @param memoize [Boolean]
474
+ # @yieldreturn [Object] the object to register as the component
475
+ #
476
+ # @overload register(key, call: true, &block)
477
+ # Registers the given block as the component. When `call: false` is given, then the block
478
+ # itself will become the component.
479
+ #
480
+ # When such a component is resolved, the block will not be called, and instead the `Proc`
481
+ # object for that block will be returned.
482
+ #
483
+ # @param key [String] the component's key
484
+ # @param call [Boolean]
485
+ #
486
+ # @return [container]
487
+ #
488
+ # @see #[]
489
+ # @see #resolve
490
+ #
491
+ # @api public
492
+ # @since 2.0.0
493
+ def register(...)
494
+ container.register(...)
495
+ end
496
+
497
+ # @overload register_provider(name, namespace: nil, from: nil, source: nil, if: true, &block)
498
+ # Registers a provider and its lifecycle hooks.
499
+ #
500
+ # In most cases, you should call this from a dedicated file for the provider in your app or
501
+ # slice's `config/providers/` dir. This allows the provider to be loaded when individual
502
+ # matching components are resolved (for prepared slices) or when slices are booted.
503
+ #
504
+ # @example Simple provider
505
+ # # config/providers/db.rb
506
+ # Hanami.app.register_provider(:db) do
507
+ # start do
508
+ # require "db"
509
+ # register("db", DB.new)
510
+ # end
511
+ # end
512
+ #
513
+ # @example Provider with lifecycle steps, also using dependencies from the target container
514
+ # # config/providers/db.rb
515
+ # Hanami.app.register_provider(:db) do
516
+ # prepare do
517
+ # require "db"
518
+ # db = DB.new(target_container["settings"].database_url)
519
+ # register("db", db)
520
+ # end
521
+ #
522
+ # start do
523
+ # container["db"].establish_connection
524
+ # end
525
+ #
526
+ # stop do
527
+ # container["db"].close_connection
528
+ # end
529
+ # end
530
+ #
531
+ # @example Probvider registration under a namespace
532
+ # # config/providers/db.rb
533
+ # Hanami.app.register_provider(:persistence, namespace: true) do
534
+ # start do
535
+ # require "db"
536
+ #
537
+ # # Namespace option above means this will be registered as "persistence.db"
538
+ # register("db", DB.new)
539
+ # end
540
+ # end
541
+ #
542
+ # @param name [Symbol] the unique name for the provider
543
+ # @param namespace [Boolean, String, nil] register components from the provider with given
544
+ # namespace. May be an explicit string, or `true` for the namespace to be the provider's
545
+ # name
546
+ # @param from [Symbol, nil] the group for an external provider source to use, with the
547
+ # provider source name inferred from `name` or passed explicitly as `source:`
548
+ # @param source [Symbol, nil] the name of the external provider source to use, if different
549
+ # from the value provided as `name`
550
+ # @param if [Boolean] a boolean-returning expression to determine whether to register the
551
+ # provider
552
+ #
553
+ # @return [container]
554
+ #
555
+ # @api public
556
+ # @since 2.0.0
557
+ def register_provider(...)
558
+ container.register_provider(...)
559
+ end
560
+
561
+ # @api public
562
+ # @since 2.1.0
563
+ def configure_provider(*args, **kwargs, &block)
564
+ container.register_provider(*args, **kwargs, from: :hanami, &block)
565
+ end
566
+
567
+ # @overload start(provider_name)
568
+ # Starts a provider.
569
+ #
570
+ # This triggers the provider's `prepare` and `start` lifecycle steps.
571
+ #
572
+ # @example
573
+ # MySlice::Slice.start(:persistence)
574
+ #
575
+ # @param provider_name [Symbol] the name of the provider to start
576
+ #
577
+ # @return [container]
578
+ #
579
+ # @api public
580
+ # @since 2.0.0
581
+ def start(...)
582
+ container.start(...)
583
+ end
584
+
585
+ # @overload stop(provider_name)
586
+ # Stops a provider.
587
+ #
588
+ # This triggers the provider's `stop` lifecycle hook.
589
+ #
590
+ # @example
591
+ # MySlice::Slice.stop(:persistence)
592
+ #
593
+ # @param provider_name [Symbol] the name of the provider to start
594
+ #
595
+ # @return [container]
596
+ #
597
+ # @api public
598
+ # @since 2.0.0
599
+ def stop(...)
600
+ container.stop(...)
601
+ end
602
+
603
+ # @overload key?(key)
604
+ # Returns true if the component with the given key is registered in the container.
605
+ #
606
+ # For a prepared slice, calling `key?` will also try to load the component if not loaded
607
+ # already.
608
+ #
609
+ # @param key [String, Symbol] the component key
610
+ #
611
+ # @return [Boolean]
612
+ #
613
+ # @api public
614
+ # @since 2.0.0
615
+ def key?(...)
616
+ container.key?(...)
617
+ end
618
+
619
+ # Required for the slice to act as a provider target
620
+ # @api public
621
+ # @since 2.2.0
622
+ def registered?(...)
623
+ container.registered?(...)
624
+ end
625
+
626
+ # Returns an array of keys for all currently registered components in the container.
627
+ #
628
+ # For a prepared slice, this will be the set of components that have been previously resolved.
629
+ # For a booted slice, this will be all components available for the slice.
630
+ #
631
+ # @return [Array<String>]
632
+ #
633
+ # @api public
634
+ # @since 2.0.0
635
+ def keys
636
+ container.keys
637
+ end
638
+
639
+ # @overload [](key)
640
+ # Resolves the component with the given key from the container.
641
+ #
642
+ # For a prepared slice, this will attempt to load and register the matching component if it
643
+ # is not loaded already. For a booted slice, this will return from already registered
644
+ # components only.
645
+ #
646
+ # @return [Object] the resolved component's object
647
+ #
648
+ # @raise Dry::Container::KeyError if the component could not be found or loaded
649
+ #
650
+ # @see #resolve
651
+ #
652
+ # @api public
653
+ # @since 2.0.0
654
+ def [](...)
655
+ container.[](...)
656
+ end
657
+
658
+ # @see #[]
659
+ #
660
+ # @api public
661
+ # @since 2.0.0
662
+ def resolve(...)
663
+ container.resolve(...)
664
+ end
665
+
666
+ # Specifies the components to export from the slice.
667
+ #
668
+ # Slices importing from this slice can import the specified components only.
669
+ #
670
+ # @example
671
+ # module MySlice
672
+ # class Slice < Hanami::Slice
673
+ # export ["search", "index_entity"]
674
+ # end
675
+ # end
676
+ #
677
+ # @param keys [Array<String>] the component keys to export
678
+ #
679
+ # @return [self]
680
+ #
681
+ # @api public
682
+ # @since 2.0.0
683
+ def export(keys)
684
+ container.config.exports = keys
685
+ self
686
+ end
687
+
688
+ # @overload import(from:, as: nil, keys: nil)
689
+ # Specifies components to import from another slice.
690
+ #
691
+ # Booting a slice will register all imported components. For a prepared slice, these
692
+ # components will be be imported automatically when resolved.
693
+ #
694
+ # @example
695
+ # module MySlice
696
+ # class Slice < Hanami:Slice
697
+ # # Component from Search::Slice will import as "search.index_entity"
698
+ # import keys: ["index_entity"], from: :search
699
+ # end
700
+ # end
701
+ #
702
+ # @example Other import variations
703
+ # # Different key namespace: component will be "search_backend.index_entity"
704
+ # import keys: ["index_entity"], from: :search, as: "search_backend"
705
+ #
706
+ # # Import to root key namespace: component will be "index_entity"
707
+ # import keys: ["index_entity"], from: :search, as: nil
708
+ #
709
+ # # Import all components
710
+ # import from: :search
711
+ #
712
+ # @param keys [Array<String>, nil] Array of component keys to import. To import all
713
+ # available components, omit this argument.
714
+ # @param from [Symbol] name of the slice to import from
715
+ # @param as [Symbol, String, nil]
716
+ #
717
+ # @see #export
718
+ #
719
+ # @api public
720
+ # @since 2.0.0
721
+ def import(from:, **kwargs)
722
+ slice = self
723
+
724
+ container.after(:configure) do
725
+ if from.is_a?(Symbol) || from.is_a?(String)
726
+ slice_name = from
727
+ from = slice.parent.slices[from.to_sym].container
728
+ end
729
+
730
+ as = kwargs[:as] || slice_name
731
+
732
+ import(from: from, as: as, **kwargs)
733
+ end
734
+ end
735
+
736
+ # Returns the slice's settings, or nil if no settings are defined.
737
+ #
738
+ # You can define your settings in `config/settings.rb`.
739
+ #
740
+ # @return [Hanami::Settings, nil]
741
+ #
742
+ # @see Hanami::Settings
743
+ #
744
+ # @api public
745
+ # @since 2.0.0
746
+ def settings
747
+ return @settings if instance_variable_defined?(:@settings)
748
+
749
+ @settings = Settings.load_for_slice(self)
750
+ end
751
+
752
+ # Returns the slice's routes, or nil if no routes are defined.
753
+ #
754
+ # You can define your routes in `config/routes.rb`.
755
+ #
756
+ # @return [Hanami::Routes, nil]
757
+ #
758
+ # @see Hanami::Routes
759
+ #
760
+ # @api public
761
+ # @since 2.0.0
762
+ def routes
763
+ return @routes if instance_variable_defined?(:@routes)
764
+
765
+ @routes = load_routes
766
+ end
767
+
768
+ # Returns the slice's router, if or nil if no routes are defined.
769
+ #
770
+ # An optional `inspector`, implementing the `Hanami::Router::Inspector` interface, may be
771
+ # provided at first call (the router is then memoized for subsequent accesses). An inspector
772
+ # is used by the `hanami routes` CLI comment to provide a list of available routes.
773
+ #
774
+ # The returned router is a {Slice::Router}, which provides all `Hanami::Router` functionality,
775
+ # with the addition of support for slice mounting with the {Slice::Router#slice}.
776
+ #
777
+ # @param inspector [Hanami::Router::Inspector, nil] an optional routes inspector
778
+ #
779
+ # @return [Hanami::Slice::Router, nil]
780
+ #
781
+ # @api public
782
+ # @since 2.0.0
783
+ def router(inspector: nil)
784
+ raise SliceLoadError, "#{self} must be prepared before loading the router" unless prepared?
785
+
786
+ @_mutex.synchronize do
787
+ @_router ||= load_router(inspector: inspector)
788
+ end
789
+ end
790
+
791
+ # Returns a [Rack][rack] app for the slice, or nil if no routes are defined.
792
+ #
793
+ # The rack app will be memoized on first access.
794
+ #
795
+ # [rack]: https://github.com/rack/rack
796
+ #
797
+ # @return [#call, nil] the rack app, or nil if no routes are defined
798
+ #
799
+ # @see #routes
800
+ # @see #router
801
+ #
802
+ # @api public
803
+ # @since 2.0.0
804
+ def rack_app
805
+ return unless router
806
+
807
+ @rack_app ||= router.to_rack_app
808
+ end
809
+
810
+ # @overload call(rack_env)
811
+ # Calls the slice's [Rack][rack] app and returns a Rack-compatible response object
812
+ #
813
+ # [rack]: https://github.com/rack/rack
814
+ #
815
+ # @param rack_env [Hash] the Rack environment for the request
816
+ #
817
+ # @return [Array] the three-element Rack response array
818
+ #
819
+ # @raise [Hanami::NoRoutesDefinedError] if the hanami-router gem is missing or
820
+ # no routes are defined.
821
+ #
822
+ # @see #rack_app
823
+ #
824
+ # @api public
825
+ # @since 2.0.0
826
+ def call(...)
827
+ if rack_app
828
+ rack_app.call(...)
829
+ else
830
+ error_message = if Hanami.bundled?("hanami-router")
831
+ "Could not handle this rack request because no routes are defined"
832
+ else
833
+ "Could not handle this rack request because the hanami router gem is missing, please add it"
834
+ end
835
+ raise NoRoutesDefinedError, error_message
836
+ end
837
+ end
838
+
839
+ private
840
+
841
+ # rubocop:disable Metrics/AbcSize
842
+
843
+ def prepare_slice
844
+ return self if prepared?
845
+
846
+ config.finalize!
847
+
848
+ ensure_slice_name
849
+ ensure_slice_consts
850
+ ensure_root
851
+
852
+ prepare_all
853
+
854
+ instance_exec(container, &@prepare_container_block) if @prepare_container_block
855
+ container.configured!
856
+
857
+ prepare_autoloader
858
+
859
+ # Load child slices last, ensuring their parent is fully prepared beforehand
860
+ # (useful e.g. for slices that may wish to access constants defined in the
861
+ # parent's autoloaded directories)
862
+ prepare_slices
863
+
864
+ @prepared = true
865
+
866
+ self
867
+ end
868
+
869
+ def ensure_slice_name
870
+ unless name
871
+ raise SliceLoadError, "Slice must have a class name before it can be prepared"
872
+ end
873
+ end
874
+
875
+ def ensure_slice_consts
876
+ if namespace.const_defined?(:Container) || namespace.const_defined?(:Deps)
877
+ raise(
878
+ SliceLoadError,
879
+ "#{namespace}::Container and #{namespace}::Deps constants must not already be defined"
880
+ )
881
+ end
882
+ end
883
+
884
+ def ensure_root
885
+ unless config.root
886
+ raise SliceLoadError, "Slice must have a `config.root` before it can be prepared"
887
+ end
888
+ end
889
+
890
+ def prepare_all
891
+ prepare_settings
892
+ prepare_container_consts
893
+ prepare_container_plugins
894
+ prepare_container_base_config
895
+ prepare_container_component_dirs
896
+ prepare_container_imports
897
+ prepare_container_providers
898
+ end
899
+
900
+ def prepare_settings
901
+ container.register(:settings, settings) if settings
902
+ end
903
+
904
+ def prepare_container_consts
905
+ namespace.const_set :Container, container
906
+ namespace.const_set :Deps, container.injector
907
+ end
908
+
909
+ def prepare_container_plugins
910
+ container.use(:env, inferrer: -> { Hanami.env })
911
+
912
+ container.use(
913
+ :zeitwerk,
914
+ loader: autoloader,
915
+ run_setup: false,
916
+ eager_load: false
917
+ )
918
+ end
919
+
920
+ def prepare_container_base_config
921
+ container.config.name = slice_name.to_sym
922
+ container.config.root = root
923
+ container.config.provider_registrar = ProviderRegistrar.for_slice(self)
924
+ container.config.provider_dirs = [File.join("config", "providers")]
925
+ container.config.registrations_dir = File.join("config", "registrations")
926
+
927
+ container.config.component_dirs.memoize = memoize_policy
928
+
929
+ container.config.env = config.env
930
+ container.config.inflector = config.inflector
931
+ end
932
+
933
+ def memoize_policy
934
+ # Do not memoize components in the test env, so they can be stubbed if required.
935
+ return false if config.env == :test
936
+
937
+ no_memoize = config.no_memoize
938
+
939
+ if no_memoize.respond_to?(:call)
940
+ ->(component) { !no_memoize.call(component) }
941
+ elsif no_memoize.is_a?(Array) && no_memoize.any?
942
+ ->(component) { !component.key.start_with?(*no_memoize) }
943
+ else
944
+ true
945
+ end
946
+ end
947
+
948
+ def prepare_container_component_dirs
949
+ return unless root.directory?
950
+
951
+ # Component files in both the root and `lib/` define classes in the slice's
952
+ # namespace
953
+
954
+ if root.join(LIB_DIR)&.directory?
955
+ container.config.component_dirs.add(LIB_DIR) do |dir|
956
+ dir.namespaces.add_root(key: nil, const: slice_name.name)
957
+ end
958
+ end
959
+
960
+ # When auto-registering components in the root, ignore files in `config/` (this is
961
+ # for framework config only), `lib/` (these will be auto-registered as above), as
962
+ # well as the configured no_auto_register_paths
963
+ no_auto_register_paths = ([LIB_DIR, CONFIG_DIR] + config.no_auto_register_paths)
964
+ .map { |path|
965
+ path.end_with?(File::SEPARATOR) ? path : "#{path}#{File::SEPARATOR}"
966
+ }
967
+
968
+ # TODO: Change `""` (signifying the root) once dry-rb/dry-system#238 is resolved
969
+ container.config.component_dirs.add("") do |dir|
970
+ dir.namespaces.add_root(key: nil, const: slice_name.name)
971
+ dir.auto_register = -> component {
972
+ relative_path = component.file_path.relative_path_from(root).to_s
973
+ !relative_path.start_with?(*no_auto_register_paths)
974
+ }
975
+ end
976
+ end
977
+
978
+ def prepare_container_imports
979
+ import(
980
+ keys: config.shared_app_component_keys,
981
+ from: app.container,
982
+ as: nil
983
+ )
984
+ end
985
+
986
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
987
+ def prepare_container_providers
988
+ # Check here for the `routes` definition only, not `router` itself, because the
989
+ # `router` requires the slice to be prepared before it can be loaded, and at this
990
+ # point we're still in the process of preparing.
991
+ if routes?
992
+ require_relative "providers/routes"
993
+ register_provider(:routes, source: Providers::Routes)
994
+ end
995
+
996
+ if assets_dir? && Hanami.bundled?("hanami-assets")
997
+ require_relative "providers/assets"
998
+ register_provider(:assets, source: Providers::Assets)
999
+ end
1000
+
1001
+ if Hanami.bundled?("hanami-db")
1002
+ # Explicit require here to ensure the provider source registers itself, to allow the user
1003
+ # to configure it within their own concrete provider file.
1004
+ require_relative "providers/db"
1005
+
1006
+ if register_db_provider?
1007
+ # Only register providers if the user hasn't provided their own
1008
+ unless container.providers[:db]
1009
+ register_provider(:db, namespace: true, source: Providers::DB)
1010
+ end
1011
+
1012
+ unless container.providers[:relations]
1013
+ register_provider(:relations, namespace: true, source: Providers::Relations)
1014
+ end
1015
+ end
1016
+ end
1017
+
1018
+ if Hanami.bundled?("i18n")
1019
+ require_relative "providers/i18n"
1020
+
1021
+ if register_i18n_provider? && !container.providers[:i18n]
1022
+ register_provider(:i18n, source: Providers::I18n)
1023
+ end
1024
+ end
1025
+
1026
+ if Hanami.bundled?("hanami-mailer")
1027
+ # Explicit require here to ensure the provider source registers itself, to allow the
1028
+ # user to configure it within their own concrete provider file.
1029
+ require_relative "providers/mailers"
1030
+
1031
+ # Only register the provider if the user hasn't provided their own.
1032
+ if register_mailers_provider? && !container.providers[:mailers]
1033
+ register_provider(:mailers, namespace: true, source: Providers::Mailers)
1034
+ end
1035
+ end
1036
+ end
1037
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
1038
+
1039
+ def prepare_autoloader
1040
+ autoloader.tag = "hanami.slices.#{slice_name}"
1041
+
1042
+ # Component dirs are automatically pushed to the autoloader by dry-system's zeitwerk plugin.
1043
+ # This method adds other dirs that are not otherwise configured as component dirs.
1044
+
1045
+ # Everything in the slice root can be autoloaded except `config/` and `slices/`,
1046
+ # which are framework-managed directories
1047
+
1048
+ if root.join(CONFIG_DIR)&.directory?
1049
+ autoloader.ignore(root.join(CONFIG_DIR))
1050
+ end
1051
+
1052
+ if root.join(SLICES_DIR)&.directory?
1053
+ autoloader.ignore(root.join(SLICES_DIR))
1054
+ end
1055
+
1056
+ autoloader.setup
1057
+ end
1058
+
1059
+ def prepare_slices
1060
+ slices.load_slices.each(&:prepare)
1061
+ slices.freeze
1062
+ end
1063
+
1064
+ def routes?
1065
+ return false unless Hanami.bundled?("hanami-router")
1066
+
1067
+ return true if namespace.const_defined?(ROUTES_CLASS_NAME)
1068
+
1069
+ root.join("#{ROUTES_PATH}#{RB_EXT}").file?
1070
+ end
1071
+
1072
+ def load_routes
1073
+ return false unless Hanami.bundled?("hanami-router")
1074
+
1075
+ if root.directory?
1076
+ routes_require_path = root.join(ROUTES_PATH).to_s
1077
+
1078
+ begin
1079
+ require_relative "./routes"
1080
+ require routes_require_path
1081
+ rescue LoadError => exception
1082
+ raise exception unless exception.path == routes_require_path
1083
+ end
1084
+ end
1085
+
1086
+ begin
1087
+ routes_class = namespace.const_get(ROUTES_CLASS_NAME)
1088
+ routes_class.routes
1089
+ rescue NameError => exception
1090
+ raise exception unless exception.name == ROUTES_CLASS_NAME.to_sym
1091
+ end
1092
+ end
1093
+
1094
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
1095
+ def load_router(inspector:)
1096
+ return unless routes
1097
+
1098
+ require_relative "slice/router"
1099
+
1100
+ slice = self
1101
+ config = self.config
1102
+ rack_monitor = self["rack.monitor"]
1103
+
1104
+ show_welcome = Hanami.env?(:development) && routes.empty?
1105
+ render_errors = render_errors?
1106
+ render_detailed_errors = render_detailed_errors?
1107
+
1108
+ error_handlers = {}.tap do |hsh|
1109
+ if render_errors || render_detailed_errors
1110
+ hsh[:not_allowed] = ROUTER_NOT_ALLOWED_HANDLER
1111
+ hsh[:not_found] = ROUTER_NOT_FOUND_HANDLER
1112
+ end
1113
+ end
1114
+
1115
+ Slice::Router.new(
1116
+ inspector: inspector,
1117
+ inflector: inflector,
1118
+ routes: routes,
1119
+ resolver: config.router.resolver.new(slice: self),
1120
+ **error_handlers,
1121
+ **config.router.options
1122
+ ) do
1123
+ use(rack_monitor)
1124
+
1125
+ use(Hanami::Web::Welcome) if show_welcome
1126
+
1127
+ use(
1128
+ Hanami::Middleware::RenderErrors,
1129
+ config,
1130
+ Hanami::Middleware::PublicErrorsApp.new(slice.root.join("public"))
1131
+ )
1132
+
1133
+ if render_detailed_errors
1134
+ require "hanami/webconsole"
1135
+ use(Hanami::Webconsole::Middleware, config)
1136
+ end
1137
+
1138
+ if Hanami.bundled?("hanami-action")
1139
+ if config.actions.method_override
1140
+ require "rack/method_override"
1141
+ use(Rack::MethodOverride)
1142
+ end
1143
+
1144
+ if config.actions.sessions.enabled?
1145
+ use(*config.actions.sessions.middleware)
1146
+ end
1147
+
1148
+ if config.actions.content_security_policy && # rubocop:disable Style/SafeNavigation
1149
+ config.actions.content_security_policy.nonce?
1150
+ use(*config.actions.content_security_policy.middleware)
1151
+ end
1152
+ end
1153
+
1154
+ if Hanami.bundled?("hanami-assets") && config.assets.serve
1155
+ use(Hanami::Middleware::Assets)
1156
+ end
1157
+
1158
+ middleware_stack.update(config.middleware_stack)
1159
+ end
1160
+ end
1161
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
1162
+
1163
+ def render_errors?
1164
+ config.render_errors
1165
+ end
1166
+
1167
+ def render_detailed_errors?
1168
+ config.render_detailed_errors && Hanami.bundled?("hanami-webconsole")
1169
+ end
1170
+
1171
+ ROUTER_NOT_ALLOWED_HANDLER = -> env, allowed_http_methods {
1172
+ raise Hanami::Router::NotAllowedError.new(env, allowed_http_methods)
1173
+ }.freeze
1174
+ private_constant :ROUTER_NOT_ALLOWED_HANDLER
1175
+
1176
+ ROUTER_NOT_FOUND_HANDLER = -> env {
1177
+ raise Hanami::Router::NotFoundError.new(env)
1178
+ }.freeze
1179
+ private_constant :ROUTER_NOT_FOUND_HANDLER
1180
+
1181
+ def assets_dir?
1182
+ source_path.join("assets").directory?
1183
+ end
1184
+
1185
+ # Ensures an i18n provider is available in every slice.
1186
+ #
1187
+ # For the app, this will always be a standalone provider. For slices, this will be a
1188
+ # standalone provider unless the slice is configured to share the app's "i18n" component.
1189
+ def register_i18n_provider?
1190
+ return true if self == app
1191
+
1192
+ !config.shared_app_component_keys.include?("i18n")
1193
+ end
1194
+
1195
+ # Ensures a mailers provider is available in every slice.
1196
+ #
1197
+ # For the app, this will always be a standalone provider. For slices, this will be a
1198
+ # standalone provider unless the slice is configured to share the app's
1199
+ # "mailers.delivery_method" component.
1200
+ def register_mailers_provider?
1201
+ return true if self == app
1202
+
1203
+ !config.shared_app_component_keys.include?("mailers.delivery_method")
1204
+ end
1205
+
1206
+ def register_db_provider?
1207
+ concrete_db_provider? ||
1208
+ db_config_dir? ||
1209
+ relations_dir? ||
1210
+ db_source_dir? ||
1211
+ import_db_from_parent?
1212
+ end
1213
+
1214
+ def concrete_db_provider?
1215
+ root.join(CONFIG_DIR, "providers", "db.rb").exist?
1216
+ end
1217
+
1218
+ def db_config_dir?
1219
+ root.join("config", "db").directory?
1220
+ end
1221
+
1222
+ def relations_dir?
1223
+ source_path.join("relations").directory?
1224
+ end
1225
+
1226
+ def db_source_dir?
1227
+ source_path.join("db").directory?
1228
+ end
1229
+
1230
+ def import_db_from_parent?
1231
+ parent &&
1232
+ config.db.import_from_parent &&
1233
+ parent.container.providers[:db]
1234
+ end
1235
+
1236
+ # rubocop:enable Metrics/AbcSize
1237
+ end
1238
+ end
1239
+ end