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 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)