dry-system 0.18.1 → 1.0.1
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 +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
|