Class: Kettle::Dev::GemSpecReader
- Inherits:
-
Object
- Object
- Kettle::Dev::GemSpecReader
- Defined in:
- lib/kettle/dev/gem_spec_reader.rb
Overview
Unified gemspec reader using RubyGems loader instead of regex parsing.
Returns a Hash with all data used by this project from gemspecs.
Cache within the process to avoid repeated loads.
Constant Summary collapse
- DEFAULT_MINIMUM_RUBY =
Default minimum Ruby version to assume when a gemspec doesn’t specify one.
Gem::Version.new("1.8").freeze
Class Method Summary collapse
-
.load(root) ⇒ Hash{Symbol=>Object}
Load gemspec data for the project at root using RubyGems.
Class Method Details
.load(root) ⇒ Hash{Symbol=>Object}
Load gemspec data for the project at root using RubyGems.
The reader is lenient: failures to load or missing fields are handled with defaults and warnings.
41 42 43 44 45 46 47 48 49 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 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/kettle/dev/gem_spec_reader.rb', line 41 def load(root) gemspec_path = Dir.glob(File.join(root.to_s, "*.gemspec")).first spec = nil if gemspec_path && File.file?(gemspec_path) begin spec = Gem::Specification.load(gemspec_path) rescue StandardError => e Kettle::Dev.debug_error(e, __method__) spec = nil end end gem_name = spec&.name.to_s if gem_name.nil? || gem_name.strip.empty? # Be lenient here for tasks that can proceed without gem_name (e.g., choosing destination filenames). Kernel.warn("kettle-dev: Could not derive gem name. Ensure a valid <name> is set in the gemspec.\n - Tip: set the gem name in your .gemspec file (spec.name).\n - Path searched: #{gemspec_path || "(none found)"}") gem_name = "" end # minimum ruby version: derived from spec.required_ruby_version # Always an instance of Gem::Version min_ruby = begin # irb(main):004> Gem::Requirement.parse(spec.required_ruby_version) # => [">=", Gem::Version.new("2.3.0")] requirement = spec&.required_ruby_version if requirement tuple = Gem::Requirement.parse(requirement) tuple[1] # an instance of Gem::Version else # Default to a minimum of Ruby 1.8 puts "WARNING: Minimum Ruby not detected" DEFAULT_MINIMUM_RUBY end rescue StandardError => e puts "WARNING: Minimum Ruby detection failed:" Kettle::Dev.debug_error(e, __method__) # Default to a minimum of Ruby 1.8 DEFAULT_MINIMUM_RUBY end homepage_val = spec&.homepage.to_s # Derive org/repo from homepage or git remote forge_info = derive_forge_and_origin_repo(homepage_val) forge_org = forge_info[:forge_org] gh_repo = forge_info[:origin_repo] if forge_org.to_s.empty? Kernel.warn("kettle-dev: Could not determine forge org from spec.homepage or git remote.\n - Ensure gemspec.homepage is set to a GitHub URL or that the git remote 'origin' points to GitHub.\n - Example homepage: https://github.com/<org>/<repo>\n - Proceeding with default org: kettle-rb.") forge_org = "kettle-rb" end camel = lambda do |s| s.to_s.split(/[_-]/).map { |p| p.gsub(/\b([a-z])/) { Regexp.last_match(1).upcase } }.join end namespace = gem_name.to_s.split("-").map { |seg| camel.call(seg) }.join("::") namespace_shield = namespace.gsub("::", "%3A%3A") entrypoint_require = gem_name.to_s.tr("-", "/") gem_shield = gem_name.to_s.gsub("-", "--").gsub("_", "__") # Funding org (Open Collective handle) detection. # Precedence: # 1) ENV["FUNDING_ORG"] when set: # - value "false" (any case) disables funding (nil) # - otherwise use the value verbatim # 2) OpenCollectiveConfig.handle(required: false) # Be lenient: allow nil when not discoverable, with a concise warning. begin env_funding = ENV["FUNDING_ORG"] if env_funding && !env_funding.to_s.strip.empty? funding_org = if env_funding.to_s.strip.casecmp("false").zero? nil else env_funding.to_s end else # Preflight: if a YAML exists under the provided root, attempt to read it here so # unexpected file IO errors surface within this rescue block (see specs). oc_path = OpenCollectiveConfig.yaml_path(root) File.read(oc_path) if File.file?(oc_path) funding_org = OpenCollectiveConfig.handle(required: false, root: root) if funding_org.to_s.strip.empty? Kernel.warn("kettle-dev: Could not determine funding org.\n - Options:\n * Set ENV['FUNDING_ORG'] to your funding handle, or 'false' to disable.\n * Or set ENV['OPENCOLLECTIVE_HANDLE'].\n * Or add .opencollective.yml with: collective: <handle> (or org: <handle>).\n * Or proceed without funding if not applicable.") funding_org = nil end end rescue StandardError => error Kettle::Dev.debug_error(error, __method__) # In an unexpected exception path, escalate to a domain error to aid callers/specs raise Kettle::Dev::Error, "Unable to determine funding org: #{error.}" end { gemspec_path: gemspec_path, gem_name: gem_name, min_ruby: min_ruby, # Gem::Version instance homepage: homepage_val.to_s, gh_org: forge_org, # Might allow divergence from forge_org someday forge_org: forge_org, funding_org: funding_org, gh_repo: gh_repo, namespace: namespace, namespace_shield: namespace_shield, entrypoint_require: entrypoint_require, gem_shield: gem_shield, # Additional fields sourced from the gemspec for templating carry-over authors: Array(spec&.).compact.uniq, email: Array(spec&.email).compact.uniq, summary: spec&.summary.to_s, description: spec&.description.to_s, licenses: Array(spec&.licenses), # licenses will include any specified as license (singular) required_ruby_version: spec&.required_ruby_version, # Gem::Requirement instance require_paths: Array(spec&.require_paths), bindir: (spec&.bindir || "").to_s, executables: Array(spec&.executables), } end |