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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2c09b2200301d76a2ac09297583d2a58711db80b43fbe191086bd859f1d3b4fe
4
- data.tar.gz: 7f08ad0fb730b2a7281737f799722c30dc649926f36e18e2cd07144637edbb20
3
+ metadata.gz: 1c0977524fa33277150e8611a851c557461c14357e1a5d9c1f1929c283f3c9b3
4
+ data.tar.gz: 12de5eaa25ede9f5f959cd77e2af8f1d3b4b287aaebea1554c2915433743c290
5
5
  SHA512:
6
- metadata.gz: 646a9ef47bce754608ae670ebb43aaad911e2c8b67d79a10f7c0f284cd379c4ce4992263fb22f48f42768e180cfdb6e9f1a3df8bb590245a9a29579858862000
7
- data.tar.gz: 293fddcf0085814fc9628ee77ad6360d04ae076fb8b623117a1a336aceb39be1d5c1a8a44c92f10b7e48ca53e4939284ae91b8e6ed2a3514039f65c15d7d5d54
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/ci/badge.svg)][actions]
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](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,29 +29,17 @@ 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
- container.register(component.identifier, memoize: component.memoize?) { component.instance }
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.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)