Class: Cinnabar::GemPath
- Inherits:
-
Object
- Object
- Cinnabar::GemPath
- Defined in:
- lib/cinnabar/gem_path.rb
Overview
Prefer using the cache whenever possible; RubyGems operations can be slow.
Cached resolver for gem load paths.
GemPathCore performs expensive operations (loading RubyGems, resolving specs, installing missing gems). This class adds a JSON cache layer to reuse results and avoid repeated slow calls.
What it caches
- A Hash mapping
gem_name => [full_require_paths...] - Serialized to JSON on disk (see #cache_file)
Typical usage
- Create an instance with a set of gems.
- Call #append_load_path! to add those directories to
$LOAD_PATH.
Constant Summary collapse
- CoreMod =
Alias to the low-level implementation module.
Cinnabar::GemPathCore
- DEFAULT_OPTS =
Default options for initialization.
{ cache_dir: File.('~/.cache/ruby'), cache_file: 'gem_path.json', gems: [], install_gem: true, }.freeze
Instance Attribute Summary collapse
-
#cache_file ⇒ Pathname
Cache file path (
*.json). -
#cache_hash ⇒ Hash{String=>Array<String>}
readonly
The in-memory cache hash.
-
#gems ⇒ Array<String>
Normalized gem names (unique, non-empty).
-
#install_gem ⇒ Boolean
Whether missing gems should be installed automatically.
Instance Method Summary collapse
-
#append_load_path! ⇒ void
Appends cached gem paths into
$LOAD_PATH. -
#atomic_write_cache_file(content) ⇒ void
protected
private
Writes cache content to disk atomically.
-
#decode_cache_file ⇒ void
protected
private
Decodes the cache file and refreshes any missing/broken entries.
-
#init_cache_file(dir, file) ⇒ Pathname
protected
private
Resolves the cache file path.
-
#initialize(opts = {}) ⇒ Cinnabar::GemPath
constructor
Creates a cache-backed gem path resolver.
-
#try_decode_cache_hash ⇒ Hash
protected
private
Attempts to decode the cache JSON file into a Hash.
-
#update_cache_file ⇒ void
Writes the in-memory cache to disk.
Constructor Details
#initialize(opts = {}) ⇒ Cinnabar::GemPath
Creates a cache-backed gem path resolver.
Behavior
- Normalizes
gemsto unique, non-empty strings. - If cache file exists, it is decoded and then refreshed:
- Missing gems are generated and merged into the cache.
- Broken entries (paths not existing) are regenerated.
- If no cache exists, it generates paths for all gems and writes the cache.
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/cinnabar/gem_path.rb', line 233 def initialize(opts = {}) = DEFAULT_OPTS.merge(opts || {}) gems, cache_dir, cache_file, @install_gem = .values_at(:gems, :cache_dir, :cache_file, :install_gem) @gems = Array(gems).map(&:to_s).reject(&:empty?).uniq raise 'Empty @gems!' if @gems.empty? @cache_file = init_cache_file(cache_dir, cache_file) @cache_hash = {} if @cache_file.exist? decode_cache_file return end merge_generated_paths!(@gems) end |
Instance Attribute Details
#cache_file ⇒ Pathname
Returns cache file path (*.json).
187 188 189 |
# File 'lib/cinnabar/gem_path.rb', line 187 def cache_file @cache_file end |
#cache_hash ⇒ Hash{String=>Array<String>} (readonly)
The in-memory cache hash.
192 193 194 |
# File 'lib/cinnabar/gem_path.rb', line 192 def cache_hash @cache_hash end |
#gems ⇒ Array<String>
Returns normalized gem names (unique, non-empty).
187 |
# File 'lib/cinnabar/gem_path.rb', line 187 attr_accessor :cache_file, :gems, :install_gem |
#install_gem ⇒ Boolean
Returns whether missing gems should be installed automatically.
187 |
# File 'lib/cinnabar/gem_path.rb', line 187 attr_accessor :cache_file, :gems, :install_gem |
Instance Method Details
#append_load_path! ⇒ void
This method returns an undefined value.
Appends cached gem paths into $LOAD_PATH.
This is typically called after initialization. It will add each directory in the cache for the configured gems to Ruby's load path.
Notes
- This is idempotent: it avoids inserting duplicates.
- It mutates the global
$LOAD_PATH($:).
277 278 279 280 281 282 283 284 285 286 |
# File 'lib/cinnabar/gem_path.rb', line 277 def append_load_path! @cache_hash .filter { |k, _| @gems.include? k.to_s } .each_value do |vals| Array(vals).each do |dir| # $: is $LOAD_PATH $:.push(dir) unless $:.include?(dir) # rubocop:disable Style/SpecialGlobalVars end end end |
#atomic_write_cache_file(content) ⇒ void (protected)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Writes cache content to disk atomically.
It writes to a temporary file and renames it to the final cache path. This reduces the chance of leaving a partially-written JSON file when the process crashes or is interrupted mid-write.
300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/cinnabar/gem_path.rb', line 300 def atomic_write_cache_file(content) path = @cache_file.to_s tmp = "#{path}.tmp.#{$$}" # rubocop:disable Style/SpecialGlobalVars begin File.write(tmp, content) File.rename(tmp, path) ensure begin File.unlink(tmp) rescue StandardError nil end end end |
#decode_cache_file ⇒ void (protected)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Decodes the cache file and refreshes any missing/broken entries.
- Loads #cache_hash from disk.
- Generates entries for missing gems in #gems.
- Regenerates entries whose paths no longer exist on disk.
365 366 367 368 369 |
# File 'lib/cinnabar/gem_path.rb', line 365 def decode_cache_file @cache_hash = try_decode_cache_hash refresh_missing_gems! refresh_broken_gems! end |
#init_cache_file(dir, file) ⇒ Pathname (protected)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Resolves the cache file path.
- If
fileis absolute, it is used directly. - Otherwise, it is resolved relative to
dir. - Ensures the directory exists (
mkpath).
326 327 328 329 330 331 332 333 334 335 |
# File 'lib/cinnabar/gem_path.rb', line 326 def init_cache_file(dir, file) f = Pathname(file) if f.absolute? f else Pathname(dir).join(f) end .tap { _1.dirname.mkpath } end |
#try_decode_cache_hash ⇒ Hash (protected)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method intentionally rescues Exception per the original design.
Attempts to decode the cache JSON file into a Hash.
If decoding fails for any reason, it warns and removes the cache file, then returns an empty hash.
346 347 348 349 350 351 352 353 354 |
# File 'lib/cinnabar/gem_path.rb', line 346 def try_decode_cache_hash @cache_file .read .then { JSON.parse _1 } rescue Exception => e # rubocop:disable Lint/RescueException Kernel.warn "[WARN] Failed to decode json file; error: #{e}; unlink #{@cache_file}" @cache_file.unlink {} end |
#update_cache_file ⇒ void
This method returns an undefined value.
Writes the in-memory cache to disk.
This serializes #cache_hash as JSON and persists it using an atomic write strategy (see #atomic_write_cache_file).
260 261 262 263 264 |
# File 'lib/cinnabar/gem_path.rb', line 260 def update_cache_file @cache_hash .then { JSON.dump _1 } .then { atomic_write_cache_file _1 } end |