dry-system 0.19.0 → 0.21.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 +75 -0
- data/README.md +5 -4
- data/dry-system.gemspec +16 -15
- data/lib/dry/system/auto_registrar.rb +3 -15
- 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 +74 -42
- data/lib/dry/system/config/component_dirs.rb +28 -41
- data/lib/dry/system/config/namespace.rb +71 -0
- data/lib/dry/system/config/namespaces.rb +121 -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 +1 -1
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1c0977524fa33277150e8611a851c557461c14357e1a5d9c1f1929c283f3c9b3
|
4
|
+
data.tar.gz: 12de5eaa25ede9f5f959cd77e2af8f1d3b4b287aaebea1554c2915433743c290
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6976cb749c98ed536c5fb3e6ca67674aee2707b40cd5d196b6b82472edadfa10877bebac77631b0ebcc5fabe008514ed60e7d9be17ccd1343e8967836f319a11
|
7
|
+
data.tar.gz: fd894f982bf214dac3ccfa0de1d4fd7f95beeea1f91234cd2868dfab4687b76ea831c153b429d8ba9bd9f04bcb46dd2815f87a7829e6b347b2d8992cf0ebd478
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,80 @@
|
|
1
1
|
<!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
|
2
2
|
|
3
|
+
## 0.21.0 2021-11-01
|
4
|
+
|
5
|
+
|
6
|
+
### Added
|
7
|
+
|
8
|
+
- Added **component dir namespaces** as a way to specify multiple, ordered, independent namespace rules within a given component dir. This replaces and expands upon the namespace support we previously provided via the singular `default_namespace` component dir setting (@timriley in #181)
|
9
|
+
|
10
|
+
### Changed
|
11
|
+
|
12
|
+
- `default_namespace` setting on component dirs has been deprecated. Add a component dir namespace instead, e.g. instead of:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
# Inside Dry::System::Container.configure
|
16
|
+
config.component_dirs.add "lib" do |dir|
|
17
|
+
dir.default_namespace = "admin"
|
18
|
+
end
|
19
|
+
```
|
20
|
+
|
21
|
+
Add this:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
config.component_dirs.add "lib" do |dir|
|
25
|
+
dir.namespaces.add "admin", key: nil
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
(@timriley in #181)
|
30
|
+
- `Dry::System::Component#path` has been removed and replaced by `Component#require_path` and `Component#const_path` (@timriley in #181)
|
31
|
+
- Unused `Dry::System::FileNotFoundError` and `Dry::System::InvalidComponentIdentifierTypeError` errors have been removed (@timriley in #194)
|
32
|
+
|
33
|
+
[Compare v0.20.0...v0.21.0](https://github.com/dry-rb/dry-system/compare/v0.20.0...v0.21.0)
|
34
|
+
|
35
|
+
## 0.20.0 2021-09-12
|
36
|
+
|
37
|
+
|
38
|
+
### Fixed
|
39
|
+
|
40
|
+
- Fixed dependency graph plugin to work with internal changes introduced in 0.19.0 (@wuarmin in #173)
|
41
|
+
- Fixed behavior of `Dry::System::Identifier#start_with?` for components identified by a single segment, or if all matching segments are provided (@wuarmin in #177)
|
42
|
+
- Fixed compatibility of `finalize!` signature provided in `Container::Stubs` (@mpokrywka in #178)
|
43
|
+
|
44
|
+
### Changed
|
45
|
+
|
46
|
+
- [internal] Upgraded to new `setting` API provided in dry-configurable 0.13.0 (@timriley in #179)
|
47
|
+
|
48
|
+
[Compare v0.19.2...v0.20.0](https://github.com/dry-rb/dry-system/compare/v0.19.2...v0.20.0)
|
49
|
+
|
50
|
+
## 0.19.2 2021-08-30
|
51
|
+
|
52
|
+
|
53
|
+
### Changed
|
54
|
+
|
55
|
+
- [internal] Improved compatibility with upcoming dry-configurable 0.13.0 release (@timriley in #186)
|
56
|
+
|
57
|
+
[Compare v0.18.2...v0.19.2](https://github.com/dry-rb/dry-system/compare/v0.18.2...v0.19.2)
|
58
|
+
|
59
|
+
## 0.18.2 2021-08-30
|
60
|
+
|
61
|
+
|
62
|
+
### Changed
|
63
|
+
|
64
|
+
- [internal] Improved compatibility with upcoming dry-configurable 0.13.0 release (@timriley in #187)
|
65
|
+
|
66
|
+
[Compare v0.19.1...v0.18.2](https://github.com/dry-rb/dry-system/compare/v0.19.1...v0.18.2)
|
67
|
+
|
68
|
+
## 0.19.1 2021-07-11
|
69
|
+
|
70
|
+
|
71
|
+
### Fixed
|
72
|
+
|
73
|
+
- Check for registered components (@timriley in #175)
|
74
|
+
|
75
|
+
|
76
|
+
[Compare v0.19.0...v0.19.1](https://github.com/dry-rb/dry-system/compare/v0.19.0...v0.19.1)
|
77
|
+
|
3
78
|
## 0.19.0 2021-04-22
|
4
79
|
|
5
80
|
This release marks a huge step forward for dry-system, bringing support for Zeitwerk and other autoloaders, plus clearer configuration and improved consistency around component resolution for both finalized and lazy loading containers. [Read the announcement post](https://dry-rb.org/news/2021/04/22/dry-system-0-19-released-with-zeitwerk-support-and-more-leading-the-way-for-hanami-2-0/) for a high-level tour of the new features.
|
data/README.md
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
<!--- this file is synced from dry-rb/template-gem project -->
|
1
2
|
[gem]: https://rubygems.org/gems/dry-system
|
2
3
|
[actions]: https://github.com/dry-rb/dry-system/actions
|
3
4
|
[codacy]: https://www.codacy.com/gh/dry-rb/dry-system
|
@@ -7,22 +8,22 @@
|
|
7
8
|
# dry-system [][chat]
|
8
9
|
|
9
10
|
[][gem]
|
10
|
-
[][actions]
|
11
12
|
[][codacy]
|
12
13
|
[][codacy]
|
13
14
|
[][inchpages]
|
14
15
|
|
15
16
|
## Links
|
16
17
|
|
17
|
-
* [User documentation](
|
18
|
+
* [User documentation](https://dry-rb.org/gems/dry-system)
|
18
19
|
* [API documentation](http://rubydoc.info/gems/dry-system)
|
19
20
|
|
20
21
|
## Supported Ruby versions
|
21
22
|
|
22
23
|
This library officially supports the following Ruby versions:
|
23
24
|
|
24
|
-
* MRI
|
25
|
-
* jruby
|
25
|
+
* MRI `>= 2.6.0`
|
26
|
+
* jruby `>= 9.3`
|
26
27
|
|
27
28
|
## License
|
28
29
|
|
data/dry-system.gemspec
CHANGED
@@ -1,37 +1,38 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
# this file is managed by dry-rb/devtools project
|
3
2
|
|
4
|
-
|
3
|
+
# this file is synced from dry-rb/template-gem project
|
4
|
+
|
5
|
+
lib = File.expand_path("lib", __dir__)
|
5
6
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
|
-
require
|
7
|
+
require "dry/system/version"
|
7
8
|
|
8
9
|
Gem::Specification.new do |spec|
|
9
|
-
spec.name =
|
10
|
+
spec.name = "dry-system"
|
10
11
|
spec.authors = ["Piotr Solnica"]
|
11
12
|
spec.email = ["piotr.solnica@gmail.com"]
|
12
|
-
spec.license =
|
13
|
+
spec.license = "MIT"
|
13
14
|
spec.version = Dry::System::VERSION.dup
|
14
15
|
|
15
16
|
spec.summary = "Organize your code into reusable components"
|
16
17
|
spec.description = spec.summary
|
17
|
-
spec.homepage =
|
18
|
+
spec.homepage = "https://dry-rb.org/gems/dry-system"
|
18
19
|
spec.files = Dir["CHANGELOG.md", "LICENSE", "README.md", "dry-system.gemspec", "lib/**/*"]
|
19
|
-
spec.bindir =
|
20
|
+
spec.bindir = "bin"
|
20
21
|
spec.executables = []
|
21
|
-
spec.require_paths = [
|
22
|
+
spec.require_paths = ["lib"]
|
22
23
|
|
23
|
-
spec.metadata[
|
24
|
-
spec.metadata[
|
25
|
-
spec.metadata[
|
26
|
-
spec.metadata[
|
24
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
25
|
+
spec.metadata["changelog_uri"] = "https://github.com/dry-rb/dry-system/blob/master/CHANGELOG.md"
|
26
|
+
spec.metadata["source_code_uri"] = "https://github.com/dry-rb/dry-system"
|
27
|
+
spec.metadata["bug_tracker_uri"] = "https://github.com/dry-rb/dry-system/issues"
|
27
28
|
|
28
|
-
spec.required_ruby_version = ">= 2.
|
29
|
+
spec.required_ruby_version = ">= 2.6.0"
|
29
30
|
|
30
31
|
# to update dependencies edit project.yml
|
31
32
|
spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
|
32
33
|
spec.add_runtime_dependency "dry-auto_inject", ">= 0.4.0"
|
33
|
-
spec.add_runtime_dependency "dry-configurable", "~> 0.
|
34
|
-
spec.add_runtime_dependency "dry-container", "~> 0.
|
34
|
+
spec.add_runtime_dependency "dry-configurable", "~> 0.13", ">= 0.13.0"
|
35
|
+
spec.add_runtime_dependency "dry-container", "~> 0.9", ">= 0.9.0"
|
35
36
|
spec.add_runtime_dependency "dry-core", "~> 0.5", ">= 0.5"
|
36
37
|
spec.add_runtime_dependency "dry-inflector", "~> 0.1", ">= 0.1.2"
|
37
38
|
spec.add_runtime_dependency "dry-struct", "~> 1.0"
|
@@ -29,29 +29,17 @@ module Dry
|
|
29
29
|
|
30
30
|
# @api private
|
31
31
|
def call(component_dir)
|
32
|
-
|
32
|
+
component_dir.each_component do |component|
|
33
33
|
next unless register_component?(component)
|
34
34
|
|
35
|
-
container.register(component.
|
35
|
+
container.register(component.key, memoize: component.memoize?) { component.instance }
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
39
|
private
|
40
40
|
|
41
|
-
def components(component_dir)
|
42
|
-
files(component_dir.full_path).map { |file_path|
|
43
|
-
component_dir.component_for_path(file_path)
|
44
|
-
}
|
45
|
-
end
|
46
|
-
|
47
|
-
def files(dir)
|
48
|
-
raise ComponentDirNotFoundError, dir unless Dir.exist?(dir)
|
49
|
-
|
50
|
-
Dir["#{dir}/**/#{RB_GLOB}"].sort
|
51
|
-
end
|
52
|
-
|
53
41
|
def register_component?(component)
|
54
|
-
!container.registered?(component) && component.auto_register?
|
42
|
+
!container.registered?(component.key) && component.auto_register?
|
55
43
|
end
|
56
44
|
end
|
57
45
|
end
|
@@ -21,13 +21,13 @@ module Dry
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def exists?(name)
|
24
|
-
components.any? { |component| component.
|
24
|
+
components.any? { |component| component.name == name }
|
25
25
|
end
|
26
26
|
|
27
27
|
def [](name)
|
28
|
-
component = components.detect { |
|
28
|
+
component = components.detect { |c| c.name == name }
|
29
29
|
|
30
|
-
component || raise(
|
30
|
+
component || raise(InvalidComponentNameError, name)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
data/lib/dry/system/booter.rb
CHANGED
@@ -179,15 +179,15 @@ module Dry
|
|
179
179
|
end
|
180
180
|
|
181
181
|
def load_component(path)
|
182
|
-
|
182
|
+
name = Pathname(path).basename(RB_EXT).to_s.to_sym
|
183
183
|
|
184
|
-
Kernel.require path unless components.exists?(
|
184
|
+
Kernel.require path unless components.exists?(name)
|
185
185
|
|
186
186
|
self
|
187
187
|
end
|
188
188
|
|
189
|
-
def require_boot_file(
|
190
|
-
boot_file = find_boot_file(
|
189
|
+
def require_boot_file(name)
|
190
|
+
boot_file = find_boot_file(name)
|
191
191
|
|
192
192
|
Kernel.require boot_file if boot_file
|
193
193
|
end
|
data/lib/dry/system/component.rb
CHANGED
@@ -17,7 +17,7 @@ module Dry
|
|
17
17
|
#
|
18
18
|
# @api public
|
19
19
|
class Component
|
20
|
-
include Dry::Equalizer(:identifier, :
|
20
|
+
include Dry::Equalizer(:identifier, :namespace, :options)
|
21
21
|
|
22
22
|
DEFAULT_OPTIONS = {
|
23
23
|
separator: DEFAULT_SEPARATOR,
|
@@ -26,43 +26,34 @@ module Dry
|
|
26
26
|
}.freeze
|
27
27
|
|
28
28
|
# @!attribute [r] identifier
|
29
|
-
# @return [String] component's unique identifier
|
29
|
+
# @return [String] the component's unique identifier
|
30
30
|
attr_reader :identifier
|
31
31
|
|
32
|
-
# @!attribute [r]
|
33
|
-
# @return [
|
34
|
-
attr_reader :
|
32
|
+
# @!attribute [r] namespace
|
33
|
+
# @return [Dry::System::Config::Namespace] the component's namespace
|
34
|
+
attr_reader :namespace
|
35
35
|
|
36
36
|
# @!attribute [r] options
|
37
|
-
# @return [Hash] component's options
|
37
|
+
# @return [Hash] the component's options
|
38
38
|
attr_reader :options
|
39
39
|
|
40
40
|
# @api private
|
41
|
-
def
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
separator = options.delete(:separator)
|
46
|
-
|
47
|
-
identifier =
|
48
|
-
if identifier.is_a?(Identifier)
|
49
|
-
identifier
|
50
|
-
else
|
51
|
-
Identifier.new(
|
52
|
-
identifier,
|
53
|
-
namespace: namespace,
|
54
|
-
separator: separator
|
55
|
-
)
|
56
|
-
end
|
57
|
-
|
58
|
-
super(identifier, **options)
|
41
|
+
def initialize(identifier, namespace:, **options)
|
42
|
+
@identifier = identifier
|
43
|
+
@namespace = namespace
|
44
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
59
45
|
end
|
60
46
|
|
47
|
+
# Returns true, indicating that the component is directly loadable from the files
|
48
|
+
# managed by the container
|
49
|
+
#
|
50
|
+
# This is the inverse of {IndirectComponent#loadable?}
|
51
|
+
#
|
52
|
+
# @return [TrueClass]
|
53
|
+
#
|
61
54
|
# @api private
|
62
|
-
def
|
63
|
-
|
64
|
-
@file_path = file_path
|
65
|
-
@options = options
|
55
|
+
def loadable?
|
56
|
+
true
|
66
57
|
end
|
67
58
|
|
68
59
|
# Returns the component's instance
|
@@ -74,39 +65,95 @@ module Dry
|
|
74
65
|
end
|
75
66
|
ruby2_keywords(:instance) if respond_to?(:ruby2_keywords, true)
|
76
67
|
|
77
|
-
#
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
68
|
+
# Returns the component's unique key
|
69
|
+
#
|
70
|
+
# @return [String] the key
|
71
|
+
#
|
72
|
+
# @see Identifier#key
|
73
|
+
#
|
74
|
+
# @api public
|
82
75
|
def key
|
83
|
-
identifier.
|
84
|
-
end
|
85
|
-
|
86
|
-
def path
|
87
|
-
identifier.path
|
76
|
+
identifier.key
|
88
77
|
end
|
89
78
|
|
79
|
+
# Returns the root namespace segment of the component's key, as a symbol
|
80
|
+
#
|
81
|
+
# @see Identifier#root_key
|
82
|
+
#
|
83
|
+
# @return [Symbol] the root key
|
84
|
+
#
|
85
|
+
# @api public
|
90
86
|
def root_key
|
91
87
|
identifier.root_key
|
92
88
|
end
|
93
89
|
|
94
|
-
# Returns
|
90
|
+
# Returns a path-delimited representation of the compnent, appropriate for passing
|
91
|
+
# to `Kernel#require` to require its source file
|
95
92
|
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
|
99
|
-
|
93
|
+
# The path takes into account the rules of the namespace used to load the component.
|
94
|
+
#
|
95
|
+
# @example Component from a root namespace
|
96
|
+
# component.key # => "articles.create"
|
97
|
+
# component.require_path # => "articles/create"
|
98
|
+
#
|
99
|
+
# @example Component from an "admin/" path namespace (with `key: nil`)
|
100
|
+
# component.key # => "articles.create"
|
101
|
+
# component.require_path # => "admin/articles/create"
|
102
|
+
#
|
103
|
+
# @see Config::Namespaces#add
|
104
|
+
# @see Config::Namespace
|
105
|
+
#
|
106
|
+
# @return [String] the require path
|
107
|
+
#
|
108
|
+
# @api public
|
109
|
+
def require_path
|
110
|
+
if namespace.path
|
111
|
+
"#{namespace.path}#{PATH_SEPARATOR}#{path_in_namespace}"
|
112
|
+
else
|
113
|
+
path_in_namespace
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns an "underscored", path-delimited representation of the component,
|
118
|
+
# appropriate for passing to the inflector for constantizing
|
119
|
+
#
|
120
|
+
# The const path takes into account the rules of the namespace used to load the
|
121
|
+
# component.
|
122
|
+
#
|
123
|
+
# @example Component from a namespace with `const: nil`
|
124
|
+
# component.key # => "articles.create_article"
|
125
|
+
# component.const_path # => "articles/create_article"
|
126
|
+
# component.inflector.constantize(component.const_path) # => Articles::CreateArticle
|
127
|
+
#
|
128
|
+
# @example Component from a namespace with `const: "admin"`
|
129
|
+
# component.key # => "articles.create_article"
|
130
|
+
# component.const_path # => "admin/articles/create_article"
|
131
|
+
# component.inflector.constantize(component.const_path) # => Admin::Articles::CreateArticle
|
132
|
+
#
|
133
|
+
# @see Config::Namespaces#add
|
134
|
+
# @see Config::Namespace
|
135
|
+
#
|
136
|
+
# @return [String] the const path
|
137
|
+
#
|
138
|
+
# @api public
|
139
|
+
def const_path
|
140
|
+
namespace_const_path = namespace.const&.gsub(identifier.separator, PATH_SEPARATOR)
|
141
|
+
|
142
|
+
if namespace_const_path
|
143
|
+
"#{namespace_const_path}#{PATH_SEPARATOR}#{path_in_namespace}"
|
144
|
+
else
|
145
|
+
path_in_namespace
|
146
|
+
end
|
100
147
|
end
|
101
148
|
|
102
149
|
# @api private
|
103
150
|
def loader
|
104
|
-
options
|
151
|
+
options.fetch(:loader)
|
105
152
|
end
|
106
153
|
|
107
154
|
# @api private
|
108
155
|
def inflector
|
109
|
-
options
|
156
|
+
options.fetch(:inflector)
|
110
157
|
end
|
111
158
|
|
112
159
|
# @api private
|
@@ -121,6 +168,17 @@ module Dry
|
|
121
168
|
|
122
169
|
private
|
123
170
|
|
171
|
+
def path_in_namespace
|
172
|
+
identifier_in_namespace =
|
173
|
+
if namespace.key
|
174
|
+
identifier.namespaced(from: namespace.key, to: nil)
|
175
|
+
else
|
176
|
+
identifier
|
177
|
+
end
|
178
|
+
|
179
|
+
identifier_in_namespace.key_with_separator(PATH_SEPARATOR)
|
180
|
+
end
|
181
|
+
|
124
182
|
def callable_option?(value)
|
125
183
|
if value.respond_to?(:call)
|
126
184
|
!!value.call(self)
|
@@ -1,4 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "pathname"
|
4
|
+
require "dry/system/constants"
|
2
5
|
require_relative "constants"
|
3
6
|
require_relative "identifier"
|
4
7
|
require_relative "magic_comments_parser"
|
@@ -28,40 +31,101 @@ module Dry
|
|
28
31
|
@container = container
|
29
32
|
end
|
30
33
|
|
31
|
-
# Returns a component for
|
32
|
-
#
|
34
|
+
# Returns a component for the given key if a matching source file is found within
|
35
|
+
# the component dir
|
33
36
|
#
|
34
|
-
# This
|
35
|
-
#
|
37
|
+
# This searches according to the component dir's configured namespaces, in order of
|
38
|
+
# definition, with the first match returned as the component.
|
36
39
|
#
|
37
|
-
# @param
|
40
|
+
# @param key [String] the component's key
|
38
41
|
# @return [Dry::System::Component, nil] the component, if found
|
39
42
|
#
|
40
43
|
# @api private
|
41
|
-
def
|
42
|
-
|
43
|
-
identifier,
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
44
|
+
def component_for_key(key)
|
45
|
+
namespaces.each do |namespace|
|
46
|
+
identifier = Identifier.new(key, separator: container.config.namespace_separator)
|
47
|
+
|
48
|
+
next unless identifier.start_with?(namespace.key)
|
49
|
+
|
50
|
+
if (file_path = find_component_file(identifier, namespace))
|
51
|
+
return build_component(identifier, namespace, file_path)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def each_component
|
59
|
+
return enum_for(:each_component) unless block_given?
|
60
|
+
|
61
|
+
each_file do |file_path, namespace|
|
62
|
+
yield component_for_path(file_path, namespace)
|
50
63
|
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def namespaces
|
69
|
+
config.namespaces.to_a.map { |namespace| normalize_namespace(namespace) }
|
70
|
+
end
|
51
71
|
|
52
|
-
|
53
|
-
|
54
|
-
|
72
|
+
# Returns an array of "normalized" namespaces, safe for loading components
|
73
|
+
#
|
74
|
+
# This works around the issue of a namespace being added for a nested path but
|
75
|
+
# _without_ specifying a key namespace. In this case, the key namespace will defaut
|
76
|
+
# to match the path, meaning it will contain path separators instead of the
|
77
|
+
# container's configured `namespace_separator` (due to `Config::Namespaces` not
|
78
|
+
# being able to know the configured `namespace_separator`), so we need to replace
|
79
|
+
# the path separators with the proper `namespace_separator` here (where we _do_ know
|
80
|
+
# what it is).
|
81
|
+
def normalize_namespace(namespace)
|
82
|
+
if namespace.path&.include?(PATH_SEPARATOR) && namespace.default_key?
|
83
|
+
namespace = namespace.class.new(
|
84
|
+
path: namespace.path,
|
85
|
+
key: namespace.key.gsub(PATH_SEPARATOR, container.config.namespace_separator),
|
86
|
+
const: namespace.const
|
87
|
+
)
|
55
88
|
end
|
89
|
+
|
90
|
+
namespace
|
91
|
+
end
|
92
|
+
|
93
|
+
def each_file
|
94
|
+
return enum_for(:each_file) unless block_given?
|
95
|
+
|
96
|
+
raise ComponentDirNotFoundError, full_path unless Dir.exist?(full_path)
|
97
|
+
|
98
|
+
namespaces.each do |namespace|
|
99
|
+
files(namespace).each do |file|
|
100
|
+
yield file, namespace
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def files(namespace)
|
106
|
+
if namespace.path?
|
107
|
+
Dir[File.join(full_path, namespace.path, "**", RB_GLOB)].sort
|
108
|
+
else
|
109
|
+
non_root_paths = namespaces.to_a.reject(&:root?).map(&:path)
|
110
|
+
|
111
|
+
Dir[File.join(full_path, "**", RB_GLOB)].reject { |file_path|
|
112
|
+
Pathname(file_path).relative_path_from(full_path).to_s.start_with?(*non_root_paths)
|
113
|
+
}.sort
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns the full path of the component directory
|
118
|
+
#
|
119
|
+
# @return [Pathname]
|
120
|
+
def full_path
|
121
|
+
container.root.join(path)
|
56
122
|
end
|
57
123
|
|
58
124
|
# Returns a component for a full path to a Ruby source file within the component dir
|
59
125
|
#
|
60
126
|
# @param path [String] the full path to the file
|
61
127
|
# @return [Dry::System::Component] the component
|
62
|
-
|
63
|
-
# @api private
|
64
|
-
def component_for_path(path)
|
128
|
+
def component_for_path(path, namespace)
|
65
129
|
separator = container.config.namespace_separator
|
66
130
|
|
67
131
|
key = Pathname(path).relative_path_from(full_path).to_s
|
@@ -70,46 +134,49 @@ module Dry
|
|
70
134
|
.join(separator)
|
71
135
|
|
72
136
|
identifier = Identifier.new(key, separator: separator)
|
137
|
+
.namespaced(
|
138
|
+
from: namespace.path&.gsub(PATH_SEPARATOR, separator),
|
139
|
+
to: namespace.key
|
140
|
+
)
|
141
|
+
|
142
|
+
build_component(identifier, namespace, path)
|
143
|
+
end
|
73
144
|
|
74
|
-
|
75
|
-
|
145
|
+
def find_component_file(identifier, namespace)
|
146
|
+
# To properly find the file within a namespace with a key, we should strip the key
|
147
|
+
# from beginning of our given identifier
|
148
|
+
if namespace.key
|
149
|
+
identifier = identifier.namespaced(from: namespace.key, to: nil)
|
76
150
|
end
|
77
151
|
|
78
|
-
|
79
|
-
end
|
152
|
+
file_name = "#{identifier.key_with_separator(PATH_SEPARATOR)}#{RB_EXT}"
|
80
153
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
end
|
154
|
+
component_file =
|
155
|
+
if namespace.path?
|
156
|
+
full_path.join(namespace.path, file_name)
|
157
|
+
else
|
158
|
+
full_path.join(file_name)
|
159
|
+
end
|
88
160
|
|
89
|
-
|
90
|
-
def component_options
|
91
|
-
{
|
92
|
-
auto_register: auto_register,
|
93
|
-
loader: loader,
|
94
|
-
memoize: memoize
|
95
|
-
}
|
161
|
+
component_file if component_file.exist?
|
96
162
|
end
|
97
163
|
|
98
|
-
|
99
|
-
|
100
|
-
def build_component(identifier, file_path)
|
164
|
+
def build_component(identifier, namespace, file_path)
|
101
165
|
options = {
|
102
166
|
inflector: container.config.inflector,
|
103
167
|
**component_options,
|
104
168
|
**MagicCommentsParser.(file_path)
|
105
169
|
}
|
106
170
|
|
107
|
-
Component.new(identifier,
|
171
|
+
Component.new(identifier, namespace: namespace, **options)
|
108
172
|
end
|
109
173
|
|
110
|
-
def
|
111
|
-
|
112
|
-
|
174
|
+
def component_options
|
175
|
+
{
|
176
|
+
auto_register: auto_register,
|
177
|
+
loader: loader,
|
178
|
+
memoize: memoize
|
179
|
+
}
|
113
180
|
end
|
114
181
|
|
115
182
|
def method_missing(name, *args, &block)
|