dry-system 0.15.0 → 0.19.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 +142 -2
- data/LICENSE +1 -1
- data/README.md +1 -1
- data/dry-system.gemspec +5 -4
- data/lib/dry-system.rb +1 -1
- data/lib/dry/system.rb +2 -2
- data/lib/dry/system/auto_registrar.rb +17 -59
- data/lib/dry/system/booter.rb +68 -41
- data/lib/dry/system/component.rb +62 -100
- data/lib/dry/system/component_dir.rb +128 -0
- data/lib/dry/system/components.rb +2 -2
- data/lib/dry/system/components/bootable.rb +6 -34
- data/lib/dry/system/components/config.rb +2 -2
- data/lib/dry/system/config/component_dir.rb +202 -0
- data/lib/dry/system/config/component_dirs.rb +184 -0
- data/lib/dry/system/constants.rb +5 -5
- data/lib/dry/system/container.rb +133 -184
- data/lib/dry/system/errors.rb +21 -16
- data/lib/dry/system/identifier.rb +157 -0
- data/lib/dry/system/lifecycle.rb +2 -2
- data/lib/dry/system/loader.rb +40 -41
- data/lib/dry/system/loader/autoloading.rb +26 -0
- data/lib/dry/system/magic_comments_parser.rb +2 -2
- data/lib/dry/system/manual_registrar.rb +1 -1
- data/lib/dry/system/plugins.rb +7 -7
- data/lib/dry/system/plugins/bootsnap.rb +3 -3
- data/lib/dry/system/plugins/dependency_graph.rb +3 -3
- data/lib/dry/system/plugins/dependency_graph/strategies.rb +1 -1
- data/lib/dry/system/plugins/logging.rb +5 -5
- data/lib/dry/system/plugins/monitoring.rb +3 -3
- data/lib/dry/system/plugins/monitoring/proxy.rb +3 -3
- data/lib/dry/system/plugins/notifications.rb +1 -1
- data/lib/dry/system/provider.rb +3 -3
- data/lib/dry/system/settings.rb +6 -6
- data/lib/dry/system/settings/file_loader.rb +2 -2
- data/lib/dry/system/settings/file_parser.rb +1 -1
- data/lib/dry/system/stubs.rb +1 -1
- data/lib/dry/system/system_components/settings.rb +1 -1
- data/lib/dry/system/version.rb +1 -1
- metadata +21 -25
- data/lib/dry/system/auto_registrar/configuration.rb +0 -43
data/lib/dry/system/errors.rb
CHANGED
@@ -2,13 +2,22 @@
|
|
2
2
|
|
3
3
|
module Dry
|
4
4
|
module System
|
5
|
+
# Error raised when a component dir is added to configuration more than once
|
6
|
+
#
|
7
|
+
# @api public
|
8
|
+
ComponentDirAlreadyAddedError = Class.new(StandardError) do
|
9
|
+
def initialize(dir)
|
10
|
+
super("Component directory #{dir.inspect} already added")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
5
14
|
# Error raised when the container tries to load a component with missing
|
6
15
|
# file
|
7
16
|
#
|
8
17
|
# @api public
|
9
18
|
FileNotFoundError = Class.new(StandardError) do
|
10
19
|
def initialize(component)
|
11
|
-
super("could not resolve require file for #{component.identifier}")
|
20
|
+
super("could not resolve require file for component '#{component.identifier}'")
|
12
21
|
end
|
13
22
|
end
|
14
23
|
|
@@ -17,25 +26,12 @@ module Dry
|
|
17
26
|
# @api public
|
18
27
|
ComponentFileMismatchError = Class.new(StandardError) do
|
19
28
|
def initialize(component)
|
20
|
-
path = component.boot_path
|
21
|
-
files = component.container_boot_files
|
22
|
-
|
23
29
|
super(<<-STR)
|
24
|
-
|
25
|
-
Container boot files under #{path}: #{files.inspect}")
|
30
|
+
Bootable component '#{component.identifier}' not found
|
26
31
|
STR
|
27
32
|
end
|
28
33
|
end
|
29
34
|
|
30
|
-
# Error raised when a resolved component couldn't be found
|
31
|
-
#
|
32
|
-
# @api public
|
33
|
-
ComponentLoadError = Class.new(StandardError) do
|
34
|
-
def initialize(component)
|
35
|
-
super("could not load component #{component.inspect}")
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
35
|
# Error raised when resolved component couldn't be loaded
|
40
36
|
#
|
41
37
|
# @api public
|
@@ -85,8 +81,17 @@ module Dry
|
|
85
81
|
end
|
86
82
|
end
|
87
83
|
|
88
|
-
|
84
|
+
# Error raised when a configured component directory could not be found
|
85
|
+
#
|
86
|
+
# @api public
|
87
|
+
ComponentDirNotFoundError = Class.new(StandardError) do
|
88
|
+
def initialize(dir)
|
89
|
+
super("Component dir '#{dir}' not found")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
89
93
|
DuplicatedComponentKeyError = Class.new(ArgumentError)
|
94
|
+
|
90
95
|
InvalidSettingsError = Class.new(ArgumentError) do
|
91
96
|
# @api private
|
92
97
|
def initialize(attributes)
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
require_relative "constants"
|
5
|
+
|
6
|
+
module Dry
|
7
|
+
module System
|
8
|
+
# An identifier representing a component to be registered.
|
9
|
+
#
|
10
|
+
# Components are eventually registered in the container using plain string
|
11
|
+
# identifiers, available as the `identifier` or `key` attribute here. Additional
|
12
|
+
# methods are provided to make it easier to evaluate or manipulate these identifiers.
|
13
|
+
#
|
14
|
+
# @api public
|
15
|
+
class Identifier
|
16
|
+
include Dry::Equalizer(:identifier, :namespace, :separator)
|
17
|
+
|
18
|
+
# @return [String] the identifier string
|
19
|
+
# @api public
|
20
|
+
attr_reader :identifier
|
21
|
+
|
22
|
+
# @return [String, nil] the namespace for the component
|
23
|
+
# @api public
|
24
|
+
attr_reader :namespace
|
25
|
+
|
26
|
+
# @return [String] the configured namespace separator
|
27
|
+
# @api public
|
28
|
+
attr_reader :separator
|
29
|
+
|
30
|
+
# @api private
|
31
|
+
def initialize(identifier, namespace: nil, separator: DEFAULT_SEPARATOR)
|
32
|
+
@identifier = identifier.to_s
|
33
|
+
@namespace = namespace
|
34
|
+
@separator = separator
|
35
|
+
end
|
36
|
+
|
37
|
+
# @!method key
|
38
|
+
# Returns the identifier string
|
39
|
+
#
|
40
|
+
# @return [String]
|
41
|
+
# @see #identifier
|
42
|
+
# @api public
|
43
|
+
alias_method :key, :identifier
|
44
|
+
|
45
|
+
# @!method to_s
|
46
|
+
# Returns the identifier string
|
47
|
+
#
|
48
|
+
# @return [String]
|
49
|
+
# @see #identifier
|
50
|
+
# @api public
|
51
|
+
alias_method :to_s, :identifier
|
52
|
+
|
53
|
+
# Returns the root namespace segment of the identifier string, as a symbol
|
54
|
+
#
|
55
|
+
# @example
|
56
|
+
# identifier.key # => "articles.operations.create"
|
57
|
+
# identifier.root_key # => :articles
|
58
|
+
#
|
59
|
+
# @return [Symbol] the root key
|
60
|
+
# @api public
|
61
|
+
def root_key
|
62
|
+
segments.first.to_sym
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns a path-delimited representation of the identifier, with the namespace
|
66
|
+
# incorporated. This path is intended for usage when requiring the component's
|
67
|
+
# source file.
|
68
|
+
#
|
69
|
+
# @example
|
70
|
+
# identifier.key # => "articles.operations.create"
|
71
|
+
# identifier.namespace # => "admin"
|
72
|
+
#
|
73
|
+
# identifier.path # => "admin/articles/operations/create"
|
74
|
+
#
|
75
|
+
# @return [String] the path
|
76
|
+
# @api public
|
77
|
+
def path
|
78
|
+
@require_path ||= identifier.gsub(separator, PATH_SEPARATOR).yield_self { |path|
|
79
|
+
if namespace
|
80
|
+
namespace_path = namespace.to_s.gsub(separator, PATH_SEPARATOR)
|
81
|
+
"#{namespace_path}#{PATH_SEPARATOR}#{path}"
|
82
|
+
else
|
83
|
+
path
|
84
|
+
end
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns true if the given namespace prefix is part of the identifier's leading
|
89
|
+
# namespaces
|
90
|
+
#
|
91
|
+
# @example
|
92
|
+
# identifier.key # => "articles.operations.create"
|
93
|
+
#
|
94
|
+
# identifier.start_with?("articles.operations") # => true
|
95
|
+
# identifier.start_with?("articles") # => true
|
96
|
+
# identifier.start_with?("article") # => false
|
97
|
+
#
|
98
|
+
# @param leading_namespaces [String] the one or more leading namespaces to check
|
99
|
+
# @return [Boolean]
|
100
|
+
# @api public
|
101
|
+
def start_with?(leading_namespaces)
|
102
|
+
identifier.start_with?("#{leading_namespaces}#{separator}")
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns a copy of the identifier with the given leading namespaces removed from
|
106
|
+
# the identifier string.
|
107
|
+
#
|
108
|
+
# Additional options may be provided, which are passed to #initialize when
|
109
|
+
# constructing the new copy of the identifier
|
110
|
+
#
|
111
|
+
# @param leading_namespace [String] the one or more leading namespaces to remove
|
112
|
+
# @param options [Hash] additional options for initialization
|
113
|
+
#
|
114
|
+
# @return [Dry::System::Identifier] the copy of the identifier
|
115
|
+
#
|
116
|
+
# @see #initialize
|
117
|
+
# @api private
|
118
|
+
def dequalified(leading_namespaces, **options)
|
119
|
+
new_identifier = identifier.gsub(
|
120
|
+
/^#{Regexp.escape(leading_namespaces)}#{Regexp.escape(separator)}/,
|
121
|
+
EMPTY_STRING
|
122
|
+
)
|
123
|
+
|
124
|
+
return self if new_identifier == identifier
|
125
|
+
|
126
|
+
self.class.new(
|
127
|
+
new_identifier,
|
128
|
+
namespace: namespace,
|
129
|
+
separator: separator,
|
130
|
+
**options
|
131
|
+
)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns a copy of the identifier with the given options applied
|
135
|
+
#
|
136
|
+
# @param namespace [String, nil] a new namespace to be used
|
137
|
+
#
|
138
|
+
# @return [Dry::System::Identifier] the copy of the identifier
|
139
|
+
#
|
140
|
+
# @see #initialize
|
141
|
+
# @api private
|
142
|
+
def with(namespace:)
|
143
|
+
self.class.new(
|
144
|
+
identifier,
|
145
|
+
namespace: namespace,
|
146
|
+
separator: separator
|
147
|
+
)
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def segments
|
153
|
+
@segments ||= identifier.split(separator)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
data/lib/dry/system/lifecycle.rb
CHANGED
data/lib/dry/system/loader.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'dry/inflector'
|
4
|
-
|
5
3
|
module Dry
|
6
4
|
module System
|
7
5
|
# Default component loader implementation
|
@@ -25,52 +23,53 @@ module Dry
|
|
25
23
|
#
|
26
24
|
# @api public
|
27
25
|
class Loader
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
class << self
|
27
|
+
# Requires the component's source file
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
def require!(component)
|
31
|
+
require(component.path) if component.file_exists?
|
32
|
+
self
|
33
|
+
end
|
31
34
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
+
# Returns an instance of the component
|
36
|
+
#
|
37
|
+
# Provided optional args are passed to object's constructor
|
38
|
+
#
|
39
|
+
# @param [Array] args Optional constructor args
|
40
|
+
#
|
41
|
+
# @return [Object]
|
42
|
+
#
|
43
|
+
# @api public
|
44
|
+
def call(component, *args)
|
45
|
+
require!(component)
|
35
46
|
|
36
|
-
|
37
|
-
def initialize(path, inflector = Dry::Inflector.new)
|
38
|
-
@path = path
|
39
|
-
@inflector = inflector
|
40
|
-
end
|
47
|
+
constant = self.constant(component)
|
41
48
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
#
|
48
|
-
# @return [Object]
|
49
|
-
#
|
50
|
-
# @api public
|
51
|
-
def call(*args)
|
52
|
-
if singleton?(constant)
|
53
|
-
constant.instance(*args)
|
54
|
-
else
|
55
|
-
constant.new(*args)
|
49
|
+
if singleton?(constant)
|
50
|
+
constant.instance(*args)
|
51
|
+
else
|
52
|
+
constant.new(*args)
|
53
|
+
end
|
56
54
|
end
|
57
|
-
|
58
|
-
ruby2_keywords(:call) if respond_to?(:ruby2_keywords, true)
|
55
|
+
ruby2_keywords(:call) if respond_to?(:ruby2_keywords, true)
|
59
56
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
57
|
+
# Returns the component's class constant
|
58
|
+
#
|
59
|
+
# @return [Class]
|
60
|
+
#
|
61
|
+
# @api public
|
62
|
+
def constant(component)
|
63
|
+
inflector = component.inflector
|
64
|
+
|
65
|
+
inflector.constantize(inflector.camelize(component.path))
|
66
|
+
end
|
68
67
|
|
69
|
-
|
68
|
+
private
|
70
69
|
|
71
|
-
|
72
|
-
|
73
|
-
|
70
|
+
def singleton?(constant)
|
71
|
+
constant.respond_to?(:instance) && !constant.respond_to?(:new)
|
72
|
+
end
|
74
73
|
end
|
75
74
|
end
|
76
75
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../loader"
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module System
|
7
|
+
class Loader
|
8
|
+
# Component loader for autoloading-enabled applications
|
9
|
+
#
|
10
|
+
# This behaves like the default loader, except instead of requiring the given path,
|
11
|
+
# it loads the respective constant, allowing the autoloader to load the
|
12
|
+
# corresponding file per its own configuration.
|
13
|
+
#
|
14
|
+
# @see Loader
|
15
|
+
# @api public
|
16
|
+
class Autoloading < Loader
|
17
|
+
class << self
|
18
|
+
def require!(component)
|
19
|
+
constant(component)
|
20
|
+
self
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/dry/system/plugins.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "dry/system/constants"
|
4
4
|
|
5
5
|
module Dry
|
6
6
|
module System
|
@@ -116,22 +116,22 @@ module Dry
|
|
116
116
|
@enabled_plugins ||= []
|
117
117
|
end
|
118
118
|
|
119
|
-
require
|
119
|
+
require "dry/system/plugins/bootsnap"
|
120
120
|
register(:bootsnap, Plugins::Bootsnap)
|
121
121
|
|
122
|
-
require
|
122
|
+
require "dry/system/plugins/logging"
|
123
123
|
register(:logging, Plugins::Logging)
|
124
124
|
|
125
|
-
require
|
125
|
+
require "dry/system/plugins/env"
|
126
126
|
register(:env, Plugins::Env)
|
127
127
|
|
128
|
-
require
|
128
|
+
require "dry/system/plugins/notifications"
|
129
129
|
register(:notifications, Plugins::Notifications)
|
130
130
|
|
131
|
-
require
|
131
|
+
require "dry/system/plugins/monitoring"
|
132
132
|
register(:monitoring, Plugins::Monitoring)
|
133
133
|
|
134
|
-
require
|
134
|
+
require "dry/system/plugins/dependency_graph"
|
135
135
|
register(:dependency_graph, Plugins::DependencyGraph)
|
136
136
|
end
|
137
137
|
end
|
@@ -22,7 +22,7 @@ module Dry
|
|
22
22
|
|
23
23
|
# @api private
|
24
24
|
def self.dependencies
|
25
|
-
{
|
25
|
+
{bootsnap: "bootsnap"}
|
26
26
|
end
|
27
27
|
|
28
28
|
# Set up bootsnap for faster booting
|
@@ -31,12 +31,12 @@ module Dry
|
|
31
31
|
def setup_bootsnap
|
32
32
|
return unless bootsnap_available?
|
33
33
|
|
34
|
-
::Bootsnap.setup(config.bootsnap.merge(cache_dir: root.join(
|
34
|
+
::Bootsnap.setup(config.bootsnap.merge(cache_dir: root.join("tmp/cache").to_s))
|
35
35
|
end
|
36
36
|
|
37
37
|
# @api private
|
38
38
|
def bootsnap_available?
|
39
|
-
RUBY_ENGINE ==
|
39
|
+
RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.3.0" && RUBY_VERSION < "2.5.0"
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|