dry-system 0.22.0 → 0.25.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +424 -0
- data/LICENSE +1 -1
- data/README.md +3 -3
- data/dry-system.gemspec +4 -5
- data/lib/dry/system/component.rb +10 -5
- data/lib/dry/system/component_dir.rb +14 -35
- data/lib/dry/system/components.rb +8 -4
- data/lib/dry/system/config/component_dir.rb +60 -16
- data/lib/dry/system/config/component_dirs.rb +23 -10
- data/lib/dry/system/config/namespace.rb +4 -6
- data/lib/dry/system/constants.rb +1 -1
- data/lib/dry/system/container.rb +275 -192
- data/lib/dry/system/errors.rb +73 -53
- data/lib/dry/system/identifier.rb +62 -20
- data/lib/dry/system/importer.rb +90 -12
- data/lib/dry/system/indirect_component.rb +1 -1
- data/lib/dry/system/loader.rb +6 -1
- data/lib/dry/system/{manual_registrar.rb → manifest_registrar.rb} +9 -6
- data/lib/dry/system/plugins/bootsnap.rb +2 -1
- data/lib/dry/system/plugins/dependency_graph/strategies.rb +37 -1
- data/lib/dry/system/plugins/dependency_graph.rb +26 -20
- data/lib/dry/system/plugins/env.rb +2 -1
- data/lib/dry/system/plugins/logging.rb +2 -2
- data/lib/dry/system/plugins/monitoring.rb +1 -1
- data/lib/dry/system/plugins/notifications.rb +1 -1
- data/lib/dry/system/plugins/zeitwerk/compat_inflector.rb +22 -0
- data/lib/dry/system/plugins/zeitwerk.rb +109 -0
- data/lib/dry/system/plugins.rb +7 -4
- data/lib/dry/system/provider/source.rb +329 -0
- data/lib/dry/system/provider/source_dsl.rb +94 -0
- data/lib/dry/system/provider.rb +262 -22
- data/lib/dry/system/provider_registrar.rb +276 -0
- data/lib/dry/system/provider_source_registry.rb +70 -0
- data/lib/dry/system/provider_sources/settings/config.rb +86 -0
- data/lib/dry/system/provider_sources/settings/loader.rb +53 -0
- data/lib/dry/system/provider_sources/settings.rb +40 -0
- data/lib/dry/system/provider_sources.rb +5 -0
- data/lib/dry/system/version.rb +1 -1
- data/lib/dry/system.rb +44 -12
- metadata +23 -37
- data/lib/dry/system/booter/component_registry.rb +0 -35
- data/lib/dry/system/booter.rb +0 -200
- data/lib/dry/system/components/bootable.rb +0 -280
- data/lib/dry/system/components/config.rb +0 -35
- data/lib/dry/system/lifecycle.rb +0 -135
- data/lib/dry/system/provider_registry.rb +0 -27
- data/lib/dry/system/settings/file_loader.rb +0 -30
- data/lib/dry/system/settings/file_parser.rb +0 -51
- data/lib/dry/system/settings.rb +0 -64
- data/lib/dry/system/system_components/settings.rb +0 -11
data/lib/dry/system/errors.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "dry/core/deprecations"
|
4
|
+
|
3
5
|
module Dry
|
4
6
|
module System
|
7
|
+
extend Dry::Core::Deprecations["dry-system"]
|
8
|
+
|
5
9
|
# Error raised when a component dir is added to configuration more than once
|
6
10
|
#
|
7
11
|
# @api public
|
@@ -11,8 +15,19 @@ module Dry
|
|
11
15
|
end
|
12
16
|
end
|
13
17
|
|
18
|
+
# Error raised when a configured component directory could not be found
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
ComponentDirNotFoundError = Class.new(StandardError) do
|
22
|
+
def initialize(dir)
|
23
|
+
super("Component dir '#{dir}' not found")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
14
27
|
# Error raised when a namespace for a component dir is added to configuration more
|
15
28
|
# than once
|
29
|
+
#
|
30
|
+
# @api public
|
16
31
|
NamespaceAlreadyAddedError = Class.new(StandardError) do
|
17
32
|
def initialize(path)
|
18
33
|
path_label = path ? "path #{path.inspect}" : "root path"
|
@@ -21,45 +36,40 @@ module Dry
|
|
21
36
|
end
|
22
37
|
end
|
23
38
|
|
24
|
-
# Error raised when
|
25
|
-
#
|
26
|
-
# @api public
|
27
|
-
ComponentFileMismatchError = Class.new(StandardError) do
|
28
|
-
def initialize(component)
|
29
|
-
super(<<-STR)
|
30
|
-
Bootable component '#{component.name}' not found
|
31
|
-
STR
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
# Error raised when resolved component couldn't be loaded
|
39
|
+
# Error raised when attempting to register provider using a name that has already been
|
40
|
+
# registered
|
36
41
|
#
|
37
42
|
# @api public
|
38
|
-
|
39
|
-
def initialize(
|
40
|
-
super(
|
41
|
-
"Tried to create an invalid #{name.inspect} component - #{reason}"
|
42
|
-
)
|
43
|
+
ProviderAlreadyRegisteredError = Class.new(ArgumentError) do
|
44
|
+
def initialize(provider_name)
|
45
|
+
super("Provider #{provider_name.inspect} has already been registered")
|
43
46
|
end
|
44
47
|
end
|
48
|
+
DuplicatedComponentKeyError = ProviderAlreadyRegisteredError
|
49
|
+
deprecate_constant :DuplicatedComponentKeyError
|
45
50
|
|
46
|
-
# Error raised when
|
51
|
+
# Error raised when a named provider could not be found
|
47
52
|
#
|
48
53
|
# @api public
|
49
|
-
|
54
|
+
ProviderNotFoundError = Class.new(ArgumentError) do
|
50
55
|
def initialize(name)
|
51
|
-
super(
|
52
|
-
"component +#{name}+ is invalid or boot file is missing"
|
53
|
-
)
|
56
|
+
super("Provider #{name.inspect} not found")
|
54
57
|
end
|
55
58
|
end
|
59
|
+
InvalidComponentError = ProviderNotFoundError
|
60
|
+
deprecate_constant :InvalidComponentError
|
56
61
|
|
57
|
-
# Error raised when
|
62
|
+
# Error raised when a named provider source could not be found
|
58
63
|
#
|
59
64
|
# @api public
|
60
|
-
|
61
|
-
def initialize(
|
62
|
-
|
65
|
+
ProviderSourceNotFoundError = Class.new(StandardError) do
|
66
|
+
def initialize(name:, group:, keys:)
|
67
|
+
msg = "Provider source not found: #{name.inspect}, group: #{group.inspect}"
|
68
|
+
|
69
|
+
key_list = keys.map { |key| "- #{key[:name].inspect}, group: #{key[:group].inspect}" }
|
70
|
+
msg += "Available provider sources:\n\n#{key_list}"
|
71
|
+
|
72
|
+
super(msg)
|
63
73
|
end
|
64
74
|
end
|
65
75
|
|
@@ -72,43 +82,53 @@ module Dry
|
|
72
82
|
end
|
73
83
|
end
|
74
84
|
|
75
|
-
#
|
85
|
+
# Exception raise when a plugin dependency failed to load
|
76
86
|
#
|
77
87
|
# @api public
|
78
|
-
|
79
|
-
|
80
|
-
|
88
|
+
PluginDependencyMissing = Class.new(StandardError) do
|
89
|
+
# @api private
|
90
|
+
def initialize(plugin, message, gem = nil)
|
91
|
+
details = gem ? "#{message} - add #{gem} to your Gemfile" : message
|
92
|
+
super("dry-system plugin #{plugin.inspect} failed to load its dependencies: #{details}")
|
81
93
|
end
|
82
94
|
end
|
83
95
|
|
84
|
-
|
85
|
-
|
86
|
-
|
96
|
+
# Exception raised when auto-registerable component is not loadable
|
97
|
+
#
|
98
|
+
# @api public
|
99
|
+
ComponentNotLoadableError = Class.new(NameError) do
|
87
100
|
# @api private
|
88
|
-
def initialize(
|
89
|
-
|
90
|
-
|
101
|
+
def initialize(component, error,
|
102
|
+
corrections: DidYouMean::ClassNameChecker.new(error).corrections)
|
103
|
+
full_class_name = [error.receiver, error.name].join("::")
|
91
104
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
105
|
+
message = [
|
106
|
+
"Component '#{component.key}' is not loadable.",
|
107
|
+
"Looking for #{full_class_name}."
|
108
|
+
]
|
96
109
|
|
97
|
-
|
110
|
+
if corrections.any?
|
111
|
+
case_correction = corrections.find { |correction| correction.casecmp?(full_class_name) }
|
112
|
+
if case_correction
|
113
|
+
acronyms_needed = case_correction.split("::").difference(full_class_name.split("::"))
|
114
|
+
stringified_acronyms_needed = acronyms_needed.map { |acronym|
|
115
|
+
"'#{acronym}'"
|
116
|
+
} .join(", ")
|
117
|
+
message <<
|
118
|
+
<<~ERROR_MESSAGE
|
98
119
|
|
99
|
-
|
100
|
-
attributes.map { |key, error| "#{key.name}: #{error}" }
|
101
|
-
end
|
102
|
-
end
|
120
|
+
You likely need to add:
|
103
121
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
122
|
+
acronym(#{stringified_acronyms_needed})
|
123
|
+
|
124
|
+
to your container's inflector, since we found a #{case_correction} class.
|
125
|
+
ERROR_MESSAGE
|
126
|
+
else
|
127
|
+
message << DidYouMean.formatter.message_for(corrections)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
super message.join("\n")
|
112
132
|
end
|
113
133
|
end
|
114
134
|
end
|
@@ -13,20 +13,15 @@ module Dry
|
|
13
13
|
#
|
14
14
|
# @api public
|
15
15
|
class Identifier
|
16
|
-
include Dry::Equalizer(:key
|
16
|
+
include Dry::Equalizer(:key)
|
17
17
|
|
18
18
|
# @return [String] the identifier's string key
|
19
19
|
# @api public
|
20
20
|
attr_reader :key
|
21
21
|
|
22
|
-
# @return [String] the configured namespace separator
|
23
|
-
# @api public
|
24
|
-
attr_reader :separator
|
25
|
-
|
26
22
|
# @api private
|
27
|
-
def initialize(key
|
23
|
+
def initialize(key)
|
28
24
|
@key = key.to_s
|
29
|
-
@separator = separator
|
30
25
|
end
|
31
26
|
|
32
27
|
# @!method to_s
|
@@ -49,11 +44,9 @@ module Dry
|
|
49
44
|
segments.first.to_sym
|
50
45
|
end
|
51
46
|
|
52
|
-
# Returns true if the given leading
|
53
|
-
# identifier's key
|
47
|
+
# Returns true if the given leading segments string is a leading part of the {key}.
|
54
48
|
#
|
55
|
-
# Also returns true if nil
|
56
|
-
# wrought)
|
49
|
+
# Also returns true if nil or an empty string is given.
|
57
50
|
#
|
58
51
|
# @example
|
59
52
|
# identifier.key # => "articles.operations.create"
|
@@ -63,13 +56,62 @@ module Dry
|
|
63
56
|
# identifier.start_with?("article") # => false
|
64
57
|
# identifier.start_with?(nil) # => true
|
65
58
|
#
|
66
|
-
# @param
|
59
|
+
# @param leading_segments [String] the one or more leading segments to check
|
60
|
+
# @return [Boolean]
|
61
|
+
# @api public
|
62
|
+
def start_with?(leading_segments)
|
63
|
+
leading_segments.to_s.empty? ||
|
64
|
+
key.start_with?("#{leading_segments}#{KEY_SEPARATOR}") ||
|
65
|
+
key.eql?(leading_segments)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns true if the given trailing segments string is the end part of the {key}.
|
69
|
+
#
|
70
|
+
# Also returns true if nil or an empty string is given.
|
71
|
+
#
|
72
|
+
# @example
|
73
|
+
# identifier.key # => "articles.operations.create"
|
74
|
+
#
|
75
|
+
# identifier.end_with?("create") # => true
|
76
|
+
# identifier.end_with?("operations.create") # => true
|
77
|
+
# identifier.end_with?("ate") # => false, not a whole segment
|
78
|
+
# identifier.end_with?("nup") # => false, not in key at all
|
79
|
+
#
|
80
|
+
# @param trailing_segments [String] the one or more trailing key segments to check
|
81
|
+
# @return [Boolean]
|
82
|
+
# @api public
|
83
|
+
def end_with?(trailing_segments)
|
84
|
+
trailing_segments.to_s.empty? ||
|
85
|
+
key.end_with?("#{KEY_SEPARATOR}#{trailing_segments}") ||
|
86
|
+
key.eql?(trailing_segments)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns true if the given segments string matches whole segments within the {key}.
|
90
|
+
#
|
91
|
+
# @example
|
92
|
+
# identifier.key # => "articles.operations.create"
|
93
|
+
#
|
94
|
+
# identifier.include?("operations") # => true
|
95
|
+
# identifier.include?("articles.operations") # => true
|
96
|
+
# identifier.include?("operations.create") # => true
|
97
|
+
#
|
98
|
+
# identifier.include?("article") # => false, not a whole segment
|
99
|
+
# identifier.include?("update") # => false, not in key at all
|
100
|
+
#
|
101
|
+
# @param segments [String] the one of more key segments to check
|
67
102
|
# @return [Boolean]
|
68
103
|
# @api public
|
69
|
-
def
|
70
|
-
|
71
|
-
|
72
|
-
|
104
|
+
def include?(segments)
|
105
|
+
return false if segments.to_s.empty?
|
106
|
+
|
107
|
+
sep_re = Regexp.escape(KEY_SEPARATOR)
|
108
|
+
key.match?(
|
109
|
+
/
|
110
|
+
(\A|#{sep_re})
|
111
|
+
#{Regexp.escape(segments)}
|
112
|
+
(\Z|#{sep_re})
|
113
|
+
/x
|
114
|
+
)
|
73
115
|
end
|
74
116
|
|
75
117
|
# Returns the key with its segments separated by the given separator
|
@@ -108,27 +150,27 @@ module Dry
|
|
108
150
|
def namespaced(from:, to:)
|
109
151
|
return self if from == to
|
110
152
|
|
111
|
-
separated_to = "#{to}#{
|
153
|
+
separated_to = "#{to}#{KEY_SEPARATOR}" if to
|
112
154
|
|
113
155
|
new_key =
|
114
156
|
if from.nil?
|
115
157
|
"#{separated_to}#{key}"
|
116
158
|
else
|
117
159
|
key.sub(
|
118
|
-
/^#{Regexp.escape(from.to_s)}#{Regexp.escape(
|
160
|
+
/^#{Regexp.escape(from.to_s)}#{Regexp.escape(KEY_SEPARATOR)}/,
|
119
161
|
separated_to || EMPTY_STRING
|
120
162
|
)
|
121
163
|
end
|
122
164
|
|
123
165
|
return self if new_key == key
|
124
166
|
|
125
|
-
self.class.new(new_key
|
167
|
+
self.class.new(new_key)
|
126
168
|
end
|
127
169
|
|
128
170
|
private
|
129
171
|
|
130
172
|
def segments
|
131
|
-
@segments ||= key.split(
|
173
|
+
@segments ||= key.split(KEY_SEPARATOR)
|
132
174
|
end
|
133
175
|
end
|
134
176
|
end
|
data/lib/dry/system/importer.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "dry/container"
|
4
|
+
require_relative "constants"
|
5
|
+
|
3
6
|
module Dry
|
4
7
|
module System
|
5
8
|
# Default importer implementation
|
@@ -11,25 +14,34 @@ module Dry
|
|
11
14
|
#
|
12
15
|
# @api private
|
13
16
|
class Importer
|
14
|
-
|
17
|
+
# @api private
|
18
|
+
class Item
|
19
|
+
attr_reader :namespace, :container, :import_keys
|
20
|
+
|
21
|
+
def initialize(namespace:, container:, import_keys:)
|
22
|
+
@namespace = namespace
|
23
|
+
@container = container
|
24
|
+
@import_keys = import_keys
|
25
|
+
end
|
26
|
+
end
|
15
27
|
|
16
|
-
attr_reader :
|
28
|
+
attr_reader :container
|
17
29
|
|
18
30
|
attr_reader :registry
|
19
31
|
|
20
32
|
# @api private
|
21
33
|
def initialize(container)
|
22
34
|
@container = container
|
23
|
-
@separator = container.config.namespace_separator
|
24
35
|
@registry = {}
|
25
36
|
end
|
26
37
|
|
27
38
|
# @api private
|
28
|
-
def
|
29
|
-
registry
|
30
|
-
|
31
|
-
|
32
|
-
|
39
|
+
def register(namespace:, container:, keys: nil)
|
40
|
+
registry[namespace] = Item.new(
|
41
|
+
namespace: namespace,
|
42
|
+
container: container,
|
43
|
+
import_keys: keys
|
44
|
+
)
|
33
45
|
end
|
34
46
|
|
35
47
|
# @api private
|
@@ -41,15 +53,81 @@ module Dry
|
|
41
53
|
def key?(name)
|
42
54
|
registry.key?(name)
|
43
55
|
end
|
56
|
+
alias_method :namespace?, :key?
|
44
57
|
|
45
58
|
# @api private
|
46
|
-
def
|
47
|
-
|
59
|
+
def finalize!
|
60
|
+
registry.each_key { import(_1) }
|
61
|
+
self
|
48
62
|
end
|
49
63
|
|
50
64
|
# @api private
|
51
|
-
def
|
52
|
-
|
65
|
+
def import(namespace, keys: Undefined)
|
66
|
+
item = self[namespace]
|
67
|
+
keys = Undefined.default(keys, item.import_keys)
|
68
|
+
|
69
|
+
if keys
|
70
|
+
import_keys(item.container, namespace, keys_to_import(keys, item))
|
71
|
+
else
|
72
|
+
import_all(item.container, namespace)
|
73
|
+
end
|
74
|
+
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def keys_to_import(keys, item)
|
81
|
+
keys
|
82
|
+
.then { (arr = item.import_keys) ? _1 & arr : _1 }
|
83
|
+
.then { (arr = item.container.exports) ? _1 & arr : _1 }
|
84
|
+
end
|
85
|
+
|
86
|
+
def import_keys(other, namespace, keys)
|
87
|
+
merge(container, build_merge_container(other, keys), namespace: namespace)
|
88
|
+
end
|
89
|
+
|
90
|
+
def import_all(other, namespace)
|
91
|
+
merge_container =
|
92
|
+
if other.exports
|
93
|
+
build_merge_container(other, other.exports)
|
94
|
+
else
|
95
|
+
build_merge_container(other.finalize!, other.keys)
|
96
|
+
end
|
97
|
+
|
98
|
+
merge(container, merge_container, namespace: namespace)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Merges `other` into `container`, favoring the container's existing registrations
|
102
|
+
def merge(container, other, namespace:)
|
103
|
+
container.merge(other, namespace: namespace) { |_key, old_item, new_item|
|
104
|
+
old_item || new_item
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
def build_merge_container(other, keys)
|
109
|
+
keys.each_with_object(Dry::Container.new) { |key, ic|
|
110
|
+
next unless other.key?(key)
|
111
|
+
|
112
|
+
# Access the other container's items directly so that we can preserve all their
|
113
|
+
# options when we merge them with the target container (e.g. if a component in
|
114
|
+
# the provider container was registered with a block, we want block registration
|
115
|
+
# behavior to be exhibited when later resolving that component from the target
|
116
|
+
# container). TODO: Make this part of dry-system's public API.
|
117
|
+
item = other._container[key]
|
118
|
+
|
119
|
+
# By default, we "protect" components that were themselves imported into the
|
120
|
+
# other container from being implicitly exported; imported components are
|
121
|
+
# considered "private" and must be explicitly included in `exports` to be
|
122
|
+
# exported.
|
123
|
+
next if item.options[:imported] && !other.exports
|
124
|
+
|
125
|
+
if item.callable?
|
126
|
+
ic.register(key, **item.options, imported: true, &item.item)
|
127
|
+
else
|
128
|
+
ic.register(key, item.item, **item.options, imported: true)
|
129
|
+
end
|
130
|
+
}
|
53
131
|
end
|
54
132
|
end
|
55
133
|
end
|
@@ -6,7 +6,7 @@ module Dry
|
|
6
6
|
module System
|
7
7
|
# An indirect component is a component that cannot be directly from a source file
|
8
8
|
# directly managed by the container. It may be component that needs to be loaded
|
9
|
-
# indirectly, either via a
|
9
|
+
# indirectly, either via a registration manifest file or an imported container
|
10
10
|
#
|
11
11
|
# Indirect components are an internal abstraction and, unlike ordinary components, are
|
12
12
|
# not exposed to users via component dir configuration hooks.
|
data/lib/dry/system/loader.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "dry/system/errors"
|
4
|
+
|
3
5
|
module Dry
|
4
6
|
module System
|
5
7
|
# Default component loader implementation
|
@@ -61,7 +63,10 @@ module Dry
|
|
61
63
|
# @api public
|
62
64
|
def constant(component)
|
63
65
|
inflector = component.inflector
|
64
|
-
inflector.
|
66
|
+
const_name = inflector.camelize(component.const_path)
|
67
|
+
inflector.constantize(const_name)
|
68
|
+
rescue NameError => e
|
69
|
+
raise ComponentNotLoadableError.new(component, e)
|
65
70
|
end
|
66
71
|
|
67
72
|
private
|
@@ -4,19 +4,21 @@ require "dry/system/constants"
|
|
4
4
|
|
5
5
|
module Dry
|
6
6
|
module System
|
7
|
-
# Default
|
7
|
+
# Default manifest registration implementation
|
8
8
|
#
|
9
|
-
# This is
|
10
|
-
#
|
11
|
-
# manual registration paths, which should hold code to explicitly register
|
9
|
+
# This is configured by default for every System::Container. The manifest registrar is
|
10
|
+
# responsible for loading manifest files that contain code to manually register
|
12
11
|
# certain objects with the container.
|
13
12
|
#
|
14
13
|
# @api private
|
15
|
-
class
|
14
|
+
class ManifestRegistrar
|
15
|
+
# @api private
|
16
16
|
attr_reader :container
|
17
17
|
|
18
|
+
# @api private
|
18
19
|
attr_reader :config
|
19
20
|
|
21
|
+
# @api private
|
20
22
|
def initialize(container)
|
21
23
|
@container = container
|
22
24
|
@config = container.config
|
@@ -25,7 +27,7 @@ module Dry
|
|
25
27
|
# @api private
|
26
28
|
def finalize!
|
27
29
|
::Dir[registrations_dir.join(RB_GLOB)].sort.each do |file|
|
28
|
-
call(File.basename(file, RB_EXT))
|
30
|
+
call(Identifier.new(File.basename(file, RB_EXT)))
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
@@ -34,6 +36,7 @@ module Dry
|
|
34
36
|
require(root.join(config.registrations_dir, component.root_key.to_s))
|
35
37
|
end
|
36
38
|
|
39
|
+
# @api private
|
37
40
|
def file_exists?(component)
|
38
41
|
File.exist?(File.join(registrations_dir, "#{component.root_key}#{RB_EXT}"))
|
39
42
|
end
|
@@ -15,8 +15,9 @@ module Dry
|
|
15
15
|
# @api private
|
16
16
|
def self.extended(system)
|
17
17
|
super
|
18
|
+
|
18
19
|
system.use(:env)
|
19
|
-
system.
|
20
|
+
system.setting :bootsnap, default: DEFAULT_OPTIONS
|
20
21
|
system.after(:configure, &:setup_bootsnap)
|
21
22
|
end
|
22
23
|
|
@@ -15,13 +15,49 @@ module Dry
|
|
15
15
|
# @api private
|
16
16
|
def define_initialize(klass)
|
17
17
|
@container["notifications"].instrument(
|
18
|
-
:resolved_dependency,
|
18
|
+
:resolved_dependency,
|
19
|
+
dependency_map: dependency_map.to_h,
|
20
|
+
target_class: klass
|
19
21
|
)
|
22
|
+
|
23
|
+
super(klass)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
class Args < Dry::AutoInject::Strategies::Args
|
29
|
+
private
|
30
|
+
|
31
|
+
# @api private
|
32
|
+
def define_initialize(klass)
|
33
|
+
@container["notifications"].instrument(
|
34
|
+
:resolved_dependency,
|
35
|
+
dependency_map: dependency_map.to_h,
|
36
|
+
target_class: klass
|
37
|
+
)
|
38
|
+
|
39
|
+
super(klass)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Hash < Dry::AutoInject::Strategies::Hash
|
44
|
+
private
|
45
|
+
|
46
|
+
# @api private
|
47
|
+
def define_initialize(klass)
|
48
|
+
@container["notifications"].instrument(
|
49
|
+
:resolved_dependency,
|
50
|
+
dependency_map: dependency_map.to_h,
|
51
|
+
target_class: klass
|
52
|
+
)
|
53
|
+
|
20
54
|
super(klass)
|
21
55
|
end
|
22
56
|
end
|
23
57
|
|
24
58
|
register :kwargs, Kwargs
|
59
|
+
register :args, Args
|
60
|
+
register :hash, Hash
|
25
61
|
register :default, Kwargs
|
26
62
|
end
|
27
63
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require "dry/system/plugins/dependency_graph/strategies"
|
3
|
+
require_relative "dependency_graph/strategies"
|
5
4
|
|
6
5
|
module Dry
|
7
6
|
module System
|
@@ -12,36 +11,43 @@ module Dry
|
|
12
11
|
def self.extended(system)
|
13
12
|
super
|
14
13
|
|
15
|
-
system.
|
14
|
+
system.instance_eval do
|
15
|
+
use(:notifications)
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
system.after(:configure) do
|
22
|
-
self[:notifications].register_event(:resolved_dependency)
|
23
|
-
self[:notifications].register_event(:registered_dependency)
|
17
|
+
setting :dependency_graph do
|
18
|
+
setting :ignored_dependencies, default: []
|
19
|
+
end
|
24
20
|
|
25
|
-
|
21
|
+
after(:configure) do
|
22
|
+
self[:notifications].register_event(:resolved_dependency)
|
23
|
+
self[:notifications].register_event(:registered_dependency)
|
24
|
+
end
|
26
25
|
end
|
27
26
|
end
|
28
27
|
|
29
28
|
# @api private
|
30
29
|
def self.dependencies
|
31
|
-
{
|
30
|
+
{"dry-events" => "dry/events/publisher"}
|
31
|
+
end
|
32
|
+
|
33
|
+
# @api private
|
34
|
+
def injector(**options)
|
35
|
+
super(**options, strategies: DependencyGraph::Strategies)
|
32
36
|
end
|
33
37
|
|
34
38
|
# @api private
|
35
39
|
def register(key, contents = nil, options = {}, &block)
|
36
|
-
super
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
40
|
+
super.tap do
|
41
|
+
key = key.to_s
|
42
|
+
|
43
|
+
unless config.dependency_graph.ignored_dependencies.include?(key)
|
44
|
+
self[:notifications].instrument(
|
45
|
+
:registered_dependency,
|
46
|
+
key: key,
|
47
|
+
class: self[key].class
|
48
|
+
)
|
49
|
+
end
|
42
50
|
end
|
43
|
-
|
44
|
-
self
|
45
51
|
end
|
46
52
|
end
|
47
53
|
end
|