class Licensed::AppConfiguration

Constants

ALL_NAME_GENERATOR_KEYS
ANY_VERSION_REQUIREMENT
DEFAULT_CACHE_PATH
DEFAULT_RELATIVE_PATH_NAME_SEPARATOR
DIRECTORY_NAME_GENERATOR_KEY
RELATIVE_PATH_GENERATOR_KEY

Public Class Methods

new(options = {}, inherited_options = {}) click to toggle source
Calls superclass method
# File lib/licensed/configuration.rb, line 23
def initialize(options = {}, inherited_options = {})
  super()

  # update order:
  # 1. anything inherited from root config
  # 2. explicitly configured app settings
  update(inherited_options)
  update(options)
  verify_arg "source_path"

  self["sources"] ||= {}
  self["reviewed"] ||= {}
  self["ignored"] ||= {}
  self["allowed"] ||= []
  self["root"] = AppConfiguration.root_for(self)
  self["name"] = generate_app_name
  # setting the cache path might need a valid app name.
  # this must come after setting self["name"]
  self["cache_path"] = detect_cache_path(options, inherited_options)
end
root_for(configuration) click to toggle source

Returns the root for a configuration in following order of precedence:

  1. explicitly configured “root” property

  2. a found git repository root

  3. the current directory

# File lib/licensed/configuration.rb, line 19
def self.root_for(configuration)
  configuration["root"] || Licensed::Git.repository_root || Dir.pwd
end

Public Instance Methods

additional_terms_for_dependency(dependency) click to toggle source

Returns an array of paths to files containing additional license terms.

# File lib/licensed/configuration.rb, line 117
def additional_terms_for_dependency(dependency)
  amendment_paths = Array(self.dig("additional_terms", dependency["type"], dependency["name"]))
  amendment_paths.flat_map { |path| Dir.glob(self.root.join(path)) }.sort
end
allow(license) click to toggle source

Set a license as explicitly allowed

# File lib/licensed/configuration.rb, line 112
def allow(license)
  self["allowed"] << license
end
allowed?(license) click to toggle source

Is the license of the dependency allowed?

# File lib/licensed/configuration.rb, line 93
def allowed?(license)
  Array(self["allowed"]).include?(license)
end
cache_path() click to toggle source

Returns the path to the app cache directory as a Pathname

# File lib/licensed/configuration.rb, line 50
def cache_path
  root.join(self["cache_path"])
end
enabled?(source_type) click to toggle source

Returns whether a source type is enabled

# File lib/licensed/configuration.rb, line 71
def enabled?(source_type)
  # the default is false if any sources are set to true, true otherwise
  default = !self["sources"].any? { |_, enabled| enabled }
  self["sources"].fetch(source_type, default)
end
ignore(dependency, at_version: false) click to toggle source

Ignore a dependency

# File lib/licensed/configuration.rb, line 98
def ignore(dependency, at_version: false)
  id = dependency["name"]
  id += "@#{dependency["version"]}" if at_version && dependency["version"]
  (self["ignored"][dependency["type"]] ||= []) << id
end
ignored?(dependency, require_version: false) click to toggle source

Is the given dependency ignored?

# File lib/licensed/configuration.rb, line 88
def ignored?(dependency, require_version: false)
  any_list_pattern_matched? self["ignored"][dependency["type"]], dependency, require_version: require_version
end
pwd() click to toggle source
# File lib/licensed/configuration.rb, line 59
def pwd
  Pathname.pwd
end
review(dependency, at_version: false) click to toggle source

Set a dependency as reviewed

# File lib/licensed/configuration.rb, line 105
def review(dependency, at_version: false)
  id = dependency["name"]
  id += "@#{dependency["version"]}" if at_version && dependency["version"]
  (self["reviewed"][dependency["type"]] ||= []) << id
end
reviewed?(dependency, require_version: false) click to toggle source

Is the given dependency reviewed?

# File lib/licensed/configuration.rb, line 78
def reviewed?(dependency, require_version: false)
  any_list_pattern_matched? self["reviewed"][dependency["type"]], dependency, require_version: require_version
end
reviewed_versions(dependency) click to toggle source

Find all reviewed dependencies that match the provided dependency’s name

# File lib/licensed/configuration.rb, line 83
def reviewed_versions(dependency)
  similar_list_patterns self["reviewed"][dependency["type"]], dependency
end
root() click to toggle source

Returns the path to the workspace root as a Pathname.

# File lib/licensed/configuration.rb, line 45
def root
  @root ||= Pathname.new(self["root"])
end
source_path() click to toggle source

Returns the path to the app source directory as a Pathname

# File lib/licensed/configuration.rb, line 55
def source_path
  root.join(self["source_path"])
end
sources() click to toggle source

Returns an array of enabled app sources

# File lib/licensed/configuration.rb, line 64
def sources
  @sources ||= Licensed::Sources::Source.sources
                                        .select { |source_class| enabled?(source_class.type) }
                                        .map { |source_class| source_class.new(self) }
end

Private Instance Methods

any_list_pattern_matched?(list, dependency, require_version: false) click to toggle source
# File lib/licensed/configuration.rb, line 124
def any_list_pattern_matched?(list, dependency, require_version: false)
  Array(list).any? do |pattern|
    # parse a name and version requirement value from the pattern
    name, requirement = pattern.rpartition("@").values_at(0, 2).map(&:strip)

    if name == ""
      # if name == "", then the pattern doesn't contain a valid version value.
      # treat the entire pattern as the dependency name with no version.
      name = pattern
      requirement = nil
    elsif !requirement.to_s.empty?
      # check if the version requirement is a valid Gem::Requirement
      # for range matching
      requirements = requirement.split(",").map(&:strip)
      if requirements.all? { |r| Gem::Requirement::PATTERN.match?(r) }
        requirement = Gem::Requirement.new(requirements)
      end
    end

    # the pattern's name must match the dependency's name
    next false unless File.fnmatch?(name, dependency["name"], File::FNM_PATHNAME | File::FNM_CASEFOLD)

    # if there is no version requirement configured or if the dependency doesn't have a version
    # specified, return a value based on whether a version match is required
    next !require_version if requirement.nil? || dependency["version"].to_s.empty?

    case requirement
    when String
      # string match the requirement against "*" or the dependency's version
      [ANY_VERSION_REQUIREMENT, dependency["version"]].any? { |r| requirement == r }
    when Gem::Requirement
      # if the version was parsed as a gem requirement, check whether the version requirement
      # matches the dependency's version
      Gem::Version.correct?(dependency["version"]) && requirement.satisfied_by?(Gem::Version.new(dependency["version"]))
    end
  end
end
detect_cache_path(options, inherited_options) click to toggle source

Returns the cache path for the application based on:

  1. An explicitly set cache path for the application, if set

  2. An inherited shared cache path

  3. An inherited cache path joined with the app name if not shared

  4. The default cache path joined with the app name

# File lib/licensed/configuration.rb, line 176
def detect_cache_path(options, inherited_options)
  return options["cache_path"] unless options["cache_path"].to_s.empty?

  # if cache_path and shared_cache are both set in inherited_options,
  # don't append the app name to the cache path
  cache_path = inherited_options["cache_path"]
  return cache_path if cache_path && inherited_options["shared_cache"] == true

  cache_path ||= DEFAULT_CACHE_PATH
  File.join(cache_path, self["name"])
end
generate_app_name() click to toggle source

Returns a name for the application as one of:

  1. An explicitly configured app name, if set

  2. A generated app name based on an configured “name” options hash

  3. A default value - the source_path directory name

# File lib/licensed/configuration.rb, line 198
def generate_app_name
  # use default_app_name if a name value is not set
  return source_path_directory_app_name if self["name"].to_s.empty?
  # keep the same name value unless a hash is given with naming options
  return self["name"] unless self["name"].is_a?(Hash)

  generator = self.dig("name", "generator")
  case generator
  when nil, DIRECTORY_NAME_GENERATOR_KEY
    source_path_directory_app_name
  when RELATIVE_PATH_GENERATOR_KEY
    relative_path_app_name
  else
    raise Licensed::Configuration::LoadError,
      "Invalid value configured for name.generator: #{generator}.  Value must be one of #{ALL_NAME_GENERATOR_KEYS.join(",")}"
  end
end
relative_path_app_name() click to toggle source

Returns an app name from the relative path from the configured app root to the configured app source path.

# File lib/licensed/configuration.rb, line 223
def relative_path_app_name
  source_path_parts = File.expand_path(self["source_path"]).split("/")
  root_path_parts = File.expand_path(self["root"]).split("/")

  # if the source path is equivalent to the root path,
  # return the root directory name
  return root_path_parts[-1] if source_path_parts == root_path_parts

  if source_path_parts[0..root_path_parts.size-1] != root_path_parts
    raise Licensed::Configuration::LoadError,
      "source_path must be a descendent of the app root to generate an app name from the relative source_path"
  end

  name_parts = source_path_parts[root_path_parts.size..-1]

  separator = self.dig("name", "separator") || DEFAULT_RELATIVE_PATH_NAME_SEPARATOR
  depth = self.dig("name", "depth") || 0
  if depth < 0
    raise Licensed::Configuration::LoadError, "name.depth configuration value cannot be less than -1"
  end

  # offset the depth value by -1 to work as an offset from the end of the array
  # 0 becomes -1, with a start index of (-1 - -1) = 0, or the full array
  # 1 becomes 0, with a start index of (-1 - 0) = -1, or only the last element
  # and so on...
  depth = depth - 1
  start_index = depth >= name_parts.length ? 0 : -1 - depth
  name_parts[start_index..-1].join(separator)
end
similar_list_patterns(list, dependency) click to toggle source
# File lib/licensed/configuration.rb, line 162
def similar_list_patterns(list, dependency)
  Array(list).select do |pattern|
    pattern, version = pattern.rpartition("@").values_at(0, 2)
    next if pattern == "" || version == ""

    File.fnmatch?(pattern, dependency["name"], File::FNM_PATHNAME | File::FNM_CASEFOLD)
  end
end
source_path_directory_app_name() click to toggle source

Returns an app name from the directory name of the configured source path

# File lib/licensed/configuration.rb, line 217
def source_path_directory_app_name
  File.basename(self["source_path"])
end
verify_arg(property) click to toggle source
# File lib/licensed/configuration.rb, line 188
def verify_arg(property)
  return if self[property]
  raise Licensed::Configuration::LoadError,
    "App #{self["name"]} is missing required property #{property}"
end