class Dry::System::ComponentDir
A configured component directory within the container's root. Provides access to the component directory's configuration, as well as methods for locating component files within the directory
@see Dry::System::Config::ComponentDir
@api private
Attributes
@!attribute [r] config
@return [Dry::System::Config::ComponentDir] the component directory configuration @api private
@!attribute [r] container
@return [Dry::System::Container] the container managing the component directory @api private
Public Class Methods
@api private
# File lib/dry/system/component_dir.rb, line 29 def initialize(config:, container:) @config = config @container = container end
Public Instance Methods
Returns a component for the given key if a matching source file is found within the component dir
This searches according to the component dir's configured namespaces, in order of definition, with the first match returned as the component.
@param key [String] the component's key @return [Dry::System::Component, nil] the component, if found
@api private
# File lib/dry/system/component_dir.rb, line 44 def component_for_key(key) namespaces.each do |namespace| identifier = Identifier.new(key, separator: container.config.namespace_separator) next unless identifier.start_with?(namespace.key) if (file_path = find_component_file(identifier, namespace)) return build_component(identifier, namespace, file_path) end end nil end
# File lib/dry/system/component_dir.rb, line 58 def each_component return enum_for(:each_component) unless block_given? each_file do |file_path, namespace| yield component_for_path(file_path, namespace) end end
Private Instance Methods
# File lib/dry/system/component_dir.rb, line 164 def build_component(identifier, namespace, file_path) options = { inflector: container.config.inflector, **component_options, **MagicCommentsParser.(file_path) } Component.new(identifier, namespace: namespace, **options) end
Returns a component for a full path to a Ruby source file within the component dir
@param path [String] the full path to the file @return [Dry::System::Component] the component
# File lib/dry/system/component_dir.rb, line 128 def component_for_path(path, namespace) separator = container.config.namespace_separator key = Pathname(path).relative_path_from(full_path).to_s .sub(RB_EXT, EMPTY_STRING) .scan(WORD_REGEX) .join(separator) identifier = Identifier.new(key, separator: separator) .namespaced( from: namespace.path&.gsub(PATH_SEPARATOR, separator), to: namespace.key ) build_component(identifier, namespace, path) end
# File lib/dry/system/component_dir.rb, line 174 def component_options { auto_register: auto_register, loader: loader, memoize: memoize } end
# File lib/dry/system/component_dir.rb, line 93 def each_file return enum_for(:each_file) unless block_given? raise ComponentDirNotFoundError, full_path unless Dir.exist?(full_path) namespaces.each do |namespace| files(namespace).each do |file| yield file, namespace end end end
# File lib/dry/system/component_dir.rb, line 105 def files(namespace) if namespace.path? Dir[File.join(full_path, namespace.path, "**", RB_GLOB)].sort else non_root_paths = namespaces.to_a.reject(&:root?).map(&:path) Dir[File.join(full_path, "**", RB_GLOB)].reject { |file_path| Pathname(file_path).relative_path_from(full_path).to_s.start_with?(*non_root_paths) }.sort end end
# File lib/dry/system/component_dir.rb, line 145 def find_component_file(identifier, namespace) # To properly find the file within a namespace with a key, we should strip the key # from beginning of our given identifier if namespace.key identifier = identifier.namespaced(from: namespace.key, to: nil) end file_name = "#{identifier.key_with_separator(PATH_SEPARATOR)}#{RB_EXT}" component_file = if namespace.path? full_path.join(namespace.path, file_name) else full_path.join(file_name) end component_file if component_file.exist? end
Returns the full path of the component directory
@return [Pathname]
# File lib/dry/system/component_dir.rb, line 120 def full_path container.root.join(path) end
# File lib/dry/system/component_dir.rb, line 182 def method_missing(name, *args, &block) if config.respond_to?(name) config.public_send(name, *args, &block) else super end end
# File lib/dry/system/component_dir.rb, line 68 def namespaces config.namespaces.to_a.map { |namespace| normalize_namespace(namespace) } end
Returns an array of “normalized” namespaces, safe for loading components
This works around the issue of a namespace being added for a nested path but without specifying a key namespace. In this case, the key namespace will defaut to match the path, meaning it will contain path separators instead of the container's configured `namespace_separator` (due to `Config::Namespaces` not being able to know the configured `namespace_separator`), so we need to replace the path separators with the proper `namespace_separator` here (where we do know what it is).
# File lib/dry/system/component_dir.rb, line 81 def normalize_namespace(namespace) if namespace.path&.include?(PATH_SEPARATOR) && namespace.default_key? namespace = namespace.class.new( path: namespace.path, key: namespace.key.gsub(PATH_SEPARATOR, container.config.namespace_separator), const: namespace.const ) end namespace end
# File lib/dry/system/component_dir.rb, line 190 def respond_to_missing?(name, include_all = false) config.respond_to?(name) || super end