Module: Cinnabar::GemPathCore

Defined in:
lib/cinnabar/gem_path.rb

Overview

Note:

These functions may perform disk I/O and can be slow.

Low-level helpers for resolving RubyGems load paths.

This module provides the "expensive" operations: loading RubyGems, locating gem specs, and optionally installing missing gems.

Design goals

  • Lazy-load RubyGems: do not require 'rubygems' at file load time.
  • Return full require paths: use RubyGems’ resolved paths rather than hand-joining.
  • Be usable by higher-level caches: see GemPath.

Class Method Summary collapse

Class Method Details

.ensure_rubygems!void

Note:

If Ruby was not started with --disable=gems option, this function is unnecessary.

Note:

This function must be called only at the point where RubyGems is actually needed (lazy loading).

Note:

Do not call require 'rubygems' outside this method.

This method returns an undefined value.

Ensures RubyGems is loaded.

Behavior

  • If RubyGems is already available (::Gem and Gem::Specification), this is a no-op.
  • Otherwise, it calls require 'rubygems'.


65
66
67
68
69
# File 'lib/cinnabar/gem_path.rb', line 65

def ensure_rubygems!
  return if defined?(::Gem) && ::Gem.respond_to?(:Specification)

  Kernel.require 'rubygems'
end

.find_lib_paths(gem_name) ⇒ Array<String>

Returns full load paths for a gem's require paths.

This uses Gem::Specification#full_require_paths, which returns absolute directories that should be added to $LOAD_PATH to require files from the gem.

Examples:

Get full require paths

Cinnabar::GemPathCore.find_lib_paths("logger")

Parameters:

  • gem_name (String)

    gem name (e.g. "logger" or :logger)

Returns:

  • (Array<String>)

    absolute require directories (e.g. ["/path/to/gems/foo/lib"])

Raises:

  • (Gem::LoadError)

    if the gem is not installed / cannot be found



82
83
84
85
86
# File 'lib/cinnabar/gem_path.rb', line 82

def find_lib_paths(gem_name)
  ensure_rubygems!

  Gem::Specification.find_by_name(gem_name).full_require_paths
end

.find_or_install_lib_paths(gem_name) ⇒ Array<String>

Returns the gem's load paths; installs the gem when missing.

This method is intended for low-level bootstrap scenarios:

  • Try to resolve require paths via RubyGems.
  • If it fails, warn to stderr and attempt Gem.install(gem).
  • Re-resolve paths after successful installation.

Notes

  • This method intentionally prints warnings via Kernel.warn.
  • Avoid using "advanced" loggers here, because this method may run before other dependencies are available.

Examples:

Resolve paths, installing automatically if missing


logger_lib_paths = Cinnabar::GemPathCore.find_or_install_lib_paths("logger")

Parameters:

  • gem_name (String, Symbol)

    gem name

Returns:

  • (Array<String>)

    absolute require directories for the gem

Raises:

  • (RuntimeError)

    if installation returns nil or an empty spec list

  • (Exception)

    any error raised by RubyGems or filesystem operations



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/cinnabar/gem_path.rb', line 111

def find_or_install_lib_paths(gem_name)
  ensure_rubygems!

  gem = gem_name.to_s
  begin
    find_lib_paths(gem)

  # NOTE: Intentionally rescuing `Exception` here (see original intent).
  rescue Exception => e # rubocop:disable Lint/RescueException
    Kernel.warn "[WARN] #{e}; Try installing #{gem}"

    # Attempt to install; raise if installation fails.
    specs = Gem.install(gem)
    Kernel.raise "Failed to install #{gem}" if specs.nil? || specs.empty?

    find_lib_paths(gem)
  end
end

.init_gem_dir_hash(gems, install_gem: true) ⇒ Hash{(String)=>Array<String>}

Builds a mapping from gem name to resolved load paths.

Examples:

Build a mapping without installing

Cinnabar::GemPathCore.init_gem_dir_hash(%w[rdoc logger irb reline fiddle], install_gem: false)

Parameters:

  • gems (Array<String>)

    gem names

  • install_gem (Boolean) (defaults to: true)

    whether to attempt installing missing gems

Returns:

  • (Hash{(String)=>Array<String>})

    mapping: gem_name => full require paths



138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/cinnabar/gem_path.rb', line 138

def init_gem_dir_hash(gems, install_gem: true)
  gems.map { |name|
    paths =
      if install_gem
        find_or_install_lib_paths(name)
      else
        find_lib_paths(name)
      end

    [name, paths]
  }.to_h
end