ameba-custom-rules
Use when creating custom Ameba rules for Crystal code analysis including rule development, AST traversal, issue reporting, and rule testing.
What this skill does
# Ameba Custom Rules
Create custom linting rules for Ameba to enforce project-specific code quality standards and catch domain-specific code smells in Crystal projects.
## Understanding Custom Rules
Custom Ameba rules allow you to:
- Enforce project-specific coding standards
- Catch domain-specific anti-patterns
- Validate business logic constraints
- Ensure consistency across large codebases
- Create reusable rule libraries for your organization
- Extend Ameba's built-in capabilities
## Rule Anatomy
### Basic Rule Structure
Every Ameba rule inherits from `Ameba::Rule::Base` and follows this structure:
```crystal
module Ameba::Rule::Custom
# Rule that enforces documentation on public classes
class DocumentedClasses < Base
properties do
description "Enforces public classes to be documented"
end
MSG = "Class must be documented with a comment"
def test(source)
AST::NodeVisitor.new self, source
end
def test(source, node : Crystal::ClassDef)
return unless node.visibility.public?
doc = node.doc
issue_for(node, MSG) if doc.nil? || doc.empty?
end
end
end
```
### Key Components
1. **Module namespace** - Custom rules typically use `Ameba::Rule::Custom` or `Ameba::Rule::<Category>`
2. **Base class** - All rules inherit from `Ameba::Rule::Base`
3. **Properties block** - Defines rule metadata and configuration
4. **Message constant** - The error message shown to users
5. **Test method** - Entry point that initializes the AST visitor
6. **Overloaded test methods** - Handle specific AST node types
## Creating Your First Custom Rule
### Step 1: Project Setup
Create a Crystal library for your custom rules:
```bash
# Initialize a new Crystal library
crystal init lib ameba-custom-rules
cd ameba-custom-rules
```
Update `shard.yml`:
```yaml
name: ameba-custom-rules
user-invocable: false
version: 0.1.0
authors:
- Your Name <[email protected]>
description: Custom Ameba rules for your project
crystal: ">= 1.0.0"
license: MIT
development_dependencies:
ameba:
github: crystal-ameba/ameba
version: ~> 1.6.0
```
**Important:** Ameba should be a development dependency to avoid version conflicts.
### Step 2: Implement a Simple Rule
Create `src/ameba-custom-rules/no_sleep_in_production.cr`:
```crystal
require "ameba"
module Ameba::Rule::Custom
# Prevents sleep() calls in production code
class NoSleepInProduction < Base
properties do
description "Prevents sleep calls in production code"
enabled true
end
MSG = "Avoid using sleep() in production code; use proper background jobs"
def test(source)
AST::NodeVisitor.new self, source
end
def test(source, node : Crystal::Call)
return unless node.name == "sleep"
issue_for node, MSG
end
end
end
```
### Step 3: Register and Use the Rule
Create main file `src/ameba-custom-rules.cr`:
```crystal
require "ameba"
require "./ameba-custom-rules/*"
# Rules are automatically registered through inheritance
```
Update your project's Ameba configuration:
```yaml
# .ameba.yml
Custom/NoSleepInProduction:
Enabled: true
Severity: Warning
```
### Step 4: Test Your Rule
Create `spec/ameba-custom-rules/no_sleep_in_production_spec.cr`:
```crystal
require "../spec_helper"
module Ameba::Rule::Custom
describe NoSleepInProduction do
it "reports sleep calls" do
rule = NoSleepInProduction.new
source = Source.new %(
def process
sleep 5.seconds
end
)
rule.test(source)
source.issues.size.should eq(1)
end
it "allows code without sleep" do
rule = NoSleepInProduction.new
source = Source.new %(
def process
puts "Processing"
end
)
rule.test(source)
source.issues.should be_empty
end
end
end
```
## Advanced Rule Examples
### Enforcing Naming Conventions
```crystal
module Ameba::Rule::Custom
# Enforces that service classes end with "Service"
class ServiceClassNaming < Base
properties do
description "Service classes must end with 'Service'"
enabled true
end
MSG = "Service class name should end with 'Service'"
def test(source)
AST::NodeVisitor.new self, source
end
def test(source, node : Crystal::ClassDef)
class_name = node.name.to_s
# Check if class is in services directory
return unless source.path.includes?("/services/")
# Check if name ends with Service
unless class_name.ends_with?("Service")
issue_for node.name, MSG
end
end
end
end
```
### Detecting Dangerous Method Calls
```crystal
module Ameba::Rule::Custom
# Prevents dangerous ActiveRecord-like methods
class NoDangerousDatabaseCalls < Base
properties do
description "Prevents dangerous database operations"
dangerous_methods ["delete_all", "destroy_all", "update_all"]
end
MSG = "Dangerous method %s without conditions"
def test(source)
AST::NodeVisitor.new self, source
end
def test(source, node : Crystal::Call)
return unless dangerous_methods.includes?(node.name)
# Check if call has arguments (conditions)
if node.args.empty?
message = MSG % node.name
issue_for node, message
end
end
end
end
```
### Enforcing Error Handling
```crystal
module Ameba::Rule::Custom
# Ensures HTTP client calls have error handling
class HttpErrorHandling < Base
properties do
description "HTTP client calls must handle errors"
enabled true
end
MSG = "HTTP client calls should be wrapped in begin/rescue"
def test(source)
@in_rescue_block = false
AST::NodeVisitor.new self, source
end
def test(source, node : Crystal::ExceptionHandler)
@in_rescue_block = true
true # Continue visiting children
end
def test(source, node : Crystal::Call)
return if @in_rescue_block
# Check for HTTP client calls
if node.obj.try(&.to_s.includes?("HTTP"))
issue_for node, MSG
end
end
end
end
```
### Validating Method Complexity
```crystal
module Ameba::Rule::Custom
# Limits method complexity
class MethodComplexity < Base
properties do
description "Methods should not be too complex"
max_complexity 10
end
MSG = "Method complexity (%d) exceeds maximum (%d)"
def test(source)
AST::NodeVisitor.new self, source
end
def test(source, node : Crystal::Def)
complexity = calculate_complexity(node)
if complexity > max_complexity
message = MSG % [complexity, max_complexity]
issue_for node, message
end
end
private def calculate_complexity(node)
counter = ComplexityCounter.new
node.accept(counter)
counter.complexity
end
private class ComplexityCounter < Crystal::Visitor
getter complexity : Int32 = 1
def visit(node : Crystal::If)
@complexity += 1
true
end
def visit(node : Crystal::Case)
@complexity += 1
true
end
def visit(node : Crystal::While)
@complexity += 1
true
end
def visit(node : Crystal::Call)
# Count logical operators
if node.name.in?("&&", "||")
@complexity += 1
end
true
end
def visit(node : Crystal::ASTNode)
true
end
end
end
end
```
### Enforcing Documentation Standards
```crystal
module Ameba::Rule::Custom
# Requires documentation with specific format
class DocumentationFormat < Base
properties do
description "Public methods must have documentation with examples"
enabled true
require_examples true
end
MSG_NO_DOC = "Public method must have documentation"
MSG_NO_EXAMPLE = "Documentation must include usage examples"
def test(source)
AST::NodeVisitor.new self, source
end
def test(source, node : Crystal:Related in Data & Analytics
clawarr-suite
IncludedComprehensive management for self-hosted media stacks (Sonarr, Radarr, Lidarr, Readarr, Prowlarr, Bazarr, Overseerr, Plex, Tautulli, SABnzbd, Recyclarr, Unpackerr, Notifiarr, Maintainerr, Kometa, FlareSolverr). Deep library exploration, analytics, dashboard generation, content management, request handling, subtitle management, indexer control, download monitoring, quality profile sync, library cleanup automation, notification routing, collection/overlay management, and media tracker integration (Trakt, Letterboxd, Simkl).
querying-soql
IncludedSOQL query generation, optimization, and analysis with 100-point scoring. Use this skill when the user needs SOQL/SOSL authoring or optimization: natural-language-to-query generation, relationship queries, aggregates, query-plan analysis, and performance or safety improvements for Salesforce queries. TRIGGER when: user writes, optimizes, or debugs SOQL/SOSL queries, touches .soql files, or asks about relationship queries, aggregates, or query performance. DO NOT TRIGGER when: bulk data operations (use handling-sf-data), Apex DML logic (use generating-apex), or report/dashboard queries.
app-store-optimization
IncludedApp Store Optimization (ASO) toolkit for researching keywords, analyzing competitor rankings, generating metadata suggestions, and improving app visibility on Apple App Store and Google Play Store. Use when the user asks about ASO, app store rankings, app metadata, app titles and descriptions, app store listings, app visibility, or mobile app marketing on iOS or Android. Supports keyword research and scoring, competitor keyword analysis, metadata optimization, A/B test planning, launch checklists, and tracking ranking changes.
habit-flow
IncludedAI-powered atomic habit tracker with natural language logging, streak tracking, smart reminders, and coaching. Use for creating habits, logging completions naturally ("I meditated today"), viewing progress, and getting personalized coaching.
app-store-optimization
IncludedApp Store Optimization (ASO) toolkit for researching keywords, analyzing competitor rankings, generating metadata suggestions, and improving app visibility on Apple App Store and Google Play Store. Use when the user asks about ASO, app store rankings, app metadata, app titles and descriptions, app store listings, app visibility, or mobile app marketing on iOS or Android. Supports keyword research and scoring, competitor keyword analysis, metadata optimization, A/B test planning, launch checklists, and tracking ranking changes.
visualizing-data
IncludedBuilds dashboards, reports, and data-driven interfaces requiring charts, graphs, or visual analytics. Provides systematic framework for selecting appropriate visualizations based on data characteristics and analytical purpose. Includes 24+ visualization types organized by purpose (trends, comparisons, distributions, relationships, flows, hierarchies, geospatial), accessibility patterns (WCAG 2.1 AA compliance), colorblind-safe palettes, and performance optimization strategies. Use when creating visualizations, choosing chart types, displaying data graphically, or designing data interfaces.