dry-system 0.18.2 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +88 -4
- data/LICENSE +1 -1
- data/README.md +1 -1
- data/dry-system.gemspec +3 -4
- data/lib/dry/system/auto_registrar.rb +16 -58
- data/lib/dry/system/booter.rb +34 -15
- data/lib/dry/system/component.rb +56 -94
- data/lib/dry/system/component_dir.rb +128 -0
- data/lib/dry/system/config/component_dir.rb +202 -0
- data/lib/dry/system/config/component_dirs.rb +184 -0
- data/lib/dry/system/container.rb +78 -144
- data/lib/dry/system/errors.rb +21 -12
- data/lib/dry/system/identifier.rb +157 -0
- data/lib/dry/system/loader/autoloading.rb +26 -0
- data/lib/dry/system/loader.rb +40 -41
- data/lib/dry/system/plugins/logging.rb +4 -1
- data/lib/dry/system/version.rb +1 -1
- metadata +16 -26
- data/lib/dry/system/auto_registrar/configuration.rb +0 -43
data/lib/dry/system/container.rb
CHANGED
@@ -11,7 +11,6 @@ require "dry/core/deprecations"
|
|
11
11
|
|
12
12
|
require "dry/system"
|
13
13
|
require "dry/system/errors"
|
14
|
-
require "dry/system/loader"
|
15
14
|
require "dry/system/booter"
|
16
15
|
require "dry/system/auto_registrar"
|
17
16
|
require "dry/system/manual_registrar"
|
@@ -20,6 +19,9 @@ require "dry/system/component"
|
|
20
19
|
require "dry/system/constants"
|
21
20
|
require "dry/system/plugins"
|
22
21
|
|
22
|
+
require_relative "component_dir"
|
23
|
+
require_relative "config/component_dirs"
|
24
|
+
|
23
25
|
module Dry
|
24
26
|
module System
|
25
27
|
# Abstract container class to inherit from
|
@@ -61,7 +63,7 @@ module Dry
|
|
61
63
|
# end
|
62
64
|
#
|
63
65
|
# # this will configure $LOAD_PATH to include your `lib` dir
|
64
|
-
#
|
66
|
+
# add_dirs_to_load_paths!('lib')
|
65
67
|
# end
|
66
68
|
#
|
67
69
|
# @api public
|
@@ -71,14 +73,12 @@ module Dry
|
|
71
73
|
extend Dry::System::Plugins
|
72
74
|
|
73
75
|
setting :name
|
74
|
-
setting :default_namespace
|
75
76
|
setting(:root, Pathname.pwd.freeze) { |path| Pathname(path) }
|
76
77
|
setting :system_dir, "system"
|
77
78
|
setting :bootable_dirs, ["system/boot"]
|
78
79
|
setting :registrations_dir, "container"
|
79
|
-
setting :
|
80
|
+
setting :component_dirs, Config::ComponentDirs.new, cloneable: true
|
80
81
|
setting :inflector, Dry::Inflector.new
|
81
|
-
setting :loader, Dry::System::Loader
|
82
82
|
setting :booter, Dry::System::Booter
|
83
83
|
setting :auto_registrar, Dry::System::AutoRegistrar
|
84
84
|
setting :manual_registrar, Dry::System::ManualRegistrar
|
@@ -107,7 +107,6 @@ module Dry
|
|
107
107
|
config._settings << _settings[name]
|
108
108
|
self
|
109
109
|
end
|
110
|
-
ruby2_keywords(:setting) if respond_to?(:ruby2_keywords, true)
|
111
110
|
|
112
111
|
# Configures the container
|
113
112
|
#
|
@@ -126,7 +125,6 @@ module Dry
|
|
126
125
|
def configure(&block)
|
127
126
|
hooks[:before_configure].each { |hook| instance_eval(&hook) }
|
128
127
|
super(&block)
|
129
|
-
load_paths!(config.system_dir)
|
130
128
|
hooks[:after_configure].each { |hook| instance_eval(&hook) }
|
131
129
|
self
|
132
130
|
end
|
@@ -385,7 +383,7 @@ module Dry
|
|
385
383
|
self
|
386
384
|
end
|
387
385
|
|
388
|
-
#
|
386
|
+
# Adds the directories (relative to the container's root) to the Ruby load path
|
389
387
|
#
|
390
388
|
# @example
|
391
389
|
# class MyApp < Dry::System::Container
|
@@ -393,7 +391,7 @@ module Dry
|
|
393
391
|
# # ...
|
394
392
|
# end
|
395
393
|
#
|
396
|
-
#
|
394
|
+
# add_to_load_path!('lib')
|
397
395
|
# end
|
398
396
|
#
|
399
397
|
# @param [Array<String>] dirs
|
@@ -401,12 +399,9 @@ module Dry
|
|
401
399
|
# @return [self]
|
402
400
|
#
|
403
401
|
# @api public
|
404
|
-
def
|
405
|
-
dirs.map(&root.method(:join)).each do |path|
|
406
|
-
|
407
|
-
|
408
|
-
load_paths << path
|
409
|
-
$LOAD_PATH.unshift(path.to_s)
|
402
|
+
def add_to_load_path!(*dirs)
|
403
|
+
dirs.reverse.map(&root.method(:join)).each do |path|
|
404
|
+
$LOAD_PATH.prepend(path.to_s) unless $LOAD_PATH.include?(path.to_s)
|
410
405
|
end
|
411
406
|
self
|
412
407
|
end
|
@@ -417,47 +412,6 @@ module Dry
|
|
417
412
|
self
|
418
413
|
end
|
419
414
|
|
420
|
-
# Auto-registers components from the provided directory
|
421
|
-
#
|
422
|
-
# Typically you want to configure auto_register directories, and it will
|
423
|
-
# work automatically. Use this method in cases where you want to have an
|
424
|
-
# explicit way where some components are auto-registered, or if you want
|
425
|
-
# to exclude some components from being auto-registered
|
426
|
-
#
|
427
|
-
# @example
|
428
|
-
# class MyApp < Dry::System::Container
|
429
|
-
# configure do |config|
|
430
|
-
# # ...
|
431
|
-
# end
|
432
|
-
#
|
433
|
-
# # with a dir
|
434
|
-
# auto_register!('lib/core')
|
435
|
-
#
|
436
|
-
# # with a dir and a custom registration block
|
437
|
-
# auto_register!('lib/core') do |config|
|
438
|
-
# config.instance do |component|
|
439
|
-
# # custom way of initializing a component
|
440
|
-
# end
|
441
|
-
#
|
442
|
-
# config.exclude do |component|
|
443
|
-
# # return true to exclude component from auto-registration
|
444
|
-
# end
|
445
|
-
# end
|
446
|
-
# end
|
447
|
-
#
|
448
|
-
# @param [String] dir The dir name relative to the root dir
|
449
|
-
#
|
450
|
-
# @yield AutoRegistrar::Configuration
|
451
|
-
# @see AutoRegistrar::Configuration
|
452
|
-
#
|
453
|
-
# @return [self]
|
454
|
-
#
|
455
|
-
# @api public
|
456
|
-
def auto_register!(dir, &block)
|
457
|
-
auto_registrar.(dir, &block)
|
458
|
-
self
|
459
|
-
end
|
460
|
-
|
461
415
|
# Builds injector for this container
|
462
416
|
#
|
463
417
|
# An injector is a useful mixin which injects dependencies into
|
@@ -526,8 +480,8 @@ module Dry
|
|
526
480
|
end
|
527
481
|
|
528
482
|
# @api public
|
529
|
-
def resolve(key
|
530
|
-
load_component(key
|
483
|
+
def resolve(key)
|
484
|
+
load_component(key) unless finalized?
|
531
485
|
|
532
486
|
super
|
533
487
|
end
|
@@ -558,8 +512,8 @@ module Dry
|
|
558
512
|
end
|
559
513
|
|
560
514
|
# @api private
|
561
|
-
def
|
562
|
-
|
515
|
+
def component_dirs
|
516
|
+
config.component_dirs.to_a.map { |dir| ComponentDir.new(config: dir, container: self) }
|
563
517
|
end
|
564
518
|
|
565
519
|
# @api private
|
@@ -595,67 +549,6 @@ module Dry
|
|
595
549
|
@importer ||= config.importer.new(self)
|
596
550
|
end
|
597
551
|
|
598
|
-
# @api private
|
599
|
-
def component(identifier, **options)
|
600
|
-
if (component = booter.components.detect { |c| c.identifier == identifier })
|
601
|
-
component
|
602
|
-
else
|
603
|
-
Component.new(
|
604
|
-
identifier,
|
605
|
-
loader: config.loader,
|
606
|
-
namespace: config.default_namespace,
|
607
|
-
separator: config.namespace_separator,
|
608
|
-
inflector: config.inflector,
|
609
|
-
**options
|
610
|
-
)
|
611
|
-
end
|
612
|
-
end
|
613
|
-
|
614
|
-
# @api private
|
615
|
-
def require_component(component)
|
616
|
-
return if registered?(component.identifier)
|
617
|
-
|
618
|
-
raise FileNotFoundError, component unless component.file_exists?(load_paths)
|
619
|
-
|
620
|
-
require_path(component.path)
|
621
|
-
|
622
|
-
yield
|
623
|
-
end
|
624
|
-
|
625
|
-
# Allows subclasses to use a different strategy for required files.
|
626
|
-
#
|
627
|
-
# E.g. apps that use `ActiveSupport::Dependencies::Loadable#require_dependency`
|
628
|
-
# will override this method to allow container managed dependencies to be reloaded
|
629
|
-
# for non-finalized containers.
|
630
|
-
#
|
631
|
-
# @api private
|
632
|
-
def require_path(path)
|
633
|
-
require path
|
634
|
-
end
|
635
|
-
|
636
|
-
# @api private
|
637
|
-
def load_component(key, &block)
|
638
|
-
return self if registered?(key)
|
639
|
-
|
640
|
-
component(key).tap do |component|
|
641
|
-
if component.bootable?
|
642
|
-
booter.start(component)
|
643
|
-
else
|
644
|
-
root_key = component.root_key
|
645
|
-
|
646
|
-
if (root_bootable = component(root_key)).bootable?
|
647
|
-
booter.start(root_bootable)
|
648
|
-
elsif importer.key?(root_key)
|
649
|
-
load_imported_component(component.namespaced(root_key))
|
650
|
-
end
|
651
|
-
|
652
|
-
load_local_component(component, &block) unless registered?(key)
|
653
|
-
end
|
654
|
-
end
|
655
|
-
|
656
|
-
self
|
657
|
-
end
|
658
|
-
|
659
552
|
# @api private
|
660
553
|
def after(event, &block)
|
661
554
|
hooks[:"after_#{event}"] << block
|
@@ -672,46 +565,87 @@ module Dry
|
|
672
565
|
|
673
566
|
# @api private
|
674
567
|
def inherited(klass)
|
675
|
-
new_hooks = Container.hooks.dup
|
676
|
-
|
677
568
|
hooks.each do |event, blocks|
|
678
|
-
|
679
|
-
new_hooks[event].concat(klass.hooks[event])
|
569
|
+
klass.hooks[event].concat blocks.dup
|
680
570
|
end
|
681
571
|
|
682
|
-
klass.instance_variable_set(:@hooks, new_hooks)
|
683
572
|
klass.instance_variable_set(:@__finalized__, false)
|
573
|
+
|
684
574
|
super
|
685
575
|
end
|
686
576
|
|
687
|
-
|
577
|
+
protected
|
688
578
|
|
689
579
|
# @api private
|
690
|
-
def
|
691
|
-
|
692
|
-
booter.boot_dependency(component) unless finalized?
|
580
|
+
def load_component(key)
|
581
|
+
return self if registered?(key)
|
693
582
|
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
583
|
+
component = component(key)
|
584
|
+
|
585
|
+
if component.bootable?
|
586
|
+
booter.start(component)
|
587
|
+
return self
|
588
|
+
end
|
589
|
+
|
590
|
+
booter.boot_dependency(component)
|
591
|
+
return self if registered?(key)
|
592
|
+
|
593
|
+
if component.file_exists?
|
594
|
+
load_local_component(component)
|
699
595
|
elsif manual_registrar.file_exists?(component)
|
700
596
|
manual_registrar.(component)
|
701
|
-
elsif
|
702
|
-
|
703
|
-
else
|
704
|
-
raise ComponentLoadError, component
|
597
|
+
elsif importer.key?(component.identifier.root_key)
|
598
|
+
load_imported_component(component.identifier)
|
705
599
|
end
|
600
|
+
|
601
|
+
self
|
706
602
|
end
|
707
603
|
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
604
|
+
private
|
605
|
+
|
606
|
+
def load_local_component(component)
|
607
|
+
if component.auto_register?
|
608
|
+
register(component.identifier, memoize: component.memoize?) { component.instance }
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
612
|
+
def load_imported_component(identifier)
|
613
|
+
import_namespace = identifier.root_key
|
614
|
+
|
615
|
+
container = importer[import_namespace]
|
616
|
+
|
617
|
+
container.load_component(identifier.dequalified(import_namespace).key)
|
618
|
+
|
619
|
+
importer.(import_namespace, container)
|
620
|
+
end
|
621
|
+
|
622
|
+
def component(identifier)
|
623
|
+
if (bootable_component = booter.find_component(identifier))
|
624
|
+
return bootable_component
|
625
|
+
end
|
626
|
+
|
627
|
+
# Find the first matching component from within the configured component dirs.
|
628
|
+
# If no matching component is found, return a plain component instance with no
|
629
|
+
# associated file path. This fallback is important because the component may
|
630
|
+
# still be loadable via the manual registrar or an imported container.
|
631
|
+
component_dirs.detect { |dir|
|
632
|
+
if (component = dir.component_for_identifier(identifier))
|
633
|
+
break component
|
634
|
+
end
|
635
|
+
} || Component.new(identifier)
|
713
636
|
end
|
714
637
|
end
|
638
|
+
|
639
|
+
# Default hooks
|
640
|
+
after :configure do
|
641
|
+
# Add appropriately configured component dirs to the load path
|
642
|
+
#
|
643
|
+
# Do this in a single pass to preserve ordering (i.e. earliest dirs win)
|
644
|
+
paths = config.component_dirs.to_a.each_with_object([]) { |dir, arr|
|
645
|
+
arr << dir.path if dir.add_to_load_path
|
646
|
+
}
|
647
|
+
add_to_load_path!(*paths)
|
648
|
+
end
|
715
649
|
end
|
716
650
|
end
|
717
651
|
end
|
data/lib/dry/system/errors.rb
CHANGED
@@ -2,13 +2,22 @@
|
|
2
2
|
|
3
3
|
module Dry
|
4
4
|
module System
|
5
|
+
# Error raised when a component dir is added to configuration more than once
|
6
|
+
#
|
7
|
+
# @api public
|
8
|
+
ComponentDirAlreadyAddedError = Class.new(StandardError) do
|
9
|
+
def initialize(dir)
|
10
|
+
super("Component directory #{dir.inspect} already added")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
5
14
|
# Error raised when the container tries to load a component with missing
|
6
15
|
# file
|
7
16
|
#
|
8
17
|
# @api public
|
9
18
|
FileNotFoundError = Class.new(StandardError) do
|
10
19
|
def initialize(component)
|
11
|
-
super("could not resolve require file for #{component.identifier}")
|
20
|
+
super("could not resolve require file for component '#{component.identifier}'")
|
12
21
|
end
|
13
22
|
end
|
14
23
|
|
@@ -18,20 +27,11 @@ module Dry
|
|
18
27
|
ComponentFileMismatchError = Class.new(StandardError) do
|
19
28
|
def initialize(component)
|
20
29
|
super(<<-STR)
|
21
|
-
Bootable component #{component.identifier
|
30
|
+
Bootable component '#{component.identifier}' not found
|
22
31
|
STR
|
23
32
|
end
|
24
33
|
end
|
25
34
|
|
26
|
-
# Error raised when a resolved component couldn't be found
|
27
|
-
#
|
28
|
-
# @api public
|
29
|
-
ComponentLoadError = Class.new(StandardError) do
|
30
|
-
def initialize(component)
|
31
|
-
super("could not load component #{component.inspect}")
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
35
|
# Error raised when resolved component couldn't be loaded
|
36
36
|
#
|
37
37
|
# @api public
|
@@ -81,8 +81,17 @@ module Dry
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
-
|
84
|
+
# Error raised when a configured component directory could not be found
|
85
|
+
#
|
86
|
+
# @api public
|
87
|
+
ComponentDirNotFoundError = Class.new(StandardError) do
|
88
|
+
def initialize(dir)
|
89
|
+
super("Component dir '#{dir}' not found")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
85
93
|
DuplicatedComponentKeyError = Class.new(ArgumentError)
|
94
|
+
|
86
95
|
InvalidSettingsError = Class.new(ArgumentError) do
|
87
96
|
# @api private
|
88
97
|
def initialize(attributes)
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
require_relative "constants"
|
5
|
+
|
6
|
+
module Dry
|
7
|
+
module System
|
8
|
+
# An identifier representing a component to be registered.
|
9
|
+
#
|
10
|
+
# Components are eventually registered in the container using plain string
|
11
|
+
# identifiers, available as the `identifier` or `key` attribute here. Additional
|
12
|
+
# methods are provided to make it easier to evaluate or manipulate these identifiers.
|
13
|
+
#
|
14
|
+
# @api public
|
15
|
+
class Identifier
|
16
|
+
include Dry::Equalizer(:identifier, :namespace, :separator)
|
17
|
+
|
18
|
+
# @return [String] the identifier string
|
19
|
+
# @api public
|
20
|
+
attr_reader :identifier
|
21
|
+
|
22
|
+
# @return [String, nil] the namespace for the component
|
23
|
+
# @api public
|
24
|
+
attr_reader :namespace
|
25
|
+
|
26
|
+
# @return [String] the configured namespace separator
|
27
|
+
# @api public
|
28
|
+
attr_reader :separator
|
29
|
+
|
30
|
+
# @api private
|
31
|
+
def initialize(identifier, namespace: nil, separator: DEFAULT_SEPARATOR)
|
32
|
+
@identifier = identifier.to_s
|
33
|
+
@namespace = namespace
|
34
|
+
@separator = separator
|
35
|
+
end
|
36
|
+
|
37
|
+
# @!method key
|
38
|
+
# Returns the identifier string
|
39
|
+
#
|
40
|
+
# @return [String]
|
41
|
+
# @see #identifier
|
42
|
+
# @api public
|
43
|
+
alias_method :key, :identifier
|
44
|
+
|
45
|
+
# @!method to_s
|
46
|
+
# Returns the identifier string
|
47
|
+
#
|
48
|
+
# @return [String]
|
49
|
+
# @see #identifier
|
50
|
+
# @api public
|
51
|
+
alias_method :to_s, :identifier
|
52
|
+
|
53
|
+
# Returns the root namespace segment of the identifier string, as a symbol
|
54
|
+
#
|
55
|
+
# @example
|
56
|
+
# identifier.key # => "articles.operations.create"
|
57
|
+
# identifier.root_key # => :articles
|
58
|
+
#
|
59
|
+
# @return [Symbol] the root key
|
60
|
+
# @api public
|
61
|
+
def root_key
|
62
|
+
segments.first.to_sym
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns a path-delimited representation of the identifier, with the namespace
|
66
|
+
# incorporated. This path is intended for usage when requiring the component's
|
67
|
+
# source file.
|
68
|
+
#
|
69
|
+
# @example
|
70
|
+
# identifier.key # => "articles.operations.create"
|
71
|
+
# identifier.namespace # => "admin"
|
72
|
+
#
|
73
|
+
# identifier.path # => "admin/articles/operations/create"
|
74
|
+
#
|
75
|
+
# @return [String] the path
|
76
|
+
# @api public
|
77
|
+
def path
|
78
|
+
@require_path ||= identifier.gsub(separator, PATH_SEPARATOR).yield_self { |path|
|
79
|
+
if namespace
|
80
|
+
namespace_path = namespace.to_s.gsub(separator, PATH_SEPARATOR)
|
81
|
+
"#{namespace_path}#{PATH_SEPARATOR}#{path}"
|
82
|
+
else
|
83
|
+
path
|
84
|
+
end
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns true if the given namespace prefix is part of the identifier's leading
|
89
|
+
# namespaces
|
90
|
+
#
|
91
|
+
# @example
|
92
|
+
# identifier.key # => "articles.operations.create"
|
93
|
+
#
|
94
|
+
# identifier.start_with?("articles.operations") # => true
|
95
|
+
# identifier.start_with?("articles") # => true
|
96
|
+
# identifier.start_with?("article") # => false
|
97
|
+
#
|
98
|
+
# @param leading_namespaces [String] the one or more leading namespaces to check
|
99
|
+
# @return [Boolean]
|
100
|
+
# @api public
|
101
|
+
def start_with?(leading_namespaces)
|
102
|
+
identifier.start_with?("#{leading_namespaces}#{separator}")
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns a copy of the identifier with the given leading namespaces removed from
|
106
|
+
# the identifier string.
|
107
|
+
#
|
108
|
+
# Additional options may be provided, which are passed to #initialize when
|
109
|
+
# constructing the new copy of the identifier
|
110
|
+
#
|
111
|
+
# @param leading_namespace [String] the one or more leading namespaces to remove
|
112
|
+
# @param options [Hash] additional options for initialization
|
113
|
+
#
|
114
|
+
# @return [Dry::System::Identifier] the copy of the identifier
|
115
|
+
#
|
116
|
+
# @see #initialize
|
117
|
+
# @api private
|
118
|
+
def dequalified(leading_namespaces, **options)
|
119
|
+
new_identifier = identifier.gsub(
|
120
|
+
/^#{Regexp.escape(leading_namespaces)}#{Regexp.escape(separator)}/,
|
121
|
+
EMPTY_STRING
|
122
|
+
)
|
123
|
+
|
124
|
+
return self if new_identifier == identifier
|
125
|
+
|
126
|
+
self.class.new(
|
127
|
+
new_identifier,
|
128
|
+
namespace: namespace,
|
129
|
+
separator: separator,
|
130
|
+
**options
|
131
|
+
)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns a copy of the identifier with the given options applied
|
135
|
+
#
|
136
|
+
# @param namespace [String, nil] a new namespace to be used
|
137
|
+
#
|
138
|
+
# @return [Dry::System::Identifier] the copy of the identifier
|
139
|
+
#
|
140
|
+
# @see #initialize
|
141
|
+
# @api private
|
142
|
+
def with(namespace:)
|
143
|
+
self.class.new(
|
144
|
+
identifier,
|
145
|
+
namespace: namespace,
|
146
|
+
separator: separator
|
147
|
+
)
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def segments
|
153
|
+
@segments ||= identifier.split(separator)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../loader"
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module System
|
7
|
+
class Loader
|
8
|
+
# Component loader for autoloading-enabled applications
|
9
|
+
#
|
10
|
+
# This behaves like the default loader, except instead of requiring the given path,
|
11
|
+
# it loads the respective constant, allowing the autoloader to load the
|
12
|
+
# corresponding file per its own configuration.
|
13
|
+
#
|
14
|
+
# @see Loader
|
15
|
+
# @api public
|
16
|
+
class Autoloading < Loader
|
17
|
+
class << self
|
18
|
+
def require!(component)
|
19
|
+
constant(component)
|
20
|
+
self
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/dry/system/loader.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "dry/inflector"
|
4
|
-
|
5
3
|
module Dry
|
6
4
|
module System
|
7
5
|
# Default component loader implementation
|
@@ -25,52 +23,53 @@ module Dry
|
|
25
23
|
#
|
26
24
|
# @api public
|
27
25
|
class Loader
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
class << self
|
27
|
+
# Requires the component's source file
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
def require!(component)
|
31
|
+
require(component.path) if component.file_exists?
|
32
|
+
self
|
33
|
+
end
|
31
34
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
+
# Returns an instance of the component
|
36
|
+
#
|
37
|
+
# Provided optional args are passed to object's constructor
|
38
|
+
#
|
39
|
+
# @param [Array] args Optional constructor args
|
40
|
+
#
|
41
|
+
# @return [Object]
|
42
|
+
#
|
43
|
+
# @api public
|
44
|
+
def call(component, *args)
|
45
|
+
require!(component)
|
35
46
|
|
36
|
-
|
37
|
-
def initialize(path, inflector = Dry::Inflector.new)
|
38
|
-
@path = path
|
39
|
-
@inflector = inflector
|
40
|
-
end
|
47
|
+
constant = self.constant(component)
|
41
48
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
#
|
48
|
-
# @return [Object]
|
49
|
-
#
|
50
|
-
# @api public
|
51
|
-
def call(*args)
|
52
|
-
if singleton?(constant)
|
53
|
-
constant.instance(*args)
|
54
|
-
else
|
55
|
-
constant.new(*args)
|
49
|
+
if singleton?(constant)
|
50
|
+
constant.instance(*args)
|
51
|
+
else
|
52
|
+
constant.new(*args)
|
53
|
+
end
|
56
54
|
end
|
57
|
-
|
58
|
-
ruby2_keywords(:call) if respond_to?(:ruby2_keywords, true)
|
55
|
+
ruby2_keywords(:call) if respond_to?(:ruby2_keywords, true)
|
59
56
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
57
|
+
# Returns the component's class constant
|
58
|
+
#
|
59
|
+
# @return [Class]
|
60
|
+
#
|
61
|
+
# @api public
|
62
|
+
def constant(component)
|
63
|
+
inflector = component.inflector
|
64
|
+
|
65
|
+
inflector.constantize(inflector.camelize(component.path))
|
66
|
+
end
|
68
67
|
|
69
|
-
|
68
|
+
private
|
70
69
|
|
71
|
-
|
72
|
-
|
73
|
-
|
70
|
+
def singleton?(constant)
|
71
|
+
constant.respond_to?(:instance) && !constant.respond_to?(:new)
|
72
|
+
end
|
74
73
|
end
|
75
74
|
end
|
76
75
|
end
|
@@ -13,7 +13,10 @@ module Dry
|
|
13
13
|
|
14
14
|
setting :log_dir, "log"
|
15
15
|
|
16
|
-
setting :log_levels,
|
16
|
+
setting :log_levels,
|
17
|
+
development: Logger::DEBUG,
|
18
|
+
test: Logger::DEBUG,
|
19
|
+
production: Logger::ERROR
|
17
20
|
|
18
21
|
setting :logger_class, ::Logger, reader: true
|
19
22
|
end
|