Examples and Recipes¶
Practical examples for common tasks with cov-loupe. Examples are organized by skill level and use case.
For brevity, these examples use
clp, an alias to the demo fixture with partial coverage:
alias clp='cov-loupe -R docs/fixtures/demo_project' # -R = --rootSwap
clpforcov-loupeto run against your own project and resultset.
Table of Contents¶
- Quick Start Examples
- CLI Examples
- Ruby Library Examples
- AI Assistant Prompts
- CI/CD Integration
- Advanced Usage
Quick Start Examples¶
View All Coverage¶
# Default: show all files, best coverage first
clp
# Show files with worst coverage first
clp -o a list # -o = --sort-order, a = ascending
# Export to JSON for processing
clp -fJ list > coverage-report.json
Check Specific File¶
# Quick summary
clp summary app/models/order.rb
# See which lines aren't covered
clp uncovered app/controllers/orders_controller.rb
# View uncovered code with context
clp -s u -c 3 uncovered app/controllers/orders_controller.rb # -s = --source (u = uncovered), -c = --context-lines
Find Coverage Gaps¶
# Files with worst coverage
clp list | tail -10
# Only show files below 80%
clp -fJ list | jq '.files[] | select(.percentage < 95)'
# Ruby alternative:
clp -fJ list | ruby -r json -e '
JSON.parse($stdin.read)["files"].select { |f| f["percentage"] < 95 }.each do |f|
puts JSON.pretty_generate(f)
end
'
# Rexe alternative:
clp -fJ list | rexe -ij -mb -oJ 'self["files"].select { |f| f["percentage"] < 95 }'
# Check specific directory
clp -g "lib/payments/**/*.rb" list # -g = --tracked-globs
CLI Examples¶
Coverage Analysis¶
Detailed investigation:
# See detailed hit counts
clp detailed lib/api/client.rb
# Show full source with coverage markers
clp -s f summary lib/api/client.rb # f = full
# Focus on uncovered areas only
clp -s u -c 5 uncovered lib/payments/refund_service.rb # u = uncovered
Working with JSON Output¶
In addition to the benefit of JSON encoding being human readable, it can be used in single line commands to fetch and compute values using jq, Ruby's JSON library, or rexe. Here are some examples:
Parse and filter:
# Files below threshold
clp -fJ list | jq '.files[] | select(.percentage < 95) | {file, coverage: .percentage}'
# Ruby alternative:
clp -fJ list | ruby -r json -e '
JSON.parse($stdin.read)["files"].select { |f| f["percentage"] < 95 }.each do |f|
puts JSON.pretty_generate({file: f["file"], coverage: f["percentage"]})
end
'
# Rexe alternative:
clp -fJ list | rexe -ij -mb -oJ '
self["files"].select { |f| f["percentage"] < 95 }.map do |f|
{file: f["file"], coverage: f["percentage"]}
end
'
# Count total uncovered lines
clp -fJ totals | jq '.lines.uncovered'
# Ruby alternative:
clp -fJ totals | ruby -r json -e '
puts JSON.parse($stdin.read)["lines"]["uncovered"]
'
# Rexe alternative:
clp -fJ totals | rexe -ij -mb -op 'self["lines"]["uncovered"]'
# Group by directory (full path)
clp -fJ list |
jq '.files
| map(. + {dir: (.file | split("/") | .[0:-1] | join("/"))})
| sort_by(.dir)
| group_by(.dir)
| map({dir: .[0].dir, avg: (map(.percentage) | add / length)})'
# Ruby alternative:
clp -fJ list | ruby -r json -e '
grouped = JSON.parse($stdin.read)["files"]
.map { |f| f.merge("dir" => File.dirname(f["file"])) }
.group_by { |f| f["dir"] }
.map { |dir, files|
avg = files.sum { |f| f["percentage"] } / files.size
{dir: dir, avg: avg}
}
puts JSON.pretty_generate(grouped)
'
# Rexe alternative:
clp -fJ list | rexe -ij -mb -oJ '
self["files"]
.map { |f| f.merge("dir" => File.dirname(f["file"])) }
.group_by { |f| f["dir"] }
.map { |dir, files|
avg = files.sum { |f| f["percentage"] } / files.size
{dir: dir, avg: avg}
}
'
Generate reports:
# Create markdown table
echo "| Coverage | File |" > report.md
echo "|----------|------|" >> report.md
clp -fJ list | jq -r '.files[] | "| \(.percentage)% | \(.file) |"' >> report.md
# Ruby alternative:
clp -fJ list | ruby -r json -e '
JSON.parse($stdin.read)["files"].each do |f|
puts "| #{f["percentage"]}% | #{f["file"]} |"
end
' >> report.md
# Rexe alternative:
clp -fJ list | rexe -ij -mb '
self["files"].each { |f| puts "| #{f["percentage"]}% | #{f["file"]} |" }
' >> report.md
# Export for spreadsheet
clp -fJ list | jq -r '.files[] | [.file, .percentage] | @csv' > coverage.csv
# Ruby alternative:
clp -fJ list | ruby -r json -r csv -e '
JSON.parse($stdin.read)["files"].each do |f|
puts CSV.generate_line([f["file"], f["percentage"]]).chomp
end
' > coverage.csv
# Rexe alternative:
clp -fJ list | rexe -r csv -ij -mb '
self["files"].each { |f| puts CSV.generate_line([f["file"], f["percentage"]]).chomp }
' > coverage.csv
Ruby Library Examples¶
Basic Usage¶
require "cov_loupe"
root = "docs/fixtures/demo_project"
model = CovLoupe::CoverageModel.new(root: root)
# Project totals
totals = model.project_totals
puts "Total files: #{totals['files']['total']}"
puts "Average coverage: #{totals['percentage']}%"
# Check specific file
summary = model.summary_for("app/models/order.rb")
puts "Coverage: #{summary['summary']['percentage']}%"
# Find uncovered lines
uncovered = model.uncovered_for("lib/payments/refund_service.rb")
puts "Uncovered lines: #{uncovered['uncovered'].join(', ')}"
Filtering and Analysis¶
require "cov_loupe"
root = "docs/fixtures/demo_project"
model = CovLoupe::CoverageModel.new(root: root)
list = model.list['files']
# Find files below threshold
THRESHOLD = 80.0
low_coverage = list.select { |f| f['percentage'] < THRESHOLD }
if low_coverage.any?
puts "Files below #{THRESHOLD}%:"
low_coverage.each do |file|
puts " #{file['file']}: #{file['percentage']}%"
end
end
# Group by directory using totals command logic
dirs = %w[app lib lib/payments lib/ops/jobs].uniq
dirs.each do |dir|
pattern = File.join(dir, '**/*.rb')
totals = model.project_totals(tracked_globs: pattern)
puts "#{dir}: #{totals['percentage'].round(2)}% (#{totals['files']['total']} files)"
end
Custom Formatting¶
require "cov_loupe"
require "pathname"
root = "docs/fixtures/demo_project"
model = CovLoupe::CoverageModel.new(root: root)
list = model.list['files']
# Filter to lib/payments (coverage data stores absolute paths)
lib_root = File.expand_path("lib/payments", File.expand_path(root, Dir.pwd))
lib_files = list.select { |f| f['file'].start_with?(lib_root) }
# Generate custom table
table = model.format_table(lib_files, sort_order: :ascending)
puts table
# Or create your own format
lib_files.each do |file|
status = file['percentage'] >= 90 ? '✓' : '⚠'
relative_path = Pathname.new(file['file']).relative_path_from(Pathname.pwd)
puts "#{status} #{relative_path}: #{file['percentage']}%"
end
AI Assistant Prompts¶
Coverage Analysis¶
"Using cov-loupe, show me a table of all files sorted by coverage percentage."
"Using cov-loupe, find the 10 files with the lowest coverage and create a markdown report with: 1. File path 2. Current coverage % 3. Number of uncovered lines"
"Using cov-loupe, analyze the lib/payments/ directory and identify which files should be prioritized for additional testing based on coverage gaps and file complexity."
Finding Specific Issues¶
"Using cov-loupe, show me the uncovered lines in app/controllers/orders_controller.rb with 5 lines of context around each uncovered block."
"Using cov-loupe, find all files in lib/payments/ with less than 80% coverage and list the specific uncovered line numbers for each."
Test Generation¶
"Using cov-loupe, identify the uncovered lines in lib/ops/jobs/report_job.rb and write meaningful RSpec tests to cover them."
"Using cov-loupe, analyze coverage gaps in the app/controllers/ directory and generate a test plan prioritizing: 1. Public API methods 2. Error handling paths 3. Edge cases"
Reporting¶
"Using cov-loupe, create a coverage report showing: - Overall project coverage percentage - Top 5 files with worst coverage - Recommended next steps to improve coverage
Format as markdown."
Test Run Integration¶
Display Low Coverage Files¶
Add this to your spec/spec_helper.rb to automatically report files below a coverage threshold after each test run:
require 'simplecov'
SimpleCov.start do
enable_coverage :branch
add_filter %r{^/spec/}
track_files 'lib/**/*.rb' # Ensures new/untested files show up with 0%
end
# Report lowest coverage files at the end of the test run
SimpleCov.at_exit do
SimpleCov.result.format!
require 'cov_loupe'
report = CovLoupe::CoverageReporter.report(threshold: 80, count: 5)
puts report if report
end
This produces output like:
Lowest coverage files (< 80%):
0.0% lib/myapp/config_parser.rb
19.3% lib/myapp/formatters/source_formatter.rb
24.0% lib/myapp/model.rb
26.0% lib/myapp/cli.rb
45.2% lib/myapp/commands/base.rb
Parameters: - threshold: - Coverage percentage below which files are included (default: 80) - count: - Maximum number of files to show (default: 5)
Returns: Formatted string, or nil if no files are below the threshold.
CI/CD Integration¶
GitHub Actions¶
Fail on low coverage (Cross-Platform):
name: Coverage Check
on: [push, pull_request]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
ruby-version: ['3.4']
steps:
- uses: actions/checkout@v4
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
bundler-cache: true
- name: Run tests
run: bundle exec rspec
- name: Install cov-loupe
run: gem install cov-loupe
- name: Check coverage threshold
shell: bash
run: |
# Generate JSON report using the full command (aliases like 'clp' are not available here)
cov-loupe -fJ list > coverage.json
# Verify coverage using Ruby for cross-platform compatibility
# (Tools like jq and rexe are not guaranteed to be installed on all runners)
ruby -r json -e '
data = JSON.parse(File.read("coverage.json"))
files = data["files"]
low_cov_files = files.select { |f| f["percentage"] < 80 }
if low_cov_files.any?
puts "❌ #{low_cov_files.count} files below 80% coverage:"
low_cov_files.each do |f|
puts " #{f["percentage"]}% #{f["file"]}"
end
exit 1
end
puts "✓ All files meet coverage threshold"
'
- name: Upload coverage report
# Saves the coverage file as an artifact so you can download/inspect it
# from the GitHub Actions run summary page.
uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-report-${{ matrix.os }}
path: coverage.json
Check for stale coverage:
GitLab CI¶
test:
image: ruby:3.4
before_script:
- gem install cov-loupe
script:
- bundle exec rspec
- cov-loupe --raise-on-stale true list
artifacts:
paths:
- coverage/
reports:
coverage_report:
coverage_format: simplecov
path: coverage/.resultset.json
Custom Success Predicate¶
# coverage_policy.rb
->(model) do
list = model.list['files']
# Must have at least 80% average coverage
totals = model.project_totals
return false if totals['percentage'] < 80.0
# No files below 60%
return false if list.any? { |f| f['percentage'] < 60.0 }
# lib/ files must average 90%
lib_totals = model.project_totals(tracked_globs: ['lib/**/*.rb'])
return false if lib_totals['percentage'] < 90.0
true
end
Advanced Usage¶
Directory-Level Analysis¶
require "cov_loupe"
root = "docs/fixtures/demo_project"
model = CovLoupe::CoverageModel.new(root: root)
# Calculate coverage by directory (uses the same data as `cov-loupe totals`)
patterns = %w[
app/**/*.rb
lib/payments/**/*.rb
lib/ops/jobs/**/*.rb
]
results = patterns.map do |pattern|
totals = model.project_totals(tracked_globs: pattern)
{
directory: pattern,
files: totals['files']['total'],
coverage: totals['percentage'].round(2),
covered: totals['lines']['covered'],
total: totals['lines']['total']
}
end
# Sort by coverage ascending
results.sort_by { |r| r[:coverage] }.each do |r|
puts "#{r[:directory]}: #{r[:coverage]}% (#{r[:files]} files)"
end
Integration with Code Review¶
# pr_coverage_check.rb
require "cov_loupe"
require "json"
model = CovLoupe::CoverageModel.new
# Get changed files from PR (example using git)
changed_files = `git diff --name-only origin/main`.split("\n")
changed_files.select! { |f| f.end_with?('.rb') }
puts "## Coverage Report for Changed Files\n\n"
puts "| File | Coverage | Status |"
puts "|------|----------|--------|"
changed_files.each do |file|
begin
summary = model.summary_for(file)
percentage = summary['summary']['percentage']
status = percentage >= 80 ? '✅' : '⚠️'
puts "| #{file} | #{percentage}% | #{status} |"
rescue
puts "| #{file} | N/A | ❌ No coverage |"
end
end
Example Scripts¶
The https://github.com/keithrbennett/cov-loupe/tree/main/examples directory contains runnable scripts:
- filter_and_table_demo.rb - Filter and format coverage data
- [success_predicathttps://github.com/keithrbennett/cov-loupe/tree/mainhttps://github.com/keithrbennett/cov-loupe/tree/mainhttps://github.com/keithrbennett/cov-loupe/tree/main/examples/success_predicates/) - Custom coverage policy examples
- Coverage Delta Tracking recipe in the Library API Guide
Related Documentation¶
- CLI Usage Guide - Complete command reference
- Library API Guide - Ruby API documentation
- MCP Integration - AI assistant setup
- Troubleshooting - Common issues