dry-system 0.10.0 → 0.10.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 +14 -2
- data/lib/dry/system/booter.rb +9 -0
- data/lib/dry/system/container.mod.rb +664 -0
- data/lib/dry/system/container.rb +18 -1
- data/lib/dry/system/errors.rb +9 -0
- data/lib/dry/system/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 691aea0bf618106d932309794bcf87afa5d84a3c423871342c2ceb39b45a6ce6
|
4
|
+
data.tar.gz: 1ac5655641e9ffd965e3f0b37a199367e7157074a14250745a49925bab0ec517
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 589e77b1739b554528f13633f93bf61c39f4945067318f08afcc5be5bf0afbc53db137f54d0e1b2202aa45a5f2a3df697564cfc7f208cb3fa043d28b0f174706
|
7
|
+
data.tar.gz: 8271fb82fde50c7bfed6e52da6a58ea90a447ee7097562402cfd38d94823edb29bee835b0e462bc893ddb59ddb76f1d0fe0c9dab99d4cf14062f74082ab0ccdc
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
# 0.10.1 - 2018-07-05
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* Support for stopping bootable components with `Container.stop(component_name)` (GustavoCaso)
|
6
|
+
|
7
|
+
### Fixed
|
8
|
+
|
9
|
+
* When using a non-finalized container, you can now resolve multiple different container objects registered using the same root key as a bootable component (timriley)
|
10
|
+
|
11
|
+
[Compare v0.10.0...v0.10.1](https://github.com/dry-rb/dry-system/compare/v0.10.0...v0.10.1)
|
12
|
+
|
1
13
|
# 0.10.0 - 2018-06-07
|
2
14
|
|
3
15
|
### Added
|
@@ -17,10 +29,10 @@
|
|
17
29
|
|
18
30
|
* A helpful error will be raised if an invalid setting value is provided (GustavoCaso)
|
19
31
|
* When using setting plugin, will use default values from types (GustavoCaso)
|
20
|
-
* Minimal supported ruby version was
|
32
|
+
* Minimal supported ruby version was bumped to `2.3` (flash-gordon)
|
21
33
|
* `dry-struct` was updated to `~> 0.5` (flash-gordon)
|
22
34
|
|
23
|
-
[Compare v0.9.2...
|
35
|
+
[Compare v0.9.2...v0.10.0](https://github.com/dry-rb/dry-system/compare/v0.9.2...v0.10.0)
|
24
36
|
|
25
37
|
# 0.9.2 - 2018-02-08
|
26
38
|
|
data/lib/dry/system/booter.rb
CHANGED
@@ -96,6 +96,15 @@ module Dry
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
+
# @api private
|
100
|
+
def stop(name_or_component)
|
101
|
+
call(name_or_component) do |component|
|
102
|
+
raise ComponentNotStartedError.new(name_or_component) unless booted.include?(component)
|
103
|
+
component.stop
|
104
|
+
yield if block_given?
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
99
108
|
# @api private
|
100
109
|
def call(name_or_component)
|
101
110
|
with_component(name_or_component) do |component|
|
@@ -0,0 +1,664 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
require 'dry-auto_inject'
|
4
|
+
require 'dry-configurable'
|
5
|
+
require 'dry-container'
|
6
|
+
require 'dry/inflector'
|
7
|
+
|
8
|
+
require 'dry/core/deprecations'
|
9
|
+
|
10
|
+
require 'dry/system'
|
11
|
+
require 'dry/system/errors'
|
12
|
+
require 'dry/system/loader'
|
13
|
+
require 'dry/system/booter'
|
14
|
+
require 'dry/system/auto_registrar'
|
15
|
+
require 'dry/system/manual_registrar'
|
16
|
+
require 'dry/system/importer'
|
17
|
+
require 'dry/system/component'
|
18
|
+
require 'dry/system/constants'
|
19
|
+
require 'dry/system/plugins'
|
20
|
+
|
21
|
+
module Dry
|
22
|
+
module System
|
23
|
+
# Abstract container class to inherit from
|
24
|
+
#
|
25
|
+
# Container class is treated as a global registry with all system components.
|
26
|
+
# Container can also import dependencies from other containers, which is
|
27
|
+
# useful in complex systems that are split into sub-systems.
|
28
|
+
#
|
29
|
+
# Container can be finalized, which triggers loading of all the defined
|
30
|
+
# components within a system, after finalization it becomes frozen. This
|
31
|
+
# typically happens in cases like booting a web application.
|
32
|
+
#
|
33
|
+
# Before finalization, Container can lazy-load components on demand. A
|
34
|
+
# component can be a simple class defined in a single file, or a complex
|
35
|
+
# component which has init/start/stop lifecycle, and it's defined in a boot
|
36
|
+
# file. Components which specify their dependencies using Import module can
|
37
|
+
# be safely required in complete isolation, and Container will resolve and
|
38
|
+
# load these dependencies automatically.
|
39
|
+
#
|
40
|
+
# Furthermore, Container supports auto-registering components based on
|
41
|
+
# dir/file naming conventions. This reduces a lot of boilerplate code as all
|
42
|
+
# you have to do is to put your classes under configured directories and
|
43
|
+
# their instances will be automatically registered within a container.
|
44
|
+
#
|
45
|
+
# Every container needs to be configured with following settings:
|
46
|
+
#
|
47
|
+
# * `:name` - a unique container identifier
|
48
|
+
# * `:root` - a system root directory (defaults to `pwd`)
|
49
|
+
# * `:system_dir` - directory name relative to root, where bootable components
|
50
|
+
# can be defined in `boot` dir this defaults to `system`
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# class MyApp < Dry::System::Container
|
54
|
+
# configure do |config|
|
55
|
+
# config.name = :my_app
|
56
|
+
#
|
57
|
+
# # this will auto-register classes from 'lib/components'. ie if you add
|
58
|
+
# # `lib/components/repo.rb` which defines `Repo` class, then it's
|
59
|
+
# # instance will be automatically available as `MyApp['repo']`
|
60
|
+
# config.auto_register = %w(lib/components)
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# # this will configure $LOAD_PATH to include your `lib` dir
|
64
|
+
# load_paths!('lib')
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# @api public
|
68
|
+
class Container
|
69
|
+
extend Dry::Configurable
|
70
|
+
extend Dry::Container::Mixin
|
71
|
+
extend Dry::System::Plugins
|
72
|
+
|
73
|
+
setting :name
|
74
|
+
setting :default_namespace
|
75
|
+
setting(:root, Pathname.pwd.freeze) { |path| Pathname(path) }
|
76
|
+
setting :system_dir, 'system'.freeze
|
77
|
+
setting :registrations_dir, 'container'.freeze
|
78
|
+
setting :auto_register, []
|
79
|
+
setting :inflector, Dry::Inflector.new
|
80
|
+
setting :loader, Dry::System::Loader
|
81
|
+
setting :booter, Dry::System::Booter
|
82
|
+
setting :auto_registrar, Dry::System::AutoRegistrar
|
83
|
+
setting :manual_registrar, Dry::System::ManualRegistrar
|
84
|
+
setting :importer, Dry::System::Importer
|
85
|
+
setting(:components, {}, reader: true) { |v| v.dup }
|
86
|
+
|
87
|
+
class << self
|
88
|
+
extend Dry::Core::Deprecations['Dry::System::Container']
|
89
|
+
|
90
|
+
# Configures the container
|
91
|
+
#
|
92
|
+
# @example
|
93
|
+
# class MyApp < Dry::System::Container
|
94
|
+
# configure do |config|
|
95
|
+
# config.root = Pathname("/path/to/app")
|
96
|
+
# config.name = :my_app
|
97
|
+
# config.auto_register = %w(lib/apis lib/core)
|
98
|
+
# end
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# @return [self]
|
102
|
+
#
|
103
|
+
# @api public
|
104
|
+
def configure(&block)
|
105
|
+
super(&block)
|
106
|
+
load_paths!(config.system_dir)
|
107
|
+
hooks[:configure].each { |hook| instance_eval(&hook) }
|
108
|
+
self
|
109
|
+
end
|
110
|
+
|
111
|
+
# Registers another container for import
|
112
|
+
#
|
113
|
+
# @example
|
114
|
+
# # system/container.rb
|
115
|
+
# class Core < Dry::System::Container
|
116
|
+
# configure do |config|
|
117
|
+
# config.root = Pathname("/path/to/app")
|
118
|
+
# config.auto_register = %w(lib/apis lib/core)
|
119
|
+
# end
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# # apps/my_app/system/container.rb
|
123
|
+
# require 'system/container'
|
124
|
+
#
|
125
|
+
# class MyApp < Dry::System::Container
|
126
|
+
# configure do |config|
|
127
|
+
# config.root = Pathname("/path/to/app")
|
128
|
+
# config.auto_register = %w(lib/apis lib/core)
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
# import core: Core
|
132
|
+
# end
|
133
|
+
#
|
134
|
+
# @param other [Hash, Dry::Container::Namespace]
|
135
|
+
#
|
136
|
+
# @api public
|
137
|
+
def import(other)
|
138
|
+
case other
|
139
|
+
when Hash then importer.register(other)
|
140
|
+
when Dry::Container::Namespace then super
|
141
|
+
else
|
142
|
+
raise ArgumentError, "+other+ must be a hash of names and systems, or a Dry::Container namespace"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Registers finalization function for a bootable component
|
147
|
+
#
|
148
|
+
# By convention, boot files for components should be placed in
|
149
|
+
# `%{system_dir}/boot` and they will be loaded on demand when components
|
150
|
+
# are loaded in isolation, or during finalization process.
|
151
|
+
#
|
152
|
+
# @example
|
153
|
+
# # system/container.rb
|
154
|
+
# class MyApp < Dry::System::Container
|
155
|
+
# configure do |config|
|
156
|
+
# config.root = Pathname("/path/to/app")
|
157
|
+
# config.name = :core
|
158
|
+
# config.auto_register = %w(lib/apis lib/core)
|
159
|
+
# end
|
160
|
+
#
|
161
|
+
# # system/boot/db.rb
|
162
|
+
# #
|
163
|
+
# # Simple component registration
|
164
|
+
# MyApp.boot(:db) do |container|
|
165
|
+
# require 'db'
|
166
|
+
#
|
167
|
+
# container.register(:db, DB.new)
|
168
|
+
# end
|
169
|
+
#
|
170
|
+
# # system/boot/db.rb
|
171
|
+
# #
|
172
|
+
# # Component registration with lifecycle triggers
|
173
|
+
# MyApp.boot(:db) do |container|
|
174
|
+
# init do
|
175
|
+
# require 'db'
|
176
|
+
# DB.configure(ENV['DB_URL'])
|
177
|
+
# container.register(:db, DB.new)
|
178
|
+
# end
|
179
|
+
#
|
180
|
+
# start do
|
181
|
+
# db.establish_connection
|
182
|
+
# end
|
183
|
+
#
|
184
|
+
# stop do
|
185
|
+
# db.close_connection
|
186
|
+
# end
|
187
|
+
# end
|
188
|
+
#
|
189
|
+
# # system/boot/db.rb
|
190
|
+
# #
|
191
|
+
# # Component registration which uses another bootable component
|
192
|
+
# MyApp.boot(:db) do |container|
|
193
|
+
# use :logger
|
194
|
+
#
|
195
|
+
# start do
|
196
|
+
# require 'db'
|
197
|
+
# DB.configure(ENV['DB_URL'], logger: logger)
|
198
|
+
# container.register(:db, DB.new)
|
199
|
+
# end
|
200
|
+
# end
|
201
|
+
#
|
202
|
+
# # system/boot/db.rb
|
203
|
+
# #
|
204
|
+
# # Component registration under a namespace. This will register the
|
205
|
+
# # db object under `persistence.db` key
|
206
|
+
# MyApp.namespace(:persistence) do |persistence|
|
207
|
+
# require 'db'
|
208
|
+
# DB.configure(ENV['DB_URL'], logger: logger)
|
209
|
+
# persistence.register(:db, DB.new)
|
210
|
+
# end
|
211
|
+
#
|
212
|
+
# @param name [Symbol] a unique identifier for a bootable component
|
213
|
+
#
|
214
|
+
# @see Lifecycle
|
215
|
+
#
|
216
|
+
# @return [self]
|
217
|
+
#
|
218
|
+
# @api public
|
219
|
+
def boot(name, opts = {}, &block)
|
220
|
+
if components.key?(name)
|
221
|
+
raise DuplicatedComponentKeyError, "Bootable component #{name.inspect} was already registered"
|
222
|
+
end
|
223
|
+
|
224
|
+
component =
|
225
|
+
if opts[:from]
|
226
|
+
boot_external(name, opts, &block)
|
227
|
+
else
|
228
|
+
boot_local(name, opts, &block)
|
229
|
+
end
|
230
|
+
self
|
231
|
+
|
232
|
+
components[name] = component
|
233
|
+
end
|
234
|
+
deprecate :finalize, :boot
|
235
|
+
|
236
|
+
# @api private
|
237
|
+
def boot_external(identifier, from:, key: nil, namespace: nil, &block)
|
238
|
+
component = System.providers[from].component(
|
239
|
+
identifier, key: key, namespace: namespace, finalize: block, container: self
|
240
|
+
)
|
241
|
+
|
242
|
+
booter.register_component(component)
|
243
|
+
|
244
|
+
component
|
245
|
+
end
|
246
|
+
|
247
|
+
# @api private
|
248
|
+
def boot_local(identifier, namespace: nil, &block)
|
249
|
+
component = Components::Bootable.new(identifier, container: self, namespace: namespace, &block)
|
250
|
+
|
251
|
+
booter.register_component(component)
|
252
|
+
|
253
|
+
component
|
254
|
+
end
|
255
|
+
|
256
|
+
# Return if a container was finalized
|
257
|
+
#
|
258
|
+
# @return [TrueClass, FalseClass]
|
259
|
+
#
|
260
|
+
# @api public
|
261
|
+
def finalized?
|
262
|
+
@__finalized__.equal?(true)
|
263
|
+
end
|
264
|
+
|
265
|
+
# Finalizes the container
|
266
|
+
#
|
267
|
+
# This triggers importing components from other containers, booting
|
268
|
+
# registered components and auto-registering components. It should be
|
269
|
+
# called only in places where you want to finalize your system as a
|
270
|
+
# whole, ie when booting a web application
|
271
|
+
#
|
272
|
+
# @example
|
273
|
+
# # system/container.rb
|
274
|
+
# class MyApp < Dry::System::Container
|
275
|
+
# configure do |config|
|
276
|
+
# config.root = Pathname("/path/to/app")
|
277
|
+
# config.name = :my_app
|
278
|
+
# config.auto_register = %w(lib/apis lib/core)
|
279
|
+
# end
|
280
|
+
# end
|
281
|
+
#
|
282
|
+
# # You can put finalization file anywhere you want, ie system/boot.rb
|
283
|
+
# MyApp.finalize!
|
284
|
+
#
|
285
|
+
# # If you need last-moment adjustments just before the finalization
|
286
|
+
# # you can pass a block and do it there
|
287
|
+
# MyApp.finalize! do |container|
|
288
|
+
# # stuff that only needs to happen for finalization
|
289
|
+
# end
|
290
|
+
#
|
291
|
+
# @return [self] frozen container
|
292
|
+
#
|
293
|
+
# @api public
|
294
|
+
def finalize!(freeze: true, &block)
|
295
|
+
return self if finalized?
|
296
|
+
|
297
|
+
yield(self) if block
|
298
|
+
|
299
|
+
importer.finalize!
|
300
|
+
booter.finalize!
|
301
|
+
manual_registrar.finalize!
|
302
|
+
auto_registrar.finalize!
|
303
|
+
|
304
|
+
@__finalized__ = true
|
305
|
+
|
306
|
+
self.freeze if freeze
|
307
|
+
end
|
308
|
+
|
309
|
+
# Boots a specific component
|
310
|
+
#
|
311
|
+
# As a result, `init` and `start` lifecycle triggers are called
|
312
|
+
#
|
313
|
+
# @example
|
314
|
+
# MyApp.start(:persistence)
|
315
|
+
#
|
316
|
+
# @param name [Symbol] the name of a registered bootable component
|
317
|
+
#
|
318
|
+
# @return [self]
|
319
|
+
#
|
320
|
+
# @api public
|
321
|
+
def start(name)
|
322
|
+
booter.start(name)
|
323
|
+
self
|
324
|
+
end
|
325
|
+
|
326
|
+
# Boots a specific component but calls only `init` lifecycle trigger
|
327
|
+
#
|
328
|
+
# This way of booting is useful in places where a heavy dependency is
|
329
|
+
# needed but its started environment is not required
|
330
|
+
#
|
331
|
+
# @example
|
332
|
+
# MyApp.init(:persistence)
|
333
|
+
#
|
334
|
+
# @param [Symbol] name The name of a registered bootable component
|
335
|
+
#
|
336
|
+
# @return [self]
|
337
|
+
#
|
338
|
+
# @api public
|
339
|
+
def init(name)
|
340
|
+
booter.init(name)
|
341
|
+
self
|
342
|
+
end
|
343
|
+
|
344
|
+
# Stop a specific component but calls only `stop` lifecycle trigger
|
345
|
+
#
|
346
|
+
# @example
|
347
|
+
# MyApp.stop(:persistence)
|
348
|
+
#
|
349
|
+
# @param [Symbol] name The name of a registered bootable component
|
350
|
+
#
|
351
|
+
# @return [self]
|
352
|
+
#
|
353
|
+
# @api public
|
354
|
+
def stop(name)
|
355
|
+
booter.stop(name)
|
356
|
+
self
|
357
|
+
end
|
358
|
+
|
359
|
+
# Sets load paths relative to the container's root dir
|
360
|
+
#
|
361
|
+
# @example
|
362
|
+
# class MyApp < Dry::System::Container
|
363
|
+
# configure do |config|
|
364
|
+
# # ...
|
365
|
+
# end
|
366
|
+
#
|
367
|
+
# load_paths!('lib')
|
368
|
+
# end
|
369
|
+
#
|
370
|
+
# @param [Array<String>] dirs
|
371
|
+
#
|
372
|
+
# @return [self]
|
373
|
+
#
|
374
|
+
# @api public
|
375
|
+
def load_paths!(*dirs)
|
376
|
+
dirs.map(&root.method(:join)).each do |path|
|
377
|
+
next if load_paths.include?(path)
|
378
|
+
load_paths << path
|
379
|
+
$LOAD_PATH.unshift(path.to_s)
|
380
|
+
end
|
381
|
+
self
|
382
|
+
end
|
383
|
+
|
384
|
+
# @api public
|
385
|
+
def load_registrations!(name)
|
386
|
+
manual_registrar.(name)
|
387
|
+
self
|
388
|
+
end
|
389
|
+
|
390
|
+
# Auto-registers components from the provided directory
|
391
|
+
#
|
392
|
+
# Typically you want to configure auto_register directories, and it will
|
393
|
+
# work automatically. Use this method in cases where you want to have an
|
394
|
+
# explicit way where some components are auto-registered, or if you want
|
395
|
+
# to exclude some components from being auto-registered
|
396
|
+
#
|
397
|
+
# @example
|
398
|
+
# class MyApp < Dry::System::Container
|
399
|
+
# configure do |config|
|
400
|
+
# # ...
|
401
|
+
# end
|
402
|
+
#
|
403
|
+
# # with a dir
|
404
|
+
# auto_register!('lib/core')
|
405
|
+
#
|
406
|
+
# # with a dir and a custom registration block
|
407
|
+
# auto_register!('lib/core') do |config|
|
408
|
+
# config.instance do |component|
|
409
|
+
# # custom way of initializing a component
|
410
|
+
# end
|
411
|
+
#
|
412
|
+
# config.exclude do |component|
|
413
|
+
# # return true to exclude component from auto-registration
|
414
|
+
# end
|
415
|
+
# end
|
416
|
+
# end
|
417
|
+
#
|
418
|
+
# @param [String] dir The dir name relative to the root dir
|
419
|
+
#
|
420
|
+
# @yield AutoRegistrar::Configuration
|
421
|
+
# @see AutoRegistrar::Configuration
|
422
|
+
#
|
423
|
+
# @return [self]
|
424
|
+
#
|
425
|
+
# @api public
|
426
|
+
def auto_register!(dir, &block)
|
427
|
+
auto_registrar.(dir, &block)
|
428
|
+
self
|
429
|
+
end
|
430
|
+
|
431
|
+
# Builds injector for this container
|
432
|
+
#
|
433
|
+
# An injector is a useful mixin which injects dependencies into
|
434
|
+
# automatically defined constructor.
|
435
|
+
#
|
436
|
+
# @example
|
437
|
+
# # Define an injection mixin
|
438
|
+
# #
|
439
|
+
# # system/import.rb
|
440
|
+
# Import = MyApp.injector
|
441
|
+
#
|
442
|
+
# # Use it in your auto-registered classes
|
443
|
+
# #
|
444
|
+
# # lib/user_repo.rb
|
445
|
+
# require 'import'
|
446
|
+
#
|
447
|
+
# class UserRepo
|
448
|
+
# include Import['persistence.db']
|
449
|
+
# end
|
450
|
+
#
|
451
|
+
# MyApp['user_repo].db # instance under 'persistence.db' key
|
452
|
+
#
|
453
|
+
# @param options [Hash] injector options
|
454
|
+
#
|
455
|
+
# @api public
|
456
|
+
def injector(options = {})
|
457
|
+
Dry::AutoInject(self, options)
|
458
|
+
end
|
459
|
+
|
460
|
+
# Requires one or more files relative to the container's root
|
461
|
+
#
|
462
|
+
# @example
|
463
|
+
# # single file
|
464
|
+
# MyApp.require_from_root('lib/core')
|
465
|
+
#
|
466
|
+
# # glob
|
467
|
+
# MyApp.require_from_root('lib/**/*')
|
468
|
+
#
|
469
|
+
# @param paths [Array<String>] one or more paths, supports globs too
|
470
|
+
#
|
471
|
+
# @api public
|
472
|
+
def require_from_root(*paths)
|
473
|
+
paths.flat_map { |path|
|
474
|
+
path.to_s.include?('*') ? Dir[root.join(path)] : root.join(path)
|
475
|
+
}.each { |path|
|
476
|
+
require path.to_s
|
477
|
+
}
|
478
|
+
end
|
479
|
+
|
480
|
+
# Returns container's root path
|
481
|
+
#
|
482
|
+
# @example
|
483
|
+
# class MyApp < Dry::System::Container
|
484
|
+
# configure do |config|
|
485
|
+
# config.root = Pathname('/my/app')
|
486
|
+
# end
|
487
|
+
# end
|
488
|
+
#
|
489
|
+
# MyApp.root # returns '/my/app' pathname
|
490
|
+
#
|
491
|
+
# @return [Pathname]
|
492
|
+
#
|
493
|
+
# @api public
|
494
|
+
def root
|
495
|
+
config.root
|
496
|
+
end
|
497
|
+
|
498
|
+
# @api public
|
499
|
+
def resolve(key)
|
500
|
+
load_component(key) unless finalized?
|
501
|
+
|
502
|
+
super
|
503
|
+
end
|
504
|
+
|
505
|
+
# @api private
|
506
|
+
def load_paths
|
507
|
+
@load_paths ||= []
|
508
|
+
end
|
509
|
+
|
510
|
+
# @api private
|
511
|
+
def booter
|
512
|
+
@booter ||= config.booter.new(boot_path)
|
513
|
+
end
|
514
|
+
|
515
|
+
# @api private
|
516
|
+
def boot_path
|
517
|
+
root.join("#{config.system_dir}/boot")
|
518
|
+
end
|
519
|
+
|
520
|
+
# @api private
|
521
|
+
def auto_registrar
|
522
|
+
@auto_registrar ||= config.auto_registrar.new(self)
|
523
|
+
end
|
524
|
+
|
525
|
+
# @api private
|
526
|
+
def manual_registrar
|
527
|
+
@manual_registrar ||= config.manual_registrar.new(self)
|
528
|
+
end
|
529
|
+
|
530
|
+
# @api private
|
531
|
+
def importer
|
532
|
+
@importer ||= config.importer.new(self)
|
533
|
+
end
|
534
|
+
|
535
|
+
# @api private
|
536
|
+
def component(identifier, **options)
|
537
|
+
if (component = booter.components.detect { |c| c.identifier == identifier })
|
538
|
+
component
|
539
|
+
else
|
540
|
+
Component.new(
|
541
|
+
identifier,
|
542
|
+
loader: config.loader,
|
543
|
+
namespace: config.default_namespace,
|
544
|
+
separator: config.namespace_separator,
|
545
|
+
inflector: config.inflector,
|
546
|
+
**options,
|
547
|
+
)
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
551
|
+
# @api private
|
552
|
+
def require_component(component)
|
553
|
+
return if key?(component.identifier)
|
554
|
+
|
555
|
+
unless component.file_exists?(load_paths)
|
556
|
+
raise FileNotFoundError, component
|
557
|
+
end
|
558
|
+
|
559
|
+
require component.path
|
560
|
+
|
561
|
+
yield
|
562
|
+
end
|
563
|
+
|
564
|
+
# @api private
|
565
|
+
def load_component(key)
|
566
|
+
puts "load_component #{key}"
|
567
|
+
return self if key?(key)
|
568
|
+
|
569
|
+
component(key).tap do |component|
|
570
|
+
if component.boot?
|
571
|
+
booter.start(component)
|
572
|
+
else
|
573
|
+
root_key = component.root_key
|
574
|
+
|
575
|
+
puts "root key:"
|
576
|
+
p root_key
|
577
|
+
|
578
|
+
# byebug
|
579
|
+
|
580
|
+
|
581
|
+
# if (bootable_dep = component(root_key)).boot?
|
582
|
+
# booter.start(bootable_dep)
|
583
|
+
|
584
|
+
if importer.key?(root_key)
|
585
|
+
load_imported_component(component.namespaced(root_key))
|
586
|
+
else
|
587
|
+
# Feels like we don't even need this if load_local_component gets involved with booting?
|
588
|
+
booter.start(root_key) if booter.bootable?(root_key)
|
589
|
+
|
590
|
+
|
591
|
+
load_local_component(component)
|
592
|
+
end
|
593
|
+
|
594
|
+
|
595
|
+
|
596
|
+
# if booter.bootable?(root_key)
|
597
|
+
# booter.start(root_key)
|
598
|
+
# elsif importer.key?(root_key)
|
599
|
+
# load_imported_component(component.namespaced(root_key))
|
600
|
+
# else
|
601
|
+
# load_local_component(component)
|
602
|
+
# end
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
self
|
607
|
+
end
|
608
|
+
|
609
|
+
# @api private
|
610
|
+
def after(event, &block)
|
611
|
+
hooks[event] << block
|
612
|
+
end
|
613
|
+
|
614
|
+
# @api private
|
615
|
+
def hooks
|
616
|
+
@__hooks__ ||= Hash.new { |h, k| h[k] = [] }
|
617
|
+
end
|
618
|
+
|
619
|
+
# @api private
|
620
|
+
def inherited(klass)
|
621
|
+
new_hooks = Container.hooks.dup
|
622
|
+
|
623
|
+
hooks.each do |event, blocks|
|
624
|
+
new_hooks[event].concat(blocks)
|
625
|
+
new_hooks[event].concat(klass.hooks[event])
|
626
|
+
end
|
627
|
+
|
628
|
+
klass.instance_variable_set(:@__hooks__, new_hooks)
|
629
|
+
super
|
630
|
+
end
|
631
|
+
|
632
|
+
private
|
633
|
+
|
634
|
+
# @api private
|
635
|
+
def load_local_component(component, default_namespace_fallback = false)
|
636
|
+
# byebug
|
637
|
+
|
638
|
+
# if booter.bootable?(component)
|
639
|
+
# booter.boot_dependency(component) unless finalized?
|
640
|
+
# end
|
641
|
+
|
642
|
+
if component.file_exists?(load_paths)
|
643
|
+
require_component(component) do
|
644
|
+
register(component.identifier) { component.instance }
|
645
|
+
end
|
646
|
+
elsif !default_namespace_fallback
|
647
|
+
load_local_component(component.prepend(config.default_namespace), true)
|
648
|
+
elsif manual_registrar.file_exists?(component)
|
649
|
+
manual_registrar.(component)
|
650
|
+
else
|
651
|
+
raise ComponentLoadError, component
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
# @api private
|
656
|
+
def load_imported_component(component)
|
657
|
+
container = importer[component.namespace]
|
658
|
+
container.load_component(component.identifier)
|
659
|
+
importer.(component.namespace, container)
|
660
|
+
end
|
661
|
+
end
|
662
|
+
end
|
663
|
+
end
|
664
|
+
end
|
data/lib/dry/system/container.rb
CHANGED
@@ -341,6 +341,21 @@ module Dry
|
|
341
341
|
self
|
342
342
|
end
|
343
343
|
|
344
|
+
# Stop a specific component but calls only `stop` lifecycle trigger
|
345
|
+
#
|
346
|
+
# @example
|
347
|
+
# MyApp.stop(:persistence)
|
348
|
+
#
|
349
|
+
# @param [Symbol] name The name of a registered bootable component
|
350
|
+
#
|
351
|
+
# @return [self]
|
352
|
+
#
|
353
|
+
# @api public
|
354
|
+
def stop(name)
|
355
|
+
booter.stop(name)
|
356
|
+
self
|
357
|
+
end
|
358
|
+
|
344
359
|
# Sets load paths relative to the container's root dir
|
345
360
|
#
|
346
361
|
# @example
|
@@ -560,7 +575,9 @@ module Dry
|
|
560
575
|
booter.start(bootable_dep)
|
561
576
|
elsif importer.key?(root_key)
|
562
577
|
load_imported_component(component.namespaced(root_key))
|
563
|
-
|
578
|
+
end
|
579
|
+
|
580
|
+
if !key?(key)
|
564
581
|
load_local_component(component)
|
565
582
|
end
|
566
583
|
end
|
data/lib/dry/system/errors.rb
CHANGED
@@ -58,5 +58,14 @@ module Dry
|
|
58
58
|
super("component identifier #{name.inspect} must be a symbol")
|
59
59
|
end
|
60
60
|
end
|
61
|
+
|
62
|
+
# Error raised when trying to stop a component that hasn't started yet
|
63
|
+
#
|
64
|
+
# @api public
|
65
|
+
ComponentNotStartedError = Class.new(StandardError) do
|
66
|
+
def initialize(component_name)
|
67
|
+
super("component +#{component_name}+ has not been started")
|
68
|
+
end
|
69
|
+
end
|
61
70
|
end
|
62
71
|
end
|
data/lib/dry/system/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dry-system
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.
|
4
|
+
version: 0.10.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Solnica
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-07-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -197,6 +197,7 @@ files:
|
|
197
197
|
- lib/dry/system/components/bootable.rb
|
198
198
|
- lib/dry/system/components/config.rb
|
199
199
|
- lib/dry/system/constants.rb
|
200
|
+
- lib/dry/system/container.mod.rb
|
200
201
|
- lib/dry/system/container.rb
|
201
202
|
- lib/dry/system/errors.rb
|
202
203
|
- lib/dry/system/importer.rb
|
@@ -240,7 +241,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
240
241
|
version: '0'
|
241
242
|
requirements: []
|
242
243
|
rubyforge_project:
|
243
|
-
rubygems_version: 2.7.
|
244
|
+
rubygems_version: 2.7.5
|
244
245
|
signing_key:
|
245
246
|
specification_version: 4
|
246
247
|
summary: Organize your code into reusable components
|