Module: FlossFunding::Config

Defined in:
lib/floss_funding/config.rb

Overview

Handles configuration loading from a .floss_funding.yml file located at the
root of the including project (heuristically discovered by walking upward
from the including file path until a Gemfile or *.gemspec is found).

All APIs in this module require the including file path (e.g., __FILE__).

The loaded config is merged over DEFAULT_CONFIG, so any unspecified keys fall
back to defaults.

Constant Summary collapse

CONFIG_FILE_NAME =

The file name to look for in the project root.

Returns:

  • (String)
".floss_funding.yml"
DEFAULT_CONFIG =

Default configuration values for FlossFunding prompting.
Also includes slots for gemspec-derived attributes we track per gem.

Returns:

  • (Hash{String=>Object})
{
  "suggested_donation_amount" => [5],
  "floss_funding_url" => ["https://floss-funding.dev"],
  # Tracks the including modules' names,
  #   for every module including FlossFunding::Poke.new.
  # These inform the ENV variable name,
  #   except when overridden by custom_namespaces.
  "namespace" => [],
  # Tracks the custom namespace passed into FlossFunding::Poke.new as the :namespace option.
  "custom_namespaces" => [],
  # Whether to silence all output from the FlossFunding library when included.
  # Accepts truthy values or callable objects (Proc/lambda) that return truthy.
  "silent" => [],
  # Gemspec-derived attributes (nil when unknown)
  "gem_name" => [],
  "homepage" => [],
  "authors" => [],
  "funding_uri" => [],
}.freeze

Class Method Summary collapse

Class Method Details

.load_config(including_path) ⇒ Hash{String=>Object}

Loads configuration from .floss_funding.yml by walking up from the
provided including file path to discover the project root.

Parameters:

  • including_path (String)

    the including file path (e.g., FILE)

Returns:

  • (Hash{String=>Object})

    configuration hash with defaults merged

Raises:



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/floss_funding/config.rb', line 50

def load_config(including_path)
  unless including_path.is_a?(String)
    raise ::FlossFunding::Error, "including must be a String file path (e.g., __FILE__), got #{including_path.class}"
  end

  # Discover project root (Gemfile or *.gemspec)
  project_root = find_project_root(including_path)

  # Load YAML config if present (respect test stubs of find_config_file)
  config_file = find_config_file(including_path)
  raw_config = config_file ? load_yaml_file(config_file) : {}

  # Strict filter: only allow known string keys, then normalize to arrays
  filtered = {}
  raw_config.each do |k, v|
    next unless k.is_a?(String)
    next unless DEFAULT_CONFIG.key?(k)
    filtered[k] = normalize_to_array(v)
  end

  # Load gemspec data for defaults if available
  gemspec_data = project_root ? read_gemspec_data(project_root) : {}
  # Prepare defaults from gemspec:
  # - Store all gemspec attributes into config slots, as arrays
  # - If floss_funding_url not set in YAML, default to gemspec funding_uri
  gemspec_defaults = {}
  unless gemspec_data.empty?
    gemspec_defaults["gem_name"] = normalize_to_array(gemspec_data[:name]) # name is required by rubygems
    gemspec_defaults["homepage"] = normalize_to_array(gemspec_data[:homepage]) if gemspec_data[:homepage]
    gemspec_defaults["authors"] = normalize_to_array(gemspec_data[:authors]) # authors defaults to []
    gemspec_defaults["funding_uri"] = normalize_to_array(gemspec_data[:funding_uri]) if gemspec_data[:funding_uri]
    if gemspec_data[:funding_uri] && !filtered.key?("floss_funding_url")
      gemspec_defaults["floss_funding_url"] = normalize_to_array(gemspec_data[:funding_uri])
    end
  end

  # Merge precedence: DEFAULT < gemspec_defaults, with filtered_yaml overriding entirely when present
  merged = {}
  DEFAULT_CONFIG.keys.each do |key|
    if filtered.key?(key)
      # YAML-provided known string keys take full precedence (override defaults and gemspec values)
      merged[key] = Array(filtered[key]).compact.flatten.uniq
    else
      # Otherwise, start from defaults and enrich with gemspec-derived values when available
      merged[key] = []
      merged[key].concat(Array(DEFAULT_CONFIG[key]))
      merged[key].concat(Array(gemspec_defaults[key])) if gemspec_defaults.key?(key)
      merged[key] = merged[key].compact.flatten.uniq
    end
  end
  merged
end

.silence_requested?Boolean

Determines whether any registered configuration requests silence.
Uses ::FlossFunding.configurations internally.
For each library’s config, examines the “silent” key values. If any value
responds to :call, it will be invoked (with no args) and the truthiness of
its return value is used. Otherwise, the value’s own truthiness is used.
Returns true if any library requires silence; false otherwise.

Returns:

  • (Boolean)


111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/floss_funding/config.rb', line 111

def silence_requested?
  configurations = ::FlossFunding.configurations
  configurations.any? do |_library, cfg|
    values = Array(cfg["silent"]) # may be nil/array/scalar
    values.any? do |v|
      begin
        v.respond_to?(:call) ? !!v.call : !!v
      rescue StandardError
        # If callable raises, treat as not silencing
        false
      end
    end
  end
end