lib/licensed/configuration.rb in licensed-2.8.0 vs lib/licensed/configuration.rb in licensed-2.9.0

- old
+ new

@@ -2,44 +2,43 @@ require "pathname" module Licensed class AppConfiguration < Hash DEFAULT_CACHE_PATH = ".licenses".freeze - DEFAULT_CONFIG_FILES = [ - ".licensed.yml".freeze, - ".licensed.yaml".freeze, - ".licensed.json".freeze - ].freeze + # Returns the root for a configuration in following order of precendence: + # 1. explicitly configured "root" property + # 2. a found git repository root + # 3. the current directory + def self.root_for(configuration) + configuration["root"] || Licensed::Git.repository_root || Dir.pwd + end + def initialize(options = {}, inherited_options = {}) super() # update order: # 1. anything inherited from root config - # 2. app defaults - # 3. explicitly configured app settings + # 2. explicitly configured app settings update(inherited_options) - update(defaults_for(options, inherited_options)) update(options) + verify_arg "source_path" self["sources"] ||= {} self["reviewed"] ||= {} self["ignored"] ||= {} self["allowed"] ||= [] - - # default the root to the git repository root, - # or the current directory if no other options are available - self["root"] ||= Licensed::Git.repository_root || Dir.pwd - - verify_arg "source_path" - verify_arg "cache_path" + self["root"] = AppConfiguration.root_for(self) + # defaults to the directory name of the source path if not set + self["name"] ||= File.basename(self["source_path"]) + # setting the cache path might need a valid app name + self["cache_path"] = detect_cache_path(options, inherited_options) end # Returns the path to the workspace root as a Pathname. - # Defaults to Licensed::Git.repository_root if not explicitly set def root - Pathname.new(self["root"]) + @root ||= Pathname.new(self["root"]) end # Returns the path to the app cache directory as a Pathname def cache_path root.join(self["cache_path"]) @@ -100,29 +99,40 @@ self["allowed"] << license end private - def defaults_for(options, inherited_options) - name = options["name"] || File.basename(options["source_path"]) + # Returns the cache path for the application based on: + # 1. An explicitly set cache path for the application, if set + # 2. An inherited root cache path joined with the app name + # 3. The default cache path joined with the app name + def detect_cache_path(options, inherited_options) + return options["cache_path"] unless options["cache_path"].to_s.empty? + cache_path = inherited_options["cache_path"] || DEFAULT_CACHE_PATH - { - "name" => name, - "cache_path" => File.join(cache_path, name) - } + File.join(cache_path, self["name"]) end def verify_arg(property) return if self[property] raise Licensed::Configuration::LoadError, "App #{self["name"]} is missing required property #{property}" end end - class Configuration < AppConfiguration + class Configuration + DEFAULT_CONFIG_FILES = [ + ".licensed.yml".freeze, + ".licensed.yaml".freeze, + ".licensed.json".freeze + ].freeze + class LoadError < StandardError; end + # An array of the applications in this licensed configuration. + attr_reader :apps + # Loads and returns a Licensed::Configuration object from the given path. # The path can be relative or absolute, and can point at a file or directory. # If the path given is a directory, the directory will be searched for a # `config.yml` file. def self.load_from(path) @@ -131,25 +141,40 @@ Configuration.new(parse_config(config_path)) end def initialize(options = {}) apps = options.delete("apps") || [] - super(default_options.merge(options)) - - self["apps"] = apps.map { |app| AppConfiguration.new(app, options) } + apps << default_options.merge(options) if apps.empty? + apps = apps.flat_map { |app| self.class.expand_app_source_path(app) } + @apps = apps.map { |app| AppConfiguration.new(app, options) } end - # Returns an array of the applications for this licensed configuration. - # If the configuration did not explicitly configure any applications, - # return self as an application configuration. - def apps - return [self] if self["apps"].empty? - self["apps"] - end - private + def self.expand_app_source_path(app_config) + return app_config if app_config["source_path"].to_s.empty? + + source_path = File.expand_path(app_config["source_path"], AppConfiguration.root_for(app_config)) + expanded_source_paths = Dir.glob(source_path).select { |p| File.directory?(p) } + # return the original configuration if glob didn't result in multiple paths + return app_config if expanded_source_paths.size <= 1 + + # map the expanded paths to new application configurations + expanded_source_paths.map do |path| + config = app_config.merge("source_path" => path) + + # update configured values for name and cache_path for uniqueness. + # this is only needed when values are explicitly set, AppConfiguration + # will handle configurations that don't have these explicitly set + dir_name = File.basename(path) + config["name"] = "#{config["name"]}-#{dir_name}" if config["name"] + config["cache_path"] = File.join(config["cache_path"], dir_name) if config["cache_path"] + + config + end + end + # Find a default configuration file in the given directory. # File preference is given by the order of elements in DEFAULT_CONFIG_FILES # # Raises Licensed::Configuration::LoadError if a file isn't found def self.find_config(directory) @@ -196,10 +221,10 @@ def default_options # manually set a cache path without additional name { "source_path" => Dir.pwd, - "cache_path" => DEFAULT_CACHE_PATH + "cache_path" => AppConfiguration::DEFAULT_CACHE_PATH } end end end