dry-system 0.22.0 → 0.23.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 +400 -0
- data/LICENSE +1 -1
- data/README.md +2 -2
- data/dry-system.gemspec +2 -2
- data/lib/dry/system/component.rb +2 -3
- data/lib/dry/system/component_dir.rb +8 -34
- 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 +264 -182
- data/lib/dry/system/errors.rb +73 -53
- data/lib/dry/system/identifier.rb +62 -20
- data/lib/dry/system/importer.rb +83 -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} +8 -5
- 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 +324 -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 +18 -18
- 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/provider.rb
CHANGED
@@ -1,48 +1,288 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
4
|
-
|
5
|
-
|
3
|
+
require "dry/core/deprecations"
|
4
|
+
require_relative "constants"
|
5
|
+
require_relative "provider/source"
|
6
6
|
|
7
7
|
module Dry
|
8
8
|
module System
|
9
|
+
# Providers can prepare and register one or more objects and typically work with third
|
10
|
+
# party code. A typical provider might be for a database library, or an API client.
|
11
|
+
#
|
12
|
+
# The particular behavior for any provider is defined in a {Provider::Source}, which
|
13
|
+
# is a subclass created when you run {Container.register_provider} or
|
14
|
+
# {Dry::System.register_provider_source}. The Source provides this behavior through
|
15
|
+
# methods for each of the steps in the provider lifecycle: `prepare`, `start`, and
|
16
|
+
# `run`. These methods typically create and configure various objects, then register
|
17
|
+
# them with the {#provider_container}.
|
18
|
+
#
|
19
|
+
# The Provider manages this lifecycle by implementing common behavior around the
|
20
|
+
# lifecycle steps, such as running step callbacks, and only running steps when
|
21
|
+
# appropriate for the current status of the lifecycle.
|
22
|
+
#
|
23
|
+
# Providers can be registered via {Container.register_provider}.
|
24
|
+
#
|
25
|
+
# @example Simple provider
|
26
|
+
# class App < Dry::System::Container
|
27
|
+
# register_provider(:logger) do
|
28
|
+
# prepare do
|
29
|
+
# require "logger"
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# start do
|
33
|
+
# register(:logger, Logger.new($stdout))
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# App[:logger] # returns configured logger
|
39
|
+
#
|
40
|
+
# @example Using an external Provider Source
|
41
|
+
# class App < Dry::System::Container
|
42
|
+
# register_provider(:logger, from: :some_external_provider_source) do
|
43
|
+
# configure do |config|
|
44
|
+
# config.log_level = :debug
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# after :start do
|
48
|
+
# register(:my_extra_logger, resolve(:logger))
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# App[:my_extra_logger] # returns the extra logger registered in the callback
|
54
|
+
#
|
55
|
+
# @api public
|
9
56
|
class Provider
|
57
|
+
# Returns the provider's unique name.
|
58
|
+
#
|
59
|
+
# @return [Symbol]
|
60
|
+
#
|
61
|
+
# @api public
|
10
62
|
attr_reader :name
|
11
63
|
|
12
|
-
|
64
|
+
# Returns the default namespace for the provider's container keys.
|
65
|
+
#
|
66
|
+
# @return [Symbol,String]
|
67
|
+
#
|
68
|
+
# @api public
|
69
|
+
attr_reader :namespace
|
13
70
|
|
14
|
-
|
71
|
+
# Returns an array of lifecycle steps that have been run.
|
72
|
+
#
|
73
|
+
# @return [Array<Symbol>]
|
74
|
+
#
|
75
|
+
# @example
|
76
|
+
# provider.statuses # => [:prepare, :start]
|
77
|
+
#
|
78
|
+
# @api public
|
79
|
+
attr_reader :statuses
|
15
80
|
|
16
|
-
|
81
|
+
# Returns the name of the currently running step, if any.
|
82
|
+
#
|
83
|
+
# @return [Symbol, nil]
|
84
|
+
#
|
85
|
+
# @api private
|
86
|
+
attr_reader :step_running
|
87
|
+
private :step_running
|
88
|
+
|
89
|
+
# Returns the container for the provider.
|
90
|
+
#
|
91
|
+
# This is where the provider's source will register its components, which are then
|
92
|
+
# later marged into the target container after the `prepare` and `start` lifecycle
|
93
|
+
# steps.
|
94
|
+
#
|
95
|
+
# @return [Dry::Container]
|
96
|
+
#
|
97
|
+
# @api public
|
98
|
+
attr_reader :provider_container
|
99
|
+
alias_method :container, :provider_container
|
100
|
+
|
101
|
+
# Returns the target container for the provider.
|
102
|
+
#
|
103
|
+
# This is the container with which the provider is registered (via
|
104
|
+
# {Dry::System::Container.register_provider}).
|
105
|
+
#
|
106
|
+
# Registered components from the provider's container will be merged into this
|
107
|
+
# container after the `prepare` and `start` lifecycle steps.
|
108
|
+
#
|
109
|
+
# @return [Dry::System::Container]
|
110
|
+
#
|
111
|
+
# @api public
|
112
|
+
attr_reader :target_container
|
113
|
+
alias_method :target, :target_container
|
114
|
+
|
115
|
+
# Returns the provider's source
|
116
|
+
#
|
117
|
+
# The source provides the specific behavior for the provider via methods
|
118
|
+
# implementing the lifecycle steps.
|
119
|
+
#
|
120
|
+
# The provider's source is defined when registering a provider with the container,
|
121
|
+
# or an external provider source.
|
122
|
+
#
|
123
|
+
# @see Dry::System::Container.register_provider
|
124
|
+
# @see Dry::System.register_provider_source
|
125
|
+
#
|
126
|
+
# @return [Dry::System::Provider::Source]
|
127
|
+
#
|
128
|
+
# @api private
|
129
|
+
attr_reader :source
|
130
|
+
|
131
|
+
# @api private
|
132
|
+
def initialize(name:, namespace: nil, target_container:, source_class:, &block) # rubocop:disable Style/KeywordParametersOrder
|
17
133
|
@name = name
|
18
|
-
@
|
19
|
-
@
|
134
|
+
@namespace = namespace
|
135
|
+
@target_container = target_container
|
136
|
+
|
137
|
+
@provider_container = build_provider_container
|
138
|
+
@statuses = []
|
139
|
+
@step_running = nil
|
140
|
+
|
141
|
+
@source = source_class.new(
|
142
|
+
provider_container: provider_container,
|
143
|
+
target_container: target_container,
|
144
|
+
&block
|
145
|
+
)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Runs the `prepare` lifecycle step.
|
149
|
+
#
|
150
|
+
# Also runs any callbacks for the step, and then merges any registered components
|
151
|
+
# from the provider container into the target container.
|
152
|
+
#
|
153
|
+
# @return [self]
|
154
|
+
#
|
155
|
+
# @api public
|
156
|
+
def prepare
|
157
|
+
run_step(:prepare)
|
20
158
|
end
|
21
159
|
|
22
|
-
|
23
|
-
|
160
|
+
# Runs the `start` lifecycle step.
|
161
|
+
#
|
162
|
+
# Also runs any callbacks for the step, and then merges any registered components
|
163
|
+
# from the provider container into the target container.
|
164
|
+
#
|
165
|
+
# @return [self]
|
166
|
+
#
|
167
|
+
# @api public
|
168
|
+
def start
|
169
|
+
run_step(:prepare)
|
170
|
+
run_step(:start)
|
24
171
|
end
|
25
172
|
|
26
|
-
|
27
|
-
|
173
|
+
# Runs the `stop` lifecycle step.
|
174
|
+
#
|
175
|
+
# Also runs any callbacks for the step.
|
176
|
+
#
|
177
|
+
# @return [self]
|
178
|
+
#
|
179
|
+
# @api public
|
180
|
+
def stop
|
181
|
+
return self unless started?
|
182
|
+
|
183
|
+
run_step(:stop)
|
28
184
|
end
|
29
185
|
|
30
|
-
|
31
|
-
|
186
|
+
# Returns true if the provider's `prepare` lifecycle step has run
|
187
|
+
#
|
188
|
+
# @api public
|
189
|
+
def prepared?
|
190
|
+
statuses.include?(:prepare)
|
32
191
|
end
|
33
192
|
|
34
|
-
|
35
|
-
|
193
|
+
# Returns true if the provider's `start` lifecycle step has run
|
194
|
+
#
|
195
|
+
# @api public
|
196
|
+
def started?
|
197
|
+
statuses.include?(:start)
|
36
198
|
end
|
37
199
|
|
38
|
-
|
39
|
-
|
40
|
-
|
200
|
+
# Returns true if the provider's `stop` lifecycle step has run
|
201
|
+
#
|
202
|
+
# @api public
|
203
|
+
def stopped?
|
204
|
+
statuses.include?(:stop)
|
41
205
|
end
|
42
206
|
|
43
|
-
|
44
|
-
|
45
|
-
|
207
|
+
private
|
208
|
+
|
209
|
+
# @api private
|
210
|
+
def build_provider_container
|
211
|
+
container = Dry::Container.new
|
212
|
+
|
213
|
+
case namespace
|
214
|
+
when String, Symbol
|
215
|
+
container.namespace(namespace) { |c| return c }
|
216
|
+
when true
|
217
|
+
container.namespace(name) { |c| return c }
|
218
|
+
when nil
|
219
|
+
container
|
220
|
+
else
|
221
|
+
raise ArgumentError,
|
222
|
+
"+namespace:+ must be true, string or symbol: #{namespace.inspect} given."
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# @api private
|
227
|
+
def run_step(step_name)
|
228
|
+
return self if step_running? || statuses.include?(step_name)
|
229
|
+
|
230
|
+
@step_running = step_name
|
231
|
+
|
232
|
+
source.run_callback(:before, step_name)
|
233
|
+
source.public_send(step_name)
|
234
|
+
source.run_callback(:after, step_name)
|
235
|
+
|
236
|
+
statuses << step_name
|
237
|
+
|
238
|
+
apply
|
239
|
+
|
240
|
+
@step_running = nil
|
241
|
+
|
242
|
+
self
|
243
|
+
end
|
244
|
+
|
245
|
+
# Returns true if a step is currenly running.
|
246
|
+
#
|
247
|
+
# This is important for short-circuiting the invocation of {#run_step} and avoiding
|
248
|
+
# infinite loops if a provider step happens to result in resolution of a component
|
249
|
+
# with the same root key as the provider's own name (which ordinarily results in
|
250
|
+
# that provider being started).
|
251
|
+
#
|
252
|
+
# @return [Boolean]
|
253
|
+
#
|
254
|
+
# @see {#run_step}
|
255
|
+
#
|
256
|
+
# @api private
|
257
|
+
def step_running?
|
258
|
+
!!step_running
|
259
|
+
end
|
260
|
+
|
261
|
+
# Registers any components from the provider's container in the main container.
|
262
|
+
#
|
263
|
+
# Called after each lifecycle step runs.
|
264
|
+
#
|
265
|
+
# @return [self]
|
266
|
+
#
|
267
|
+
# @api private
|
268
|
+
def apply
|
269
|
+
provider_container.each_key do |key|
|
270
|
+
next if target_container.registered?(key)
|
271
|
+
|
272
|
+
# Access the provider's container items directly so that we can preserve all
|
273
|
+
# their options when we merge them with the target container (e.g. if a
|
274
|
+
# component in the provider container was registered with a block, we want block
|
275
|
+
# registration behavior to be exhibited when later resolving that component from
|
276
|
+
# the target container). TODO: Make this part of dry-system's public API.
|
277
|
+
item = provider_container._container[key]
|
278
|
+
|
279
|
+
if item.callable?
|
280
|
+
target_container.register(key, **item.options, &item.item)
|
281
|
+
else
|
282
|
+
target_container.register(key, item.item, **item.options)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
46
286
|
self
|
47
287
|
end
|
48
288
|
end
|
@@ -0,0 +1,276 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/deprecations"
|
4
|
+
require "dry/system"
|
5
|
+
require "pathname"
|
6
|
+
require_relative "errors"
|
7
|
+
require_relative "constants"
|
8
|
+
require_relative "provider"
|
9
|
+
|
10
|
+
module Dry
|
11
|
+
module System
|
12
|
+
# Default provider registrar implementation
|
13
|
+
#
|
14
|
+
# This is currently configured by default for every Dry::System::Container. The
|
15
|
+
# provider registrar is responsible for loading provider files and exposing an API for
|
16
|
+
# running the provider lifecycle steps.
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
class ProviderRegistrar
|
20
|
+
extend Dry::Core::Deprecations["Dry::System::Container"]
|
21
|
+
|
22
|
+
# @api private
|
23
|
+
attr_reader :providers
|
24
|
+
|
25
|
+
# @api private
|
26
|
+
attr_reader :container
|
27
|
+
|
28
|
+
# @api private
|
29
|
+
def initialize(container)
|
30
|
+
@providers = {}
|
31
|
+
@container = container
|
32
|
+
end
|
33
|
+
|
34
|
+
# @api private
|
35
|
+
def freeze
|
36
|
+
providers.freeze
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
41
|
+
|
42
|
+
# @see Container.register_provider
|
43
|
+
# @api private
|
44
|
+
def register_provider(name, namespace: nil, from: nil, source: nil, if: true, &block)
|
45
|
+
raise ProviderAlreadyRegisteredError, name if providers.key?(name)
|
46
|
+
|
47
|
+
if from && source.is_a?(Class)
|
48
|
+
raise ArgumentError, "You must supply a block when using a provider source"
|
49
|
+
end
|
50
|
+
|
51
|
+
if block && source.is_a?(Class)
|
52
|
+
raise ArgumentError, "You must supply only a `source:` option or a block, not both"
|
53
|
+
end
|
54
|
+
|
55
|
+
return self unless binding.local_variable_get(:if)
|
56
|
+
|
57
|
+
provider =
|
58
|
+
if from
|
59
|
+
build_provider_from_source(
|
60
|
+
name,
|
61
|
+
namespace: namespace,
|
62
|
+
source: source || name,
|
63
|
+
group: from,
|
64
|
+
&block
|
65
|
+
)
|
66
|
+
else
|
67
|
+
build_provider(name, namespace: namespace, source: source, &block)
|
68
|
+
end
|
69
|
+
|
70
|
+
providers[provider.name] = provider
|
71
|
+
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
76
|
+
|
77
|
+
# Returns a provider for the given name, if it has already been loaded
|
78
|
+
#
|
79
|
+
# @api public
|
80
|
+
def [](provider_name)
|
81
|
+
providers[provider_name]
|
82
|
+
end
|
83
|
+
alias_method :provider, :[]
|
84
|
+
|
85
|
+
# @api private
|
86
|
+
def key?(provider_name)
|
87
|
+
providers.key?(provider_name)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns a provider if it can be found or loaded, otherwise nil
|
91
|
+
#
|
92
|
+
# @return [Dry::System::Provider, nil]
|
93
|
+
#
|
94
|
+
# @api private
|
95
|
+
def find_and_load_provider(name)
|
96
|
+
name = name.to_sym
|
97
|
+
|
98
|
+
if (provider = providers[name])
|
99
|
+
return provider
|
100
|
+
end
|
101
|
+
|
102
|
+
return if finalized?
|
103
|
+
|
104
|
+
require_provider_file(name)
|
105
|
+
|
106
|
+
providers[name]
|
107
|
+
end
|
108
|
+
|
109
|
+
# @api private
|
110
|
+
def start_provider_dependency(component)
|
111
|
+
if (provider = find_and_load_provider(component.root_key))
|
112
|
+
provider.start
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns all provider files within the configured provider_paths.
|
117
|
+
#
|
118
|
+
# Searches for files in the order of the configured provider_paths. In the case of multiple
|
119
|
+
# identically-named boot files within different provider_paths, the file found first will be
|
120
|
+
# returned, and other matching files will be discarded.
|
121
|
+
#
|
122
|
+
# This method is public to allow other tools extending dry-system (like dry-rails)
|
123
|
+
# to access a canonical list of real, in-use provider files.
|
124
|
+
#
|
125
|
+
# @see Container.provider_paths
|
126
|
+
#
|
127
|
+
# @return [Array<Pathname>]
|
128
|
+
# @api public
|
129
|
+
def provider_files
|
130
|
+
@provider_files ||= provider_paths.each_with_object([[], []]) { |path, (provider_files, loaded)| # rubocop:disable Layout/LineLength
|
131
|
+
files = Dir["#{path}/#{RB_GLOB}"].sort
|
132
|
+
|
133
|
+
files.each do |file|
|
134
|
+
basename = File.basename(file)
|
135
|
+
|
136
|
+
unless loaded.include?(basename)
|
137
|
+
provider_files << Pathname(file)
|
138
|
+
loaded << basename
|
139
|
+
end
|
140
|
+
end
|
141
|
+
}.first
|
142
|
+
end
|
143
|
+
deprecate :boot_files, :provider_files
|
144
|
+
|
145
|
+
# @api private
|
146
|
+
def finalize!
|
147
|
+
provider_files.each do |path|
|
148
|
+
load_provider(path)
|
149
|
+
end
|
150
|
+
|
151
|
+
providers.each_value(&:start)
|
152
|
+
|
153
|
+
freeze
|
154
|
+
end
|
155
|
+
|
156
|
+
# @!method finalized?
|
157
|
+
# Returns true if the booter has been finalized
|
158
|
+
#
|
159
|
+
# @return [Boolean]
|
160
|
+
# @api private
|
161
|
+
alias_method :finalized?, :frozen?
|
162
|
+
|
163
|
+
# @api private
|
164
|
+
def shutdown
|
165
|
+
providers.each_value(&:stop)
|
166
|
+
self
|
167
|
+
end
|
168
|
+
|
169
|
+
# @api private
|
170
|
+
def prepare(provider_name)
|
171
|
+
with_provider(provider_name, &:prepare)
|
172
|
+
self
|
173
|
+
end
|
174
|
+
|
175
|
+
# @api private
|
176
|
+
def start(provider_name)
|
177
|
+
with_provider(provider_name, &:start)
|
178
|
+
self
|
179
|
+
end
|
180
|
+
|
181
|
+
# @api private
|
182
|
+
def stop(provider_name)
|
183
|
+
with_provider(provider_name, &:stop)
|
184
|
+
self
|
185
|
+
end
|
186
|
+
|
187
|
+
private
|
188
|
+
|
189
|
+
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Layout/LineLength
|
190
|
+
# @api private
|
191
|
+
def provider_paths
|
192
|
+
provider_dirs = container.config.provider_dirs
|
193
|
+
bootable_dirs = container.config.bootable_dirs || ["system/boot"]
|
194
|
+
|
195
|
+
if container.config.provider_dirs == ["system/providers"] && \
|
196
|
+
provider_dirs.none? { |d| container.root.join(d).exist? } && \
|
197
|
+
bootable_dirs.any? { |d| container.root.join(d).exist? }
|
198
|
+
Dry::Core::Deprecations.announce(
|
199
|
+
"Dry::System::Container.config.bootable_dirs (defaulting to 'system/boot')",
|
200
|
+
"Use `Dry::System::Container.config.provider_dirs` (defaulting to 'system/providers') instead",
|
201
|
+
tag: "dry-system",
|
202
|
+
uplevel: 2
|
203
|
+
)
|
204
|
+
|
205
|
+
provider_dirs = bootable_dirs
|
206
|
+
end
|
207
|
+
|
208
|
+
provider_dirs.map { |dir|
|
209
|
+
dir = Pathname(dir)
|
210
|
+
|
211
|
+
if dir.relative?
|
212
|
+
container.root.join(dir)
|
213
|
+
else
|
214
|
+
dir
|
215
|
+
end
|
216
|
+
}
|
217
|
+
end
|
218
|
+
# rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity, Layout/LineLength
|
219
|
+
|
220
|
+
def build_provider(name, namespace:, source: nil, &block)
|
221
|
+
source_class = source || Provider::Source.for(
|
222
|
+
name: name,
|
223
|
+
target_container: container,
|
224
|
+
&block
|
225
|
+
)
|
226
|
+
|
227
|
+
Provider.new(
|
228
|
+
name: name,
|
229
|
+
namespace: namespace,
|
230
|
+
target_container: container,
|
231
|
+
source_class: source_class
|
232
|
+
)
|
233
|
+
end
|
234
|
+
|
235
|
+
def build_provider_from_source(name, source:, group:, namespace:, &block)
|
236
|
+
source_class = System.provider_sources.resolve(name: source, group: group)
|
237
|
+
|
238
|
+
Provider.new(
|
239
|
+
name: name,
|
240
|
+
namespace: namespace,
|
241
|
+
target_container: container,
|
242
|
+
source_class: source_class,
|
243
|
+
&block
|
244
|
+
)
|
245
|
+
end
|
246
|
+
|
247
|
+
def with_provider(provider_name)
|
248
|
+
require_provider_file(provider_name) unless providers.key?(provider_name)
|
249
|
+
|
250
|
+
provider = providers[provider_name]
|
251
|
+
|
252
|
+
raise ProviderNotFoundError, provider_name unless provider
|
253
|
+
|
254
|
+
yield(provider)
|
255
|
+
end
|
256
|
+
|
257
|
+
def load_provider(path)
|
258
|
+
name = Pathname(path).basename(RB_EXT).to_s.to_sym
|
259
|
+
|
260
|
+
Kernel.require path unless providers.key?(name)
|
261
|
+
|
262
|
+
self
|
263
|
+
end
|
264
|
+
|
265
|
+
def require_provider_file(name)
|
266
|
+
provider_file = find_provider_file(name)
|
267
|
+
|
268
|
+
Kernel.require provider_file if provider_file
|
269
|
+
end
|
270
|
+
|
271
|
+
def find_provider_file(name)
|
272
|
+
provider_files.detect { |file| File.basename(file, RB_EXT) == name.to_s }
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/deprecations"
|
4
|
+
require_relative "constants"
|
5
|
+
require_relative "provider/source"
|
6
|
+
|
7
|
+
module Dry
|
8
|
+
module System
|
9
|
+
# @api private
|
10
|
+
class ProviderSourceRegistry
|
11
|
+
attr_reader :sources
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@sources = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def load_sources(path)
|
18
|
+
Dir[File.join(path, "**/#{RB_GLOB}")].sort.each do |file|
|
19
|
+
require file
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def register(name:, group:, source:)
|
24
|
+
sources[key(name, group)] = source
|
25
|
+
end
|
26
|
+
|
27
|
+
def register_from_block(name:, group:, target_container:, &block)
|
28
|
+
register(
|
29
|
+
name: name,
|
30
|
+
group: group,
|
31
|
+
source: Provider::Source.for(
|
32
|
+
name: name,
|
33
|
+
group: group,
|
34
|
+
target_container: target_container,
|
35
|
+
&block
|
36
|
+
)
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def resolve(name:, group:)
|
41
|
+
if group == :system
|
42
|
+
Dry::Core::Deprecations.announce(
|
43
|
+
"Providers using `from: :system`",
|
44
|
+
"Use `from: :dry_system` instead",
|
45
|
+
tag: "dry-system",
|
46
|
+
uplevel: 1
|
47
|
+
)
|
48
|
+
|
49
|
+
group = :dry_system
|
50
|
+
end
|
51
|
+
|
52
|
+
sources[key(name, group)].tap { |source|
|
53
|
+
unless source
|
54
|
+
raise ProviderSourceNotFoundError.new(
|
55
|
+
name: name,
|
56
|
+
group: group,
|
57
|
+
keys: sources.keys
|
58
|
+
)
|
59
|
+
end
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def key(name, group)
|
66
|
+
{group: group, name: name}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|