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/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
|