dry-system 0.19.1 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd9ff0c5114ab780f7fc44ea9cb2b963ed980e75a0aefa248232363955cb8e8d
4
- data.tar.gz: 94c666638d7a2786ee1cd34f77a1594ea6ffeb5a49951e4412ba0539bb3d7947
3
+ metadata.gz: 2ce0d3b9c3094e2e7d1b20907a31107d6329221e6213839708f8cc7bac1321bd
4
+ data.tar.gz: 2e28bfe4758cace04f1978c2b17fa138921915d5db0b894796772fa428e12549
5
5
  SHA512:
6
- metadata.gz: e778ff07b49f0e2e670d253263a110b5a81145d6e9149f6c12d6684a1a3b10fe3963f8d0b085fab0e9e8e4c8cc099289feb36c3ad154644ab8af9d0ef13c887c
7
- data.tar.gz: 6b94ff8beab87af8621ee6e7e33e0360c4bf9e367f79bb5c5242d5ba09f60906e0c8a30e0e7e49b77b6ce17aa80ea4e82da5d310ba425d6c9a7801b94b4cabd3
6
+ metadata.gz: 33e7e5e5cccf47481498a95c8dc37fdb282ab9005c595f4db30b05f84c842d5144ba7cfa0f3a15f895212dbb0034554f36d16649d004bf54eebc99dca70d53d1
7
+ data.tar.gz: dc441f94a5d1506e7c7bc4afd9e8ebfd43d43e7956af09e2a793df6d1b02b409ac8eb0252183cae6c388969f2e33b02522675794c1aa4c340f6c52efa0ad7344
data/CHANGELOG.md CHANGED
@@ -1,5 +1,95 @@
1
1
  <!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
2
2
 
3
+ ## 0.22.0 2022-01-06
4
+
5
+
6
+ ### Added
7
+
8
+ - Expanded public interfaces for `Dry::System::Config::ComponentDirs` and `Dry::System::Config::Namespaces` to better support programmatic construction and inspection of these configs (@timriley in #195)
9
+
10
+ ### Changed
11
+
12
+ - Deprecated `Dry::System::Config::Namespaces#root` as the way to add and configure a root namespace. Use `#add_root` instead (@timriley in #195)
13
+ - Allow bootsnap plugin to use bootsnap on Ruby versions up to 3.0 (pusewicz in #196)
14
+
15
+ [Compare v0.21.0...v0.22.0](https://github.com/dry-rb/dry-system/compare/v0.21.0...v0.22.0)
16
+
17
+ ## 0.21.0 2021-11-01
18
+
19
+
20
+ ### Added
21
+
22
+ - 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)
23
+
24
+ ### Changed
25
+
26
+ - `default_namespace` setting on component dirs has been deprecated. Add a component dir namespace instead, e.g. instead of:
27
+
28
+ ```ruby
29
+ # Inside Dry::System::Container.configure
30
+ config.component_dirs.add "lib" do |dir|
31
+ dir.default_namespace = "admin"
32
+ end
33
+ ```
34
+
35
+ Add this:
36
+
37
+ ```ruby
38
+ config.component_dirs.add "lib" do |dir|
39
+ dir.namespaces.add "admin", key: nil
40
+ end
41
+ ```
42
+
43
+ (@timriley in #181)
44
+ - `Dry::System::Component#path` has been removed and replaced by `Component#require_path` and `Component#const_path` (@timriley in #181)
45
+ - Unused `Dry::System::FileNotFoundError` and `Dry::System::InvalidComponentIdentifierTypeError` errors have been removed (@timriley in #194)
46
+ - Allow bootsnap for Rubies up to 3.0.x (via #196) (@pusewicz)
47
+
48
+ [Compare v0.20.0...v0.21.0](https://github.com/dry-rb/dry-system/compare/v0.20.0...v0.21.0)
49
+
50
+ ## 0.20.0 2021-09-12
51
+
52
+
53
+ ### Fixed
54
+
55
+ - Fixed dependency graph plugin to work with internal changes introduced in 0.19.0 (@wuarmin in #173)
56
+ - 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)
57
+ - Fixed compatibility of `finalize!` signature provided in `Container::Stubs` (@mpokrywka in #178)
58
+
59
+ ### Changed
60
+
61
+ - [internal] Upgraded to new `setting` API provided in dry-configurable 0.13.0 (@timriley in #179)
62
+
63
+ [Compare v0.19.2...v0.20.0](https://github.com/dry-rb/dry-system/compare/v0.19.2...v0.20.0)
64
+
65
+ ## 0.19.2 2021-08-30
66
+
67
+
68
+ ### Changed
69
+
70
+ - [internal] Improved compatibility with upcoming dry-configurable 0.13.0 release (@timriley in #186)
71
+
72
+ [Compare v0.18.2...v0.19.2](https://github.com/dry-rb/dry-system/compare/v0.18.2...v0.19.2)
73
+
74
+ ## 0.18.2 2021-08-30
75
+
76
+
77
+ ### Changed
78
+
79
+ - [internal] Improved compatibility with upcoming dry-configurable 0.13.0 release (@timriley in #187)
80
+
81
+ [Compare v0.19.1...v0.18.2](https://github.com/dry-rb/dry-system/compare/v0.19.1...v0.18.2)
82
+
83
+ ## 0.19.1 2021-07-11
84
+
85
+
86
+ ### Fixed
87
+
88
+ - Check for registered components (@timriley in #175)
89
+
90
+
91
+ [Compare v0.19.0...v0.19.1](https://github.com/dry-rb/dry-system/compare/v0.19.0...v0.19.1)
92
+
3
93
  ## 0.19.0 2021-04-22
4
94
 
5
95
  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
@@ -14,15 +15,15 @@
14
15
 
15
16
  ## Links
16
17
 
17
- * [User documentation](http://dry-rb.org/gems/dry-system)
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 >= `2.5`
25
- * jruby >= `9.2`
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
- lib = File.expand_path('lib', __dir__)
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 'dry/system/version'
7
+ require "dry/system/version"
7
8
 
8
9
  Gem::Specification.new do |spec|
9
- spec.name = 'dry-system'
10
+ spec.name = "dry-system"
10
11
  spec.authors = ["Piotr Solnica"]
11
12
  spec.email = ["piotr.solnica@gmail.com"]
12
- spec.license = 'MIT'
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 = 'https://dry-rb.org/gems/dry-system'
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 = 'bin'
20
+ spec.bindir = "bin"
20
21
  spec.executables = []
21
- spec.require_paths = ['lib']
22
+ spec.require_paths = ["lib"]
22
23
 
23
- spec.metadata['allowed_push_host'] = 'https://rubygems.org'
24
- spec.metadata['changelog_uri'] = 'https://github.com/dry-rb/dry-system/blob/master/CHANGELOG.md'
25
- spec.metadata['source_code_uri'] = 'https://github.com/dry-rb/dry-system'
26
- spec.metadata['bug_tracker_uri'] = 'https://github.com/dry-rb/dry-system/issues'
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.5.0"
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.12", ">= 0.12.1"
34
- spec.add_runtime_dependency "dry-container", "~> 0.7", ">= 0.7.2"
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,7 +29,7 @@ module Dry
29
29
 
30
30
  # @api private
31
31
  def call(component_dir)
32
- components(component_dir).each do |component|
32
+ component_dir.each_component do |component|
33
33
  next unless register_component?(component)
34
34
 
35
35
  container.register(component.key, memoize: component.memoize?) { component.instance }
@@ -38,18 +38,6 @@ module Dry
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
42
  !container.registered?(component.key) && component.auto_register?
55
43
  end
@@ -21,13 +21,13 @@ module Dry
21
21
  end
22
22
 
23
23
  def exists?(name)
24
- components.any? { |component| component.identifier == name }
24
+ components.any? { |component| component.name == name }
25
25
  end
26
26
 
27
27
  def [](name)
28
- component = components.detect { |component| component.identifier == name }
28
+ component = components.detect { |c| c.name == name }
29
29
 
30
- component || raise(InvalidComponentIdentifierError, name)
30
+ component || raise(InvalidComponentNameError, name)
31
31
  end
32
32
  end
33
33
  end
@@ -179,15 +179,15 @@ module Dry
179
179
  end
180
180
 
181
181
  def load_component(path)
182
- identifier = Pathname(path).basename(RB_EXT).to_s.to_sym
182
+ name = Pathname(path).basename(RB_EXT).to_s.to_sym
183
183
 
184
- Kernel.require path unless components.exists?(identifier)
184
+ Kernel.require path unless components.exists?(name)
185
185
 
186
186
  self
187
187
  end
188
188
 
189
- def require_boot_file(identifier)
190
- boot_file = find_boot_file(identifier)
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
@@ -17,7 +17,7 @@ module Dry
17
17
  #
18
18
  # @api public
19
19
  class Component
20
- include Dry::Equalizer(:identifier, :file_path, :options)
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] file_path
33
- # @return [String, nil] full path to the component's file, if found
34
- attr_reader :file_path
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 self.new(identifier, options = EMPTY_HASH)
42
- options = DEFAULT_OPTIONS.merge(options)
43
-
44
- namespace = options.delete(:namespace)
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 initialize(identifier, file_path: nil, **options)
63
- @identifier = identifier
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
- # @api private
78
- def bootable?
79
- false
80
- end
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.to_s
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 true if the component has a corresponding file
90
+ # Returns a path-delimited representation of the compnent, appropriate for passing
91
+ # to `Kernel#require` to require its source file
95
92
  #
96
- # @return [Boolean]
97
- # @api private
98
- def file_exists?
99
- !!file_path
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[:loader]
151
+ options.fetch(:loader)
105
152
  end
106
153
 
107
154
  # @api private
108
155
  def inflector
109
- options[:inflector]
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 a given identifier if a matching component file could be
32
- # found within the component dir
34
+ # Returns a component for the given key if a matching source file is found within
35
+ # the component dir
33
36
  #
34
- # This will search within the component dir's configured default_namespace first,
35
- # then fall back to searching for a non-namespaced file
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 identifier [String] the identifier string
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 component_for_identifier(identifier)
42
- identifier = Identifier.new(
43
- identifier,
44
- namespace: default_namespace,
45
- separator: container.config.namespace_separator
46
- )
47
-
48
- if (file_path = find_component_file(identifier.path))
49
- return build_component(identifier, file_path)
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
- identifier = identifier.with(namespace: nil)
53
- if (file_path = find_component_file(identifier.path))
54
- build_component(identifier, file_path)
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
- if identifier.start_with?(default_namespace)
75
- identifier = identifier.dequalified(default_namespace, namespace: default_namespace)
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
- build_component(identifier, path)
79
- end
152
+ file_name = "#{identifier.key_with_separator(PATH_SEPARATOR)}#{RB_EXT}"
80
153
 
81
- # Returns the full path of the component directory
82
- #
83
- # @return [Pathname]
84
- # @api private
85
- def full_path
86
- container.root.join(path)
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
- # @api private
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
- private
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, file_path: file_path, **options)
171
+ Component.new(identifier, namespace: namespace, **options)
108
172
  end
109
173
 
110
- def find_component_file(component_path)
111
- component_file = full_path.join("#{component_path}#{RB_EXT}")
112
- component_file if component_file.exist?
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)