dry-system 0.12.0 → 0.13.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 +101 -86
- data/README.md +2 -2
- data/lib/dry-system.rb +2 -0
- data/lib/dry/system.rb +3 -1
- data/lib/dry/system/auto_registrar.rb +12 -8
- data/lib/dry/system/auto_registrar/configuration.rb +2 -0
- data/lib/dry/system/booter.rb +10 -9
- data/lib/dry/system/booter/component_registry.rb +3 -5
- data/lib/dry/system/component.rb +5 -2
- data/lib/dry/system/components.rb +2 -0
- data/lib/dry/system/components/bootable.rb +13 -11
- data/lib/dry/system/components/config.rb +2 -0
- data/lib/dry/system/constants.rb +8 -7
- data/lib/dry/system/container.rb +64 -25
- data/lib/dry/system/errors.rb +9 -1
- data/lib/dry/system/importer.rb +2 -0
- data/lib/dry/system/lifecycle.rb +3 -1
- data/lib/dry/system/loader.rb +2 -0
- data/lib/dry/system/magic_comments_parser.rb +3 -3
- data/lib/dry/system/manual_registrar.rb +3 -1
- data/lib/dry/system/plugins.rb +9 -4
- data/lib/dry/system/plugins/bootsnap.rb +4 -1
- data/lib/dry/system/plugins/dependency_graph.rb +47 -0
- data/lib/dry/system/plugins/dependency_graph/strategies.rb +30 -0
- data/lib/dry/system/plugins/env.rb +2 -0
- data/lib/dry/system/plugins/logging.rb +8 -7
- data/lib/dry/system/plugins/monitoring.rb +3 -1
- data/lib/dry/system/plugins/monitoring/proxy.rb +2 -0
- data/lib/dry/system/plugins/notifications.rb +4 -1
- data/lib/dry/system/provider.rb +3 -1
- data/lib/dry/system/provider_registry.rb +2 -0
- data/lib/dry/system/settings.rb +9 -7
- data/lib/dry/system/settings/file_loader.rb +4 -2
- data/lib/dry/system/settings/file_parser.rb +5 -3
- data/lib/dry/system/stubs.rb +2 -0
- data/lib/dry/system/system_components/settings.rb +2 -0
- data/lib/dry/system/version.rb +3 -1
- metadata +25 -17
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dry
|
2
4
|
module System
|
3
5
|
class Booter
|
@@ -25,11 +27,7 @@ module Dry
|
|
25
27
|
def [](name)
|
26
28
|
component = components.detect { |component| component.identifier == name }
|
27
29
|
|
28
|
-
|
29
|
-
component
|
30
|
-
else
|
31
|
-
raise InvalidComponentIdentifierError, name
|
32
|
-
end
|
30
|
+
component || raise(InvalidComponentIdentifierError, name)
|
33
31
|
end
|
34
32
|
end
|
35
33
|
end
|
data/lib/dry/system/component.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'concurrent/map'
|
2
4
|
|
3
5
|
require 'dry-equalizer'
|
@@ -83,9 +85,10 @@ module Dry
|
|
83
85
|
|
84
86
|
# @api private
|
85
87
|
def initialize(identifier, path, options)
|
86
|
-
@identifier
|
88
|
+
@identifier = identifier
|
89
|
+
@path = path
|
87
90
|
@options = options
|
88
|
-
@file = "#{path}#{RB_EXT}"
|
91
|
+
@file = "#{path}#{RB_EXT}"
|
89
92
|
@loader = options.fetch(:loader)
|
90
93
|
freeze
|
91
94
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'dry/system/lifecycle'
|
2
4
|
require 'dry/system/settings'
|
3
5
|
require 'dry/system/components/config'
|
@@ -65,10 +67,12 @@ module Dry
|
|
65
67
|
# @return [Symbol,String] default namespace for the container keys
|
66
68
|
attr_reader :namespace
|
67
69
|
|
70
|
+
TRIGGER_MAP = Hash.new { |h, k| h[k] = [] }.freeze
|
71
|
+
|
68
72
|
# @api private
|
69
73
|
def initialize(identifier, options = {}, &block)
|
70
74
|
@identifier = identifier
|
71
|
-
@triggers = { before:
|
75
|
+
@triggers = { before: TRIGGER_MAP.dup, after: TRIGGER_MAP.dup }
|
72
76
|
@options = block ? options.merge(block: block) : options
|
73
77
|
@namespace = options[:namespace]
|
74
78
|
finalize = options[:finalize] || DEFAULT_FINALIZE
|
@@ -159,11 +163,7 @@ module Dry
|
|
159
163
|
#
|
160
164
|
# @api public
|
161
165
|
def config
|
162
|
-
|
163
|
-
@config
|
164
|
-
else
|
165
|
-
configure!
|
166
|
-
end
|
166
|
+
@config || configure!
|
167
167
|
end
|
168
168
|
|
169
169
|
# Return a list of lifecycle steps that were executed
|
@@ -191,7 +191,7 @@ module Dry
|
|
191
191
|
# @api private
|
192
192
|
def finalize
|
193
193
|
lifecycle.container.each do |key, item|
|
194
|
-
container.register(key, item) unless container.
|
194
|
+
container.register(key, item) unless container.registered?(key)
|
195
195
|
end
|
196
196
|
self
|
197
197
|
end
|
@@ -241,8 +241,8 @@ module Dry
|
|
241
241
|
#
|
242
242
|
# @api private
|
243
243
|
def boot_file
|
244
|
-
container_boot_files
|
245
|
-
detect { |path| Pathname(path).basename(RB_EXT).to_s == identifier.to_s }
|
244
|
+
container_boot_files
|
245
|
+
.detect { |path| Pathname(path).basename(RB_EXT).to_s == identifier.to_s }
|
246
246
|
end
|
247
247
|
|
248
248
|
# Return path to boot dir
|
@@ -260,7 +260,7 @@ module Dry
|
|
260
260
|
#
|
261
261
|
# @api private
|
262
262
|
def container_boot_files
|
263
|
-
Dir[container.boot_path.join("**/#{RB_GLOB}")]
|
263
|
+
::Dir[container.boot_path.join("**/#{RB_GLOB}")].sort
|
264
264
|
end
|
265
265
|
|
266
266
|
private
|
@@ -290,7 +290,9 @@ module Dry
|
|
290
290
|
when nil
|
291
291
|
container
|
292
292
|
else
|
293
|
-
raise
|
293
|
+
raise <<-STR
|
294
|
+
+namespace+ boot option must be true, string or symbol #{namespace.inspect} given.
|
295
|
+
STR
|
294
296
|
end
|
295
297
|
end
|
296
298
|
|
data/lib/dry/system/constants.rb
CHANGED
@@ -1,24 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'dry/core/constants'
|
2
4
|
|
3
5
|
module Dry
|
4
6
|
module System
|
5
7
|
include Dry::Core::Constants
|
6
8
|
|
7
|
-
RB_EXT = '.rb'
|
8
|
-
RB_GLOB = '*.rb'
|
9
|
-
PATH_SEPARATOR = '/'
|
10
|
-
DEFAULT_SEPARATOR = '.'
|
9
|
+
RB_EXT = '.rb'
|
10
|
+
RB_GLOB = '*.rb'
|
11
|
+
PATH_SEPARATOR = '/'
|
12
|
+
DEFAULT_SEPARATOR = '.'
|
11
13
|
WORD_REGEX = /\w+/.freeze
|
12
14
|
|
13
15
|
DuplicatedComponentKeyError = Class.new(ArgumentError)
|
14
16
|
InvalidSettingsError = Class.new(ArgumentError) do
|
15
17
|
# @api private
|
16
18
|
def initialize(attributes)
|
17
|
-
message = <<~
|
19
|
+
message = <<~STR
|
18
20
|
Could not initialize settings. The following settings were invalid:
|
19
21
|
|
20
22
|
#{attributes_errors(attributes).join("\n")}
|
21
|
-
|
23
|
+
STR
|
22
24
|
super(message)
|
23
25
|
end
|
24
26
|
|
@@ -38,6 +40,5 @@ module Dry
|
|
38
40
|
super("dry-system plugin #{plugin.inspect} failed to load its dependencies: #{message}")
|
39
41
|
end
|
40
42
|
end
|
41
|
-
|
42
43
|
end
|
43
44
|
end
|
data/lib/dry/system/container.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'pathname'
|
2
4
|
|
3
5
|
require 'dry-auto_inject'
|
@@ -73,8 +75,8 @@ module Dry
|
|
73
75
|
setting :name
|
74
76
|
setting :default_namespace
|
75
77
|
setting(:root, Pathname.pwd.freeze) { |path| Pathname(path) }
|
76
|
-
setting :system_dir, 'system'
|
77
|
-
setting :registrations_dir, 'container'
|
78
|
+
setting :system_dir, 'system'
|
79
|
+
setting :registrations_dir, 'container'
|
78
80
|
setting :auto_register, []
|
79
81
|
setting :inflector, Dry::Inflector.new
|
80
82
|
setting :loader, Dry::System::Loader
|
@@ -82,9 +84,17 @@ module Dry
|
|
82
84
|
setting :auto_registrar, Dry::System::AutoRegistrar
|
83
85
|
setting :manual_registrar, Dry::System::ManualRegistrar
|
84
86
|
setting :importer, Dry::System::Importer
|
85
|
-
setting(:components, {}, reader: true
|
87
|
+
setting(:components, {}, reader: true, &:dup)
|
86
88
|
|
87
89
|
class << self
|
90
|
+
def strategies(value = nil)
|
91
|
+
if value
|
92
|
+
@strategies = value
|
93
|
+
else
|
94
|
+
@strategies ||= Dry::AutoInject::Strategies
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
88
98
|
extend Dry::Core::Deprecations['Dry::System::Container']
|
89
99
|
|
90
100
|
# Configures the container
|
@@ -139,7 +149,9 @@ module Dry
|
|
139
149
|
when Hash then importer.register(other)
|
140
150
|
when Dry::Container::Namespace then super
|
141
151
|
else
|
142
|
-
raise ArgumentError,
|
152
|
+
raise ArgumentError, <<-STR
|
153
|
+
+other+ must be a hash of names and systems, or a Dry::Container namespace
|
154
|
+
STR
|
143
155
|
end
|
144
156
|
end
|
145
157
|
|
@@ -218,7 +230,9 @@ module Dry
|
|
218
230
|
# @api public
|
219
231
|
def boot(name, opts = {}, &block)
|
220
232
|
if components.key?(name)
|
221
|
-
raise DuplicatedComponentKeyError,
|
233
|
+
raise DuplicatedComponentKeyError, <<-STR
|
234
|
+
Bootable component #{name.inspect} was already registered
|
235
|
+
STR
|
222
236
|
end
|
223
237
|
|
224
238
|
component =
|
@@ -227,7 +241,6 @@ module Dry
|
|
227
241
|
else
|
228
242
|
boot_local(name, opts, &block)
|
229
243
|
end
|
230
|
-
self
|
231
244
|
|
232
245
|
components[name] = component
|
233
246
|
end
|
@@ -246,7 +259,9 @@ module Dry
|
|
246
259
|
|
247
260
|
# @api private
|
248
261
|
def boot_local(identifier, namespace: nil, &block)
|
249
|
-
component = Components::Bootable.new(
|
262
|
+
component = Components::Bootable.new(
|
263
|
+
identifier, container: self, namespace: namespace, &block
|
264
|
+
)
|
250
265
|
|
251
266
|
booter.register_component(component)
|
252
267
|
|
@@ -381,6 +396,7 @@ module Dry
|
|
381
396
|
def load_paths!(*dirs)
|
382
397
|
dirs.map(&root.method(:join)).each do |path|
|
383
398
|
next if load_paths.include?(path)
|
399
|
+
|
384
400
|
load_paths << path
|
385
401
|
$LOAD_PATH.unshift(path.to_s)
|
386
402
|
end
|
@@ -459,7 +475,7 @@ module Dry
|
|
459
475
|
# @param options [Hash] injector options
|
460
476
|
#
|
461
477
|
# @api public
|
462
|
-
def injector(options = {})
|
478
|
+
def injector(options = { strategies: strategies })
|
463
479
|
Dry::AutoInject(self, options)
|
464
480
|
end
|
465
481
|
|
@@ -477,7 +493,7 @@ module Dry
|
|
477
493
|
# @api public
|
478
494
|
def require_from_root(*paths)
|
479
495
|
paths.flat_map { |path|
|
480
|
-
path.to_s.include?('*') ? Dir[root.join(path)] : root.join(path)
|
496
|
+
path.to_s.include?('*') ? ::Dir[root.join(path)].sort : root.join(path)
|
481
497
|
}.each { |path|
|
482
498
|
require path.to_s
|
483
499
|
}
|
@@ -502,12 +518,37 @@ module Dry
|
|
502
518
|
end
|
503
519
|
|
504
520
|
# @api public
|
505
|
-
def resolve(key)
|
506
|
-
load_component(key) unless finalized?
|
521
|
+
def resolve(key, &block)
|
522
|
+
load_component(key, &block) unless finalized?
|
507
523
|
|
508
524
|
super
|
509
525
|
end
|
510
526
|
|
527
|
+
alias_method :registered?, :key?
|
528
|
+
#
|
529
|
+
# @!method registered?(key)
|
530
|
+
# Whether a +key+ is registered (doesn't trigger loading)
|
531
|
+
# @param [String,Symbol] key Identifier
|
532
|
+
# @return [Boolean]
|
533
|
+
# @api public
|
534
|
+
#
|
535
|
+
|
536
|
+
# Check if identifier is registered.
|
537
|
+
# If not, try to load the component
|
538
|
+
#
|
539
|
+
# @param [String,Symbol] key Identifier
|
540
|
+
# @return [Boolean]
|
541
|
+
#
|
542
|
+
# @api public
|
543
|
+
def key?(key)
|
544
|
+
if finalized?
|
545
|
+
registered?(key)
|
546
|
+
else
|
547
|
+
registered?(key) || resolve(key) { return false }
|
548
|
+
true
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
511
552
|
# @api private
|
512
553
|
def load_paths
|
513
554
|
@load_paths ||= []
|
@@ -549,18 +590,16 @@ module Dry
|
|
549
590
|
namespace: config.default_namespace,
|
550
591
|
separator: config.namespace_separator,
|
551
592
|
inflector: config.inflector,
|
552
|
-
**options
|
593
|
+
**options
|
553
594
|
)
|
554
595
|
end
|
555
596
|
end
|
556
597
|
|
557
598
|
# @api private
|
558
599
|
def require_component(component)
|
559
|
-
return if
|
600
|
+
return if registered?(component.identifier)
|
560
601
|
|
561
|
-
unless component.file_exists?(load_paths)
|
562
|
-
raise FileNotFoundError, component
|
563
|
-
end
|
602
|
+
raise FileNotFoundError, component unless component.file_exists?(load_paths)
|
564
603
|
|
565
604
|
require_path(component.path)
|
566
605
|
|
@@ -579,8 +618,8 @@ module Dry
|
|
579
618
|
end
|
580
619
|
|
581
620
|
# @api private
|
582
|
-
def load_component(key)
|
583
|
-
return self if
|
621
|
+
def load_component(key, &block)
|
622
|
+
return self if registered?(key)
|
584
623
|
|
585
624
|
component(key).tap do |component|
|
586
625
|
if component.boot?
|
@@ -594,9 +633,7 @@ module Dry
|
|
594
633
|
load_imported_component(component.namespaced(root_key))
|
595
634
|
end
|
596
635
|
|
597
|
-
|
598
|
-
load_local_component(component)
|
599
|
-
end
|
636
|
+
load_local_component(component, &block) unless registered?(key)
|
600
637
|
end
|
601
638
|
end
|
602
639
|
|
@@ -610,7 +647,7 @@ module Dry
|
|
610
647
|
|
611
648
|
# @api private
|
612
649
|
def hooks
|
613
|
-
@
|
650
|
+
@hooks ||= Hash.new { |h, k| h[k] = [] }
|
614
651
|
end
|
615
652
|
|
616
653
|
# @api private
|
@@ -622,14 +659,14 @@ module Dry
|
|
622
659
|
new_hooks[event].concat(klass.hooks[event])
|
623
660
|
end
|
624
661
|
|
625
|
-
klass.instance_variable_set(:@
|
662
|
+
klass.instance_variable_set(:@hooks, new_hooks)
|
626
663
|
super
|
627
664
|
end
|
628
665
|
|
629
666
|
private
|
630
667
|
|
631
668
|
# @api private
|
632
|
-
def load_local_component(component, default_namespace_fallback = false)
|
669
|
+
def load_local_component(component, default_namespace_fallback = false, &block)
|
633
670
|
if booter.bootable?(component) || component.file_exists?(load_paths)
|
634
671
|
booter.boot_dependency(component) unless finalized?
|
635
672
|
|
@@ -637,9 +674,11 @@ module Dry
|
|
637
674
|
register(component.identifier) { component.instance }
|
638
675
|
end
|
639
676
|
elsif !default_namespace_fallback
|
640
|
-
load_local_component(component.prepend(config.default_namespace), true)
|
677
|
+
load_local_component(component.prepend(config.default_namespace), true, &block)
|
641
678
|
elsif manual_registrar.file_exists?(component)
|
642
679
|
manual_registrar.(component)
|
680
|
+
elsif block_given?
|
681
|
+
yield
|
643
682
|
else
|
644
683
|
raise ComponentLoadError, component
|
645
684
|
end
|
data/lib/dry/system/errors.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dry
|
2
4
|
module System
|
3
5
|
# Error raised when the container tries to load a component with missing
|
@@ -15,7 +17,13 @@ module Dry
|
|
15
17
|
# @api public
|
16
18
|
ComponentFileMismatchError = Class.new(StandardError) do
|
17
19
|
def initialize(component)
|
18
|
-
|
20
|
+
path = component.boot_path
|
21
|
+
files = component.container_boot_files
|
22
|
+
|
23
|
+
super(<<-STR)
|
24
|
+
Boot file for component #{component.identifier.inspect} not found.
|
25
|
+
Container boot files under #{path}: #{files.inspect}")
|
26
|
+
STR
|
19
27
|
end
|
20
28
|
end
|
21
29
|
|
data/lib/dry/system/importer.rb
CHANGED
data/lib/dry/system/lifecycle.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'concurrent/map'
|
2
4
|
|
3
5
|
require 'dry/system/settings'
|
@@ -124,7 +126,7 @@ module Dry
|
|
124
126
|
|
125
127
|
# @api private
|
126
128
|
def method_missing(meth, *args, &block)
|
127
|
-
if target.
|
129
|
+
if target.registered?(meth)
|
128
130
|
target[meth]
|
129
131
|
elsif container.key?(meth)
|
130
132
|
container[meth]
|
data/lib/dry/system/loader.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dry
|
2
4
|
module System
|
3
5
|
class MagicCommentsParser
|
@@ -6,7 +8,7 @@ module Dry
|
|
6
8
|
|
7
9
|
COERCIONS = {
|
8
10
|
'true' => true,
|
9
|
-
'false' => false
|
11
|
+
'false' => false
|
10
12
|
}.freeze
|
11
13
|
|
12
14
|
def self.call(file_name)
|
@@ -21,8 +23,6 @@ module Dry
|
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
24
|
-
private
|
25
|
-
|
26
26
|
def self.coerce(value)
|
27
27
|
COERCIONS.fetch(value) { value }
|
28
28
|
end
|