hanami 2.0.0.beta4 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -0
  3. data/hanami.gemspec +8 -7
  4. data/lib/hanami/app.rb +47 -36
  5. data/lib/hanami/assets/app_config.rb +7 -15
  6. data/lib/hanami/assets/config.rb +5 -6
  7. data/lib/hanami/config/actions/content_security_policy.rb +1 -1
  8. data/lib/hanami/config/actions/cookies.rb +27 -0
  9. data/lib/hanami/config/actions/sessions.rb +42 -5
  10. data/lib/hanami/config/actions.rb +81 -17
  11. data/lib/hanami/config/logger.rb +112 -23
  12. data/lib/hanami/config/router.rb +0 -1
  13. data/lib/hanami/config/views.rb +6 -10
  14. data/lib/hanami/config.rb +235 -73
  15. data/lib/hanami/constants.rb +4 -0
  16. data/lib/hanami/errors.rb +17 -0
  17. data/lib/hanami/extensions/action/slice_configured_action.rb +9 -5
  18. data/lib/hanami/extensions/action.rb +59 -7
  19. data/lib/hanami/extensions/view/context.rb +3 -4
  20. data/lib/hanami/extensions/view/slice_configured_view.rb +4 -4
  21. data/lib/hanami/extensions/view.rb +7 -5
  22. data/lib/hanami/providers/inflector.rb +6 -0
  23. data/lib/hanami/providers/logger.rb +8 -0
  24. data/lib/hanami/providers/rack.rb +12 -0
  25. data/lib/hanami/providers/routes.rb +14 -4
  26. data/lib/hanami/routes.rb +36 -1
  27. data/lib/hanami/settings/env_store.rb +1 -1
  28. data/lib/hanami/settings.rb +102 -36
  29. data/lib/hanami/slice/router.rb +38 -16
  30. data/lib/hanami/slice/routing/middleware/stack.rb +66 -42
  31. data/lib/hanami/slice/routing/resolver.rb +10 -17
  32. data/lib/hanami/slice/view_name_inferrer.rb +1 -1
  33. data/lib/hanami/slice.rb +553 -14
  34. data/lib/hanami/slice_registrar.rb +20 -15
  35. data/lib/hanami/version.rb +2 -3
  36. data/lib/hanami/web/rack_logger.rb +14 -4
  37. data/lib/hanami.rb +122 -23
  38. data/spec/integration/action/csrf_protection_spec.rb +1 -1
  39. data/spec/integration/container/application_routes_helper_spec.rb +3 -1
  40. data/spec/integration/container/provider_lifecycle_spec.rb +61 -0
  41. data/spec/integration/container/standard_providers/rack_provider_spec.rb +44 -0
  42. data/spec/integration/container/{standard_bootable_components_spec.rb → standard_providers_spec.rb} +3 -3
  43. data/spec/integration/rack_app/body_parser_spec.rb +3 -0
  44. data/spec/integration/rack_app/middleware_spec.rb +427 -3
  45. data/spec/integration/rack_app/non_booted_rack_app_spec.rb +2 -1
  46. data/spec/integration/rack_app/rack_app_spec.rb +39 -11
  47. data/spec/integration/setup_spec.rb +4 -4
  48. data/spec/integration/slices/external_slice_spec.rb +2 -1
  49. data/spec/integration/slices/slice_configuration_spec.rb +3 -1
  50. data/spec/integration/slices/slice_loading_spec.rb +4 -4
  51. data/spec/integration/slices/slice_routing_spec.rb +4 -3
  52. data/spec/integration/slices_spec.rb +100 -0
  53. data/spec/isolation/hanami/boot/success_spec.rb +1 -1
  54. data/spec/support/app_integration.rb +2 -10
  55. data/spec/unit/hanami/config/actions/content_security_policy_spec.rb +7 -7
  56. data/spec/unit/hanami/config/actions/default_values_spec.rb +1 -1
  57. data/spec/unit/hanami/config/actions/sessions_spec.rb +1 -3
  58. data/spec/unit/hanami/config/actions_spec.rb +1 -12
  59. data/spec/unit/hanami/config/logger_spec.rb +38 -55
  60. data/spec/unit/hanami/config/router_spec.rb +1 -1
  61. data/spec/unit/hanami/config/views_spec.rb +3 -13
  62. data/spec/unit/hanami/settings_spec.rb +1 -1
  63. data/spec/unit/hanami/slice_configurable_spec.rb +5 -5
  64. data/spec/unit/hanami/slice_spec.rb +32 -0
  65. data/spec/unit/hanami/version_spec.rb +1 -1
  66. data/spec/unit/hanami/web/rack_logger_spec.rb +13 -2
  67. metadata +54 -45
  68. data/lib/hanami/config/sessions.rb +0 -50
  69. data/spec/unit/hanami/config_spec.rb +0 -43
data/lib/hanami/slice.rb CHANGED
@@ -3,12 +3,8 @@
3
3
  require "zeitwerk"
4
4
  require "dry/system"
5
5
 
6
- require_relative "../hanami"
7
6
  require_relative "constants"
8
7
  require_relative "errors"
9
- require_relative "settings"
10
- require_relative "slice_name"
11
- require_relative "slice_registrar"
12
8
 
13
9
  module Hanami
14
10
  # A slice represents any distinct area of concern within an Hanami app.
@@ -35,6 +31,7 @@ module Hanami
35
31
  class Slice
36
32
  @_mutex = Mutex.new
37
33
 
34
+ # @api private
38
35
  def self.inherited(subclass)
39
36
  super
40
37
 
@@ -51,29 +48,146 @@ module Hanami
51
48
 
52
49
  # rubocop:disable Metrics/ModuleLength
53
50
  module ClassMethods
54
- attr_reader :parent, :autoloader, :container
55
-
51
+ # Returns the slice's parent.
52
+ #
53
+ # For top-level slices defined in `slices/` or `config/slices/`, this will be the Hanami app
54
+ # itself (`Hanami.app`). For nested slices, this will be the slice in which they were
55
+ # registered.
56
+ #
57
+ # @return [Hanami::Slice]
58
+ #
59
+ # @see #register_slice
60
+ #
61
+ # @api public
62
+ # @since 2.0.0
63
+ attr_reader :parent
64
+
65
+ # Returns the slice's autoloader.
66
+ #
67
+ # Each slice has its own `Zeitwerk::Loader` autoloader instance, which is setup when the slice
68
+ # is {#prepare prepared}.
69
+ #
70
+ # @return [Zeitwerk::Loader]
71
+ #
72
+ # @see https://github.com/fxn/zeitwerk
73
+ #
74
+ # @api public
75
+ # @since 2.0.0
76
+ attr_reader :autoloader
77
+
78
+ # Returns the slice's container.
79
+ #
80
+ # This is a `Dry::System::Container` that is already configured for the slice.
81
+ #
82
+ # In ordinary usage, you shouldn't need direct access the container at all, since the slice
83
+ # provides its own methods for interacting with the container (such as {#[]}, {#keys}, {#key?}
84
+ # {#register}, {#register_provider}, {#prepare}, {#start}, {#stop}).
85
+ #
86
+ # If you need to configure the container directly, use {#prepare_container}.
87
+ #
88
+ # @see https://dry-rb.org/gems/dry-system
89
+ #
90
+ # @api public
91
+ # @since 2.0.0
92
+ attr_reader :container
93
+
94
+ # Returns the Hanami app.
95
+ #
96
+ # @return [Hanami::App]
97
+ #
98
+ # @api public
99
+ # @since 2.0.0
56
100
  def app
57
101
  Hanami.app
58
102
  end
59
103
 
60
- # A slice's config is copied from the app config at time of first access. The app should have
61
- # its config completed before slices are loaded.
104
+ # Returns the slice's config.
105
+ #
106
+ # A slice's config is copied from the app config at time of first access.
107
+ #
108
+ # @return [Hanami::Config]
109
+ #
110
+ # @see App::ClassMethods.config
111
+ #
112
+ # @api public
113
+ # @since 2.0.0
62
114
  def config
63
115
  @config ||= app.config.dup.tap do |slice_config|
64
- # Remove specific values from app that will not apply to this slice
116
+ # Unset config from app that does not apply to ordinary slices
65
117
  slice_config.root = nil
66
118
  end
67
119
  end
68
120
 
121
+ # Evaluates the block for a given app environment only.
122
+ #
123
+ # If the given `env_name` matches {Hanami.env}, then the block will be evaluated in the
124
+ # context of `self` (the slice) via `instance_eval`. The slice is also passed as the block's
125
+ # optional argument.
126
+ #
127
+ # If the env does not match, then the block is not evaluated at all.
128
+ #
129
+ # @example
130
+ # module MySlice
131
+ # class Slice < Hanami::Slice
132
+ # environment(:test) do
133
+ # config.logger.level = :info
134
+ # end
135
+ # end
136
+ # end
137
+ #
138
+ # @overload environment(env_name)
139
+ # @param env_name [Symbol] the environment name
140
+ #
141
+ # @overload environment(env_name)
142
+ # @param env_name [Symbol] the environment name
143
+ # @yieldparam slice [self] the slice
144
+ #
145
+ # @return [self]
146
+ #
147
+ # @see Hanami.env
148
+ #
149
+ # @api public
150
+ # @since 2.0.0
151
+ def environment(env_name, &block)
152
+ instance_eval(&block) if env_name == config.env
153
+ self
154
+ end
155
+
156
+ # Returns a {SliceName} for the slice, an object with methods returning the name of the slice
157
+ # in various formats.
158
+ #
159
+ # @return [SliceName]
160
+ #
161
+ # @api public
162
+ # @since 2.0.0
69
163
  def slice_name
70
164
  @slice_name ||= SliceName.new(self, inflector: method(:inflector))
71
165
  end
72
166
 
167
+ # Returns the constant for the slice's module namespace.
168
+ #
169
+ # @example
170
+ # MySlice::Slice.namespace # => MySlice
171
+ #
172
+ # @return [Module] the namespace module constant
173
+ #
174
+ # @see SliceName#namespace
175
+ #
176
+ # @api public
177
+ # @since 2.0.0
73
178
  def namespace
74
179
  slice_name.namespace
75
180
  end
76
181
 
182
+ # Returns the slice's root, either the root as explicitly configured, or a default fallback of
183
+ # the slice's name within the app's `slices/` dir.
184
+ #
185
+ # @return [Pathname]
186
+ #
187
+ # @see Config#root
188
+ #
189
+ # @api public
190
+ # @since 2.0.0
77
191
  def root
78
192
  # Provide a best guess for a root when it is not yet configured.
79
193
  #
@@ -89,23 +203,105 @@ module Hanami
89
203
  config.root || app.root.join(SLICES_DIR, slice_name.to_s)
90
204
  end
91
205
 
206
+ # Returns the slice's configured inflector.
207
+ #
208
+ # Unless explicitly re-configured for the slice, this will be the app's inflector.
209
+ #
210
+ # @return [Dry::Inflector]
211
+ #
212
+ # @see Config#inflector
213
+ # @see Config#inflections
214
+ #
215
+ # @api public
216
+ # @since 2.0.0
92
217
  def inflector
93
218
  config.inflector
94
219
  end
95
220
 
221
+ # @overload prepare
222
+ # Prepares the slice.
223
+ #
224
+ # This will define the slice's `Slice` and `Deps` constants, make all Ruby source files
225
+ # inside the slice's root dir autoloadable, as well as lazily loadable as container
226
+ # components.
227
+ #
228
+ # Call `prepare` when you want to access particular components within the slice while still
229
+ # minimizing load time. Preparing slices is the approach taken when loading the Hanami
230
+ # console or when running tests.
231
+ #
232
+ # @return [self]
233
+ #
234
+ # @see #boot
235
+ #
236
+ # @api public
237
+ # @since 2.0.0
238
+ #
239
+ # @overload prepare(provider_name)
240
+ # Prepares a provider.
241
+ #
242
+ # This triggers the provider's `prepare` lifecycle step.
243
+ #
244
+ # @param provider_name [Symbol] the name of the provider to start
245
+ #
246
+ # @return [self]
247
+ #
248
+ # @api public
249
+ # @since 2.0.0
96
250
  def prepare(provider_name = nil)
97
251
  if provider_name
98
252
  container.prepare(provider_name)
99
- self
100
253
  else
101
254
  prepare_slice
102
255
  end
256
+
257
+ self
103
258
  end
104
259
 
260
+ # Captures the given block to be called with the slice's container during the slice's
261
+ # `prepare` step, after the slice has already configured the container.
262
+ #
263
+ # This is intended for advanced usage only and should not be needed for ordinary slice
264
+ # configuration and usage.
265
+ #
266
+ # @example
267
+ # module MySlice
268
+ # class Sliice < Hanami::Slice
269
+ # prepare_container do |container|
270
+ # # ...
271
+ # end
272
+ # end
273
+ # end
274
+ #
275
+ # @yieldparam container [Dry::System::Container] the slice's container
276
+ #
277
+ # @return [self]
278
+ #
279
+ # @see #prepare
280
+ #
281
+ # @api public
282
+ # @since 2.0.0
105
283
  def prepare_container(&block)
106
284
  @prepare_container_block = block
285
+ self
107
286
  end
108
287
 
288
+ # Boots the slice.
289
+ #
290
+ # This will prepare the slice (if not already prepared), start each of its providers, register
291
+ # all the slice's components from its Ruby source files, and import components from any other
292
+ # slices. It will also boot any of the slice's own registered nested slices. It will then
293
+ # freeze its container so no further components can be registered.
294
+ #
295
+ # Call `boot` if you want to fully load a slice and incur all load time up front, such as when
296
+ # preparing an app to serve web requests. Booting slices is the approach taken when running
297
+ # Hanami's standard Puma setup (see `config.ru`).
298
+ #
299
+ # @return [self]
300
+ #
301
+ # @see #prepare
302
+ #
303
+ # @api public
304
+ # @since 2.0.0
109
305
  def boot
110
306
  return self if booted?
111
307
 
@@ -119,64 +315,346 @@ module Hanami
119
315
  self
120
316
  end
121
317
 
318
+ # Shuts down the slice's providers, as well as the providers in any nested slices.
319
+ #
320
+ # @return [self]
321
+ #
322
+ # @api public
323
+ # @since 2.0.0
122
324
  def shutdown
123
325
  slices.each(&:shutdown)
124
326
  container.shutdown!
125
327
  self
126
328
  end
127
329
 
330
+ # Returns true if the slice has been prepared.
331
+ #
332
+ # @return [Boolean]
333
+ #
334
+ # @see #prepare
335
+ #
336
+ # @api public
337
+ # @since 2.0.0
128
338
  def prepared?
129
339
  !!@prepared
130
340
  end
131
341
 
342
+ # Returns true if the slice has been booted.
343
+ #
344
+ # @return [Boolean]
345
+ #
346
+ # @see #boot
347
+ #
348
+ # @api public
349
+ # @since 2.0.0
132
350
  def booted?
133
351
  !!@booted
134
352
  end
135
353
 
354
+ # Returns the slice's collection of nested slices.
355
+ #
356
+ # @return [SliceRegistrar]
357
+ #
358
+ # @see #register_slice
359
+ #
360
+ # @api public
361
+ # @since 2.0.0
136
362
  def slices
137
363
  @slices ||= SliceRegistrar.new(self)
138
364
  end
139
365
 
366
+ # @overload register_slice(name, &block)
367
+ # Registers a nested slice with the given name.
368
+ #
369
+ # This will define a new {Slice} subclass for the slice. If a block is given, it is passed
370
+ # the class object, and will be evaluated in the context of the class like `class_eval`.
371
+ #
372
+ # @example
373
+ # MySlice::Slice.register_slice do
374
+ # # Configure the slice or do other class-level things here
375
+ # end
376
+ #
377
+ # @param name [Symbol] the identifier for the slice to be registered
378
+ # @yieldparam slice [Hanami::Slice] the newly defined slice class
379
+ #
380
+ # @overload register_slice(name, slice_class)
381
+ # Registers a nested slice with the given name.
382
+ #
383
+ # The given `slice_class` will be registered as the slice. It must be a subclass of {Slice}.
384
+ #
385
+ # @param name [Symbol] the identifier for the slice to be registered
386
+ # @param slice_class [Hanami::Slice]
387
+ #
388
+ # @return [slices]
389
+ #
390
+ # @see SliceRegistrar#register
391
+ #
392
+ # @api public
393
+ # @since 2.0.0
140
394
  def register_slice(...)
141
395
  slices.register(...)
142
396
  end
143
397
 
398
+ # Registers a component in the slice's container.
399
+ #
400
+ # @overload register(key, object)
401
+ # Registers the given object as the component. This same object will be returned whenever
402
+ # the component is resolved.
403
+ #
404
+ # @param key [String] the component's key
405
+ # @param object [Object] the object to register as the component
406
+ #
407
+ # @overload reigster(key, memoize: false, &block)
408
+ # Registers the given block as the component. When the component is resolved, the return
409
+ # value of the block will be returned.
410
+ #
411
+ # Since the block is not called until resolution-time, this is a useful way to register
412
+ # components that have dependencies on other components in the container, which as yet may
413
+ # be unavailable at the time of registration.
414
+ #
415
+ # All auto-registered components are registered in block form.
416
+ #
417
+ # When `memoize` is true, the component will be memoized upon first resolution and the same
418
+ # object returned on all subsequent resolutions, meaning the block is only called once.
419
+ # Otherwise, the block will be called and a new object returned on every resolution.
420
+ #
421
+ # @param key [String] the component's key
422
+ # @param memoize [Boolean]
423
+ # @yieldreturn [Object] the object to register as the component
424
+ #
425
+ # @overload reigster(key, call: true, &block)
426
+ # Registers the given block as the component. When `call: false` is given, then the block
427
+ # itself will become the component.
428
+ #
429
+ # When such a component is resolved, the block will not be called, and instead the `Proc`
430
+ # object for that block will be returned.
431
+ #
432
+ # @param key [String] the component's key
433
+ # @param call [Booelan]
434
+ #
435
+ # @return [container]
436
+ #
437
+ # @see #[]
438
+ # @see #resolve
439
+ #
440
+ # @api public
441
+ # @since 2.0.0
144
442
  def register(...)
145
443
  container.register(...)
146
444
  end
147
445
 
446
+ # @overload register_provider(name, namespace: nil, from: nil, source: nil, if: true, &block)
447
+ # Registers a provider and its lifecycle hooks.
448
+ #
449
+ # In most cases, you should call this from a dedicated file for the provider in your app or
450
+ # slice's `config/providers/` dir. This allows the provider to be loaded when individual
451
+ # matching components are resolved (for prepared slices) or when slices are booted.
452
+ #
453
+ # @example Simple provider
454
+ # # config/providers/db.rb
455
+ # Hanami.app.register_provider(:db) do
456
+ # start do
457
+ # require "db"
458
+ # register("db", DB.new)
459
+ # end
460
+ # end
461
+ #
462
+ # @example Provider with lifecycle steps, also using dependencies from the target container
463
+ # # config/providers/db.rb
464
+ # Hanami.app.register_provider(:db) do
465
+ # prepare do
466
+ # require "db"
467
+ # db = DB.new(target_container["settings"].database_url)
468
+ # register("db", db)
469
+ # end
470
+ #
471
+ # start do
472
+ # container["db"].establish_connection
473
+ # end
474
+ #
475
+ # stop do
476
+ # container["db"].close_connection
477
+ # end
478
+ # end
479
+ #
480
+ # @example Probvider registration under a namespace
481
+ # # config/providers/db.rb
482
+ # Hanami.app.register_provider(:persistence, namespace: true) do
483
+ # start do
484
+ # require "db"
485
+ #
486
+ # # Namespace option above means this will be registered as "persistence.db"
487
+ # register("db", DB.new)
488
+ # end
489
+ # end
490
+ #
491
+ # @param name [Symbol] the unique name for the provider
492
+ # @param namespace [Boolean, String, nil] register components from the provider with given
493
+ # namespace. May be an explicit string, or `true` for the namespace to be the provider's
494
+ # name
495
+ # @param from [Symbol, nil] the group for an external provider source to use, with the
496
+ # provider source name inferred from `name` or passsed explicitly as `source:`
497
+ # @param source [Symbol, nil] the name of the external provider source to use, if different
498
+ # from the value provided as `name`
499
+ # @param if [Boolean] a boolean-returning expression to determine whether to register the
500
+ # provider
501
+ #
502
+ # @return [container]
503
+ #
504
+ # @api public
505
+ # @since 2.0.0
148
506
  def register_provider(...)
149
507
  container.register_provider(...)
150
508
  end
151
509
 
510
+ # @overload start(provider_name)
511
+ # Starts a provider.
512
+ #
513
+ # This triggers the provider's `prepare` and `start` lifecycle steps.
514
+ #
515
+ # @example
516
+ # MySlice::Slice.start(:persistence)
517
+ #
518
+ # @param provider_name [Symbol] the name of the provider to start
519
+ #
520
+ # @return [container]
521
+ #
522
+ # @api public
523
+ # @since 2.0.0
152
524
  def start(...)
153
525
  container.start(...)
154
526
  end
155
527
 
528
+ # @overload stop(provider_name)
529
+ # Stops a provider.
530
+ #
531
+ # This triggers the provider's `stop` lifecycle hook.
532
+ #
533
+ # @example
534
+ # MySlice::Slice.stop(:persistence)
535
+ #
536
+ # @param provider_name [Symbol] the name of the provider to start
537
+ #
538
+ # @return [container]
539
+ #
540
+ # @api public
541
+ # @since 2.0.0
542
+ def stop(...)
543
+ container.stop(...)
544
+ end
545
+
546
+ # @overload key?(key)
547
+ # Returns true if the component with the given key is registered in the container.
548
+ #
549
+ # For a prepared slice, calling `key?` will also try to load the component if not loaded
550
+ # already.
551
+ #
552
+ # @param key [String, Symbol] the component key
553
+ #
554
+ # @return [Boolean]
555
+ #
556
+ # @api public
557
+ # @since 2.0.0
156
558
  def key?(...)
157
559
  container.key?(...)
158
560
  end
159
561
 
562
+ # Returns an array of keys for all currently registered components in the container.
563
+ #
564
+ # For a prepared slice, this will be the set of components that have been previously resolved.
565
+ # For a booted slice, this will be all components available for the slice.
566
+ #
567
+ # @return [Array<String>]
568
+ #
569
+ # @api public
570
+ # @since 2.0.0
160
571
  def keys
161
572
  container.keys
162
573
  end
163
574
 
575
+ # @overload [](key)
576
+ # Resolves the component with the given key from the container.
577
+ #
578
+ # For a prepared slice, this will attempt to load and register the matching component if it
579
+ # is not loaded already. For a booted slice, this will return from already registered
580
+ # components only.
581
+ #
582
+ # @return [Object] the resolved component's object
583
+ #
584
+ # @raise Dry::Container::KeyError if the component could not be found or loaded
585
+ #
586
+ # @see #resolve
587
+ #
588
+ # @api public
589
+ # @since 2.0.0
164
590
  def [](...)
165
591
  container.[](...)
166
592
  end
167
593
 
594
+ # @see #[]
595
+ #
596
+ # @api public
597
+ # @since 2.0.0
168
598
  def resolve(...)
169
599
  container.resolve(...)
170
600
  end
171
601
 
602
+ # Specifies the components to export from the slice.
603
+ #
604
+ # Slices importing from this slice can import the specified components only.
605
+ #
606
+ # @example
607
+ # module MySlice
608
+ # class Slice < Hanami::Slice
609
+ # export ["search", "index_entity"]
610
+ # end
611
+ # end
612
+ #
613
+ # @param keys [Array<String>] the component keys to export
614
+ #
615
+ # @return [self]
616
+ #
617
+ # @api public
618
+ # @since 2.0.0
172
619
  def export(keys)
173
620
  container.config.exports = keys
621
+ self
174
622
  end
175
623
 
624
+ # @overload import(from:, as: nil, keys: nil)
625
+ # Specifies components to import from another slice.
626
+ #
627
+ # Booting a slice will register all imported components. For a prepared slice, these
628
+ # components will be be imported automatically when resolved.
629
+ #
630
+ # @example
631
+ # module MySlice
632
+ # class Slice < Hanami:Slice
633
+ # # Component from Search::Slice will import as "search.index_entity"
634
+ # import keys: ["index_entity"], from: :search
635
+ # end
636
+ # end
637
+ #
638
+ # @example Other import variations
639
+ # # Different key namespace: component will be "search_backend.index_entity"
640
+ # import keys: ["index_entity"], from: :search, as: "search_backend"
641
+ #
642
+ # # Import to root key namespace: component will be "index_entity"
643
+ # import keys: ["index_entity"], from: :search, as: nil
644
+ #
645
+ # # Import all components
646
+ # import from: :search
647
+ #
648
+ # @param keys [Array<String>, nil] Array of component keys to import. To import all
649
+ # available components, omit this argument.
650
+ # @param from [Symbol] name of the slice to import from
651
+ # @param as [Symbol, String, nil]
652
+ #
653
+ # @see #export
654
+ #
655
+ # @api public
656
+ # @since 2.0.0
176
657
  def import(from:, **kwargs)
177
- # TODO: This should be handled via dry-system (see dry-rb/dry-system#228)
178
- raise "Cannot import after booting" if booted?
179
-
180
658
  slice = self
181
659
 
182
660
  container.after(:configure) do
@@ -191,16 +669,51 @@ module Hanami
191
669
  end
192
670
  end
193
671
 
672
+ # Returns the slice's settings, or nil if no settings are defined.
673
+ #
674
+ # You can define your settings in `config/settings.rb`.
675
+ #
676
+ # @return [Hanami::Settings, nil]
677
+ #
678
+ # @see Hanami::Settings
679
+ #
680
+ # @api public
681
+ # @since 2.0.0
194
682
  def settings
195
683
  return @settings if instance_variable_defined?(:@settings)
196
684
 
197
685
  @settings = Settings.load_for_slice(self)
198
686
  end
199
687
 
688
+ # Returns the slice's routes, or nil if no routes are defined.
689
+ #
690
+ # You can define your routes in `config/routes.rb`.
691
+ #
692
+ # @return [Hanami::Routes, nil]
693
+ #
694
+ # @see Hanami::Routes
695
+ #
696
+ # @api public
697
+ # @since 2.0.0
200
698
  def routes
201
699
  @routes ||= load_routes
202
700
  end
203
701
 
702
+ # Returns the slice's router, if or nil if no routes are defined.
703
+ #
704
+ # An optional `inspector`, implementing the `Hanami::Router::Inspector` interface, may be
705
+ # provided at first call (the router is then memoized for subsequent accesses). An inspector
706
+ # is used by the `hanami routes` CLI comment to provide a list of available routes.
707
+ #
708
+ # The returned router is a {Slice::Router}, which provides all `Hanami::Router` functionality,
709
+ # with the addition of support for slice mounting with the {Slice::Router#slice}.
710
+ #
711
+ # @param inspector [Hanami::Router::Inspector, nil] an optional routes inspector
712
+ #
713
+ # @return [Hanami::Slice::Router, nil]
714
+ #
715
+ # @api public
716
+ # @since 2.0.0
204
717
  def router(inspector: nil)
205
718
  raise SliceLoadError, "#{self} must be prepared before loading the router" unless prepared?
206
719
 
@@ -209,12 +722,38 @@ module Hanami
209
722
  end
210
723
  end
211
724
 
725
+ # Returns a [Rack][rack] app for the slice, or nil if no routes are defined.
726
+ #
727
+ # The rack app will be memoized on first access.
728
+ #
729
+ # [rack]: https://github.com/rack/rack
730
+ #
731
+ # @return [#call, nil] the rack app, or nil if no routes are defined
732
+ #
733
+ # @see #routes
734
+ # @see #router
735
+ #
736
+ # @api public
737
+ # @since 2.0.0
212
738
  def rack_app
213
739
  return unless router
214
740
 
215
741
  @rack_app ||= router.to_rack_app
216
742
  end
217
743
 
744
+ # @overload call(rack_env)
745
+ # Calls the slice's [Rack][rack] app and returns a Rack-compatible response object
746
+ #
747
+ # [rack]: https://github.com/rack/rack
748
+ #
749
+ # @param rack_env [Hash] the Rack environment for the request
750
+ #
751
+ # @return [Array] the three-element Rack response array
752
+ #
753
+ # @see #rack_app
754
+ #
755
+ # @api public
756
+ # @since 2.0.0
218
757
  def call(...)
219
758
  rack_app.call(...)
220
759
  end
@@ -418,7 +957,7 @@ module Hanami
418
957
  **config.router.options
419
958
  ) do
420
959
  use(rack_monitor)
421
- use(*config.sessions.middleware) if config.sessions.enabled?
960
+ use(*config.actions.sessions.middleware) if config.actions.sessions.enabled?
422
961
 
423
962
  middleware_stack.update(config.middleware_stack)
424
963
  end