dry-system 0.18.1 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +678 -0
- data/LICENSE +1 -1
- data/README.md +5 -4
- data/dry-system.gemspec +18 -21
- data/lib/dry/system/auto_registrar.rb +9 -64
- data/lib/dry/system/component.rb +124 -104
- data/lib/dry/system/component_dir.rb +171 -0
- data/lib/dry/system/config/component_dir.rb +228 -0
- data/lib/dry/system/config/component_dirs.rb +289 -0
- data/lib/dry/system/config/namespace.rb +75 -0
- data/lib/dry/system/config/namespaces.rb +196 -0
- data/lib/dry/system/constants.rb +2 -4
- data/lib/dry/system/container.rb +305 -345
- data/lib/dry/system/errors.rb +73 -56
- data/lib/dry/system/identifier.rb +176 -0
- data/lib/dry/system/importer.rb +89 -12
- data/lib/dry/system/indirect_component.rb +63 -0
- data/lib/dry/system/loader/autoloading.rb +24 -0
- data/lib/dry/system/loader.rb +49 -41
- data/lib/dry/system/{manual_registrar.rb → manifest_registrar.rb} +13 -14
- data/lib/dry/system/plugins/bootsnap.rb +3 -2
- data/lib/dry/system/plugins/dependency_graph/strategies.rb +38 -2
- data/lib/dry/system/plugins/dependency_graph.rb +25 -21
- data/lib/dry/system/plugins/env.rb +3 -2
- data/lib/dry/system/plugins/logging.rb +9 -8
- data/lib/dry/system/plugins/monitoring.rb +1 -2
- data/lib/dry/system/plugins/notifications.rb +1 -1
- data/lib/dry/system/plugins/plugin.rb +61 -0
- 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 +5 -73
- data/lib/dry/system/provider/source.rb +276 -0
- data/lib/dry/system/provider/source_dsl.rb +55 -0
- data/lib/dry/system/provider.rb +261 -23
- data/lib/dry/system/provider_registrar.rb +251 -0
- data/lib/dry/system/provider_source_registry.rb +56 -0
- data/lib/dry/system/provider_sources/settings/config.rb +73 -0
- data/lib/dry/system/provider_sources/settings/loader.rb +44 -0
- data/lib/dry/system/provider_sources/settings.rb +40 -0
- data/lib/dry/system/provider_sources.rb +5 -0
- data/lib/dry/system/stubs.rb +6 -2
- data/lib/dry/system/version.rb +1 -1
- data/lib/dry/system.rb +35 -13
- metadata +48 -97
- data/lib/dry/system/auto_registrar/configuration.rb +0 -43
- data/lib/dry/system/booter/component_registry.rb +0 -35
- data/lib/dry/system/booter.rb +0 -181
- data/lib/dry/system/components/bootable.rb +0 -289
- data/lib/dry/system/components/config.rb +0 -35
- data/lib/dry/system/components.rb +0 -8
- 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 -67
- data/lib/dry/system/system_components/settings.rb +0 -11
@@ -0,0 +1,276 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module System
|
5
|
+
class Provider
|
6
|
+
# A provider's source provides the specific behavior for a given provider to serve
|
7
|
+
# its purpose.
|
8
|
+
#
|
9
|
+
# Sources should be subclasses of `Dry::System::Source::Provider`, with instance
|
10
|
+
# methods for each lifecycle step providing their behavior: {#prepare}, {#start},
|
11
|
+
# and {#stop}.
|
12
|
+
#
|
13
|
+
# Inside each of these methods, you should create and configure your provider's
|
14
|
+
# objects as required, and then {#register} them with the {#provider_container}.
|
15
|
+
# When the provider's lifecycle steps are run (via {Dry::System::Provider}), these
|
16
|
+
# registered components will be merged into the target container.
|
17
|
+
#
|
18
|
+
# You can prepare a provider's source in two ways:
|
19
|
+
#
|
20
|
+
# 1. Passing a bock when registering the provider, which is then evaluated via
|
21
|
+
# {Dry::System::Provider::SourceDSL} to prepare the provider subclass. This
|
22
|
+
# approach is easiest for simple providers.
|
23
|
+
# 2. Manually creare your own subclass of {Dry::System::Provider} and implement your
|
24
|
+
# own instance methods for the lifecycle steps (you should not implement your own
|
25
|
+
# `#initialize`). This approach may be useful for more complex providers.
|
26
|
+
#
|
27
|
+
# @see Dry::System::Container.register_provider
|
28
|
+
# @see Dry::System.register_provider_source
|
29
|
+
# @see Dry::System::Source::ProviderDSL
|
30
|
+
#
|
31
|
+
# @api public
|
32
|
+
class Source
|
33
|
+
class << self
|
34
|
+
# Returns a new Dry::System::Provider::Source subclass with its behavior supplied by the
|
35
|
+
# given block, which is evaluated using Dry::System::Provider::SourceDSL.
|
36
|
+
#
|
37
|
+
# @see Dry::System::Provider::SourceDSL
|
38
|
+
#
|
39
|
+
# @api private
|
40
|
+
def for(name:, group: nil, &block)
|
41
|
+
Class.new(self) { |klass|
|
42
|
+
klass.source_name name
|
43
|
+
klass.source_group group
|
44
|
+
SourceDSL.evaluate(klass, &block) if block
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def inherited(subclass)
|
49
|
+
super
|
50
|
+
|
51
|
+
# Include Dry::Configurable only when first subclassing to ensure that
|
52
|
+
# distinct Source subclasses do not share settings.
|
53
|
+
#
|
54
|
+
# The superclass check here allows deeper Source class hierarchies to be
|
55
|
+
# created without running into a Dry::Configurable::AlreadyIncluded error.
|
56
|
+
if subclass.superclass == Source
|
57
|
+
subclass.include Dry::Configurable
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# @api private
|
62
|
+
def name
|
63
|
+
source_str = source_name
|
64
|
+
source_str = "#{source_group}->#{source_str}" if source_group
|
65
|
+
|
66
|
+
"Dry::System::Provider::Source[#{source_str}]"
|
67
|
+
end
|
68
|
+
|
69
|
+
# @api private
|
70
|
+
def to_s
|
71
|
+
"#<#{name}>"
|
72
|
+
end
|
73
|
+
|
74
|
+
# @api private
|
75
|
+
def inspect
|
76
|
+
to_s
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
CALLBACK_MAP = Hash.new { |h, k| h[k] = [] }.freeze
|
81
|
+
|
82
|
+
extend Dry::Core::ClassAttributes
|
83
|
+
|
84
|
+
defines :source_name, :source_group
|
85
|
+
|
86
|
+
# @api private
|
87
|
+
attr_reader :callbacks
|
88
|
+
|
89
|
+
# Returns the provider's own container for the provider.
|
90
|
+
#
|
91
|
+
# This container is namespaced based on the provider's `namespace:` configuration.
|
92
|
+
#
|
93
|
+
# Registered components in this container will be merged into the target container
|
94
|
+
# after the `prepare` and `start` lifecycle steps.
|
95
|
+
#
|
96
|
+
# @return [Dry::Container]
|
97
|
+
#
|
98
|
+
# @see #target_container
|
99
|
+
# @see Dry::System::Provider
|
100
|
+
#
|
101
|
+
# @api public
|
102
|
+
attr_reader :provider_container
|
103
|
+
alias_method :container, :provider_container
|
104
|
+
|
105
|
+
# Returns the target container for the provider.
|
106
|
+
#
|
107
|
+
# This is the container with which the provider is registered (via
|
108
|
+
# {Dry::System::Container.register_provider}).
|
109
|
+
#
|
110
|
+
# Registered components from the provider's container will be merged into this
|
111
|
+
# container after the `prepare` and `start` lifecycle steps.
|
112
|
+
#
|
113
|
+
# @return [Dry::System::Container]
|
114
|
+
#
|
115
|
+
# @see #provider_container
|
116
|
+
# @see Dry::System::Provider
|
117
|
+
#
|
118
|
+
# @api public
|
119
|
+
attr_reader :target_container
|
120
|
+
alias_method :target, :target_container
|
121
|
+
|
122
|
+
# @api private
|
123
|
+
def initialize(provider_container:, target_container:, &block)
|
124
|
+
super()
|
125
|
+
@callbacks = {before: CALLBACK_MAP.dup, after: CALLBACK_MAP.dup}
|
126
|
+
@provider_container = provider_container
|
127
|
+
@target_container = target_container
|
128
|
+
instance_exec(&block) if block
|
129
|
+
end
|
130
|
+
|
131
|
+
# Returns a string containing a human-readable representation of the provider.
|
132
|
+
#
|
133
|
+
# @return [String]
|
134
|
+
#
|
135
|
+
# @api private
|
136
|
+
def inspect
|
137
|
+
ivars = instance_variables.map { |ivar|
|
138
|
+
"#{ivar}=#{instance_variable_get(ivar).inspect}"
|
139
|
+
}.join(" ")
|
140
|
+
|
141
|
+
"#<#{self.class.name} #{ivars}>"
|
142
|
+
end
|
143
|
+
|
144
|
+
# Runs the behavior for the "prepare" lifecycle step.
|
145
|
+
#
|
146
|
+
# This should be implemented by your source subclass or specified by
|
147
|
+
# `SourceDSL#prepare` when registering a provider using a block.
|
148
|
+
#
|
149
|
+
# @return [void]
|
150
|
+
#
|
151
|
+
# @see SourceDSL#prepare
|
152
|
+
#
|
153
|
+
# @api public
|
154
|
+
def prepare; end
|
155
|
+
|
156
|
+
# Runs the behavior for the "start" lifecycle step.
|
157
|
+
#
|
158
|
+
# This should be implemented by your source subclass or specified by
|
159
|
+
# `SourceDSL#start` when registering a provider using a block.
|
160
|
+
#
|
161
|
+
# You can presume that {#prepare} has already run by the time this method is
|
162
|
+
# called.
|
163
|
+
#
|
164
|
+
# @return [void]
|
165
|
+
#
|
166
|
+
# @see SourceDSL#start
|
167
|
+
#
|
168
|
+
# @api public
|
169
|
+
def start; end
|
170
|
+
|
171
|
+
# Runs the behavior for the "stop" lifecycle step.
|
172
|
+
#
|
173
|
+
# This should be implemented by your source subclass or specified by
|
174
|
+
# `SourceDSL#stop` when registering a provider using a block.
|
175
|
+
#
|
176
|
+
# You can presume that {#prepare} and {#start} have already run by the time this
|
177
|
+
# method is called.
|
178
|
+
#
|
179
|
+
# @return [void]
|
180
|
+
#
|
181
|
+
# @see SourceDSL#stop
|
182
|
+
#
|
183
|
+
# @api public
|
184
|
+
def stop; end
|
185
|
+
|
186
|
+
# Registers a "before" callback for the given lifecycle step.
|
187
|
+
#
|
188
|
+
# The given block will be run before the lifecycle step method is run. The block
|
189
|
+
# will be evaluated in the context of the instance of this source.
|
190
|
+
#
|
191
|
+
# @param step_name [Symbol]
|
192
|
+
# @param block [Proc] the callback block
|
193
|
+
#
|
194
|
+
# @return [self]
|
195
|
+
#
|
196
|
+
# @see #after
|
197
|
+
#
|
198
|
+
# @api public
|
199
|
+
def before(step_name, &block)
|
200
|
+
callbacks[:before][step_name] << block
|
201
|
+
self
|
202
|
+
end
|
203
|
+
|
204
|
+
# Registers an "after" callback for the given lifecycle step.
|
205
|
+
#
|
206
|
+
# The given block will be run after the lifecycle step method is run. The block
|
207
|
+
# will be evaluated in the context of the instance of this source.
|
208
|
+
#
|
209
|
+
# @param step_name [Symbol]
|
210
|
+
# @param block [Proc] the callback block
|
211
|
+
#
|
212
|
+
# @return [self]
|
213
|
+
#
|
214
|
+
# @see #before
|
215
|
+
#
|
216
|
+
# @api public
|
217
|
+
def after(step_name, &block)
|
218
|
+
callbacks[:after][step_name] << block
|
219
|
+
self
|
220
|
+
end
|
221
|
+
|
222
|
+
# @api private
|
223
|
+
def run_callback(hook, step)
|
224
|
+
callbacks[hook][step].each do |callback|
|
225
|
+
instance_eval(&callback)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
private
|
230
|
+
|
231
|
+
# Registers a component in the provider container.
|
232
|
+
#
|
233
|
+
# When the provider's lifecycle steps are run (via {Dry::System::Provider}), these
|
234
|
+
# registered components will be merged into the target container.
|
235
|
+
#
|
236
|
+
# @return [Dry::Container] the provider container
|
237
|
+
#
|
238
|
+
# @api public
|
239
|
+
def register(...)
|
240
|
+
provider_container.register(...)
|
241
|
+
end
|
242
|
+
|
243
|
+
# Resolves a previously registered component from the provider container.
|
244
|
+
#
|
245
|
+
# @param key [String] the key for the component to resolve
|
246
|
+
#
|
247
|
+
# @return [Object] the previously registered component
|
248
|
+
#
|
249
|
+
# @api public
|
250
|
+
def resolve(key)
|
251
|
+
provider_container.resolve(key)
|
252
|
+
end
|
253
|
+
|
254
|
+
# @api private
|
255
|
+
def run_step_block(step_name)
|
256
|
+
step_block = self.class.step_blocks[step_name]
|
257
|
+
instance_eval(&step_block) if step_block
|
258
|
+
end
|
259
|
+
|
260
|
+
# @api private
|
261
|
+
def method_missing(name, *args, &block)
|
262
|
+
if container.key?(name)
|
263
|
+
container[name]
|
264
|
+
else
|
265
|
+
super
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# @api private
|
270
|
+
def respond_to_missing?(name, include_all = false)
|
271
|
+
container.key?(name) || super
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module System
|
5
|
+
class Provider
|
6
|
+
# Configures a Dry::System::Provider::Source subclass using a DSL that makes it
|
7
|
+
# nicer to define source behaviour via a single block.
|
8
|
+
#
|
9
|
+
# @see Dry::System::Container.register_provider
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
class SourceDSL
|
13
|
+
def self.evaluate(source_class, &block)
|
14
|
+
new(source_class).instance_eval(&block)
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :source_class
|
18
|
+
|
19
|
+
def initialize(source_class)
|
20
|
+
@source_class = source_class
|
21
|
+
end
|
22
|
+
|
23
|
+
def setting(...)
|
24
|
+
source_class.setting(...)
|
25
|
+
end
|
26
|
+
|
27
|
+
def prepare(&block)
|
28
|
+
source_class.define_method(:prepare, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def start(&block)
|
32
|
+
source_class.define_method(:start, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
def stop(&block)
|
36
|
+
source_class.define_method(:stop, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def method_missing(name, *args, &block)
|
42
|
+
if source_class.respond_to?(name)
|
43
|
+
source_class.public_send(name, *args, &block)
|
44
|
+
else
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def respond_to_missing?(name, include_all = false)
|
50
|
+
source_class.respond_to?(name, include_all) || super
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/dry/system/provider.rb
CHANGED
@@ -1,48 +1,286 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "concurrent/map"
|
4
3
|
require "dry/system/constants"
|
5
|
-
require "dry/system/components/bootable"
|
6
4
|
|
7
5
|
module Dry
|
8
6
|
module System
|
7
|
+
# Providers can prepare and register one or more objects and typically work with third
|
8
|
+
# party code. A typical provider might be for a database library, or an API client.
|
9
|
+
#
|
10
|
+
# The particular behavior for any provider is defined in a {Provider::Source}, which
|
11
|
+
# is a subclass created when you run {Container.register_provider} or
|
12
|
+
# {Dry::System.register_provider_source}. The Source provides this behavior through
|
13
|
+
# methods for each of the steps in the provider lifecycle: `prepare`, `start`, and
|
14
|
+
# `run`. These methods typically create and configure various objects, then register
|
15
|
+
# them with the {#provider_container}.
|
16
|
+
#
|
17
|
+
# The Provider manages this lifecycle by implementing common behavior around the
|
18
|
+
# lifecycle steps, such as running step callbacks, and only running steps when
|
19
|
+
# appropriate for the current status of the lifecycle.
|
20
|
+
#
|
21
|
+
# Providers can be registered via {Container.register_provider}.
|
22
|
+
#
|
23
|
+
# @example Simple provider
|
24
|
+
# class App < Dry::System::Container
|
25
|
+
# register_provider(:logger) do
|
26
|
+
# prepare do
|
27
|
+
# require "logger"
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# start do
|
31
|
+
# register(:logger, Logger.new($stdout))
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# App[:logger] # returns configured logger
|
37
|
+
#
|
38
|
+
# @example Using an external Provider Source
|
39
|
+
# class App < Dry::System::Container
|
40
|
+
# register_provider(:logger, from: :some_external_provider_source) do
|
41
|
+
# configure do |config|
|
42
|
+
# config.log_level = :debug
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# after :start do
|
46
|
+
# register(:my_extra_logger, resolve(:logger))
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# App[:my_extra_logger] # returns the extra logger registered in the callback
|
52
|
+
#
|
53
|
+
# @api public
|
9
54
|
class Provider
|
10
|
-
|
55
|
+
# Returns the provider's unique name.
|
56
|
+
#
|
57
|
+
# @return [Symbol]
|
58
|
+
#
|
59
|
+
# @api public
|
60
|
+
attr_reader :name
|
11
61
|
|
12
|
-
|
62
|
+
# Returns the default namespace for the provider's container keys.
|
63
|
+
#
|
64
|
+
# @return [Symbol,String]
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
attr_reader :namespace
|
13
68
|
|
14
|
-
|
69
|
+
# Returns an array of lifecycle steps that have been run.
|
70
|
+
#
|
71
|
+
# @return [Array<Symbol>]
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# provider.statuses # => [:prepare, :start]
|
75
|
+
#
|
76
|
+
# @api public
|
77
|
+
attr_reader :statuses
|
15
78
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
79
|
+
# Returns the name of the currently running step, if any.
|
80
|
+
#
|
81
|
+
# @return [Symbol, nil]
|
82
|
+
#
|
83
|
+
# @api private
|
84
|
+
attr_reader :step_running
|
85
|
+
private :step_running
|
86
|
+
|
87
|
+
# Returns the container for the provider.
|
88
|
+
#
|
89
|
+
# This is where the provider's source will register its components, which are then
|
90
|
+
# later marged into the target container after the `prepare` and `start` lifecycle
|
91
|
+
# steps.
|
92
|
+
#
|
93
|
+
# @return [Dry::Core::Container]
|
94
|
+
#
|
95
|
+
# @api public
|
96
|
+
attr_reader :provider_container
|
97
|
+
alias_method :container, :provider_container
|
98
|
+
|
99
|
+
# Returns the target container for the provider.
|
100
|
+
#
|
101
|
+
# This is the container with which the provider is registered (via
|
102
|
+
# {Dry::System::Container.register_provider}).
|
103
|
+
#
|
104
|
+
# Registered components from the provider's container will be merged into this
|
105
|
+
# container after the `prepare` and `start` lifecycle steps.
|
106
|
+
#
|
107
|
+
# @return [Dry::System::Container]
|
108
|
+
#
|
109
|
+
# @api public
|
110
|
+
attr_reader :target_container
|
111
|
+
alias_method :target, :target_container
|
112
|
+
|
113
|
+
# Returns the provider's source
|
114
|
+
#
|
115
|
+
# The source provides the specific behavior for the provider via methods
|
116
|
+
# implementing the lifecycle steps.
|
117
|
+
#
|
118
|
+
# The provider's source is defined when registering a provider with the container,
|
119
|
+
# or an external provider source.
|
120
|
+
#
|
121
|
+
# @see Dry::System::Container.register_provider
|
122
|
+
# @see Dry::System.register_provider_source
|
123
|
+
#
|
124
|
+
# @return [Dry::System::Provider::Source]
|
125
|
+
#
|
126
|
+
# @api private
|
127
|
+
attr_reader :source
|
128
|
+
|
129
|
+
# @api private
|
130
|
+
def initialize(name:, namespace: nil, target_container:, source_class:, &block) # rubocop:disable Style/KeywordParametersOrder
|
131
|
+
@name = name
|
132
|
+
@namespace = namespace
|
133
|
+
@target_container = target_container
|
134
|
+
|
135
|
+
@provider_container = build_provider_container
|
136
|
+
@statuses = []
|
137
|
+
@step_running = nil
|
138
|
+
|
139
|
+
@source = source_class.new(
|
140
|
+
provider_container: provider_container,
|
141
|
+
target_container: target_container,
|
142
|
+
&block
|
143
|
+
)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Runs the `prepare` lifecycle step.
|
147
|
+
#
|
148
|
+
# Also runs any callbacks for the step, and then merges any registered components
|
149
|
+
# from the provider container into the target container.
|
150
|
+
#
|
151
|
+
# @return [self]
|
152
|
+
#
|
153
|
+
# @api public
|
154
|
+
def prepare
|
155
|
+
run_step(:prepare)
|
20
156
|
end
|
21
157
|
|
22
|
-
|
23
|
-
|
158
|
+
# Runs the `start` lifecycle step.
|
159
|
+
#
|
160
|
+
# Also runs any callbacks for the step, and then merges any registered components
|
161
|
+
# from the provider container into the target container.
|
162
|
+
#
|
163
|
+
# @return [self]
|
164
|
+
#
|
165
|
+
# @api public
|
166
|
+
def start
|
167
|
+
run_step(:prepare)
|
168
|
+
run_step(:start)
|
24
169
|
end
|
25
170
|
|
26
|
-
|
27
|
-
|
171
|
+
# Runs the `stop` lifecycle step.
|
172
|
+
#
|
173
|
+
# Also runs any callbacks for the step.
|
174
|
+
#
|
175
|
+
# @return [self]
|
176
|
+
#
|
177
|
+
# @api public
|
178
|
+
def stop
|
179
|
+
return self unless started?
|
180
|
+
|
181
|
+
run_step(:stop)
|
28
182
|
end
|
29
183
|
|
30
|
-
|
31
|
-
|
184
|
+
# Returns true if the provider's `prepare` lifecycle step has run
|
185
|
+
#
|
186
|
+
# @api public
|
187
|
+
def prepared?
|
188
|
+
statuses.include?(:prepare)
|
32
189
|
end
|
33
190
|
|
34
|
-
|
35
|
-
|
191
|
+
# Returns true if the provider's `start` lifecycle step has run
|
192
|
+
#
|
193
|
+
# @api public
|
194
|
+
def started?
|
195
|
+
statuses.include?(:start)
|
36
196
|
end
|
37
197
|
|
38
|
-
|
39
|
-
|
40
|
-
|
198
|
+
# Returns true if the provider's `stop` lifecycle step has run
|
199
|
+
#
|
200
|
+
# @api public
|
201
|
+
def stopped?
|
202
|
+
statuses.include?(:stop)
|
41
203
|
end
|
42
204
|
|
43
|
-
|
44
|
-
|
45
|
-
|
205
|
+
private
|
206
|
+
|
207
|
+
# @api private
|
208
|
+
def build_provider_container
|
209
|
+
container = Core::Container.new
|
210
|
+
|
211
|
+
case namespace
|
212
|
+
when String, Symbol
|
213
|
+
container.namespace(namespace) { |c| return c }
|
214
|
+
when true
|
215
|
+
container.namespace(name) { |c| return c }
|
216
|
+
when nil
|
217
|
+
container
|
218
|
+
else
|
219
|
+
raise ArgumentError,
|
220
|
+
"+namespace:+ must be true, string or symbol: #{namespace.inspect} given."
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# @api private
|
225
|
+
def run_step(step_name)
|
226
|
+
return self if step_running? || statuses.include?(step_name)
|
227
|
+
|
228
|
+
@step_running = step_name
|
229
|
+
|
230
|
+
source.run_callback(:before, step_name)
|
231
|
+
source.public_send(step_name)
|
232
|
+
source.run_callback(:after, step_name)
|
233
|
+
|
234
|
+
statuses << step_name
|
235
|
+
|
236
|
+
apply
|
237
|
+
|
238
|
+
@step_running = nil
|
239
|
+
|
240
|
+
self
|
241
|
+
end
|
242
|
+
|
243
|
+
# Returns true if a step is currenly running.
|
244
|
+
#
|
245
|
+
# This is important for short-circuiting the invocation of {#run_step} and avoiding
|
246
|
+
# infinite loops if a provider step happens to result in resolution of a component
|
247
|
+
# with the same root key as the provider's own name (which ordinarily results in
|
248
|
+
# that provider being started).
|
249
|
+
#
|
250
|
+
# @return [Boolean]
|
251
|
+
#
|
252
|
+
# @see {#run_step}
|
253
|
+
#
|
254
|
+
# @api private
|
255
|
+
def step_running?
|
256
|
+
!!step_running
|
257
|
+
end
|
258
|
+
|
259
|
+
# Registers any components from the provider's container in the main container.
|
260
|
+
#
|
261
|
+
# Called after each lifecycle step runs.
|
262
|
+
#
|
263
|
+
# @return [self]
|
264
|
+
#
|
265
|
+
# @api private
|
266
|
+
def apply
|
267
|
+
provider_container.each_key do |key|
|
268
|
+
next if target_container.registered?(key)
|
269
|
+
|
270
|
+
# Access the provider's container items directly so that we can preserve all
|
271
|
+
# their options when we merge them with the target container (e.g. if a
|
272
|
+
# component in the provider container was registered with a block, we want block
|
273
|
+
# registration behavior to be exhibited when later resolving that component from
|
274
|
+
# the target container). TODO: Make this part of dry-system's public API.
|
275
|
+
item = provider_container._container[key]
|
276
|
+
|
277
|
+
if item.callable?
|
278
|
+
target_container.register(key, **item.options, &item.item)
|
279
|
+
else
|
280
|
+
target_container.register(key, item.item, **item.options)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
46
284
|
self
|
47
285
|
end
|
48
286
|
end
|