Class: Dry::System::Container
- Inherits:
-
Object
- Object
- Dry::System::Container
- Extended by:
- Configurable, Container::Mixin
- 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 identifier:root- a system root directory (defaults topwd):system_dir- directory name relative to root, where bootable components can be defined inbootdir this defaults tosystem
Defined Under Namespace
Modules: Stubs
Class Method Summary collapse
-
.auto_register!(dir) { ... } ⇒ self
Auto-registers components from the provided directory.
- .auto_registrar ⇒ Object private
- .booter ⇒ Object private
- .component(key, **options) ⇒ Object private
-
.configure(&block) ⇒ self
Configures the container.
- .enable_stubs! ⇒ Object
-
.finalize(name, &block) ⇒ self
Registers finalization function for a bootable component.
-
.finalize!(freeze: true) {|_self| ... } ⇒ self
Finalizes the container.
-
.import(other) ⇒ Object
Registers another container for import.
- .importer ⇒ Object private
-
.init(name) ⇒ self
Boots a specific component but calls only
initlifecycle trigger. -
.injector(options = {}) ⇒ Object
Builds injector for this container.
- .load_component(key) ⇒ Object private
- .load_paths ⇒ Object private
-
.load_paths!(*dirs) ⇒ self
Sets load paths relative to the container's root dir.
- .load_registrations!(name) ⇒ Object
- .manual_registrar ⇒ Object private
-
.require(*paths) ⇒ Object
Requires one or more files relative to the container's root.
- .require_component(component) ⇒ Object private
- .resolve(key) ⇒ Object
-
.root ⇒ Pathname
Returns container's root path.
-
.start(name) ⇒ self
Boots a specific component.
Class Method Details
.auto_register!(dir) { ... } ⇒ self
Auto-registers components from the provided directory
Typically you want to configure auto_register directories, and it will work automatically. Use this method in cases where you want to have an explicit way where some components are auto-registered, or if you want to exclude some components from been auto-registered
362 363 364 365 |
# File 'lib/dry/system/container.rb', line 362 def auto_register!(dir, &block) auto_registrar.(dir, &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.
452 453 454 |
# File 'lib/dry/system/container.rb', line 452 def auto_registrar @auto_registrar ||= config.auto_registrar.new(self) end |
.booter ⇒ 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.
447 448 449 |
# File 'lib/dry/system/container.rb', line 447 def booter @booter ||= config.booter.new(root.join("#{config.system_dir}/boot")) end |
.component(key, **options) ⇒ 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.
467 468 469 470 471 472 473 474 475 |
# File 'lib/dry/system/container.rb', line 467 def component(key, **) Component.new( key, loader: config.loader, namespace: config.default_namespace, separator: config.namespace_separator, **, ) end |
.configure(&block) ⇒ self
Configures the container
97 98 99 100 101 |
# File 'lib/dry/system/container.rb', line 97 def configure(&block) super(&block) load_paths!(config.system_dir) self end |
.enable_stubs! ⇒ Object
15 16 17 |
# File 'lib/dry/system/stubs.rb', line 15 def self.enable_stubs! extend ::Dry::System::Container::Stubs end |
.finalize(name, &block) ⇒ self
Registers finalization function for a bootable component
By convention, boot files for components should be placed in
%{system_dir}/boot and they will be loaded on demand when components
are loaded in isolation, or during finalization process.
211 212 213 214 |
# File 'lib/dry/system/container.rb', line 211 def finalize(name, &block) booter[name] = [self, block] 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
245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/dry/system/container.rb', line 245 def finalize!(freeze: true, &block) return self if frozen? yield(self) if block importer.finalize! booter.finalize! manual_registrar.finalize! auto_registrar.finalize! self.freeze if freeze end |
.import(other) ⇒ Object
Registers another container for import
129 130 131 132 133 134 135 136 |
# File 'lib/dry/system/container.rb', line 129 def import(other) case other when Hash then importer.register(other) when Dry::Container::Namespace then super else raise ArgumentError, "+other+ must be a hash of names and systems, or a Dry::Container namespace" end 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.
462 463 464 |
# File 'lib/dry/system/container.rb', line 462 def importer @importer ||= config.importer.new(self) end |
.init(name) ⇒ self
Boots a specific component but calls only init lifecycle trigger
This way of booting is useful in places where a heavy dependency is needed but its started environment is not required
289 290 291 292 |
# File 'lib/dry/system/container.rb', line 289 def init(name) booter.init(name) self end |
.injector(options = {}) ⇒ Object
Builds injector for this container
An injector is a useful mixin which injects dependencies into automatically defined constructor.
392 393 394 |
# File 'lib/dry/system/container.rb', line 392 def injector( = {}) Dry::AutoInject(self, ) end |
.load_component(key) ⇒ 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.
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 |
# File 'lib/dry/system/container.rb', line 491 def load_component(key) return self if key?(key) component(key).tap do |component| root_key = component.root_key if importer.key?(root_key) load_external_component(component.namespaced(root_key)) else load_local_component(component) end end self end |
.load_paths ⇒ 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.
442 443 444 |
# File 'lib/dry/system/container.rb', line 442 def load_paths @load_paths ||= [] end |
.load_paths!(*dirs) ⇒ self
Sets load paths relative to the container's root dir
311 312 313 314 315 316 317 318 |
# File 'lib/dry/system/container.rb', line 311 def load_paths!(*dirs) dirs.map(&root.method(:join)).each do |path| next if load_paths.include?(path) load_paths << path $LOAD_PATH.unshift(path.to_s) end self end |
.load_registrations!(name) ⇒ Object
321 322 323 324 |
# File 'lib/dry/system/container.rb', line 321 def load_registrations!(name) manual_registrar.(name) self end |
.manual_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.
457 458 459 |
# File 'lib/dry/system/container.rb', line 457 def manual_registrar @manual_registrar ||= config.manual_registrar.new(self) end |
.require(*paths) ⇒ Object
Requires one or more files relative to the container's root
408 409 410 411 412 413 414 |
# File 'lib/dry/system/container.rb', line 408 def require(*paths) paths.flat_map { |path| path.to_s.include?('*') ? Dir[root.join(path)] : root.join(path) }.each { |path| Kernel.require path.to_s } end |
.require_component(component) ⇒ 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.
478 479 480 481 482 483 484 485 486 487 488 |
# File 'lib/dry/system/container.rb', line 478 def require_component(component) return if key?(component.identifier) unless component.file_exists?(load_paths) raise FileNotFoundError, component end Kernel.require(component.path) yield end |
.resolve(key) ⇒ Object
435 436 437 438 439 |
# File 'lib/dry/system/container.rb', line 435 def resolve(key) load_component(key) unless frozen? super end |
.root ⇒ Pathname
Returns container's root path
430 431 432 |
# File 'lib/dry/system/container.rb', line 430 def root config.root end |
.start(name) ⇒ self
Boots a specific component
As a result, init and start lifecycle triggers are called
270 271 272 273 |
# File 'lib/dry/system/container.rb', line 270 def start(name) booter.start(name) self end |