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) ⇒ 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
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(key) ⇒ Object
-
.register_provider(name, namespace: nil, from: nil, source: nil) ⇒ self
Registers a provider and its lifecycle hooks.
-
.registered?(key) ⇒ Boolean
Whether a
keyis 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, 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
403 404 405 406 407 408 |
# File 'lib/dry/system/container.rb', line 403 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.
585 586 587 588 |
# File 'lib/dry/system/container.rb', line 585 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.
535 536 537 |
# File 'lib/dry/system/container.rb', line 535 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.
563 564 565 566 |
# File 'lib/dry/system/container.rb', line 563 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.
525 526 527 |
# File 'lib/dry/system/container.rb', line 525 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) ⇒ 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, &) super(&) 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
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
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/dry/system/container.rb', line 313 def finalize!(freeze: true, &) return self if finalized? configured! run_hooks(:finalize) do yield(self) if block_given? [providers, auto_registrar, manifest_registrar, importer].each(&:finalize!) @__finalized__ = true end self.freeze if freeze self end |
.finalized? ⇒ TrueClass, FalseClass
Return if a container was finalized
280 281 282 |
# File 'lib/dry/system/container.rb', line 280 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.
591 592 593 |
# File 'lib/dry/system/container.rb', line 591 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.
545 546 547 |
# File 'lib/dry/system/container.rb', line 545 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.
596 597 598 599 600 601 602 603 604 605 |
# File 'lib/dry/system/container.rb', line 596 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.
441 442 443 |
# File 'lib/dry/system/container.rb', line 441 def injector(**) Dry::AutoInject(self, **) end |
.key?(key) ⇒ Boolean
Check if identifier is registered. If not, try to load the component
515 516 517 518 519 520 521 522 |
# File 'lib/dry/system/container.rb', line 515 def key?(key) if finalized? registered?(key) else registered?(key) || resolve(key) { return false } true end end |
.load_registrations!(name) ⇒ Object
411 412 413 414 |
# File 'lib/dry/system/container.rb', line 411 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.
540 541 542 |
# File 'lib/dry/system/container.rb', line 540 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
361 362 363 364 |
# File 'lib/dry/system/container.rb', line 361 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.
530 531 532 |
# File 'lib/dry/system/container.rb', line 530 def providers @providers ||= config.provider_registrar.new(self) end |
.register(key) ⇒ Object
484 485 486 487 488 489 490 |
# File 'lib/dry/system/container.rb', line 484 def register(key, *) super hooks[:after_register].each { |hook| instance_exec(key, &hook) } self end |
.register_provider(name, namespace: nil, from: nil, source: nil) ⇒ self
271 272 273 |
# File 'lib/dry/system/container.rb', line 271 def register_provider(...) providers.register_provider(...) end |
.registered?(key) ⇒ Boolean
Whether a key is registered (doesn’t trigger loading)
500 |
# File 'lib/dry/system/container.rb', line 500 alias_method :registered?, :key? |
.require_from_root(*paths) ⇒ Object
Requires one or more files relative to the container’s root
457 458 459 460 461 462 463 |
# File 'lib/dry/system/container.rb', line 457 def require_from_root(*paths) paths.flat_map { |path| path.to_s.include?("*") ? ::Dir[root.join(path)] : root.join(path) }.each { |path| Kernel.require path.to_s } end |
.resolve(key) ⇒ Object
493 494 495 496 497 |
# File 'lib/dry/system/container.rb', line 493 def resolve(key) load_component(key) unless finalized? super end |
.root ⇒ Pathname
Returns container’s root path
479 480 481 |
# File 'lib/dry/system/container.rb', line 479 def root config.root end |
.shutdown! ⇒ Object
382 383 384 385 |
# File 'lib/dry/system/container.rb', line 382 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
343 344 345 346 |
# File 'lib/dry/system/container.rb', line 343 def start(name) providers.start(name) self end |
.stop(name) ⇒ self
Stop a specific component but calls only ‘stop` lifecycle trigger
376 377 378 379 |
# File 'lib/dry/system/container.rb', line 376 def stop(name) providers.stop(name) self end |