lib/dry/system/config/component_dirs.rb in dry-system-0.21.0 vs lib/dry/system/config/component_dirs.rb in dry-system-0.22.0

- old
+ new

@@ -1,15 +1,17 @@ # frozen_string_literal: true -require "concurrent/map" require "dry/system/constants" require "dry/system/errors" require_relative "component_dir" module Dry module System module Config + # The configured component dirs for a container + # + # @api public class ComponentDirs # @!group Settings # @!method auto_register=(value) # @@ -61,86 +63,190 @@ # # Returns the configured default `memoize` # # @see memoize= + # rubocop:disable Layout/LineLength + # @!method namespaces # # Returns the default configured namespaces for all added component dirs # - # Allows namespaces to added on the returned object via {Namespaces#add}. + # Allows namespaces to added on the returned object via {Dry::System::Config::Namespaces#add}. # - # @see Namespaces#add + # @see Dry::System::Config::Namespaces#add # # @return [Namespaces] the namespaces + # rubocop:enable Layout/LineLength + # @!endgroup # A ComponentDir for configuring the default values to apply to all added # component dirs # + # @see #method_missing # @api private attr_reader :defaults + # Creates a new component dirs + # # @api private def initialize - @dirs = Concurrent::Map.new + @dirs = {} @defaults = ComponentDir.new(nil) end # @api private def initialize_copy(source) - @dirs = source.dirs.dup + @dirs = source.dirs.map { |path, dir| [path, dir.dup] }.to_h @defaults = source.defaults.dup end - # Adds and configures a component dir + # Returns and optionally yields a previously added component dir # - # @param path [String] the path for the component dir, relative to the configured - # container root + # @param path [String] the path for the component dir + # @yieldparam dir [ComponentDir] the component dir # - # @yieldparam dir [ComponentDir] the component dir to configure + # @return [ComponentDir] the component dir # - # @return [ComponentDir] the added component dir + # @api public + def dir(path) + dirs[path].tap do |dir| + # Defaults can be (re-)applied first, since the dir has already been added + apply_defaults_to_dir(dir) if dir + yield dir if block_given? + end + end + alias_method :[], :dir + + # @overload add(path) + # Adds and configures a component dir for the given path # - # @example - # component_dirs.add "lib" do |dir| - # dir.default_namespace = "my_app" - # end + # @param path [String] the path for the component dir, relative to the configured + # container root + # @yieldparam dir [ComponentDir] the component dir to configure # - # @see ComponentDir - def add(path) + # @return [ComponentDir] the added component dir + # + # @example + # component_dirs.add "lib" do |dir| + # dir.default_namespace = "my_app" + # end + # + # @see ComponentDir + # @api public + # + # @overload add(dir) + # Adds a configured component dir + # + # @param dir [ComponentDir] the configured component dir + # + # @return [ComponentDir] the added component dir + # + # @example + # dir = Dry::System::ComponentDir.new("lib") + # component_dirs.add dir + # + # @see ComponentDir + # @api public + def add(path_or_dir) + path, dir_to_add = path_and_dir(path_or_dir) + raise ComponentDirAlreadyAddedError, path if dirs.key?(path) - dirs[path] = ComponentDir.new(path).tap do |dir| - yield dir if block_given? + dirs[path] = dir_to_add.tap do |dir| + # Defaults must be applied after yielding, since the dir is being newly added, + # and must have its configuration fully in place before we can know which + # defaults to apply + yield dir if path_or_dir == path && block_given? apply_defaults_to_dir(dir) end end - # Returns the added component dirs, with default settings applied + # Deletes and returns a previously added component dir # - # @return [Hash<String, ComponentDir>] the component dirs as a hash, keyed by path - def dirs - @dirs.each { |_, dir| apply_defaults_to_dir(dir) } + # @param path [String] the path for the component dir + # + # @return [ComponentDir] the removed component dir + # + # @api public + def delete(path) + dirs.delete(path) end + # Returns the paths of the component dirs + # + # @return [Array<String>] the component dir paths + # + # @api public + def paths + dirs.keys + end + + # Returns the count of component dirs + # + # @return [Integer] + # + # @api public + def length + dirs.length + end + alias_method :size, :length + # Returns the added component dirs, with default settings applied # # @return [Array<ComponentDir>] + # + # @api public def to_a + dirs.each { |_, dir| apply_defaults_to_dir(dir) } dirs.values end # Calls the given block once for each added component dir, passing the dir as an # argument. # # @yieldparam dir [ComponentDir] the yielded component dir + # + # @api public def each(&block) to_a.each(&block) end + protected + + # Returns the hash of component dirs, keyed by their paths + # + # Recently changed default configuration may not be applied to these dirs. Use + # #to_a or #each to access dirs with default configuration fully applied. + # + # This method exists to encapsulate the instance variable and to serve the needs + # of #initialize_copy + # + # @return [Hash{String => ComponentDir}] + # + # @api private + attr_reader :dirs + private + + # Converts a path string or pre-built component dir into a path and dir tuple + # + # @param path_or_dir [String,ComponentDir] + # + # @return [Array<(String, ComponentDir)>] + # + # @see #add + def path_and_dir(path_or_dir) + if path_or_dir.is_a?(ComponentDir) + dir = path_or_dir + [dir.path, dir] + else + path = path_or_dir + [path, ComponentDir.new(path)] + end + end # Applies default settings to a component dir. This is run every time the dirs are # accessed to ensure defaults are applied regardless of when new component dirs # are added. This method must be idempotent. #