Ragex.Analysis.Quality
(Ragex v0.9.1)
View Source
High-level API for code quality analysis.
Provides a unified interface for analyzing code quality metrics and storing/querying results. Combines MetastaticBridge for analysis and QualityStore for persistence.
Features
- File and directory analysis with complexity metrics
- Automatic storage in knowledge graph
- Quality reporting and statistics
- Finding complex code patterns
- Purity analysis
Usage
alias Ragex.Analysis.Quality
# Analyze single file
{:ok, result} = Quality.analyze_file("lib/my_module.ex")
# Analyze with options
{:ok, result} = Quality.analyze_file("lib/my_module.ex",
metrics: [:cyclomatic, :cognitive],
store: true
)
# Analyze directory
{:ok, results} = Quality.analyze_directory("lib/")
# Get statistics
stats = Quality.statistics()
# Find complex files
complex = Quality.find_complex(metric: :cyclomatic, threshold: 10)
Summary
Functions
Analyzes all files in a directory.
Analyzes a single file for code quality metrics.
Analyzes a specific function's quality metrics.
Analyzes all functions in a module.
Analyzes directory quality metrics.
Clears all stored quality metrics.
Generates a comprehensive report including both quality and security metrics.
Returns the number of files with stored quality metrics.
Finds files exceeding complexity thresholds.
Finds complex code in a directory.
Finds impure files (files with side effects).
Finds files with analysis warnings.
Generates a comprehensive quality report.
Retrieves stored quality metrics for a file.
Returns the most complex files.
Returns project-wide quality statistics.
Returns quality statistics grouped by language.
Types
@type quality_report() :: %{ total_files: non_neg_integer(), avg_cyclomatic: float(), avg_cognitive: float(), avg_nesting: float(), max_cyclomatic: non_neg_integer(), max_cognitive: non_neg_integer(), max_nesting: non_neg_integer(), files_with_warnings: non_neg_integer(), impure_files: non_neg_integer(), languages: %{required(atom()) => non_neg_integer()} }
Functions
@spec analyze_directory( String.t(), keyword() ) :: {:ok, ok: analysis_result(), error: term()} | {:error, term()}
Analyzes all files in a directory.
Recursively analyzes all supported source files in the directory and optionally stores results in the knowledge graph.
Parameters
path: Directory path to analyzeopts: Keyword list of options:recursive- Recursively analyze subdirectories (default: true):store- Store results in knowledge graph (default: true):metrics- List of metrics to calculate (default: all):parallel- Use parallel processing (default: true):max_concurrency- Maximum concurrent analyses (default: System.schedulers_online())
Returns
{:ok, results}- List of analysis results (mix of{:ok, result}and{:error, reason}){:error, reason}- Error if directory access fails
Examples
# Analyze entire lib directory
{:ok, results} = Quality.analyze_directory("lib/")
# Analyze without storing
{:ok, results} = Quality.analyze_directory("lib/", store: false)
# Sequential analysis (useful for debugging)
{:ok, results} = Quality.analyze_directory("lib/", parallel: false)
@spec analyze_file( String.t(), keyword() ) :: {:ok, analysis_result()} | {:error, term()}
Analyzes a single file for code quality metrics.
Performs comprehensive quality analysis including complexity metrics and purity analysis. Optionally stores results in the knowledge graph for later querying.
Parameters
path: Path to the file to analyzeopts: Keyword list of options:metrics- List of specific metrics to calculate (default: all):store- Store results in knowledge graph (default: true):thresholds- Custom threshold map for warnings:language- Explicit language (default: auto-detect)
Returns
{:ok, analysis_result}- Analysis results with metrics{:error, reason}- Error if analysis fails
Examples
# Analyze with default options (all metrics, auto-store)
{:ok, result} = Quality.analyze_file("lib/my_module.ex")
result.complexity.cyclomatic # => 5
# Analyze specific metrics without storing
{:ok, result} = Quality.analyze_file("lib/my_module.ex",
metrics: [:cyclomatic, :cognitive],
store: false
)
# Analyze with custom thresholds
{:ok, result} = Quality.analyze_file("lib/my_module.ex",
thresholds: %{cyclomatic: 15, cognitive: 10}
)
@spec analyze_function(module(), atom(), non_neg_integer(), keyword()) :: {:ok, map()} | {:error, term()}
Analyzes a specific function's quality metrics.
Extracts function-level metrics from the file's stored analysis results.
The file must have been analyzed and stored first using analyze_file/2.
Parameters
module: Module name atomfunction: Function name atomarity: Function arity (non-negative integer)opts: Keyword list of options:path- Explicit file path (default: lookup from module in graph):analyze_if_missing- Analyze file if not found in store (default: false)
Returns
{:ok, function_metrics}- Map with function-specific metrics{:error, :not_found}- File not analyzed or function not found{:error, reason}- Other errors
Function Metrics Structure
%{
module: MyModule,
function: :my_function,
arity: 2,
cyclomatic: 3,
cognitive: 2,
nesting: 1,
halstead: %{...},
loc: %{total: 10, code: 8, comments: 2}
}Examples
# Analyze a specific function (file must be analyzed first)
{:ok, result} = Quality.analyze_file("lib/my_module.ex")
{:ok, func_metrics} = Quality.analyze_function(MyModule, :my_function, 2)
# With auto-analysis if not found
{:ok, func_metrics} = Quality.analyze_function(MyModule, :my_function, 2,
analyze_if_missing: true
)
# With explicit path
{:ok, func_metrics} = Quality.analyze_function(MyModule, :my_function, 2,
path: "lib/my_module.ex"
)
Analyzes all functions in a module.
Retrieves quality metrics for all functions in the specified module. The module's file must have been analyzed and stored first.
Parameters
module: Module name atomopts: Keyword list of options:path- Explicit file path (default: lookup from module in graph):analyze_if_missing- Analyze file if not found in store (default: false):sort_by- Sort functions by metric::cyclomatic,:cognitive,:nesting,:name(default::name):threshold- Only return functions exceeding this complexity (optional)
Returns
{:ok, module_analysis}- Map with module-level and per-function metrics{:error, reason}- Error if module not found or analysis fails
Module Analysis Structure
%{
module: MyModule,
file: "lib/my_module.ex",
language: :elixir,
total_cyclomatic: 25,
total_cognitive: 18,
function_count: 5,
functions: [
%{function: :func1, arity: 2, cyclomatic: 3, ...},
%{function: :func2, arity: 1, cyclomatic: 5, ...}
]
}Examples
# Analyze all functions in a module
{:ok, analysis} = Quality.analyze_module(MyModule)
analysis.function_count # => 5
# Sort by complexity
{:ok, analysis} = Quality.analyze_module(MyModule, sort_by: :cyclomatic)
# Only complex functions
{:ok, analysis} = Quality.analyze_module(MyModule, threshold: 10)
# With auto-analysis
{:ok, analysis} = Quality.analyze_module(MyModule, analyze_if_missing: true)
Analyzes directory quality metrics.
Convenience function that returns a quality score and statistics.
Options
:min_complexity- Complexity threshold (default: 10)
Examples
{:ok, metrics} = Quality.analyze_quality("lib/")
metrics.overall_score # => 75
@spec clear_all() :: :ok
Clears all stored quality metrics.
Removes all quality_metrics nodes from the knowledge graph. Does not affect other graph data (modules, functions, etc.).
Examples
:ok = Quality.clear_all()
Generates a comprehensive report including both quality and security metrics.
This convenience function combines quality analysis with security scanning to provide a holistic view of code health.
Parameters
path: Directory path to analyzeopts: Keyword list of options:min_severity- Minimum security severity to report (default::medium):include_security- Include security analysis (default:true)- All options from
analyze_directory/2
Returns
{:ok, report}- Comprehensive report map{:error, reason}- Analysis failed
Report Structure
%{
quality: %{
statistics: quality_report(),
most_complex: [{path, complexity}],
with_warnings: [{path, warnings}],
impure_files: [path]
},
security: %{
total_vulnerabilities: integer(),
by_severity: %{critical: integer(), high: integer(), ...},
files_with_vulnerabilities: [path],
summary: string()
}
}Examples
# Full analysis with security
{:ok, report} = Quality.comprehensive_report("lib/")
report.quality.statistics.avg_cyclomatic # => 4.5
report.security.total_vulnerabilities # => 3
# Quality only
{:ok, report} = Quality.comprehensive_report("lib/", include_security: false)
# Only critical security issues
{:ok, report} = Quality.comprehensive_report("lib/", min_severity: :critical)
@spec count() :: non_neg_integer()
Returns the number of files with stored quality metrics.
Examples
count = Quality.count() # => 42
Finds files exceeding complexity thresholds.
Parameters
opts: Keyword list of options:metric- Metric to evaluate::cyclomatic,:cognitive,:nesting(default::cyclomatic):threshold- Threshold value (default: 10):operator- Comparison::gt,:gte,:lt,:lte,:eq(default::gt):limit- Maximum results (default: 20)
Returns
- List of file paths exceeding threshold
Examples
# Find files with cyclomatic complexity > 10
complex = Quality.find_complex(threshold: 10)
# Find files with cognitive complexity >= 15
complex = Quality.find_complex(
metric: :cognitive,
threshold: 15,
operator: :gte
)
# Find top 5 most complex files
complex = Quality.find_complex(threshold: 5, limit: 5)
Finds complex code in a directory.
Convenience function that analyzes a directory and returns functions exceeding the complexity threshold.
Options
:min_complexity- Minimum cyclomatic complexity (default: 10)
Examples
{:ok, functions} = Quality.find_complex_code("lib/", min_complexity: 15)
@spec find_impure() :: [String.t()]
Finds impure files (files with side effects).
Returns
- List of file paths with side effects detected
Examples
impure = Quality.find_impure()
# => ["lib/database.ex", "lib/logger.ex"]
Finds files with analysis warnings.
Returns
- List of
{path, warnings}tuples
Examples
files_with_warnings = Quality.find_with_warnings()
# => [{"lib/complex.ex", ["High cyclomatic complexity: 15"]}]
Generates a comprehensive quality report.
Parameters
opts: Keyword list of options:type- Report type::summary,:detailed,:by_language(default::summary):format- Output format::text,:map(default::map)
Returns
- Report content (map or formatted string based on format option)
Examples
# Summary report as map
report = Quality.generate_report()
# Detailed report as text
report = Quality.generate_report(type: :detailed, format: :text)
# Language breakdown
report = Quality.generate_report(type: :by_language)
Retrieves stored quality metrics for a file.
Parameters
path: File path
Returns
{:ok, metrics}- Stored metrics map{:error, :not_found}- File not analyzed or metrics not stored
Examples
{:ok, metrics} = Quality.get_metrics("lib/my_module.ex")
metrics.cyclomatic # => 5
Returns the most complex files.
Parameters
opts: Keyword list of options:metric- Metric to rank by::cyclomatic,:cognitive,:nesting(default::cyclomatic):limit- Number of results (default: 10)
Returns
- List of
{path, metric_value}tuples, sorted by complexity
Examples
# Top 10 most complex files by cyclomatic complexity
top = Quality.most_complex()
# Top 5 by cognitive complexity
top = Quality.most_complex(metric: :cognitive, limit: 5)
@spec statistics() :: quality_report()
Returns project-wide quality statistics.
Aggregates metrics from all analyzed files stored in the knowledge graph.
Returns
- Quality statistics map with averages, maximums, and counts
Examples
stats = Quality.statistics()
stats.total_files # => 42
stats.avg_cyclomatic # => 3.5
stats.max_cognitive # => 25
@spec statistics_by_language() :: %{required(atom()) => quality_report()}
Returns quality statistics grouped by language.
Returns
- Map of language => statistics
Examples
by_lang = Quality.statistics_by_language()
by_lang[:elixir].avg_cyclomatic # => 4.2
by_lang[:python].avg_cognitive # => 5.1