dry-system 0.19.0 → 0.21.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 +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 [![Join the chat at https://dry-rb.zulipchat.com](https://img.shields.io/badge/dry--rb-join%20chat-%23346b7a.svg)][chat]
|
8
9
|
|
9
10
|
[![Gem Version](https://badge.fury.io/rb/dry-system.svg)][gem]
|
10
|
-
[![CI Status](https://github.com/dry-rb/dry-system/workflows/
|
11
|
+
[![CI Status](https://github.com/dry-rb/dry-system/workflows/CI/badge.svg)][actions]
|
11
12
|
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/3a0e30d0ae2542c7ba047ba5f923c0bb)][codacy]
|
12
13
|
[![Codacy Badge](https://api.codacy.com/project/badge/Coverage/3a0e30d0ae2542c7ba047ba5f923c0bb)][codacy]
|
13
14
|
[![Inline docs](http://inch-ci.org/github/dry-rb/dry-system.svg?branch=master)][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)
|