Class: Dry::System::Container
- Inherits:
-
Object
- Object
- Dry::System::Container
- Extended by:
- Core::Container::Mixin, Plugins
- Defined in:
- lib/dry/system/container.rb,
lib/dry/system/stubs.rb
Overview
Abstract container class to inherit from
Container class is treated as a global registry with all system components. Container can also import dependencies from other containers, which is useful in complex systems that are split into sub-systems.
Container can be finalized, which triggers loading of all the defined components within a system, after finalization it becomes frozen. This typically happens in cases like booting a web application.
Before finalization, Container can lazy-load components on demand. A component can be a simple class defined in a single file, or a complex component which has init/start/stop lifecycle, and it’s defined in a boot file. Components which specify their dependencies using Import module can be safely required in complete isolation, and Container will resolve and load these dependencies automatically.
Furthermore, Container supports auto-registering components based on dir/file naming conventions. This reduces a lot of boilerplate code as all you have to do is to put your classes under configured directories and their instances will be automatically registered within a container.
Every container needs to be configured with following settings:
-
‘:name` - a unique container name
-
‘:root` - a system root directory (defaults to `pwd`)
Defined Under Namespace
Modules: Stubs
Class Method Summary collapse
-
._configurable_finalize! ⇒ Object
private
Finalizes the config for this container.
-
.add_to_load_path!(*dirs) ⇒ self
Adds the directories (relative to the container’s root) to the Ruby load path.
-
.after(event, &block) ⇒ self
Registers a callback hook to run after container lifecycle events.
- .auto_registrar ⇒ Object private
-
.before(event, &block) ⇒ self
Registers a callback hook to run before container lifecycle events.
- .component_dirs ⇒ Object private
-
.config ⇒ Dry::Configurable::Config
Returns the configuration for the container.
-
.configure(finalize_config: true, &block) ⇒ self
Yields a configuration object for the container, which you can use to modify the configuration, then runs the after-‘configured` hooks and finalizes (freezes) the Container.config.
-
.configured!(finalize_config: true) ⇒ self
Marks the container as configured, runs the after-‘configured` hooks, then finalizes (freezes) the Container.config.
- .configured? ⇒ Boolean
-
.enable_stubs! ⇒ Object
Enables stubbing container’s components.
-
.finalize!(freeze: true) {|_self| ... } ⇒ self
Finalizes the container.
-
.finalized? ⇒ TrueClass, FalseClass
Return if a container was finalized.
- .hooks ⇒ Object private
-
.import(from:, as:, keys: nil) ⇒ Object
Registers another container for import.
- .importer ⇒ Object private
- .inherited(klass) ⇒ Object private
-
.injector(**options) ⇒ Object
Builds injector for this container.
-
.key?(key) ⇒ Boolean
Check if identifier is registered.
- .load_registrations!(name) ⇒ Object
- .manifest_registrar ⇒ Object private
-
.prepare(name) ⇒ self
Prepares a provider using its ‘prepare` lifecycle trigger.
- .providers ⇒ Object private
-
.register_provider(name, namespace: nil, from: nil, source: nil) ⇒ self
Registers a provider and its lifecycle hooks.
-
.registered?(key) ⇒ Boolean
Whether a
key
is registered (doesn’t trigger loading). -
.require_from_root(*paths) ⇒ Object
Requires one or more files relative to the container’s root.
- .resolve(key) ⇒ Object
-
.root ⇒ Pathname
Returns container’s root path.
- .shutdown! ⇒ Object
-
.start(name) ⇒ self
Starts a provider.
-
.stop(name) ⇒ self
Stop a specific component but calls only ‘stop` lifecycle trigger.
Methods included from Plugins
enabled_plugins, inherited, loaded_dependencies, register, registry, use
Class Method Details
._configurable_finalize! ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Finalizes the config for this container
141 |
# File 'lib/dry/system/container.rb', line 141 alias_method :_configurable_finalize!, :finalize! |
.add_to_load_path!(*dirs) ⇒ self
Adds the directories (relative to the container’s root) to the Ruby load path
409 410 411 412 413 414 |
# File 'lib/dry/system/container.rb', line 409 def add_to_load_path!(*dirs) dirs.reverse.map(&root.method(:join)).each do |path| $LOAD_PATH.prepend(path.to_s) unless $LOAD_PATH.include?(path.to_s) end self end |
.after(event, &block) ⇒ self
Registers a callback hook to run after container lifecycle events.
The supported events are:
-
‘:configured`, called when you run configure or configured!, or when running finalize! and neither of the prior two methods have been called.
-
‘:finalized`, called when you run finalize!.
When the given block is called, ‘self` is the container class, and no block arguments are given.
582 583 584 585 |
# File 'lib/dry/system/container.rb', line 582 def after(event, &block) hooks[:"after_#{event}"] << block self end |
.auto_registrar ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
532 533 534 |
# File 'lib/dry/system/container.rb', line 532 def auto_registrar @auto_registrar ||= config.auto_registrar.new(self) end |
.before(event, &block) ⇒ self
Registers a callback hook to run before container lifecycle events.
Currently, the only supported event is ‘:finalized`. This hook is called when you run `finalize!`.
When the given block is called, ‘self` is the container class, and no block arguments are given.
560 561 562 563 |
# File 'lib/dry/system/container.rb', line 560 def before(event, &block) hooks[:"before_#{event}"] << block self end |
.component_dirs ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
522 523 524 |
# File 'lib/dry/system/container.rb', line 522 def component_dirs config.component_dirs.to_a.map { |dir| ComponentDir.new(config: dir, container: self) } end |
.config ⇒ Dry::Configurable::Config
Returns the configuration for the container
|
# File 'lib/dry/system/container.rb', line 74
|
.configure(finalize_config: true, &block) ⇒ self
Yields a configuration object for the container, which you can use to modify the configuration, then runs the after-‘configured` hooks and finalizes (freezes) the config.
Does not finalize the config when given ‘finalize_config: false`
106 107 108 109 |
# File 'lib/dry/system/container.rb', line 106 def configure(finalize_config: true, &block) super(&block) configured!(finalize_config: finalize_config) end |
.configured!(finalize_config: true) ⇒ self
Marks the container as configured, runs the after-‘configured` hooks, then finalizes (freezes) the config.
This method is useful to call if you’re modifying the container’s config directly, rather than via the config object yielded when calling configure.
Does not finalize the config if given ‘finalize_config: false`.
126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/dry/system/container.rb', line 126 def configured!(finalize_config: true) return self if configured? hooks[:after_configure].each { |hook| instance_eval(&hook) } _configurable_finalize! if finalize_config @__configured__ = true self end |
.configured? ⇒ Boolean
143 144 145 |
# File 'lib/dry/system/container.rb', line 143 def configured? @__configured__.equal?(true) end |
.enable_stubs! ⇒ Object
Enables stubbing container’s components
32 33 34 35 36 |
# File 'lib/dry/system/stubs.rb', line 32 def self.enable_stubs! super extend ::Dry::System::Container::Stubs self end |
.finalize!(freeze: true) {|_self| ... } ⇒ self
Finalizes the container
This triggers importing components from other containers, booting registered components and auto-registering components. It should be called only in places where you want to finalize your system as a whole, ie when booting a web application
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/dry/system/container.rb', line 317 def finalize!(freeze: true, &block) return self if finalized? configured! hooks[:before_finalize].each { |hook| instance_eval(&hook) } yield(self) if block providers.finalize! auto_registrar.finalize! manifest_registrar.finalize! importer.finalize! @__finalized__ = true self.freeze if freeze hooks[:after_finalize].each { |hook| instance_eval(&hook) } self end |
.finalized? ⇒ TrueClass, FalseClass
Return if a container was finalized
284 285 286 |
# File 'lib/dry/system/container.rb', line 284 def finalized? @__finalized__.equal?(true) end |
.hooks ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
588 589 590 |
# File 'lib/dry/system/container.rb', line 588 def hooks @hooks ||= Hash.new { |h, k| h[k] = [] } end |
.import(from:, as:, keys: nil) ⇒ Object
Registers another container for import
178 179 180 181 182 183 184 |
# File 'lib/dry/system/container.rb', line 178 def import(from:, as:, keys: nil) raise Dry::System::ContainerAlreadyFinalizedError if finalized? importer.register(container: from, namespace: as, keys: keys) self end |
.importer ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
542 543 544 |
# File 'lib/dry/system/container.rb', line 542 def importer @importer ||= config.importer.new(self) end |
.inherited(klass) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
593 594 595 596 597 598 599 600 601 602 |
# File 'lib/dry/system/container.rb', line 593 def inherited(klass) hooks.each do |event, blocks| klass.hooks[event].concat blocks.dup end klass.instance_variable_set(:@__configured__, false) klass.instance_variable_set(:@__finalized__, false) super end |
.injector(**options) ⇒ Object
Builds injector for this container
An injector is a useful mixin which injects dependencies into automatically defined constructor.
447 448 449 |
# File 'lib/dry/system/container.rb', line 447 def injector(**) Dry::AutoInject(self, **) end |
.key?(key) ⇒ Boolean
Check if identifier is registered. If not, try to load the component
512 513 514 515 516 517 518 519 |
# File 'lib/dry/system/container.rb', line 512 def key?(key) if finalized? registered?(key) else registered?(key) || resolve(key) { return false } true end end |
.load_registrations!(name) ⇒ Object
417 418 419 420 |
# File 'lib/dry/system/container.rb', line 417 def load_registrations!(name) manifest_registrar.(name) self end |
.manifest_registrar ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
537 538 539 |
# File 'lib/dry/system/container.rb', line 537 def manifest_registrar @manifest_registrar ||= config.manifest_registrar.new(self) end |
.prepare(name) ⇒ self
Prepares a provider using its ‘prepare` lifecycle trigger
Preparing (as opposed to starting) a provider is useful in places where some aspects of a heavier dependency are needed, but its fully started environment
367 368 369 370 |
# File 'lib/dry/system/container.rb', line 367 def prepare(name) providers.prepare(name) self end |
.providers ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
527 528 529 |
# File 'lib/dry/system/container.rb', line 527 def providers @providers ||= config.provider_registrar.new(self) end |
.register_provider(name, namespace: nil, from: nil, source: nil) ⇒ self
273 274 275 |
# File 'lib/dry/system/container.rb', line 273 def register_provider(...) providers.register_provider(...) end |
.registered?(key) ⇒ Boolean
Whether a key
is registered (doesn’t trigger loading)
497 |
# File 'lib/dry/system/container.rb', line 497 alias_method :registered?, :key? |
.require_from_root(*paths) ⇒ Object
Requires one or more files relative to the container’s root
463 464 465 466 467 468 469 |
# File 'lib/dry/system/container.rb', line 463 def require_from_root(*paths) paths.flat_map { |path| path.to_s.include?("*") ? ::Dir[root.join(path)].sort : root.join(path) }.each { |path| Kernel.require path.to_s } end |
.resolve(key) ⇒ Object
490 491 492 493 494 |
# File 'lib/dry/system/container.rb', line 490 def resolve(key) load_component(key) unless finalized? super end |
.root ⇒ Pathname
Returns container’s root path
485 486 487 |
# File 'lib/dry/system/container.rb', line 485 def root config.root end |
.shutdown! ⇒ Object
388 389 390 391 |
# File 'lib/dry/system/container.rb', line 388 def shutdown! providers.shutdown self end |
.start(name) ⇒ self
Starts a provider
As a result, the provider’s ‘prepare` and `start` lifecycle triggers are called
349 350 351 352 |
# File 'lib/dry/system/container.rb', line 349 def start(name) providers.start(name) self end |
.stop(name) ⇒ self
Stop a specific component but calls only ‘stop` lifecycle trigger
382 383 384 385 |
# File 'lib/dry/system/container.rb', line 382 def stop(name) providers.stop(name) self end |