Module: Hanami::Slice::ClassMethods

Defined in:
lib/hanami/slice.rb

Overview

rubocop:disable Metrics/ModuleLength

Since:

  • 2.0.0

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#autoloaderZeitwerk::Loader (readonly)

Returns the slice’s autoloader.

Each slice has its own ‘Zeitwerk::Loader` autoloader instance, which is setup when the slice is prepared.

Returns:

  • (Zeitwerk::Loader)

See Also:

Since:

  • 2.0.0



76
77
78
# File 'lib/hanami/slice.rb', line 76

def autoloader
  @autoloader
end

#containerObject (readonly)

Returns the slice’s container.

This is a ‘Dry::System::Container` that is already configured for the slice.

In ordinary usage, you shouldn’t need direct access the container at all, since the slice provides its own methods for interacting with the container (such as #[], #keys, #key? #register, #register_provider, #prepare, #start, #stop).

If you need to configure the container directly, use #prepare_container.

See Also:

Since:

  • 2.0.0



92
93
94
# File 'lib/hanami/slice.rb', line 92

def container
  @container
end

#parentHanami::Slice (readonly)

Returns the slice’s parent.

For top-level slices defined in ‘slices/` or `config/slices/`, this will be the Hanami app itself (`Hanami.app`). For nested slices, this will be the slice in which they were registered.

Returns:

See Also:

Since:

  • 2.0.0



63
64
65
# File 'lib/hanami/slice.rb', line 63

def parent
  @parent
end

Instance Method Details

#[](key) ⇒ Object

Resolves the component with the given key from the container.

For a prepared slice, this will attempt to load and register the matching component if it is not loaded already. For a booted slice, this will return from already registered components only.

Returns:

  • (Object)

    the resolved component’s object

Raises:

  • Dry::Container::KeyError if the component could not be found or loaded

See Also:

Since:

  • 2.0.0

Since:

  • 2.0.0



623
624
625
# File 'lib/hanami/slice.rb', line 623

def [](...)
  container.[](...)
end

#appHanami::App

Returns the Hanami app.

Returns:

Since:

  • 2.0.0



100
101
102
# File 'lib/hanami/slice.rb', line 100

def app
  Hanami.app
end

#app?Boolean

Returns true if the slice is Hanami.app

Returns:

  • (Boolean)

Since:

  • 2.2.0



110
111
112
# File 'lib/hanami/slice.rb', line 110

def app?
  eql?(app)
end

#bootself

Boots the slice.

This will prepare the slice (if not already prepared), start each of its providers, register all the slice’s components from its Ruby source files, and import components from any other slices. It will also boot any of the slice’s own registered nested slices. It will then freeze its container so no further components can be registered.

Call ‘boot` if you want to fully load a slice and incur all load time up front, such as when preparing an app to serve web requests. Booting slices is the approach taken when running Hanami’s standard Puma setup (see ‘config.ru`).

Returns:

  • (self)

See Also:

Since:

  • 2.0.0



325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/hanami/slice.rb', line 325

def boot
  return self if booted?

  prepare

  container.finalize!
  slices.each(&:boot)

  @booted = true

  self
end

#booted?Boolean

Returns true if the slice has been booted.

Returns:

  • (Boolean)

See Also:

Since:

  • 2.0.0



370
371
372
# File 'lib/hanami/slice.rb', line 370

def booted?
  !!@booted
end

#call(rack_env) ⇒ Array

Calls the slice’s [Rack] app and returns a Rack-compatible response object

[rack]: github.com/rack/rack

Parameters:

  • rack_env (Hash)

    the Rack environment for the request

Returns:

  • (Array)

    the three-element Rack response array

See Also:

Since:

  • 2.0.0

Since:

  • 2.0.0



790
791
792
# File 'lib/hanami/slice.rb', line 790

def call(...)
  rack_app.call(...)
end

#configHanami::Config

Returns the slice’s config.

A slice’s config is copied from the app config at time of first access.

Returns:

See Also:

  • App::ClassMethods.config

Since:

  • 2.0.0



124
125
126
127
128
129
# File 'lib/hanami/slice.rb', line 124

def config
  @config ||= app.config.dup.tap do |slice_config|
    # Unset config from app that does not apply to ordinary slices
    slice_config.root = nil
  end
end

#configure_provider(*args, **kwargs, &block) ⇒ Object

Since:

  • 2.1.0



532
533
534
# File 'lib/hanami/slice.rb', line 532

def configure_provider(*args, **kwargs, &block)
  container.register_provider(*args, **kwargs, from: :hanami, &block)
end

#environment(env_name) ⇒ self #environment(env_name) {|slice| ... } ⇒ self

Evaluates the block for a given app environment only.

If the given ‘env_name` matches Hanami.env, then the block will be evaluated in the context of `self` (the slice) via `instance_eval`. The slice is also passed as the block’s optional argument.

If the env does not match, then the block is not evaluated at all.

Examples:

module MySlice
  class Slice < Hanami::Slice
    environment(:test) do
      config.logger.level = :info
    end
  end
end

Overloads:

  • #environment(env_name) ⇒ self

    Parameters:

    • env_name (Symbol)

      the environment name

  • #environment(env_name) {|slice| ... } ⇒ self

    Parameters:

    • env_name (Symbol)

      the environment name

    Yield Parameters:

    • slice (self)

      the slice

Returns:

  • (self)

See Also:

Since:

  • 2.0.0



161
162
163
164
# File 'lib/hanami/slice.rb', line 161

def environment(env_name, &block)
  instance_eval(&block) if env_name == config.env
  self
end

#export(keys) ⇒ self

Specifies the components to export from the slice.

Slices importing from this slice can import the specified components only.

Examples:

module MySlice
  class Slice < Hanami::Slice
    export ["search", "index_entity"]
  end
end

Parameters:

  • keys (Array<String>)

    the component keys to export

Returns:

  • (self)

Since:

  • 2.0.0



652
653
654
655
# File 'lib/hanami/slice.rb', line 652

def export(keys)
  container.config.exports = keys
  self
end

#import(from: , as: nil, keys: nil) ⇒ Object

Specifies components to import from another slice.

Booting a slice will register all imported components. For a prepared slice, these components will be be imported automatically when resolved.

Examples:

module MySlice
  class Slice < Hanami:Slice
    # Component from Search::Slice will import as "search.index_entity"
    import keys: ["index_entity"], from: :search
  end
end

Other import variations

# Different key namespace: component will be "search_backend.index_entity"
import keys: ["index_entity"], from: :search, as: "search_backend"

# Import to root key namespace: component will be "index_entity"
import keys: ["index_entity"], from: :search, as: nil

# Import all components
import from: :search

Parameters:

  • keys (Array<String>, nil) (defaults to: nil)

    Array of component keys to import. To import all available components, omit this argument.

  • from (Symbol) (defaults to: )

    name of the slice to import from

  • as (Symbol, String, nil) (defaults to: nil)

See Also:

Since:

  • 2.0.0

Since:

  • 2.0.0



690
691
692
693
694
695
696
697
698
699
700
701
702
703
# File 'lib/hanami/slice.rb', line 690

def import(from:, **kwargs)
  slice = self

  container.after(:configure) do
    if from.is_a?(Symbol) || from.is_a?(String)
      slice_name = from
      from = slice.parent.slices[from.to_sym].container
    end

    as = kwargs[:as] || slice_name

    import(from: from, as: as, **kwargs)
  end
end

#inflectorDry::Inflector

Returns the slice’s configured inflector.

Unless explicitly re-configured for the slice, this will be the app’s inflector.

Returns:

  • (Dry::Inflector)

See Also:

Since:

  • 2.0.0



237
238
239
# File 'lib/hanami/slice.rb', line 237

def inflector
  config.inflector
end

#key?(key) ⇒ Boolean

Returns true if the component with the given key is registered in the container.

For a prepared slice, calling ‘key?` will also try to load the component if not loaded already.

Parameters:

  • key (String, Symbol)

    the component key

Returns:

  • (Boolean)

Since:

  • 2.0.0

Since:

  • 2.0.0



584
585
586
# File 'lib/hanami/slice.rb', line 584

def key?(...)
  container.key?(...)
end

#keysArray<String>

Returns an array of keys for all currently registered components in the container.

For a prepared slice, this will be the set of components that have been previously resolved. For a booted slice, this will be all components available for the slice.

Returns:

  • (Array<String>)

Since:

  • 2.0.0



604
605
606
# File 'lib/hanami/slice.rb', line 604

def keys
  container.keys
end

#namespaceModule

Returns the constant for the slice’s module namespace.

Examples:

MySlice::Slice.namespace # => MySlice

Returns:

  • (Module)

    the namespace module constant

See Also:

Since:

  • 2.0.0



188
189
190
# File 'lib/hanami/slice.rb', line 188

def namespace
  slice_name.namespace
end

#prepareself #prepare(provider_name) ⇒ self

Overloads:

  • #prepareself

    Prepares the slice.

    This will define the slice’s ‘Slice` and `Deps` constants, make all Ruby source files inside the slice’s root dir autoloadable, as well as lazily loadable as container components.

    Call ‘prepare` when you want to access particular components within the slice while still minimizing load time. Preparing slices is the approach taken when loading the Hanami console or when running tests.

    Returns:

    • (self)

    See Also:

    Since:

    • 2.0.0

  • #prepare(provider_name) ⇒ self

    Prepares a provider.

    This triggers the provider’s ‘prepare` lifecycle step.

    Parameters:

    • provider_name (Symbol)

      the name of the provider to start

    Returns:

    • (self)

    Since:

    • 2.0.0

Since:

  • 2.0.0



270
271
272
273
274
275
276
277
278
# File 'lib/hanami/slice.rb', line 270

def prepare(provider_name = nil)
  if provider_name
    container.prepare(provider_name)
  else
    prepare_slice
  end

  self
end

#prepare_container {|container| ... } ⇒ self

Captures the given block to be called with the slice’s container during the slice’s ‘prepare` step, after the slice has already configured the container.

This is intended for advanced usage only and should not be needed for ordinary slice configuration and usage.

Examples:

module MySlice
  class Slice < Hanami::Slice
    prepare_container do |container|
      # ...
    end
  end
end

Yield Parameters:

  • container (Dry::System::Container)

    the slice’s container

Returns:

  • (self)

See Also:

Since:

  • 2.0.0



303
304
305
306
# File 'lib/hanami/slice.rb', line 303

def prepare_container(&block)
  @prepare_container_block = block
  self
end

#prepared?Boolean

Returns true if the slice has been prepared.

Returns:

  • (Boolean)

See Also:

Since:

  • 2.0.0



358
359
360
# File 'lib/hanami/slice.rb', line 358

def prepared?
  !!@prepared
end

#rack_app#call?

Returns a [Rack] app for the slice, or nil if no routes are defined.

The rack app will be memoized on first access.

[rack]: github.com/rack/rack

Returns:

  • (#call, nil)

    the rack app, or nil if no routes are defined

See Also:

Since:

  • 2.0.0



771
772
773
774
775
# File 'lib/hanami/slice.rb', line 771

def rack_app
  return unless router

  @rack_app ||= router.to_rack_app
end

#register(key, object) ⇒ container #register(key, memoize: false, &block) ⇒ container #register(key, call: true, &block) ⇒ container

Registers a component in the slice’s container.

Overloads:

  • #register(key, object) ⇒ container

    Registers the given object as the component. This same object will be returned whenever the component is resolved.

    Parameters:

    • key (String)

      the component’s key

    • object (Object)

      the object to register as the component

  • #register(key, memoize: false, &block) ⇒ container

    Registers the given block as the component. When the component is resolved, the return value of the block will be returned.

    Since the block is not called until resolution-time, this is a useful way to register components that have dependencies on other components in the container, which as yet may be unavailable at the time of registration.

    All auto-registered components are registered in block form.

    When ‘memoize` is true, the component will be memoized upon first resolution and the same object returned on all subsequent resolutions, meaning the block is only called once. Otherwise, the block will be called and a new object returned on every resolution.

    Parameters:

    • key (String)

      the component’s key

    • memoize (Boolean) (defaults to: false)

    Yield Returns:

    • (Object)

      the object to register as the component

  • #register(key, call: true, &block) ⇒ container

    Registers the given block as the component. When ‘call: false` is given, then the block itself will become the component.

    When such a component is resolved, the block will not be called, and instead the ‘Proc` object for that block will be returned.

    Parameters:

    • key (String)

      the component’s key

    • call (Boolean) (defaults to: true)

Returns:

See Also:

Since:

  • 2.0.0



462
463
464
# File 'lib/hanami/slice.rb', line 462

def register(...)
  container.register(...)
end

#register_provider(name, namespace: nil, from: nil, source: nil) ⇒ container

Registers a provider and its lifecycle hooks.

In most cases, you should call this from a dedicated file for the provider in your app or slice’s ‘config/providers/` dir. This allows the provider to be loaded when individual matching components are resolved (for prepared slices) or when slices are booted.

Examples:

Simple provider

# config/providers/db.rb
Hanami.app.register_provider(:db) do
  start do
    require "db"
    register("db", DB.new)
  end
end

Provider with lifecycle steps, also using dependencies from the target container

# config/providers/db.rb
Hanami.app.register_provider(:db) do
  prepare do
    require "db"
    db = DB.new(target_container["settings"].database_url)
    register("db", db)
  end

  start do
    container["db"].establish_connection
  end

  stop do
    container["db"].close_connection
  end
end

Probvider registration under a namespace

# config/providers/db.rb
Hanami.app.register_provider(:persistence, namespace: true) do
  start do
    require "db"

    # Namespace option above means this will be registered as "persistence.db"
    register("db", DB.new)
  end
end

Parameters:

  • name (Symbol)

    the unique name for the provider

  • namespace (Boolean, String, nil) (defaults to: nil)

    register components from the provider with given namespace. May be an explicit string, or ‘true` for the namespace to be the provider’s name

  • from (Symbol, nil) (defaults to: nil)

    the group for an external provider source to use, with the provider source name inferred from ‘name` or passed explicitly as `source:`

  • source (Symbol, nil) (defaults to: nil)

    the name of the external provider source to use, if different from the value provided as ‘name`

  • if (Boolean)

    a boolean-returning expression to determine whether to register the provider

Returns:

Since:

  • 2.0.0

Since:

  • 2.0.0



526
527
528
# File 'lib/hanami/slice.rb', line 526

def register_provider(...)
  container.register_provider(...)
end

#register_slice(name) {|slice| ... } ⇒ slices #register_slice(name, slice_class) ⇒ slices

Overloads:

  • #register_slice(name) {|slice| ... } ⇒ slices

    Registers a nested slice with the given name.

    This will define a new Hanami::Slice subclass for the slice. If a block is given, it is passed the class object, and will be evaluated in the context of the class like ‘class_eval`.

    Examples:

    MySlice::Slice.register_slice do
      # Configure the slice or do other class-level things here
    end

    Parameters:

    • name (Symbol)

      the identifier for the slice to be registered

    Yield Parameters:

  • #register_slice(name, slice_class) ⇒ slices

    Registers a nested slice with the given name.

    The given ‘slice_class` will be registered as the slice. It must be a subclass of Hanami::Slice.

    Parameters:

    • name (Symbol)

      the identifier for the slice to be registered

    • slice_class (Hanami::Slice)

Returns:

See Also:

Since:

  • 2.0.0



414
415
416
# File 'lib/hanami/slice.rb', line 414

def register_slice(...)
  slices.register(...)
end

#registered?Boolean

Required for the slice to act as a provider target

Returns:

  • (Boolean)

Since:

  • 2.2.0



591
592
593
# File 'lib/hanami/slice.rb', line 591

def registered?(...)
  container.registered?(...)
end

#resolveObject

See Also:

Since:

  • 2.0.0



631
632
633
# File 'lib/hanami/slice.rb', line 631

def resolve(...)
  container.resolve(...)
end

#rootPathname

Returns the slice’s root, either the root as explicitly configured, or a default fallback of the slice’s name within the app’s ‘slices/` dir.

Returns:

  • (Pathname)

See Also:

Since:

  • 2.0.0



201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/hanami/slice.rb', line 201

def root
  # Provides a best guess for a root when it is not yet configured.
  #
  # This is particularly useful for user-defined slice classes that access `settings` inside
  # the class body (since the root needed to find the settings file). In this case,
  # `configuration.root` may be nil when `settings` is called, since the root is configured by
  # `SliceRegistrar#configure_slice` _after_ the class is loaded.
  #
  # In common cases, this best guess will be correct since most Hanami slices will be expected
  # to live in the app SLICES_DIR. For advanced cases, the correct slice root should be
  # explicitly configured at the beginning of the slice class body, before any calls to
  # `settings`.
  config.root || app.root.join(SLICES_DIR, slice_name.to_s)
end

#router(inspector: nil) ⇒ Hanami::Slice::Router?

Returns the slice’s router, if or nil if no routes are defined.

An optional ‘inspector`, implementing the `Hanami::Router::Inspector` interface, may be provided at first call (the router is then memoized for subsequent accesses). An inspector is used by the `hanami routes` CLI comment to provide a list of available routes.

The returned router is a Router, which provides all ‘Hanami::Router` functionality, with the addition of support for slice mounting with the Router#slice.

Parameters:

  • inspector (Hanami::Router::Inspector, nil) (defaults to: nil)

    an optional routes inspector

Returns:

Raises:

Since:

  • 2.0.0



750
751
752
753
754
755
756
# File 'lib/hanami/slice.rb', line 750

def router(inspector: nil)
  raise SliceLoadError, "#{self} must be prepared before loading the router" unless prepared?

  @_mutex.synchronize do
    @_router ||= load_router(inspector: inspector)
  end
end

#routesHanami::Routes?

Returns the slice’s routes, or nil if no routes are defined.

You can define your routes in ‘config/routes.rb`.

Returns:

See Also:

Since:

  • 2.0.0



731
732
733
# File 'lib/hanami/slice.rb', line 731

def routes
  @routes ||= load_routes
end

#settingsHanami::Settings?

Returns the slice’s settings, or nil if no settings are defined.

You can define your settings in ‘config/settings.rb`.

Returns:

See Also:

Since:

  • 2.0.0



715
716
717
718
719
# File 'lib/hanami/slice.rb', line 715

def settings
  return @settings if instance_variable_defined?(:@settings)

  @settings = Settings.load_for_slice(self)
end

#shutdownself

Shuts down the slice’s providers, as well as the providers in any nested slices.

Returns:

  • (self)

Since:

  • 2.0.0



344
345
346
347
348
# File 'lib/hanami/slice.rb', line 344

def shutdown
  slices.each(&:shutdown)
  container.shutdown!
  self
end

#slice_nameSliceName

Returns a Hanami::SliceName for the slice, an object with methods returning the name of the slice in various formats.

Returns:

Since:

  • 2.0.0



173
174
175
# File 'lib/hanami/slice.rb', line 173

def slice_name
  @slice_name ||= SliceName.new(self, inflector: method(:inflector))
end

#slicesSliceRegistrar

Returns the slice’s collection of nested slices.

Returns:

See Also:

Since:

  • 2.0.0



382
383
384
# File 'lib/hanami/slice.rb', line 382

def slices
  @slices ||= SliceRegistrar.new(self)
end

#source_pathPathname

Returns the slice’s root component directory, accounting for App as a special case.

Returns:

  • (Pathname)

Since:

  • 2.2.0



222
223
224
# File 'lib/hanami/slice.rb', line 222

def source_path
  app? ? root.join(APP_DIR) : root
end

#start(provider_name) ⇒ container

Starts a provider.

This triggers the provider’s ‘prepare` and `start` lifecycle steps.

Examples:

MySlice::Slice.start(:persistence)

Parameters:

  • provider_name (Symbol)

    the name of the provider to start

Returns:

Since:

  • 2.0.0

Since:

  • 2.0.0



550
551
552
# File 'lib/hanami/slice.rb', line 550

def start(...)
  container.start(...)
end

#stop(provider_name) ⇒ container

Stops a provider.

This triggers the provider’s ‘stop` lifecycle hook.

Examples:

MySlice::Slice.stop(:persistence)

Parameters:

  • provider_name (Symbol)

    the name of the provider to start

Returns:

Since:

  • 2.0.0

Since:

  • 2.0.0



568
569
570
# File 'lib/hanami/slice.rb', line 568

def stop(...)
  container.stop(...)
end