dry-system 0.19.1 → 0.22.0
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 +90 -0
- data/README.md +4 -3
- data/dry-system.gemspec +16 -15
- data/lib/dry/system/auto_registrar.rb +1 -13
- data/lib/dry/system/booter/component_registry.rb +3 -3
- data/lib/dry/system/booter.rb +4 -4
- data/lib/dry/system/component.rb +103 -45
- data/lib/dry/system/component_dir.rb +112 -45
- data/lib/dry/system/components/bootable.rb +10 -19
- data/lib/dry/system/config/component_dir.rb +87 -43
- data/lib/dry/system/config/component_dirs.rb +153 -60
- data/lib/dry/system/config/namespace.rb +78 -0
- data/lib/dry/system/config/namespaces.rb +208 -0
- data/lib/dry/system/constants.rb +1 -1
- data/lib/dry/system/container.rb +39 -42
- data/lib/dry/system/errors.rb +11 -20
- data/lib/dry/system/identifier.rb +57 -79
- data/lib/dry/system/indirect_component.rb +65 -0
- data/lib/dry/system/loader.rb +3 -4
- data/lib/dry/system/manual_registrar.rb +4 -8
- data/lib/dry/system/plugins/bootsnap.rb +2 -2
- data/lib/dry/system/plugins/dependency_graph.rb +4 -4
- data/lib/dry/system/plugins/env.rb +1 -1
- data/lib/dry/system/plugins/logging.rb +7 -6
- data/lib/dry/system/plugins.rb +1 -3
- data/lib/dry/system/provider.rb +6 -6
- data/lib/dry/system/provider_registry.rb +4 -4
- data/lib/dry/system/settings.rb +1 -4
- data/lib/dry/system/stubs.rb +1 -1
- data/lib/dry/system/version.rb +1 -1
- data/lib/dry/system.rb +5 -5
- metadata +14 -11
@@ -0,0 +1,208 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/deprecations"
|
4
|
+
require "dry/system/errors"
|
5
|
+
require_relative "namespace"
|
6
|
+
|
7
|
+
module Dry
|
8
|
+
module System
|
9
|
+
module Config
|
10
|
+
# The configured namespaces for a ComponentDir
|
11
|
+
#
|
12
|
+
# @see Config::ComponentDir#namespaces
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
class Namespaces
|
16
|
+
# @api private
|
17
|
+
attr_reader :namespaces
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
def initialize
|
21
|
+
@namespaces = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
# @api private
|
25
|
+
def initialize_copy(source)
|
26
|
+
super
|
27
|
+
@namespaces = source.namespaces.dup
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the namespace configured for the path, or nil if no such namespace has
|
31
|
+
# been configured
|
32
|
+
#
|
33
|
+
# @return [Namespace, nil] the namespace, if configured
|
34
|
+
#
|
35
|
+
# @api public
|
36
|
+
def namespace(path)
|
37
|
+
namespaces[path]
|
38
|
+
end
|
39
|
+
alias_method :[], :namespace
|
40
|
+
|
41
|
+
# Returns the namespace configured for the root path, or nil if the root namespace
|
42
|
+
# has not been configured
|
43
|
+
#
|
44
|
+
# @return [Namespace, nil] the root namespace, if configured
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
def root(**options)
|
48
|
+
if options.any?
|
49
|
+
Dry::Core::Deprecations.announce(
|
50
|
+
"Dry::System::Config::Namespaces#root (with arguments)",
|
51
|
+
"Use `#add_root(key: nil, const: nil)` instead",
|
52
|
+
tag: "dry-system",
|
53
|
+
uplevel: 1
|
54
|
+
)
|
55
|
+
|
56
|
+
add_root(**options)
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
namespaces[Namespace::ROOT_PATH]
|
61
|
+
end
|
62
|
+
|
63
|
+
# rubocop:disable Layout/LineLength
|
64
|
+
|
65
|
+
# Adds a component dir namespace
|
66
|
+
#
|
67
|
+
# A namespace encompasses a given sub-directory of the component dir, and
|
68
|
+
# determines (1) the leading segments of its components' registered identifiers,
|
69
|
+
# and (2) the expected constant namespace of their class constants.
|
70
|
+
#
|
71
|
+
# A namespace for a path can only be added once.
|
72
|
+
#
|
73
|
+
# @example Adding a namespace with top-level identifiers
|
74
|
+
# # Components defined within admin/ (e.g. admin/my_component.rb) will be:
|
75
|
+
# #
|
76
|
+
# # - Registered with top-level identifiers ("my_component")
|
77
|
+
# # - Expected to have constants in `Admin`, matching the namespace's path (Admin::MyComponent)
|
78
|
+
#
|
79
|
+
# namespaces.add "admin", key: nil
|
80
|
+
#
|
81
|
+
# @example Adding a namespace with top-level class constants
|
82
|
+
# # Components defined within adapters/ (e.g. adapters/my_adapter.rb) will be:
|
83
|
+
# #
|
84
|
+
# # - Registered with leading identifiers matching the namespace's path ("adapters.my_adapter")
|
85
|
+
# # - Expected to have top-level constants (::MyAdapter)
|
86
|
+
#
|
87
|
+
# namespaces.add "adapters", const: nil
|
88
|
+
#
|
89
|
+
# @example Adding a namespace with distinct identifiers and class constants
|
90
|
+
# # Components defined within `bananas/` (e.g. bananas/banana_split.rb) will be:
|
91
|
+
# #
|
92
|
+
# # - Registered with the given leading identifier ("desserts.banana_split")
|
93
|
+
# # - Expected to have constants within the given namespace (EatMe::Now::BananaSplit)
|
94
|
+
#
|
95
|
+
# namespaces.add "bananas", key: "desserts", const: "eat_me/now"
|
96
|
+
#
|
97
|
+
# @param path [String] the path to the sub-directory of source files to which this
|
98
|
+
# namespace should apply, relative to the component dir
|
99
|
+
# @param key [String, nil] the leading namespace to apply to the container keys
|
100
|
+
# for the components. Set `nil` for the keys to be top-level.
|
101
|
+
# @param const [String, nil] the Ruby constant namespace to expect for constants
|
102
|
+
# defined within the components. This should be provided in underscored string
|
103
|
+
# form, e.g. "hello_there/world" for a Ruby constant of `HelloThere::World`. Set
|
104
|
+
# `nil` for the constants to be top-level.
|
105
|
+
#
|
106
|
+
# @return [Namespace] the added namespace
|
107
|
+
#
|
108
|
+
# @see Namespace
|
109
|
+
#
|
110
|
+
# @api public
|
111
|
+
def add(path, key: path, const: path)
|
112
|
+
raise NamespaceAlreadyAddedError, path if namespaces.key?(path)
|
113
|
+
|
114
|
+
namespaces[path] = Namespace.new(path: path, key: key, const: const)
|
115
|
+
end
|
116
|
+
|
117
|
+
# rubocop:enable Layout/LineLength
|
118
|
+
|
119
|
+
# Adds a root component dir namespace
|
120
|
+
#
|
121
|
+
# @see #add
|
122
|
+
#
|
123
|
+
# @api public
|
124
|
+
def add_root(key: nil, const: nil)
|
125
|
+
add(Namespace::ROOT_PATH, key: key, const: const)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Deletes the configured namespace for the given path and returns the namespace
|
129
|
+
#
|
130
|
+
# If no namespace was previously configured for the given path, returns nil
|
131
|
+
#
|
132
|
+
# @param path [String] the path for the namespace
|
133
|
+
#
|
134
|
+
# @return [Namespace, nil]
|
135
|
+
#
|
136
|
+
# @api public
|
137
|
+
def delete(path)
|
138
|
+
namespaces.delete(path)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Deletes the configured root namespace and returns the namespace
|
142
|
+
#
|
143
|
+
# If no root namespace was previously configured, returns nil
|
144
|
+
#
|
145
|
+
# @return [Namespace, nil]
|
146
|
+
#
|
147
|
+
# @api public
|
148
|
+
def delete_root
|
149
|
+
delete(Namespace::ROOT_PATH)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Returns the paths of the configured namespaces
|
153
|
+
#
|
154
|
+
# @return [Array<String,nil>] the namespace paths, with nil representing the root
|
155
|
+
# namespace
|
156
|
+
#
|
157
|
+
# @api public
|
158
|
+
def paths
|
159
|
+
namespaces.keys
|
160
|
+
end
|
161
|
+
|
162
|
+
# Returns the count of configured namespaces
|
163
|
+
#
|
164
|
+
# @return [Integer]
|
165
|
+
#
|
166
|
+
# @api public
|
167
|
+
def length
|
168
|
+
namespaces.length
|
169
|
+
end
|
170
|
+
alias_method :size, :length
|
171
|
+
|
172
|
+
# Returns true if there are no configured namespaces
|
173
|
+
#
|
174
|
+
# @return [Boolean]
|
175
|
+
#
|
176
|
+
# @api public
|
177
|
+
def empty?
|
178
|
+
namespaces.empty?
|
179
|
+
end
|
180
|
+
|
181
|
+
# Returns the configured namespaces as an array
|
182
|
+
#
|
183
|
+
# Adds a default root namespace to the end of the array if one was not added
|
184
|
+
# explicitly. This fallback ensures that all components in the component dir can
|
185
|
+
# be loaded.
|
186
|
+
#
|
187
|
+
# @return [Array<Namespace>] the namespaces
|
188
|
+
#
|
189
|
+
# @api public
|
190
|
+
def to_a
|
191
|
+
namespaces.values.tap do |arr|
|
192
|
+
arr << Namespace.default_root unless arr.any?(&:root?)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Calls the given block once for each configured namespace, passing the namespace
|
197
|
+
# as an argument.
|
198
|
+
#
|
199
|
+
# @yieldparam namespace [Namespace] the yielded namespace
|
200
|
+
#
|
201
|
+
# @api public
|
202
|
+
def each(&block)
|
203
|
+
to_a.each(&block)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
data/lib/dry/system/constants.rb
CHANGED
data/lib/dry/system/container.rb
CHANGED
@@ -7,16 +7,19 @@ require "dry-configurable"
|
|
7
7
|
require "dry-container"
|
8
8
|
require "dry/inflector"
|
9
9
|
|
10
|
+
require "dry/core/constants"
|
10
11
|
require "dry/core/deprecations"
|
11
12
|
|
12
13
|
require "dry/system"
|
13
|
-
require "dry/system/errors"
|
14
|
-
require "dry/system/booter"
|
15
14
|
require "dry/system/auto_registrar"
|
16
|
-
require "dry/system/
|
17
|
-
require "dry/system/importer"
|
15
|
+
require "dry/system/booter"
|
18
16
|
require "dry/system/component"
|
19
17
|
require "dry/system/constants"
|
18
|
+
require "dry/system/errors"
|
19
|
+
require "dry/system/identifier"
|
20
|
+
require "dry/system/importer"
|
21
|
+
require "dry/system/indirect_component"
|
22
|
+
require "dry/system/manual_registrar"
|
20
23
|
require "dry/system/plugins"
|
21
24
|
|
22
25
|
require_relative "component_dir"
|
@@ -48,7 +51,7 @@ module Dry
|
|
48
51
|
#
|
49
52
|
# Every container needs to be configured with following settings:
|
50
53
|
#
|
51
|
-
# * `:name` - a unique container
|
54
|
+
# * `:name` - a unique container name
|
52
55
|
# * `:root` - a system root directory (defaults to `pwd`)
|
53
56
|
#
|
54
57
|
# @example
|
@@ -73,17 +76,17 @@ module Dry
|
|
73
76
|
extend Dry::System::Plugins
|
74
77
|
|
75
78
|
setting :name
|
76
|
-
setting
|
77
|
-
setting :system_dir, "system"
|
78
|
-
setting :bootable_dirs, ["system/boot"]
|
79
|
-
setting :registrations_dir, "container"
|
80
|
-
setting :component_dirs, Config::ComponentDirs.new, cloneable: true
|
81
|
-
setting :inflector, Dry::Inflector.new
|
82
|
-
setting :booter, Dry::System::Booter
|
83
|
-
setting :auto_registrar, Dry::System::AutoRegistrar
|
84
|
-
setting :manual_registrar, Dry::System::ManualRegistrar
|
85
|
-
setting :importer, Dry::System::Importer
|
86
|
-
setting
|
79
|
+
setting :root, default: Pathname.pwd.freeze, constructor: -> path { Pathname(path) }
|
80
|
+
setting :system_dir, default: "system"
|
81
|
+
setting :bootable_dirs, default: ["system/boot"]
|
82
|
+
setting :registrations_dir, default: "container"
|
83
|
+
setting :component_dirs, default: Config::ComponentDirs.new, cloneable: true
|
84
|
+
setting :inflector, default: Dry::Inflector.new
|
85
|
+
setting :booter, default: Dry::System::Booter
|
86
|
+
setting :auto_registrar, default: Dry::System::AutoRegistrar
|
87
|
+
setting :manual_registrar, default: Dry::System::ManualRegistrar
|
88
|
+
setting :importer, default: Dry::System::Importer
|
89
|
+
setting :components, default: {}, reader: true, constructor: :dup.to_proc
|
87
90
|
|
88
91
|
class << self
|
89
92
|
def strategies(value = nil)
|
@@ -101,8 +104,8 @@ module Dry
|
|
101
104
|
# @see https://dry-rb.org/gems/dry-configurable
|
102
105
|
#
|
103
106
|
# @api public
|
104
|
-
def setting(name,
|
105
|
-
super(name,
|
107
|
+
def setting(name, default = Dry::Core::Constants::Undefined, **options, &block)
|
108
|
+
super(name, default, **options, &block)
|
106
109
|
# TODO: dry-configurable needs a public API for this
|
107
110
|
config._settings << _settings[name]
|
108
111
|
self
|
@@ -233,7 +236,7 @@ module Dry
|
|
233
236
|
# persistence.register(:db, DB.new)
|
234
237
|
# end
|
235
238
|
#
|
236
|
-
# @param name [Symbol] a unique
|
239
|
+
# @param name [Symbol] a unique name for a bootable component
|
237
240
|
#
|
238
241
|
# @see Lifecycle
|
239
242
|
#
|
@@ -261,17 +264,15 @@ module Dry
|
|
261
264
|
deprecate :finalize, :boot
|
262
265
|
|
263
266
|
# @api private
|
264
|
-
def boot_external(
|
267
|
+
def boot_external(name, from:, key: nil, namespace: nil, &block)
|
265
268
|
System.providers[from].component(
|
266
|
-
|
269
|
+
name, key: key, namespace: namespace, finalize: block, container: self
|
267
270
|
)
|
268
271
|
end
|
269
272
|
|
270
273
|
# @api private
|
271
|
-
def boot_local(
|
272
|
-
Components::Bootable.new(
|
273
|
-
identifier, container: self, namespace: namespace, &block
|
274
|
-
)
|
274
|
+
def boot_local(name, namespace: nil, &block)
|
275
|
+
Components::Bootable.new(name, container: self, namespace: namespace, &block)
|
275
276
|
end
|
276
277
|
|
277
278
|
# Return if a container was finalized
|
@@ -490,7 +491,7 @@ module Dry
|
|
490
491
|
#
|
491
492
|
# @!method registered?(key)
|
492
493
|
# Whether a +key+ is registered (doesn't trigger loading)
|
493
|
-
# @param [String,Symbol] key
|
494
|
+
# @param [String,Symbol] key The key
|
494
495
|
# @return [Boolean]
|
495
496
|
# @api public
|
496
497
|
#
|
@@ -580,17 +581,17 @@ module Dry
|
|
580
581
|
def load_component(key)
|
581
582
|
return self if registered?(key)
|
582
583
|
|
583
|
-
|
584
|
-
|
585
|
-
if component.bootable?
|
586
|
-
booter.start(component)
|
584
|
+
if (bootable_component = booter.find_component(key))
|
585
|
+
booter.start(bootable_component)
|
587
586
|
return self
|
588
587
|
end
|
589
588
|
|
589
|
+
component = find_component(key)
|
590
|
+
|
590
591
|
booter.boot_dependency(component)
|
591
592
|
return self if registered?(key)
|
592
593
|
|
593
|
-
if component.
|
594
|
+
if component.loadable?
|
594
595
|
load_local_component(component)
|
595
596
|
elsif manual_registrar.file_exists?(component)
|
596
597
|
manual_registrar.(component)
|
@@ -614,25 +615,21 @@ module Dry
|
|
614
615
|
|
615
616
|
container = importer[import_namespace]
|
616
617
|
|
617
|
-
container.load_component(identifier.
|
618
|
+
container.load_component(identifier.namespaced(from: import_namespace, to: nil).key)
|
618
619
|
|
619
620
|
importer.(import_namespace, container)
|
620
621
|
end
|
621
622
|
|
622
|
-
def
|
623
|
-
if (bootable_component = booter.find_component(identifier))
|
624
|
-
return bootable_component
|
625
|
-
end
|
626
|
-
|
623
|
+
def find_component(key)
|
627
624
|
# Find the first matching component from within the configured component dirs.
|
628
|
-
# If no matching component is found, return a
|
629
|
-
#
|
630
|
-
#
|
625
|
+
# If no matching component is found, return a null component; this fallback is
|
626
|
+
# important because the component may still be loadable via the manual registrar
|
627
|
+
# or an imported container.
|
631
628
|
component_dirs.detect { |dir|
|
632
|
-
if (component = dir.
|
629
|
+
if (component = dir.component_for_key(key))
|
633
630
|
break component
|
634
631
|
end
|
635
|
-
} ||
|
632
|
+
} || IndirectComponent.new(Identifier.new(key, separator: config.namespace_separator))
|
636
633
|
end
|
637
634
|
end
|
638
635
|
|
data/lib/dry/system/errors.rb
CHANGED
@@ -11,13 +11,13 @@ module Dry
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
# Error raised when
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
super("
|
14
|
+
# Error raised when a namespace for a component dir is added to configuration more
|
15
|
+
# than once
|
16
|
+
NamespaceAlreadyAddedError = Class.new(StandardError) do
|
17
|
+
def initialize(path)
|
18
|
+
path_label = path ? "path #{path.inspect}" : "root path"
|
19
|
+
|
20
|
+
super("Namespace for #{path_label} already added")
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -27,7 +27,7 @@ module Dry
|
|
27
27
|
ComponentFileMismatchError = Class.new(StandardError) do
|
28
28
|
def initialize(component)
|
29
29
|
super(<<-STR)
|
30
|
-
Bootable component '#{component.
|
30
|
+
Bootable component '#{component.name}' not found
|
31
31
|
STR
|
32
32
|
end
|
33
33
|
end
|
@@ -43,26 +43,17 @@ module Dry
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
# Error raised when component's
|
46
|
+
# Error raised when component's name is not valid
|
47
47
|
#
|
48
48
|
# @api public
|
49
|
-
|
49
|
+
InvalidComponentNameError = Class.new(ArgumentError) do
|
50
50
|
def initialize(name)
|
51
51
|
super(
|
52
|
-
"component
|
52
|
+
"component +#{name}+ is invalid or boot file is missing"
|
53
53
|
)
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
-
# Error raised when component's identifier for booting is not a symbol
|
58
|
-
#
|
59
|
-
# @api public
|
60
|
-
InvalidComponentIdentifierTypeError = Class.new(ArgumentError) do
|
61
|
-
def initialize(name)
|
62
|
-
super("component identifier #{name.inspect} must be a symbol")
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
57
|
# Error raised when trying to stop a component that hasn't started yet
|
67
58
|
#
|
68
59
|
# @api public
|
@@ -13,42 +13,29 @@ module Dry
|
|
13
13
|
#
|
14
14
|
# @api public
|
15
15
|
class Identifier
|
16
|
-
include Dry::Equalizer(:
|
16
|
+
include Dry::Equalizer(:key, :separator)
|
17
17
|
|
18
|
-
# @return [String] the identifier string
|
18
|
+
# @return [String] the identifier's string key
|
19
19
|
# @api public
|
20
|
-
attr_reader :
|
21
|
-
|
22
|
-
# @return [String, nil] the namespace for the component
|
23
|
-
# @api public
|
24
|
-
attr_reader :namespace
|
20
|
+
attr_reader :key
|
25
21
|
|
26
22
|
# @return [String] the configured namespace separator
|
27
23
|
# @api public
|
28
24
|
attr_reader :separator
|
29
25
|
|
30
26
|
# @api private
|
31
|
-
def initialize(
|
32
|
-
@
|
33
|
-
@namespace = namespace
|
27
|
+
def initialize(key, separator: DEFAULT_SEPARATOR)
|
28
|
+
@key = key.to_s
|
34
29
|
@separator = separator
|
35
30
|
end
|
36
31
|
|
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
32
|
# @!method to_s
|
46
|
-
# Returns the identifier string
|
33
|
+
# Returns the identifier string key
|
47
34
|
#
|
48
35
|
# @return [String]
|
49
|
-
# @see #
|
36
|
+
# @see #key
|
50
37
|
# @api public
|
51
|
-
alias_method :to_s, :
|
38
|
+
alias_method :to_s, :key
|
52
39
|
|
53
40
|
# Returns the root namespace segment of the identifier string, as a symbol
|
54
41
|
#
|
@@ -62,31 +49,11 @@ module Dry
|
|
62
49
|
segments.first.to_sym
|
63
50
|
end
|
64
51
|
|
65
|
-
# Returns
|
66
|
-
#
|
67
|
-
# source file.
|
68
|
-
#
|
69
|
-
# @example
|
70
|
-
# identifier.key # => "articles.operations.create"
|
71
|
-
# identifier.namespace # => "admin"
|
52
|
+
# Returns true if the given leading namespaces are a leading part of the
|
53
|
+
# identifier's key
|
72
54
|
#
|
73
|
-
#
|
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
|
55
|
+
# Also returns true if nil is given (technically, from nothing everything is
|
56
|
+
# wrought)
|
90
57
|
#
|
91
58
|
# @example
|
92
59
|
# identifier.key # => "articles.operations.create"
|
@@ -94,63 +61,74 @@ module Dry
|
|
94
61
|
# identifier.start_with?("articles.operations") # => true
|
95
62
|
# identifier.start_with?("articles") # => true
|
96
63
|
# identifier.start_with?("article") # => false
|
64
|
+
# identifier.start_with?(nil) # => true
|
97
65
|
#
|
98
66
|
# @param leading_namespaces [String] the one or more leading namespaces to check
|
99
67
|
# @return [Boolean]
|
100
68
|
# @api public
|
101
69
|
def start_with?(leading_namespaces)
|
102
|
-
|
70
|
+
leading_namespaces.nil? ||
|
71
|
+
key.start_with?("#{leading_namespaces}#{separator}") ||
|
72
|
+
key.eql?(leading_namespaces)
|
103
73
|
end
|
104
74
|
|
105
|
-
# Returns
|
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
|
75
|
+
# Returns the key with its segments separated by the given separator
|
113
76
|
#
|
114
|
-
# @
|
77
|
+
# @example
|
78
|
+
# identifier.key # => "articles.operations.create"
|
79
|
+
# identifier.key_with_separator("/") # => "articles/operations/create"
|
115
80
|
#
|
116
|
-
# @
|
81
|
+
# @return [String] the key using the separator
|
117
82
|
# @api private
|
118
|
-
def
|
119
|
-
|
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
|
-
)
|
83
|
+
def key_with_separator(separator)
|
84
|
+
segments.join(separator)
|
132
85
|
end
|
133
86
|
|
134
|
-
# Returns a copy of the identifier with the
|
87
|
+
# Returns a copy of the identifier with the key's leading namespace(s) replaced
|
88
|
+
#
|
89
|
+
# @example Changing a namespace
|
90
|
+
# identifier.key # => "articles.operations.create"
|
91
|
+
# identifier.namespaced(from: "articles", to: "posts").key # => "posts.commands.create"
|
92
|
+
#
|
93
|
+
# @example Removing a namespace
|
94
|
+
# identifier.key # => "articles.operations.create"
|
95
|
+
# identifier.namespaced(from: "articles", to: nil).key # => "operations.create"
|
135
96
|
#
|
136
|
-
# @
|
97
|
+
# @example Adding a namespace
|
98
|
+
# identifier.key # => "articles.operations.create"
|
99
|
+
# identifier.namespaced(from: nil, to: "admin").key # => "admin.articles.operations.create"
|
100
|
+
#
|
101
|
+
# @param from [String, nil] the leading namespace(s) to replace
|
102
|
+
# @param to [String, nil] the replacement for the leading namespace
|
137
103
|
#
|
138
104
|
# @return [Dry::System::Identifier] the copy of the identifier
|
139
105
|
#
|
140
106
|
# @see #initialize
|
141
107
|
# @api private
|
142
|
-
def
|
143
|
-
self
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
108
|
+
def namespaced(from:, to:)
|
109
|
+
return self if from == to
|
110
|
+
|
111
|
+
separated_to = "#{to}#{separator}" if to
|
112
|
+
|
113
|
+
new_key =
|
114
|
+
if from.nil?
|
115
|
+
"#{separated_to}#{key}"
|
116
|
+
else
|
117
|
+
key.sub(
|
118
|
+
/^#{Regexp.escape(from.to_s)}#{Regexp.escape(separator)}/,
|
119
|
+
separated_to || EMPTY_STRING
|
120
|
+
)
|
121
|
+
end
|
122
|
+
|
123
|
+
return self if new_key == key
|
124
|
+
|
125
|
+
self.class.new(new_key, separator: separator)
|
148
126
|
end
|
149
127
|
|
150
128
|
private
|
151
129
|
|
152
130
|
def segments
|
153
|
-
@segments ||=
|
131
|
+
@segments ||= key.split(separator)
|
154
132
|
end
|
155
133
|
end
|
156
134
|
end
|