class Installer
Installer class for managing context files from Ruby gems.
This class provides methods to find, list, show, and install context files
from gems that provide them in a context/
directory.
Definitions
def initialize(root: Dir.pwd, specifications: ::Gem::Specification)
Initialize a new Installer instance.
Signature
-
parameter
root
String
The root directory to work from (default: current directory).
-
parameter
specifications
Gem::Specification
The gem specifications to search (default: all installed gems).
Implementation
def initialize(root: Dir.pwd, specifications: ::Gem::Specification)
@root = root
@context_path = ".context"
@specifications = specifications
end
def find_gems_with_context(skip_local: true)
Find all gems that have a context directory
Implementation
def find_gems_with_context(skip_local: true)
gems_with_context = []
@specifications.each do |spec|
# Skip gems loaded from current working directory if requested:
next if skip_local && spec.full_gem_path == @root
context_path = File.join(spec.full_gem_path, "context")
if Dir.exist?(context_path)
gems_with_context << {
name: spec.name,
version: spec.version.to_s,
summary: spec.summary,
metadata: spec.metadata,
path: context_path
}
end
end
gems_with_context
end
def find_gem_with_context(gem_name)
Find a specific gem with context.
Implementation
def find_gem_with_context(gem_name)
spec = @specifications.find {|spec| spec.name == gem_name}
return nil unless spec
context_path = File.join(spec.full_gem_path, "context")
if Dir.exist?(context_path)
{
name: spec.name,
version: spec.version.to_s,
summary: spec.summary,
metadata: spec.metadata,
path: context_path
}
else
nil
end
end
def list_context_files(gem_name)
List context files for a gem.
Implementation
def list_context_files(gem_name)
gem = find_gem_with_context(gem_name)
return nil unless gem
Dir.glob(File.join(gem[:path], "**/*")).select {|f| File.file?(f)}
end
def show_context_file(gem_name, file_name)
Show content of a specific context file.
Implementation
def show_context_file(gem_name, file_name)
gem = find_gem_with_context(gem_name)
return nil unless gem
# Try to find the file with or without extension:
possible_paths = [
File.join(gem[:path], file_name),
File.join(gem[:path], "#{file_name}.md"),
File.join(gem[:path], "#{file_name}.md")
]
file_path = possible_paths.find {|path| File.exist?(path)}
return nil unless file_path
File.read(file_path)
end
def install_gem_context(gem_name)
Install context from a specific gem.
Implementation
def install_gem_context(gem_name)
gem = find_gem_with_context(gem_name)
return false unless gem
target_path = File.join(@context_path, gem_name)
# Remove old package directory if it exists to ensure clean install
FileUtils.rm_rf(target_path) if Dir.exist?(target_path)
FileUtils.mkdir_p(target_path)
# Copy all files from the gem's context directory:
FileUtils.cp_r(File.join(gem[:path], "."), target_path)
# Generate index.yaml if it doesn't exist, passing the full gem hash
ensure_gem_index(gem, target_path)
true
end
def install_all_context(skip_local: true)
Install context from all gems.
Implementation
def install_all_context(skip_local: true)
gems = find_gems_with_context(skip_local: skip_local)
installed = []
gems.each do |gem|
if install_gem_context(gem[:name])
installed << gem[:name]
end
end
installed
end
def generate_dynamic_index(gem, gem_directory)
Generate a dynamic index from gemspec when no index.yaml is present
Implementation
def generate_dynamic_index(gem, gem_directory)
# Collect all markdown files
markdown_files = Dir.glob(File.join(gem_directory, "**", "*.md")).sort
# Sort files: canonical first, then alpha
files_sorted = markdown_files.sort_by do |file_path|
base_filename = File.basename(file_path, ".md").downcase
canonical_index = CANONICAL_ORDER.index(base_filename)
[canonical_index ? CANONICAL_ORDER.index(base_filename) : CANONICAL_ORDER.length, base_filename]
end
files = []
files_sorted.each do |file_path|
next if File.basename(file_path) == "index.yaml" # Skip the index file itself
title, description = extract_content(file_path)
relative_path = file_path.sub("#{gem_directory}/", "")
files << {
"path" => relative_path,
"title" => title,
"description" => description
}
end
{
"description" => gem[:summary] || "Context files for #{gem[:name]}",
"version" => gem[:version],
"metadata" => gem[:metadata],
"files" => files
}
end
def ensure_gem_index(gem, gem_directory)
Check if a gem has an index.yaml file, generate one if not
Implementation
def ensure_gem_index(gem, gem_directory)
index_path = File.join(gem_directory, "index.yaml")
unless File.exist?(index_path)
# Generate dynamic index from gemspec
index = generate_dynamic_index(gem, gem_directory)
# Write the generated index
File.write(index_path, index.to_yaml)
Console.debug("Generated dynamic index for #{gem[:name]}: #{index_path}")
end
# Load and return the index
YAML.load_file(index_path)
rescue => error
Console.debug("Error generating index for #{gem[:name]}: #{error.message}")
# Return a fallback index
{
"description" => gem[:summary] || "Context files for #{gem[:name]}",
"version" => gem[:version],
"metadata" => gem[:metadata],
"files" => []
}
end