Initial commit
This commit is contained in:
412
skills/ruby-bundler/SKILL.md
Normal file
412
skills/ruby-bundler/SKILL.md
Normal file
@@ -0,0 +1,412 @@
|
||||
# Ruby Bundler Skill
|
||||
|
||||
Intelligent Bundler operations for managing Ruby dependencies.
|
||||
|
||||
## When to Activate
|
||||
|
||||
This skill activates when:
|
||||
- User requests to install gems or dependencies
|
||||
- User mentions Gemfile or bundle commands
|
||||
- User asks about dependency management
|
||||
- Gemfile.lock conflicts detected
|
||||
- Missing gem errors occur
|
||||
|
||||
## Core Capabilities
|
||||
|
||||
### 1. Install Dependencies
|
||||
|
||||
**Basic Installation:**
|
||||
```bash
|
||||
bundle install
|
||||
```
|
||||
|
||||
**Install for specific groups:**
|
||||
```bash
|
||||
# Development and test only
|
||||
bundle install --without production
|
||||
|
||||
# Production only
|
||||
bundle install --deployment
|
||||
```
|
||||
|
||||
**Update Bundler first if needed:**
|
||||
```bash
|
||||
gem install bundler
|
||||
bundle install
|
||||
```
|
||||
|
||||
### 2. Add New Gems
|
||||
|
||||
**Interactive Gem Addition:**
|
||||
|
||||
When user requests: "Add [gem_name] gem"
|
||||
|
||||
1. **Check if gem exists:**
|
||||
- Search RubyGems.org
|
||||
- Show latest version
|
||||
- Show brief description
|
||||
|
||||
2. **Suggest Gemfile entry:**
|
||||
```ruby
|
||||
# For runtime dependency
|
||||
gem 'gem_name', '~> X.Y'
|
||||
|
||||
# For development/test
|
||||
group :development, :test do
|
||||
gem 'gem_name', '~> X.Y'
|
||||
end
|
||||
|
||||
# For test only
|
||||
group :test do
|
||||
gem 'rspec', '~> 3.12'
|
||||
end
|
||||
```
|
||||
|
||||
3. **Add to appropriate group:**
|
||||
- Runtime dependencies → main section
|
||||
- Testing tools → :test group
|
||||
- Development tools → :development group
|
||||
- Code quality → :development (rubocop, etc.)
|
||||
|
||||
4. **Install the gem:**
|
||||
```bash
|
||||
bundle install
|
||||
```
|
||||
|
||||
5. **Verify installation:**
|
||||
```bash
|
||||
bundle list | grep gem_name
|
||||
```
|
||||
|
||||
### 3. Update Dependencies
|
||||
|
||||
**Update all gems:**
|
||||
```bash
|
||||
bundle update
|
||||
```
|
||||
|
||||
**Update specific gem:**
|
||||
```bash
|
||||
bundle update gem_name
|
||||
```
|
||||
|
||||
**Update conservatively (patch versions only):**
|
||||
```bash
|
||||
bundle update --patch
|
||||
```
|
||||
|
||||
**Update minor versions:**
|
||||
```bash
|
||||
bundle update --minor
|
||||
```
|
||||
|
||||
### 4. Audit for Security
|
||||
|
||||
**Check for vulnerabilities:**
|
||||
```bash
|
||||
bundle audit
|
||||
```
|
||||
|
||||
**Install bundler-audit if missing:**
|
||||
```bash
|
||||
gem install bundler-audit
|
||||
bundle audit
|
||||
```
|
||||
|
||||
**Update vulnerable gems:**
|
||||
```bash
|
||||
bundle audit check --update
|
||||
```
|
||||
|
||||
### 5. Clean Up
|
||||
|
||||
**Remove unused gems:**
|
||||
```bash
|
||||
bundle clean
|
||||
```
|
||||
|
||||
**Clean with forced removal:**
|
||||
```bash
|
||||
bundle clean --force
|
||||
```
|
||||
|
||||
### 6. Troubleshoot Issues
|
||||
|
||||
**Common Issues & Solutions:**
|
||||
|
||||
**Issue: "Could not find gem 'X'"**
|
||||
```bash
|
||||
# Solution 1: Update bundler
|
||||
gem install bundler
|
||||
bundle install
|
||||
|
||||
# Solution 2: Clear cache
|
||||
bundle clean --force
|
||||
bundle install
|
||||
|
||||
# Solution 3: Update Gemfile.lock
|
||||
rm Gemfile.lock
|
||||
bundle install
|
||||
```
|
||||
|
||||
**Issue: Version conflicts**
|
||||
```bash
|
||||
# Show dependency tree
|
||||
bundle viz --without production
|
||||
|
||||
# Or use bundle info
|
||||
bundle info gem_name
|
||||
```
|
||||
|
||||
**Issue: Platform-specific gems**
|
||||
```ruby
|
||||
# In Gemfile
|
||||
gem 'pg', platforms: :ruby
|
||||
gem 'sqlite3', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
|
||||
```
|
||||
|
||||
### 7. Gem Information
|
||||
|
||||
**Show gem details:**
|
||||
```bash
|
||||
bundle info gem_name
|
||||
```
|
||||
|
||||
**List all gems:**
|
||||
```bash
|
||||
bundle list
|
||||
```
|
||||
|
||||
**Show outdated gems:**
|
||||
```bash
|
||||
bundle outdated
|
||||
```
|
||||
|
||||
## Interactive Workflows
|
||||
|
||||
### Workflow 1: Add New Gem
|
||||
|
||||
**User Request:** "Add pry-byebug for debugging"
|
||||
|
||||
**Response:**
|
||||
```
|
||||
I'll add pry-byebug to your Gemfile.
|
||||
|
||||
Gem: pry-byebug
|
||||
Latest Version: 3.10.1
|
||||
Description: Combines pry with byebug for debugging
|
||||
|
||||
This is a development tool, so I'll add it to the :development group.
|
||||
|
||||
Adding to Gemfile:
|
||||
```ruby
|
||||
group :development do
|
||||
gem 'pry-byebug', '~> 3.10'
|
||||
end
|
||||
```
|
||||
|
||||
Running bundle install...
|
||||
✓ Successfully installed pry-byebug 3.10.1
|
||||
|
||||
You can now use it by adding `binding.pry` in your code.
|
||||
```
|
||||
|
||||
### Workflow 2: Update Dependencies
|
||||
|
||||
**User Request:** "Update all my gems"
|
||||
|
||||
**Response:**
|
||||
```
|
||||
Before updating, let me check what's outdated:
|
||||
|
||||
Outdated gems:
|
||||
- rspec: 3.11.0 → 3.12.0 (minor update)
|
||||
- rubocop: 1.45.1 → 1.50.2 (minor update)
|
||||
- nokogiri: 1.14.2 → 1.14.3 (patch update)
|
||||
|
||||
Recommendation:
|
||||
1. Update patch versions first (safest): bundle update --patch
|
||||
2. Update minor versions: bundle update --minor
|
||||
3. Update all: bundle update
|
||||
|
||||
Would you like me to:
|
||||
a) Update patch versions only (safest)
|
||||
b) Update minor versions
|
||||
c) Update everything
|
||||
d) Update specific gems only
|
||||
```
|
||||
|
||||
### Workflow 3: Security Audit
|
||||
|
||||
**User Request:** "Check for security issues"
|
||||
|
||||
**Response:**
|
||||
```
|
||||
Running security audit with bundler-audit...
|
||||
|
||||
Found 2 vulnerabilities:
|
||||
|
||||
1. HIGH: CVE-2023-XXXX in nokogiri (1.14.2)
|
||||
Solution: Update to >= 1.14.3
|
||||
|
||||
2. MEDIUM: CVE-2023-YYYY in rack (2.2.6)
|
||||
Solution: Update to >= 2.2.7
|
||||
|
||||
Recommended action:
|
||||
bundle update nokogiri rack
|
||||
|
||||
Would you like me to update these gems now?
|
||||
```
|
||||
|
||||
## Smart Suggestions
|
||||
|
||||
### Suggest Missing Gems
|
||||
|
||||
When user writes code using common patterns, suggest relevant gems:
|
||||
|
||||
**Pattern: API calls**
|
||||
```ruby
|
||||
# User writes HTTP client code
|
||||
"I notice you're making HTTP requests. Consider adding:"
|
||||
gem 'faraday', '~> 2.7' # Modern HTTP client
|
||||
# or
|
||||
gem 'httparty', '~> 0.21' # Simple HTTP client
|
||||
```
|
||||
|
||||
**Pattern: JSON parsing**
|
||||
```ruby
|
||||
# User parses JSON
|
||||
"For better JSON handling, consider:"
|
||||
gem 'oj', '~> 3.14' # Faster JSON parser
|
||||
```
|
||||
|
||||
**Pattern: Environment variables**
|
||||
```ruby
|
||||
# User uses ENV[]
|
||||
"For environment management, you already have dotenv in common libs!"
|
||||
gem 'dotenv', '~> 2.8'
|
||||
```
|
||||
|
||||
**Pattern: Background jobs**
|
||||
```ruby
|
||||
# User mentions async/background processing
|
||||
"For background jobs, you commonly use:"
|
||||
gem 'sidekiq', '~> 7.0'
|
||||
```
|
||||
|
||||
## Version Constraint Guidelines
|
||||
|
||||
**Pessimistic versioning (~>):**
|
||||
```ruby
|
||||
gem 'rails', '~> 7.0.4' # >= 7.0.4, < 7.1
|
||||
gem 'rspec', '~> 3.12' # >= 3.12, < 4.0
|
||||
```
|
||||
|
||||
**Exact version (avoid unless necessary):**
|
||||
```ruby
|
||||
gem 'specific_gem', '1.2.3' # Only 1.2.3
|
||||
```
|
||||
|
||||
**Greater than or equal:**
|
||||
```ruby
|
||||
gem 'compatible_gem', '>= 2.0' # Any version >= 2.0
|
||||
```
|
||||
|
||||
**Best Practice:** Use pessimistic (~>) for stability
|
||||
|
||||
## Gemfile Organization
|
||||
|
||||
**Recommended Structure:**
|
||||
```ruby
|
||||
source 'https://rubygems.org'
|
||||
|
||||
ruby '3.2.0' # Specify Ruby version
|
||||
|
||||
# Core runtime dependencies
|
||||
gem 'dotenv'
|
||||
gem 'pg', '~> 1.6'
|
||||
|
||||
# Development tools
|
||||
group :development do
|
||||
gem 'pry'
|
||||
gem 'rubocop', require: false
|
||||
end
|
||||
|
||||
# Test tools
|
||||
group :test do
|
||||
gem 'rspec', '~> 3.12'
|
||||
gem 'factory_bot', '~> 6.5'
|
||||
gem 'timecop'
|
||||
end
|
||||
|
||||
# Development & Test
|
||||
group :development, :test do
|
||||
gem 'pry-byebug'
|
||||
end
|
||||
|
||||
# Platform-specific
|
||||
platforms :ruby do
|
||||
gem 'sqlite3', '~> 2.1'
|
||||
end
|
||||
```
|
||||
|
||||
## Response Format
|
||||
|
||||
When performing bundle operations:
|
||||
|
||||
**Action Taken:**
|
||||
- Command executed
|
||||
- Result summary
|
||||
|
||||
**Changes:**
|
||||
- New gems added
|
||||
- Gems updated (old → new version)
|
||||
- Gems removed
|
||||
|
||||
**Next Steps:**
|
||||
- Suggested actions
|
||||
- Related configuration needed
|
||||
- Documentation references
|
||||
|
||||
## Configuration
|
||||
|
||||
Users can customize behavior in `.claude/settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": {
|
||||
"rubyist": {
|
||||
"bundler": {
|
||||
"autoInstall": true,
|
||||
"suggestGems": true,
|
||||
"securityAudit": true,
|
||||
"versionStrategy": "pessimistic"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
Always wrap bundle commands with error handling:
|
||||
|
||||
```bash
|
||||
if bundle install; then
|
||||
echo "✓ Successfully installed dependencies"
|
||||
else
|
||||
echo "✗ Installation failed"
|
||||
echo "Trying with bundle update..."
|
||||
bundle update
|
||||
fi
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always review Gemfile changes** before committing
|
||||
2. **Commit Gemfile.lock** to ensure consistent environments
|
||||
3. **Use bundle audit regularly** for security
|
||||
4. **Keep gems updated** but test thoroughly
|
||||
5. **Group gems appropriately** (development, test, production)
|
||||
6. **Use pessimistic versioning** for stability
|
||||
7. **Document why** specific versions are pinned
|
||||
98
skills/ruby-bundler/scripts/README.md
Normal file
98
skills/ruby-bundler/scripts/README.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Bundler Utility Scripts
|
||||
|
||||
Executable shell scripts for common Bundler operations.
|
||||
|
||||
## Scripts
|
||||
|
||||
### bundle_install.sh
|
||||
Install Ruby dependencies with error handling.
|
||||
|
||||
```bash
|
||||
# Basic install
|
||||
./bundle_install.sh
|
||||
|
||||
# Install with options
|
||||
./bundle_install.sh --without production
|
||||
|
||||
# Install for deployment
|
||||
./bundle_install.sh --deployment
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Checks for Gemfile existence
|
||||
- Auto-installs bundler if missing
|
||||
- Shows helpful error messages
|
||||
- Lists installed gems
|
||||
|
||||
### bundle_add.sh
|
||||
Add a gem to Gemfile and install it.
|
||||
|
||||
```bash
|
||||
# Add to main section
|
||||
./bundle_add.sh pry-byebug '~> 3.10'
|
||||
|
||||
# Add to development group
|
||||
./bundle_add.sh rubocop '~> 1.50' --group=development
|
||||
|
||||
# Add to test group
|
||||
./bundle_add.sh rspec --group=test
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Automatically detects/creates gem groups
|
||||
- Adds version constraints
|
||||
- Runs bundle install
|
||||
- Shows gem info after install
|
||||
|
||||
### bundle_update.sh
|
||||
Update gems with safety levels.
|
||||
|
||||
```bash
|
||||
# Show outdated gems (no changes)
|
||||
./bundle_update.sh conservative
|
||||
|
||||
# Update patch versions only (safest)
|
||||
./bundle_update.sh patch
|
||||
|
||||
# Update minor versions
|
||||
./bundle_update.sh minor
|
||||
|
||||
# Update all gems
|
||||
./bundle_update.sh all
|
||||
|
||||
# Update specific gem
|
||||
./bundle_update.sh nokogiri
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Multiple safety modes
|
||||
- Shows git diff of Gemfile.lock changes
|
||||
- Conservative default (shows outdated only)
|
||||
|
||||
### bundle_audit.sh
|
||||
Security audit for dependencies.
|
||||
|
||||
```bash
|
||||
./bundle_audit.sh
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Auto-installs bundler-audit if needed
|
||||
- Updates vulnerability database
|
||||
- Shows vulnerabilities with fix suggestions
|
||||
- Returns non-zero exit code if vulnerabilities found
|
||||
|
||||
## Usage in Skills
|
||||
|
||||
These scripts can be called by the ruby-bundler skill:
|
||||
|
||||
```ruby
|
||||
# From skill
|
||||
system("#{PLUGIN_DIR}/skills/ruby-bundler/scripts/bundle_install.sh")
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- Ruby installed
|
||||
- Git (for bundle_update.sh diff)
|
||||
- bundler gem (auto-installed if missing)
|
||||
69
skills/ruby-bundler/scripts/bundle_add.sh
Executable file
69
skills/ruby-bundler/scripts/bundle_add.sh
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
# Add a gem to Gemfile and install it
|
||||
|
||||
set -e
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Usage: $0 <gem_name> [version] [--group=GROUP]"
|
||||
echo "Example: $0 pry-byebug '~> 3.10' --group=development"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GEM_NAME=$1
|
||||
VERSION=${2:-}
|
||||
GROUP=""
|
||||
|
||||
# Parse group argument
|
||||
for arg in "$@"; do
|
||||
if [[ $arg == --group=* ]]; then
|
||||
GROUP="${arg#*=}"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "📦 Adding gem: $GEM_NAME"
|
||||
|
||||
# Check if Gemfile exists
|
||||
if [ ! -f "Gemfile" ]; then
|
||||
echo "❌ Error: No Gemfile found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Determine gem line to add
|
||||
if [ -n "$VERSION" ] && [[ ! $VERSION == --* ]]; then
|
||||
GEM_LINE="gem '$GEM_NAME', '$VERSION'"
|
||||
else
|
||||
GEM_LINE="gem '$GEM_NAME'"
|
||||
fi
|
||||
|
||||
# Add to appropriate group
|
||||
if [ -n "$GROUP" ]; then
|
||||
echo "Adding to group: $GROUP"
|
||||
|
||||
# Check if group exists
|
||||
if grep -q "group :$GROUP do" Gemfile; then
|
||||
# Add to existing group (before the 'end')
|
||||
sed -i.bak "/group :$GROUP do/a\\
|
||||
$GEM_LINE
|
||||
" Gemfile && rm Gemfile.bak
|
||||
else
|
||||
# Create new group at end of file
|
||||
echo "" >> Gemfile
|
||||
echo "group :$GROUP do" >> Gemfile
|
||||
echo " $GEM_LINE" >> Gemfile
|
||||
echo "end" >> Gemfile
|
||||
fi
|
||||
else
|
||||
# Add to main section (after source line)
|
||||
sed -i.bak "/^source /a\\
|
||||
$GEM_LINE
|
||||
" Gemfile && rm Gemfile.bak
|
||||
fi
|
||||
|
||||
echo "✅ Added to Gemfile"
|
||||
echo ""
|
||||
echo "Running bundle install..."
|
||||
bundle install
|
||||
|
||||
echo ""
|
||||
echo "✅ Gem installed successfully"
|
||||
bundle info "$GEM_NAME"
|
||||
32
skills/ruby-bundler/scripts/bundle_audit.sh
Executable file
32
skills/ruby-bundler/scripts/bundle_audit.sh
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
# Security audit for Ruby dependencies
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔒 Running security audit..."
|
||||
|
||||
# Check if bundler-audit is installed
|
||||
if ! gem list bundler-audit -i &> /dev/null; then
|
||||
echo "📦 Installing bundler-audit..."
|
||||
gem install bundler-audit
|
||||
bundle audit --update
|
||||
fi
|
||||
|
||||
# Update vulnerability database
|
||||
echo "Updating vulnerability database..."
|
||||
bundle audit --update
|
||||
|
||||
# Run audit
|
||||
echo ""
|
||||
echo "Checking for vulnerabilities..."
|
||||
if bundle audit check; then
|
||||
echo ""
|
||||
echo "✅ No vulnerabilities found!"
|
||||
else
|
||||
echo ""
|
||||
echo "❌ Vulnerabilities detected!"
|
||||
echo ""
|
||||
echo "To fix, run:"
|
||||
echo " bundle update <gem_name>"
|
||||
exit 1
|
||||
fi
|
||||
39
skills/ruby-bundler/scripts/bundle_install.sh
Executable file
39
skills/ruby-bundler/scripts/bundle_install.sh
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
# Bundle install with error handling and helpful output
|
||||
|
||||
set -e
|
||||
|
||||
echo "📦 Installing Ruby dependencies..."
|
||||
|
||||
# Check if Gemfile exists
|
||||
if [ ! -f "Gemfile" ]; then
|
||||
echo "❌ Error: No Gemfile found in current directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if bundler is installed
|
||||
if ! command -v bundle &> /dev/null; then
|
||||
echo "⚠️ Bundler not found. Installing bundler..."
|
||||
gem install bundler
|
||||
fi
|
||||
|
||||
# Show bundler version
|
||||
echo "Using bundler version: $(bundle --version)"
|
||||
|
||||
# Run bundle install with options
|
||||
if bundle install "$@"; then
|
||||
echo "✅ Dependencies installed successfully"
|
||||
echo ""
|
||||
echo "Installed gems:"
|
||||
bundle list --name-only | head -10
|
||||
total=$(bundle list --name-only | wc -l)
|
||||
echo "... and $(($total - 10)) more gems"
|
||||
else
|
||||
echo "❌ Bundle install failed"
|
||||
echo ""
|
||||
echo "Troubleshooting steps:"
|
||||
echo "1. Try: rm Gemfile.lock && bundle install"
|
||||
echo "2. Update bundler: gem install bundler"
|
||||
echo "3. Check Ruby version: ruby --version"
|
||||
exit 1
|
||||
fi
|
||||
40
skills/ruby-bundler/scripts/bundle_update.sh
Executable file
40
skills/ruby-bundler/scripts/bundle_update.sh
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
# Update gems with safety options
|
||||
|
||||
set -e
|
||||
|
||||
MODE=${1:-conservative}
|
||||
|
||||
echo "📦 Updating Ruby gems (mode: $MODE)..."
|
||||
|
||||
case "$MODE" in
|
||||
patch)
|
||||
echo "Updating patch versions only (safest)"
|
||||
bundle update --patch
|
||||
;;
|
||||
minor)
|
||||
echo "Updating minor versions"
|
||||
bundle update --minor
|
||||
;;
|
||||
conservative)
|
||||
echo "Showing outdated gems first..."
|
||||
bundle outdated
|
||||
echo ""
|
||||
echo "Run with 'patch' or 'minor' or 'all' to update"
|
||||
exit 0
|
||||
;;
|
||||
all)
|
||||
echo "⚠️ Updating all gems (use with caution)"
|
||||
bundle update
|
||||
;;
|
||||
*)
|
||||
echo "Updating specific gem: $MODE"
|
||||
bundle update "$MODE"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo "✅ Update complete"
|
||||
echo ""
|
||||
echo "Changes:"
|
||||
git diff Gemfile.lock 2>/dev/null || echo "No git repository found"
|
||||
634
skills/ruby-gem-scaffolder/SKILL.md
Normal file
634
skills/ruby-gem-scaffolder/SKILL.md
Normal file
@@ -0,0 +1,634 @@
|
||||
# Ruby Gem Scaffolder Skill
|
||||
|
||||
Intelligent gem creation and scaffolding following Ruby best practices.
|
||||
|
||||
## When to Activate
|
||||
|
||||
This skill activates when:
|
||||
- User requests to create a new gem
|
||||
- User asks to scaffold a Ruby library
|
||||
- User wants to extract code into a gem
|
||||
- User mentions "gemspec" or "gem structure"
|
||||
|
||||
## Core Capabilities
|
||||
|
||||
### 1. Create New Gem
|
||||
|
||||
**Using Bundler (Recommended):**
|
||||
```bash
|
||||
bundle gem gem_name
|
||||
|
||||
# With RSpec
|
||||
bundle gem gem_name --test=rspec
|
||||
|
||||
# With MIT license
|
||||
bundle gem gem_name --mit
|
||||
|
||||
# With code of conduct
|
||||
bundle gem gem_name --coc
|
||||
|
||||
# All together
|
||||
bundle gem gem_name --test=rspec --mit --coc
|
||||
```
|
||||
|
||||
**Interactive Creation:**
|
||||
|
||||
When user requests: "Create a new gem called my_awesome_gem"
|
||||
|
||||
Ask clarifying questions:
|
||||
1. Test framework? (rspec/minitest)
|
||||
2. License? (MIT/Apache-2.0/GPL-3.0)
|
||||
3. CI? (GitHub Actions/CircleCI/None)
|
||||
4. Code of Conduct? (yes/no)
|
||||
|
||||
Then scaffold appropriately.
|
||||
|
||||
### 2. Standard Gem Structure
|
||||
|
||||
```
|
||||
my_gem/
|
||||
├── .github/
|
||||
│ └── workflows/
|
||||
│ └── ci.yml # GitHub Actions CI
|
||||
├── lib/
|
||||
│ ├── my_gem/
|
||||
│ │ └── version.rb # Version constant
|
||||
│ └── my_gem.rb # Main entry point
|
||||
├── spec/
|
||||
│ ├── spec_helper.rb # RSpec configuration
|
||||
│ └── my_gem_spec.rb # Tests
|
||||
├── .gitignore # Git ignores
|
||||
├── .rubocop.yml # RuboCop config
|
||||
├── CHANGELOG.md # Version history
|
||||
├── CODE_OF_CONDUCT.md # Community guidelines
|
||||
├── Gemfile # Development dependencies
|
||||
├── LICENSE.txt # License text
|
||||
├── README.md # Documentation
|
||||
├── Rakefile # Rake tasks
|
||||
└── my_gem.gemspec # Gem specification
|
||||
```
|
||||
|
||||
### 3. Generate Gemspec
|
||||
|
||||
**Template gemspec:**
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "lib/my_gem/version"
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = "my_gem"
|
||||
spec.version = MyGem::VERSION
|
||||
spec.authors = ["Your Name"]
|
||||
spec.email = ["your.email@example.com"]
|
||||
|
||||
spec.summary = "Brief description of your gem"
|
||||
spec.description = "Longer description of what your gem does"
|
||||
spec.homepage = "https://github.com/username/my_gem"
|
||||
spec.license = "MIT"
|
||||
spec.required_ruby_version = ">= 3.0.0"
|
||||
|
||||
spec.metadata["homepage_uri"] = spec.homepage
|
||||
spec.metadata["source_code_uri"] = "https://github.com/username/my_gem"
|
||||
spec.metadata["changelog_uri"] = "https://github.com/username/my_gem/blob/main/CHANGELOG.md"
|
||||
|
||||
# Specify which files should be added to the gem when it is released.
|
||||
spec.files = Dir.glob("lib/**/*") + %w[
|
||||
README.md
|
||||
LICENSE.txt
|
||||
CHANGELOG.md
|
||||
]
|
||||
spec.require_paths = ["lib"]
|
||||
|
||||
# Runtime dependencies
|
||||
# spec.add_dependency "example-gem", "~> 1.0"
|
||||
|
||||
# Development dependencies
|
||||
spec.add_development_dependency "rake", "~> 13.0"
|
||||
spec.add_development_dependency "rspec", "~> 3.12"
|
||||
spec.add_development_dependency "rubocop", "~> 1.50"
|
||||
end
|
||||
```
|
||||
|
||||
### 4. Create Main Entry Point
|
||||
|
||||
**lib/my_gem.rb:**
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "my_gem/version"
|
||||
|
||||
module MyGem
|
||||
class Error < StandardError; end
|
||||
|
||||
# Your code goes here...
|
||||
|
||||
# Optional: Configuration
|
||||
class << self
|
||||
attr_accessor :configuration
|
||||
end
|
||||
|
||||
def self.configure
|
||||
self.configuration ||= Configuration.new
|
||||
yield(configuration)
|
||||
end
|
||||
|
||||
class Configuration
|
||||
attr_accessor :option1, :option2
|
||||
|
||||
def initialize
|
||||
@option1 = "default_value"
|
||||
@option2 = "default_value"
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
**lib/my_gem/version.rb:**
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
module MyGem
|
||||
VERSION = "0.1.0"
|
||||
end
|
||||
```
|
||||
|
||||
### 5. Set Up Testing
|
||||
|
||||
**spec/spec_helper.rb:**
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "my_gem"
|
||||
|
||||
RSpec.configure do |config|
|
||||
# Enable flags like --only-failures and --next-failure
|
||||
config.example_status_persistence_file_path = ".rspec_status"
|
||||
|
||||
# Disable RSpec exposing methods globally on `Module` and `main`
|
||||
config.disable_monkey_patching!
|
||||
|
||||
config.expect_with :rspec do |c|
|
||||
c.syntax = :expect
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
**spec/my_gem_spec.rb:**
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe MyGem do
|
||||
it "has a version number" do
|
||||
expect(MyGem::VERSION).not_to be nil
|
||||
end
|
||||
|
||||
describe ".configure" do
|
||||
it "yields configuration block" do
|
||||
MyGem.configure do |config|
|
||||
config.option1 = "custom_value"
|
||||
end
|
||||
|
||||
expect(MyGem.configuration.option1).to eq("custom_value")
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### 6. Add Rake Tasks
|
||||
|
||||
**Rakefile:**
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "bundler/gem_tasks"
|
||||
require "rspec/core/rake_task"
|
||||
require "rubocop/rake_task"
|
||||
|
||||
RSpec::Core::RakeTask.new(:spec)
|
||||
RuboCop::RakeTask.new
|
||||
|
||||
task default: %i[spec rubocop]
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
rake spec # Run tests
|
||||
rake rubocop # Run linter
|
||||
rake # Run both (default)
|
||||
rake build # Build gem
|
||||
rake install # Install gem locally
|
||||
rake release # Release to RubyGems
|
||||
```
|
||||
|
||||
### 7. CI Configuration
|
||||
|
||||
**GitHub Actions (.github/workflows/ci.yml):**
|
||||
```yaml
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
ruby-version: ['3.0', '3.1', '3.2']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby-version }}
|
||||
bundler-cache: true
|
||||
|
||||
- name: Run tests
|
||||
run: bundle exec rake spec
|
||||
|
||||
- name: Run RuboCop
|
||||
run: bundle exec rake rubocop
|
||||
```
|
||||
|
||||
### 8. Documentation Templates
|
||||
|
||||
**README.md structure:**
|
||||
```markdown
|
||||
# MyGem
|
||||
|
||||
Brief description of what your gem does.
|
||||
|
||||
## Installation
|
||||
|
||||
Add this line to your application's Gemfile:
|
||||
|
||||
```ruby
|
||||
gem 'my_gem'
|
||||
```
|
||||
|
||||
And then execute:
|
||||
|
||||
$ bundle install
|
||||
|
||||
Or install it yourself as:
|
||||
|
||||
$ gem install my_gem
|
||||
|
||||
## Usage
|
||||
|
||||
```ruby
|
||||
require 'my_gem'
|
||||
|
||||
# Basic usage
|
||||
MyGem.do_something
|
||||
|
||||
# With configuration
|
||||
MyGem.configure do |config|
|
||||
config.option1 = "value"
|
||||
end
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
|
||||
|
||||
To install this gem onto your local machine, run `bundle exec rake install`.
|
||||
|
||||
## Contributing
|
||||
|
||||
Bug reports and pull requests are welcome on GitHub at https://github.com/username/my_gem.
|
||||
|
||||
## License
|
||||
|
||||
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
||||
```
|
||||
|
||||
**CHANGELOG.md structure:**
|
||||
```markdown
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Initial release
|
||||
- Core functionality
|
||||
|
||||
## [0.1.0] - 2025-01-15
|
||||
|
||||
### Added
|
||||
- Initial release
|
||||
```
|
||||
|
||||
### 9. Common Gem Patterns
|
||||
|
||||
**Pattern: CLI Gem**
|
||||
```ruby
|
||||
# lib/my_gem/cli.rb
|
||||
require 'optparse'
|
||||
|
||||
module MyGem
|
||||
class CLI
|
||||
def self.start(args)
|
||||
new(args).execute
|
||||
end
|
||||
|
||||
def initialize(args)
|
||||
@args = args
|
||||
@options = {}
|
||||
end
|
||||
|
||||
def execute
|
||||
parse_options
|
||||
# Your CLI logic here
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_options
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "Usage: my_gem [options]"
|
||||
|
||||
opts.on("-v", "--version", "Print version") do
|
||||
puts MyGem::VERSION
|
||||
exit
|
||||
end
|
||||
|
||||
opts.on("-h", "--help", "Print help") do
|
||||
puts opts
|
||||
exit
|
||||
end
|
||||
end.parse!(@args)
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
**Pattern: Library with Data Objects**
|
||||
```ruby
|
||||
# lib/my_gem/data_object.rb
|
||||
module MyGem
|
||||
class DataObject
|
||||
attr_reader :attribute1, :attribute2
|
||||
|
||||
def self.build(attributes)
|
||||
new(
|
||||
attribute1: attributes[:attribute1],
|
||||
attribute2: attributes[:attribute2]
|
||||
)
|
||||
end
|
||||
|
||||
def initialize(attribute1:, attribute2:)
|
||||
@attribute1 = attribute1
|
||||
@attribute2 = attribute2
|
||||
end
|
||||
|
||||
def to_h
|
||||
{
|
||||
attribute1: attribute1,
|
||||
attribute2: attribute2
|
||||
}
|
||||
end
|
||||
|
||||
def to_json(*args)
|
||||
to_h.to_json(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
**Pattern: Plugin System**
|
||||
```ruby
|
||||
# lib/my_gem/plugin.rb
|
||||
module MyGem
|
||||
module Plugin
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def register_plugin(name, klass)
|
||||
plugins[name] = klass
|
||||
end
|
||||
|
||||
def plugins
|
||||
@plugins ||= {}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### 10. Interactive Workflows
|
||||
|
||||
**Workflow: Create New Gem**
|
||||
|
||||
**User:** "Create a new gem called data_processor"
|
||||
|
||||
**Response:**
|
||||
```
|
||||
I'll help you create the data_processor gem. Let me ask a few questions:
|
||||
|
||||
1. Test framework: RSpec or Minitest? [RSpec]
|
||||
2. License: MIT, Apache-2.0, GPL-3.0? [MIT]
|
||||
3. Include GitHub Actions CI? [yes]
|
||||
4. Include Code of Conduct? [yes]
|
||||
|
||||
Creating gem with:
|
||||
- Test framework: RSpec
|
||||
- License: MIT
|
||||
- CI: GitHub Actions
|
||||
- Code of Conduct: Yes
|
||||
|
||||
Running: bundle gem data_processor --test=rspec --mit --coc
|
||||
|
||||
✓ Created gem structure
|
||||
✓ Initialized git repository
|
||||
✓ Created initial commit
|
||||
|
||||
Next steps:
|
||||
1. cd data_processor
|
||||
2. Edit lib/data_processor.rb to add your code
|
||||
3. Update data_processor.gemspec with description
|
||||
4. Add tests in spec/
|
||||
5. Run: bundle exec rake spec
|
||||
|
||||
Would you like me to:
|
||||
- Generate a sample data object class?
|
||||
- Set up CI configuration?
|
||||
- Create example tests?
|
||||
```
|
||||
|
||||
**Workflow: Add Class to Existing Gem**
|
||||
|
||||
**User:** "Add a Parser class to my gem"
|
||||
|
||||
**Response:**
|
||||
```
|
||||
I'll add a Parser class following POODR principles.
|
||||
|
||||
Creating:
|
||||
1. lib/my_gem/parser.rb
|
||||
2. spec/my_gem/parser_spec.rb
|
||||
|
||||
lib/my_gem/parser.rb:
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
module MyGem
|
||||
class Parser
|
||||
def initialize(input)
|
||||
@input = input
|
||||
end
|
||||
|
||||
def parse
|
||||
# Implementation here
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :input
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
spec/my_gem/parser_spec.rb:
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe MyGem::Parser do
|
||||
describe '#parse' do
|
||||
it 'parses input correctly' do
|
||||
parser = described_class.new("input")
|
||||
result = parser.parse
|
||||
|
||||
expect(result).to eq("expected_output")
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Don't forget to require it in lib/my_gem.rb:
|
||||
```ruby
|
||||
require_relative "my_gem/parser"
|
||||
```
|
||||
|
||||
Would you like me to add the require statement?
|
||||
```
|
||||
|
||||
### 11. Version Management
|
||||
|
||||
**Semantic Versioning Guidelines:**
|
||||
|
||||
```
|
||||
MAJOR.MINOR.PATCH
|
||||
|
||||
MAJOR: Breaking changes
|
||||
MINOR: New features (backward compatible)
|
||||
PATCH: Bug fixes
|
||||
```
|
||||
|
||||
**Update version:**
|
||||
```ruby
|
||||
# lib/my_gem/version.rb
|
||||
module MyGem
|
||||
VERSION = "1.2.3" # Update this
|
||||
end
|
||||
```
|
||||
|
||||
**Add to CHANGELOG.md:**
|
||||
```markdown
|
||||
## [1.2.3] - 2025-01-15
|
||||
|
||||
### Fixed
|
||||
- Bug in parser when handling edge cases
|
||||
```
|
||||
|
||||
### 12. Publishing Checklist
|
||||
|
||||
Before `rake release`:
|
||||
|
||||
- [ ] All tests passing
|
||||
- [ ] RuboCop clean
|
||||
- [ ] README updated
|
||||
- [ ] CHANGELOG updated
|
||||
- [ ] Version bumped
|
||||
- [ ] Committed and pushed to GitHub
|
||||
- [ ] RubyGems.org account configured
|
||||
|
||||
```bash
|
||||
# First time setup
|
||||
curl -u username https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials
|
||||
chmod 0600 ~/.gem/credentials
|
||||
|
||||
# Release
|
||||
rake release
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Follow Semantic Versioning** strictly
|
||||
2. **Write comprehensive README** with examples
|
||||
3. **Maintain CHANGELOG** for all versions
|
||||
4. **Keep dependencies minimal** for gems
|
||||
5. **Test on multiple Ruby versions** in CI
|
||||
6. **Use frozen_string_literal** in all files
|
||||
7. **Namespace your gem** to avoid conflicts
|
||||
8. **Document public API** thoroughly
|
||||
9. **Keep gemspec metadata** up to date
|
||||
10. **Use pessimistic versioning** for dependencies
|
||||
|
||||
## Error Prevention
|
||||
|
||||
**Common Mistakes:**
|
||||
|
||||
1. **Missing files in gemspec**
|
||||
```ruby
|
||||
# Bad
|
||||
spec.files = `git ls-files`.split("\n")
|
||||
|
||||
# Good
|
||||
spec.files = Dir.glob("lib/**/*") + %w[README.md LICENSE.txt]
|
||||
```
|
||||
|
||||
2. **Not specifying Ruby version**
|
||||
```ruby
|
||||
# Always specify
|
||||
spec.required_ruby_version = ">= 3.0.0"
|
||||
```
|
||||
|
||||
3. **Including development gems in gemspec**
|
||||
```ruby
|
||||
# Don't do this
|
||||
spec.add_dependency "rspec" # This is for dev only!
|
||||
|
||||
# Do this
|
||||
spec.add_development_dependency "rspec"
|
||||
```
|
||||
|
||||
## Response Format
|
||||
|
||||
When scaffolding:
|
||||
|
||||
**Files Created:**
|
||||
- List each file with brief description
|
||||
|
||||
**Next Steps:**
|
||||
1. Specific actions to take
|
||||
2. Commands to run
|
||||
3. Files to edit
|
||||
|
||||
**Suggestions:**
|
||||
- Patterns that might be useful
|
||||
- Additional features to consider
|
||||
- Testing strategies
|
||||
101
skills/ruby-gem-scaffolder/scripts/README.md
Normal file
101
skills/ruby-gem-scaffolder/scripts/README.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# Gem Scaffolder Scripts
|
||||
|
||||
Executable shell scripts for creating and managing Ruby gems.
|
||||
|
||||
## Scripts
|
||||
|
||||
### create_gem.sh
|
||||
Create a new Ruby gem with best practices.
|
||||
|
||||
```bash
|
||||
# Interactive (uses defaults)
|
||||
./create_gem.sh my_gem
|
||||
|
||||
# With RSpec and MIT license
|
||||
./create_gem.sh my_gem --test=rspec --mit
|
||||
|
||||
# With Minitest and Apache license
|
||||
./create_gem.sh my_gem --test=minitest --apache
|
||||
|
||||
# With GitHub Actions CI
|
||||
./create_gem.sh my_gem --ci=github
|
||||
|
||||
# With Code of Conduct
|
||||
./create_gem.sh my_gem --coc
|
||||
|
||||
# All options
|
||||
./create_gem.sh my_gem --test=rspec --mit --ci=github --coc
|
||||
```
|
||||
|
||||
**Options:**
|
||||
- `--test=rspec|minitest` - Test framework (default: rspec)
|
||||
- `--mit|--apache|--gpl` - License (default: mit)
|
||||
- `--ci=github|circle` - CI provider
|
||||
- `--coc` - Add Code of Conduct
|
||||
|
||||
**Creates:**
|
||||
```
|
||||
my_gem/
|
||||
├── .github/workflows/ci.yml
|
||||
├── lib/
|
||||
│ ├── my_gem/version.rb
|
||||
│ └── my_gem.rb
|
||||
├── spec/
|
||||
├── Gemfile
|
||||
├── Rakefile
|
||||
└── my_gem.gemspec
|
||||
```
|
||||
|
||||
### add_gem_class.sh
|
||||
Add a new class to an existing gem with test.
|
||||
|
||||
```bash
|
||||
./add_gem_class.sh my_gem Parser
|
||||
```
|
||||
|
||||
**Creates:**
|
||||
- `lib/my_gem/parser.rb` - Class file with template
|
||||
- `spec/my_gem/parser_spec.rb` - RSpec test template
|
||||
|
||||
**Features:**
|
||||
- Follows frozen_string_literal convention
|
||||
- Proper module namespacing
|
||||
- Basic class structure with private attr_reader
|
||||
- Matching spec file with describe blocks
|
||||
- Reminder to require in main file
|
||||
|
||||
## Example Workflow
|
||||
|
||||
```bash
|
||||
# 1. Create new gem
|
||||
./create_gem.sh data_parser --test=rspec --mit --ci=github
|
||||
|
||||
# 2. Navigate to gem
|
||||
cd data_parser
|
||||
|
||||
# 3. Add a Parser class
|
||||
../add_gem_class.sh data_parser Parser
|
||||
|
||||
# 4. Add a Formatter class
|
||||
../add_gem_class.sh data_parser Formatter
|
||||
|
||||
# 5. Install dependencies
|
||||
bundle install
|
||||
|
||||
# 6. Run tests
|
||||
bundle exec rake spec
|
||||
```
|
||||
|
||||
## Templates
|
||||
|
||||
Both scripts use POODR-compliant templates:
|
||||
- Dependency injection via initialize
|
||||
- Private attr_readers
|
||||
- Clear public/private interfaces
|
||||
- Frozen string literals
|
||||
- Proper RSpec structure
|
||||
|
||||
## Requirements
|
||||
|
||||
- bundler gem installed
|
||||
- git (for gem creation)
|
||||
61
skills/ruby-gem-scaffolder/scripts/add_gem_class.sh
Executable file
61
skills/ruby-gem-scaffolder/scripts/add_gem_class.sh
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
# Add a new class to an existing gem
|
||||
|
||||
set -e
|
||||
|
||||
if [ $# -lt 2 ]; then
|
||||
echo "Usage: $0 <gem_name> <class_name>"
|
||||
echo "Example: $0 my_gem Parser"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GEM_NAME=$1
|
||||
CLASS_NAME=$2
|
||||
FILE_NAME=$(echo "$CLASS_NAME" | sed 's/\([A-Z]\)/_\L\1/g;s/^_//')
|
||||
|
||||
echo "📝 Adding class $CLASS_NAME to $GEM_NAME gem..."
|
||||
|
||||
# Create class file
|
||||
cat > "lib/${GEM_NAME}/${FILE_NAME}.rb" << RUBY
|
||||
# frozen_string_literal: true
|
||||
|
||||
module $(echo "$GEM_NAME" | sed 's/_\([a-z]\)/\U\1/g;s/^./\U&/')
|
||||
class $CLASS_NAME
|
||||
def initialize(input)
|
||||
@input = input
|
||||
end
|
||||
|
||||
def process
|
||||
# Implementation here
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :input
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
# Create spec file
|
||||
mkdir -p "spec/${GEM_NAME}"
|
||||
cat > "spec/${GEM_NAME}/${FILE_NAME}_spec.rb" << RUBY
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe $(echo "$GEM_NAME" | sed 's/_\([a-z]\)/\U\1/g;s/^./\U&/')::$CLASS_NAME do
|
||||
describe '#process' do
|
||||
it 'processes input correctly' do
|
||||
instance = described_class.new("test_input")
|
||||
result = instance.process
|
||||
|
||||
expect(result).to be_nil # Update with actual expectation
|
||||
end
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
echo "✅ Created files:"
|
||||
echo " - lib/${GEM_NAME}/${FILE_NAME}.rb"
|
||||
echo " - spec/${GEM_NAME}/${FILE_NAME}_spec.rb"
|
||||
echo ""
|
||||
echo "Don't forget to require it in lib/${GEM_NAME}.rb:"
|
||||
echo " require_relative \"${GEM_NAME}/${FILE_NAME}\""
|
||||
84
skills/ruby-gem-scaffolder/scripts/create_gem.sh
Executable file
84
skills/ruby-gem-scaffolder/scripts/create_gem.sh
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env bash
|
||||
# Create a new Ruby gem with best practices
|
||||
|
||||
set -e
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Usage: $0 <gem_name> [options]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --test=rspec|minitest (default: rspec)"
|
||||
echo " --mit|--apache|--gpl"
|
||||
echo " --ci=github|circle"
|
||||
echo " --coc (add Code of Conduct)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GEM_NAME=$1
|
||||
shift
|
||||
|
||||
TEST_FRAMEWORK="rspec"
|
||||
LICENSE="mit"
|
||||
CI=""
|
||||
COC=""
|
||||
|
||||
# Parse options
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
--test=*)
|
||||
TEST_FRAMEWORK="${arg#*=}"
|
||||
;;
|
||||
--mit)
|
||||
LICENSE="mit"
|
||||
;;
|
||||
--apache)
|
||||
LICENSE="apache"
|
||||
;;
|
||||
--gpl)
|
||||
LICENSE="gpl-3"
|
||||
;;
|
||||
--ci=*)
|
||||
CI="--ci=${arg#*=}"
|
||||
;;
|
||||
--coc)
|
||||
COC="--coc"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo "🔨 Creating gem: $GEM_NAME"
|
||||
echo " Test framework: $TEST_FRAMEWORK"
|
||||
echo " License: $LICENSE"
|
||||
echo ""
|
||||
|
||||
# Check if bundler is installed
|
||||
if ! command -v bundle &> /dev/null; then
|
||||
echo "Installing bundler..."
|
||||
gem install bundler
|
||||
fi
|
||||
|
||||
# Create gem
|
||||
bundle gem "$GEM_NAME" \
|
||||
--test="$TEST_FRAMEWORK" \
|
||||
--"$LICENSE" \
|
||||
$CI \
|
||||
$COC
|
||||
|
||||
cd "$GEM_NAME"
|
||||
|
||||
echo ""
|
||||
echo "✅ Gem created successfully!"
|
||||
echo ""
|
||||
echo "Structure:"
|
||||
tree -L 2 -I 'vendor|tmp' || ls -R
|
||||
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " cd $GEM_NAME"
|
||||
echo " bundle install"
|
||||
echo " bundle exec rake spec"
|
||||
echo ""
|
||||
echo "Edit these files:"
|
||||
echo " - ${GEM_NAME}.gemspec (add description)"
|
||||
echo " - lib/${GEM_NAME}.rb (add your code)"
|
||||
echo " - spec/${GEM_NAME}_spec.rb (add tests)"
|
||||
342
skills/ruby-pattern-detector/SKILL.md
Normal file
342
skills/ruby-pattern-detector/SKILL.md
Normal file
@@ -0,0 +1,342 @@
|
||||
# Ruby Pattern Detector Skill
|
||||
|
||||
Automatically detect and suggest common Ruby patterns when working with Ruby code.
|
||||
|
||||
## When to Activate
|
||||
|
||||
This skill activates when:
|
||||
- Reading or editing Ruby files
|
||||
- User asks about Ruby patterns or best practices
|
||||
- Refactoring Ruby code
|
||||
- Reviewing Ruby code
|
||||
|
||||
## Patterns to Detect
|
||||
|
||||
### 1. Data Object Pattern
|
||||
|
||||
**Detect:**
|
||||
- Classes with many attr_reader/attr_accessor declarations
|
||||
- Classes that primarily hold data
|
||||
- Classes with `to_h` or `to_json` methods
|
||||
|
||||
**Suggest:**
|
||||
```ruby
|
||||
class DataObject
|
||||
# Add .build class method for construction
|
||||
def self.build(attributes)
|
||||
new(
|
||||
name: attributes[:name],
|
||||
email: attributes[:email]
|
||||
)
|
||||
end
|
||||
|
||||
# Add serialization
|
||||
def to_h
|
||||
{
|
||||
name: name,
|
||||
email: email
|
||||
}
|
||||
end
|
||||
|
||||
# Add factory methods
|
||||
def self.from_json(json)
|
||||
build(JSON.parse(json, symbolize_names: true))
|
||||
end
|
||||
|
||||
def self.from_h(hash)
|
||||
build(hash)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### 2. Loggable Module Pattern
|
||||
|
||||
**Detect:**
|
||||
- Classes with logging statements
|
||||
- Multiple classes that need logging
|
||||
- Direct Logger instantiation in classes
|
||||
|
||||
**Suggest:**
|
||||
```ruby
|
||||
# Create shared Loggable module
|
||||
module Loggable
|
||||
def logger
|
||||
@logger ||= Logger.new(STDOUT).tap do |log|
|
||||
log.progname = self.class.name
|
||||
log.level = ENV.fetch('LOG_LEVEL', 'INFO')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Include in classes
|
||||
class MyClass
|
||||
include Loggable
|
||||
|
||||
def process
|
||||
logger.info "Processing started"
|
||||
# ...
|
||||
logger.debug "Details: #{details}"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### 3. Custom Exception Pattern
|
||||
|
||||
**Detect:**
|
||||
- Raising generic exceptions (RuntimeError, StandardError)
|
||||
- Classes with domain-specific errors
|
||||
- Rescue blocks catching broad exceptions
|
||||
|
||||
**Suggest:**
|
||||
```ruby
|
||||
# Define custom exceptions
|
||||
module MyApp
|
||||
class Error < StandardError; end
|
||||
class NotFoundError < Error; end
|
||||
class ValidationError < Error; end
|
||||
class AuthenticationError < Error; end
|
||||
end
|
||||
|
||||
# Use specific exceptions
|
||||
class UserService
|
||||
def find(id)
|
||||
user = repository.find(id)
|
||||
raise MyApp::NotFoundError, "User #{id} not found" unless user
|
||||
user
|
||||
end
|
||||
|
||||
def authenticate(credentials)
|
||||
raise MyApp::ValidationError, "Invalid credentials" if invalid?(credentials)
|
||||
# ...
|
||||
rescue SomeExternalError => e
|
||||
raise MyApp::AuthenticationError, "Auth failed: #{e.message}"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### 4. Dependency Injection Pattern
|
||||
|
||||
**Detect:**
|
||||
- Classes instantiating other classes directly
|
||||
- Hard-coded dependencies
|
||||
- Difficult to test classes
|
||||
- Use of global state or singletons
|
||||
|
||||
**Suggest:**
|
||||
```ruby
|
||||
# Before - hard-coded dependency
|
||||
class OrderProcessor
|
||||
def process(order)
|
||||
mailer = EmailMailer.new
|
||||
mailer.send_confirmation(order)
|
||||
end
|
||||
end
|
||||
|
||||
# After - injected dependency
|
||||
class OrderProcessor
|
||||
def initialize(mailer: EmailMailer.new)
|
||||
@mailer = mailer
|
||||
end
|
||||
|
||||
def process(order)
|
||||
@mailer.send_confirmation(order)
|
||||
end
|
||||
end
|
||||
|
||||
# Easy to test with mock
|
||||
processor = OrderProcessor.new(mailer: MockMailer.new)
|
||||
```
|
||||
|
||||
### 5. Null Object Pattern
|
||||
|
||||
**Detect:**
|
||||
- Frequent nil checks
|
||||
- Conditional logic checking for nil
|
||||
- `try` or `&.` operators used extensively
|
||||
|
||||
**Suggest:**
|
||||
```ruby
|
||||
# Create Null Object
|
||||
class NullUser
|
||||
def name
|
||||
"Guest"
|
||||
end
|
||||
|
||||
def email
|
||||
nil
|
||||
end
|
||||
|
||||
def admin?
|
||||
false
|
||||
end
|
||||
|
||||
def null?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
# Use in code
|
||||
class Session
|
||||
def current_user
|
||||
@current_user || NullUser.new
|
||||
end
|
||||
end
|
||||
|
||||
# No more nil checks needed
|
||||
session.current_user.name # Returns "Guest" instead of raising error
|
||||
```
|
||||
|
||||
### 6. Value Object Pattern
|
||||
|
||||
**Detect:**
|
||||
- Primitive obsession (lots of strings/integers used as domain concepts)
|
||||
- Data validation scattered throughout code
|
||||
- Lack of encapsulation for related data
|
||||
|
||||
**Suggest:**
|
||||
```ruby
|
||||
# Before - primitive obsession
|
||||
def send_email(email_string)
|
||||
raise "Invalid email" unless email_string =~ /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
|
||||
# ...
|
||||
end
|
||||
|
||||
# After - Value Object
|
||||
class Email
|
||||
attr_reader :value
|
||||
|
||||
def initialize(value)
|
||||
@value = value.to_s.downcase.strip
|
||||
validate!
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
value == other.value
|
||||
end
|
||||
|
||||
def to_s
|
||||
value
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate!
|
||||
raise ArgumentError, "Invalid email: #{value}" unless valid?
|
||||
end
|
||||
|
||||
def valid?
|
||||
value =~ /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
|
||||
end
|
||||
end
|
||||
|
||||
def send_email(email)
|
||||
# Email already validated
|
||||
mailer.send(to: email.to_s)
|
||||
end
|
||||
```
|
||||
|
||||
### 7. Query Object Pattern
|
||||
|
||||
**Detect:**
|
||||
- Complex ActiveRecord scopes
|
||||
- Long chains of where clauses
|
||||
- Business logic in controllers or models
|
||||
|
||||
**Suggest:**
|
||||
```ruby
|
||||
# Extract to Query Object
|
||||
class ActiveUsersQuery
|
||||
def initialize(relation = User.all)
|
||||
@relation = relation
|
||||
end
|
||||
|
||||
def call
|
||||
@relation
|
||||
.where(active: true)
|
||||
.where('last_login_at > ?', 30.days.ago)
|
||||
.order(created_at: :desc)
|
||||
end
|
||||
end
|
||||
|
||||
# Usage
|
||||
active_users = ActiveUsersQuery.new.call
|
||||
recent_active_users = ActiveUsersQuery.new(User.where('created_at > ?', 1.week.ago)).call
|
||||
```
|
||||
|
||||
### 8. Service Object Pattern
|
||||
|
||||
**Detect:**
|
||||
- Fat controllers or models
|
||||
- Complex multi-step operations
|
||||
- Methods that orchestrate multiple objects
|
||||
|
||||
**Suggest:**
|
||||
```ruby
|
||||
class CreateOrderService
|
||||
def initialize(user:, items:, payment_method:)
|
||||
@user = user
|
||||
@items = items
|
||||
@payment_method = payment_method
|
||||
end
|
||||
|
||||
def call
|
||||
ActiveRecord::Base.transaction do
|
||||
order = create_order
|
||||
process_payment(order)
|
||||
send_confirmation(order)
|
||||
order
|
||||
end
|
||||
rescue PaymentError => e
|
||||
handle_payment_failure(e)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :user, :items, :payment_method
|
||||
|
||||
def create_order
|
||||
# ...
|
||||
end
|
||||
|
||||
def process_payment(order)
|
||||
# ...
|
||||
end
|
||||
|
||||
def send_confirmation(order)
|
||||
# ...
|
||||
end
|
||||
end
|
||||
|
||||
# Usage
|
||||
result = CreateOrderService.new(
|
||||
user: current_user,
|
||||
items: cart.items,
|
||||
payment_method: params[:payment_method]
|
||||
).call
|
||||
```
|
||||
|
||||
## Activation Response
|
||||
|
||||
When a pattern is detected, respond with:
|
||||
|
||||
**Pattern Detected: [Pattern Name]**
|
||||
|
||||
I noticed [specific code smell or opportunity].
|
||||
|
||||
This is a good opportunity to use the **[Pattern Name]** pattern, which:
|
||||
- [Benefit 1]
|
||||
- [Benefit 2]
|
||||
- [Benefit 3]
|
||||
|
||||
Would you like me to refactor this code to use this pattern?
|
||||
|
||||
[Show brief before/after example]
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Only suggest patterns when clearly beneficial
|
||||
- Don't over-engineer simple code
|
||||
- Explain the "why" behind each pattern suggestion
|
||||
- Provide concrete code examples
|
||||
- Consider the context and project size
|
||||
- Balance between pattern purity and pragmatism
|
||||
169
skills/ruby-pattern-detector/scripts/README.md
Normal file
169
skills/ruby-pattern-detector/scripts/README.md
Normal file
@@ -0,0 +1,169 @@
|
||||
# Pattern Detector / RuboCop Scripts
|
||||
|
||||
Executable shell scripts for code style checking and analysis.
|
||||
|
||||
## Scripts
|
||||
|
||||
### run_rubocop.sh
|
||||
Run RuboCop with helpful options.
|
||||
|
||||
```bash
|
||||
# Check all files
|
||||
./run_rubocop.sh check
|
||||
|
||||
# Auto-fix safe violations
|
||||
./run_rubocop.sh fix
|
||||
|
||||
# Auto-fix all violations (including unsafe)
|
||||
./run_rubocop.sh fix-all
|
||||
|
||||
# Check only changed files
|
||||
./run_rubocop.sh changed
|
||||
|
||||
# Check only staged files (for pre-commit)
|
||||
./run_rubocop.sh staged
|
||||
|
||||
# Check specific file
|
||||
./run_rubocop.sh lib/user.rb
|
||||
```
|
||||
|
||||
**Modes:**
|
||||
- `check` - Check code style (no changes)
|
||||
- `fix` - Auto-fix safe violations (-a flag)
|
||||
- `fix-all` - Auto-fix all violations (-A flag, use with caution)
|
||||
- `changed` - Only check git-modified files
|
||||
- `staged` - Only check git-staged files
|
||||
- `<file_path>` - Check specific file
|
||||
|
||||
**Features:**
|
||||
- Auto-detects RuboCop availability
|
||||
- Smart git integration
|
||||
- Helpful error messages
|
||||
- Shows which mode is running
|
||||
|
||||
### rubocop_summary.sh
|
||||
Generate comprehensive RuboCop summary.
|
||||
|
||||
```bash
|
||||
./rubocop_summary.sh
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
📊 Generating RuboCop summary...
|
||||
|
||||
Summary:
|
||||
Files inspected: 45
|
||||
Total offenses: 127
|
||||
|
||||
Top offenses by cop:
|
||||
42 Style/StringLiterals
|
||||
23 Layout/LineLength
|
||||
15 Style/Documentation
|
||||
12 Metrics/MethodLength
|
||||
...
|
||||
|
||||
Offense severity breakdown:
|
||||
85 convention
|
||||
32 warning
|
||||
10 error
|
||||
|
||||
To fix auto-correctable offenses:
|
||||
bundle exec rubocop -a
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Runs RuboCop with JSON output
|
||||
- Parses and summarizes results
|
||||
- Shows top offense types
|
||||
- Severity breakdown
|
||||
- Actionable next steps
|
||||
|
||||
**Requires:**
|
||||
- jq (JSON processor)
|
||||
- RuboCop gem
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Pre-Commit Workflow
|
||||
```bash
|
||||
# Check staged files before commit
|
||||
./run_rubocop.sh staged
|
||||
|
||||
# Auto-fix if possible
|
||||
./run_rubocop.sh fix
|
||||
|
||||
# Stage fixes
|
||||
git add .
|
||||
|
||||
# Check again
|
||||
./run_rubocop.sh staged
|
||||
```
|
||||
|
||||
### Code Review Workflow
|
||||
```bash
|
||||
# Check changed files
|
||||
./run_rubocop.sh changed
|
||||
|
||||
# Get detailed summary
|
||||
./rubocop_summary.sh
|
||||
|
||||
# Fix safe violations
|
||||
./run_rubocop.sh fix
|
||||
|
||||
# Review remaining issues manually
|
||||
```
|
||||
|
||||
### Clean-up Workflow
|
||||
```bash
|
||||
# Get overview
|
||||
./rubocop_summary.sh
|
||||
|
||||
# Fix safe violations
|
||||
./run_rubocop.sh fix
|
||||
|
||||
# Review unsafe fixes
|
||||
./run_rubocop.sh check
|
||||
|
||||
# Manually fix or disable cops
|
||||
```
|
||||
|
||||
## RuboCop Configuration
|
||||
|
||||
Create `.rubocop.yml` for project-specific rules:
|
||||
|
||||
```yaml
|
||||
AllCops:
|
||||
NewCops: enable
|
||||
TargetRubyVersion: 3.2
|
||||
Exclude:
|
||||
- 'db/schema.rb'
|
||||
- 'vendor/**/*'
|
||||
- 'node_modules/**/*'
|
||||
|
||||
Metrics/MethodLength:
|
||||
Max: 10 # Sandi Metz's rule
|
||||
|
||||
Metrics/ClassLength:
|
||||
Max: 100 # Sandi Metz's rule
|
||||
|
||||
Style/Documentation:
|
||||
Enabled: false # Adjust as needed
|
||||
```
|
||||
|
||||
## Integration with Git Hooks
|
||||
|
||||
Use `staged` mode in pre-commit hook:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# .git/hooks/pre-commit
|
||||
|
||||
/path/to/run_rubocop.sh staged
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- RuboCop gem
|
||||
- jq (for rubocop_summary.sh)
|
||||
- git (for changed/staged modes)
|
||||
43
skills/ruby-pattern-detector/scripts/rubocop_summary.sh
Executable file
43
skills/ruby-pattern-detector/scripts/rubocop_summary.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env bash
|
||||
# Generate RuboCop summary report
|
||||
|
||||
set -e
|
||||
|
||||
echo "📊 Generating RuboCop summary..."
|
||||
|
||||
# Run RuboCop with JSON output
|
||||
bundle exec rubocop --format json --out tmp/rubocop_results.json --format progress || true
|
||||
|
||||
if [ ! -f "tmp/rubocop_results.json" ]; then
|
||||
echo "No RuboCop results found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse results
|
||||
TOTAL_FILES=$(jq '.files | length' tmp/rubocop_results.json)
|
||||
OFFENSE_COUNT=$(jq '.summary.offense_count' tmp/rubocop_results.json)
|
||||
INSPECTED=$(jq '.summary.inspected_file_count' tmp/rubocop_results.json)
|
||||
|
||||
echo ""
|
||||
echo "Summary:"
|
||||
echo " Files inspected: $INSPECTED"
|
||||
echo " Total offenses: $OFFENSE_COUNT"
|
||||
echo ""
|
||||
|
||||
if [ "$OFFENSE_COUNT" -eq 0 ]; then
|
||||
echo "✅ No offenses found!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Top offenses by cop:"
|
||||
jq -r '.files[].offenses[] | .cop_name' tmp/rubocop_results.json | \
|
||||
sort | uniq -c | sort -rn | head -10
|
||||
|
||||
echo ""
|
||||
echo "Offense severity breakdown:"
|
||||
jq -r '.files[].offenses[] | .severity' tmp/rubocop_results.json | \
|
||||
sort | uniq -c | sort -rn
|
||||
|
||||
echo ""
|
||||
echo "To fix auto-correctable offenses:"
|
||||
echo " bundle exec rubocop -a"
|
||||
62
skills/ruby-pattern-detector/scripts/run_rubocop.sh
Executable file
62
skills/ruby-pattern-detector/scripts/run_rubocop.sh
Executable file
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run RuboCop with helpful options
|
||||
|
||||
set -e
|
||||
|
||||
MODE=${1:-check}
|
||||
|
||||
echo "🔍 Running RuboCop (mode: $MODE)..."
|
||||
|
||||
# Check if RuboCop is available
|
||||
if ! bundle exec rubocop --version &> /dev/null; then
|
||||
echo "❌ RuboCop not found. Add to Gemfile:"
|
||||
echo " gem 'rubocop', require: false"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$MODE" in
|
||||
check)
|
||||
echo "Checking code style..."
|
||||
bundle exec rubocop
|
||||
;;
|
||||
fix)
|
||||
echo "Auto-fixing safe violations..."
|
||||
bundle exec rubocop -a
|
||||
;;
|
||||
fix-all)
|
||||
echo "⚠️ Auto-fixing all violations (including unsafe)..."
|
||||
bundle exec rubocop -A
|
||||
;;
|
||||
changed)
|
||||
echo "Checking only changed files..."
|
||||
CHANGED_FILES=$(git diff --name-only --diff-filter=AM | grep '\.rb$' || true)
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
echo "No changed Ruby files found"
|
||||
exit 0
|
||||
fi
|
||||
bundle exec rubocop $CHANGED_FILES
|
||||
;;
|
||||
staged)
|
||||
echo "Checking only staged files..."
|
||||
STAGED_FILES=$(git diff --cached --name-only --diff-filter=AM | grep '\.rb$' || true)
|
||||
if [ -z "$STAGED_FILES" ]; then
|
||||
echo "No staged Ruby files found"
|
||||
exit 0
|
||||
fi
|
||||
bundle exec rubocop $STAGED_FILES
|
||||
;;
|
||||
*)
|
||||
# Treat as file path
|
||||
if [ -f "$MODE" ]; then
|
||||
echo "Checking file: $MODE"
|
||||
bundle exec rubocop "$MODE"
|
||||
else
|
||||
echo "Unknown mode: $MODE"
|
||||
echo "Available modes: check, fix, fix-all, changed, staged, <file_path>"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo "✅ RuboCop check complete"
|
||||
192
skills/ruby-test-analyzer/SKILL.md
Normal file
192
skills/ruby-test-analyzer/SKILL.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# Ruby Test Analyzer Skill
|
||||
|
||||
Intelligently analyze RSpec test failures and provide actionable debugging guidance.
|
||||
|
||||
## When to Activate
|
||||
|
||||
This skill activates when:
|
||||
- RSpec tests fail
|
||||
- User runs test commands
|
||||
- User asks about test failures or debugging
|
||||
- Working with test files
|
||||
|
||||
## Core Capabilities
|
||||
|
||||
### 1. Failure Pattern Recognition
|
||||
|
||||
Automatically identify common failure patterns:
|
||||
|
||||
**NoMethodError Patterns:**
|
||||
- Nil object errors → Check object initialization
|
||||
- Missing method → Check for typos or missing implementation
|
||||
- Wrong receiver → Check factory setup or test doubles
|
||||
|
||||
**Expectation Failures:**
|
||||
- Off-by-one errors → Check loop boundaries
|
||||
- Wrong values → Check business logic implementation
|
||||
- Type mismatches → Check data transformation
|
||||
|
||||
**Database/State Issues:**
|
||||
- Transactional rollback problems
|
||||
- Factory/fixture conflicts
|
||||
- Test interdependencies
|
||||
|
||||
### 2. Provide Debugging Steps
|
||||
|
||||
For each failure, suggest:
|
||||
|
||||
1. **Immediate Checks:**
|
||||
- Verify object is not nil
|
||||
- Check method spelling
|
||||
- Confirm proper setup in before blocks
|
||||
|
||||
2. **Investigation Steps:**
|
||||
- Add `puts` or `binding.pry` at strategic points
|
||||
- Check factory definitions
|
||||
- Verify database state
|
||||
|
||||
3. **Likely Fixes:**
|
||||
- Code examples showing the fix
|
||||
- References to related files
|
||||
- Design improvements to prevent recurrence
|
||||
|
||||
### 3. Test Quality Analysis
|
||||
|
||||
Evaluate test quality:
|
||||
|
||||
**Good Signs:**
|
||||
- Clear describe/context structure
|
||||
- Single responsibility per test
|
||||
- Proper use of let/let!/before
|
||||
- Descriptive test names
|
||||
- Good isolation
|
||||
|
||||
**Warning Signs:**
|
||||
- Tests with multiple expectations
|
||||
- Tests depending on execution order
|
||||
- Overuse of let! (eager evaluation)
|
||||
- Brittle tests (too coupled to implementation)
|
||||
- Missing edge cases
|
||||
|
||||
### 4. Suggest Missing Tests
|
||||
|
||||
Identify untested scenarios:
|
||||
- Edge cases
|
||||
- Error conditions
|
||||
- Boundary values
|
||||
- Integration points
|
||||
- State transitions
|
||||
|
||||
## Example Analysis
|
||||
|
||||
**Failure Detected:**
|
||||
```
|
||||
Failure/Error: expect(user.full_name).to eq("John Doe")
|
||||
expected: "John Doe"
|
||||
got: "John"
|
||||
```
|
||||
|
||||
**Analysis Response:**
|
||||
|
||||
**Failure: User#full_name incomplete**
|
||||
- **Location:** spec/user_spec.rb:23
|
||||
- **Error Type:** Expectation mismatch
|
||||
|
||||
**Immediate Cause:**
|
||||
The `full_name` method is only returning the first name.
|
||||
|
||||
**Investigation Steps:**
|
||||
1. Check the implementation at lib/user.rb:15
|
||||
```ruby
|
||||
# Current implementation (likely):
|
||||
def full_name
|
||||
first_name
|
||||
end
|
||||
```
|
||||
|
||||
2. Verify test data:
|
||||
```ruby
|
||||
# Check that your factory/test setup has both names
|
||||
user = User.new(first_name: "John", last_name: "Doe")
|
||||
```
|
||||
|
||||
**Suggested Fix:**
|
||||
```ruby
|
||||
# lib/user.rb:15
|
||||
def full_name
|
||||
[first_name, last_name].compact.join(' ')
|
||||
end
|
||||
```
|
||||
|
||||
**Additional Considerations:**
|
||||
- Handle nil values gracefully (using `compact`)
|
||||
- Consider edge cases: missing last name, middle names
|
||||
- Add test for user with only first name
|
||||
|
||||
**Suggested Additional Tests:**
|
||||
```ruby
|
||||
context 'when last_name is missing' do
|
||||
it 'returns only first_name' do
|
||||
user = User.new(first_name: "John", last_name: nil)
|
||||
expect(user.full_name).to eq("John")
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Performance Analysis
|
||||
|
||||
When tests are slow:
|
||||
|
||||
**Identify Bottlenecks:**
|
||||
1. Run with profiling: `bundle exec rspec --profile`
|
||||
2. Find slowest examples
|
||||
3. Categorize delays:
|
||||
- Database operations
|
||||
- External service calls
|
||||
- Complex computations
|
||||
- Factory creation
|
||||
|
||||
**Optimization Suggestions:**
|
||||
|
||||
**Database-Heavy Tests:**
|
||||
- Use `build_stubbed` instead of `create`
|
||||
- Use `let` instead of `let!` where possible
|
||||
- Consider using transactions or database_cleaner strategies
|
||||
|
||||
**External Services:**
|
||||
- Stub external API calls
|
||||
- Use VCR for HTTP interactions
|
||||
- Mock time-consuming operations
|
||||
|
||||
**Factory Optimization:**
|
||||
- Minimize associated records
|
||||
- Use traits for specific scenarios
|
||||
- Consider using `build` instead of `create`
|
||||
|
||||
## Interactive Debugging
|
||||
|
||||
Offer to:
|
||||
1. Add debugging output to specific lines
|
||||
2. Insert `binding.pry` at failure point
|
||||
3. Show related code context
|
||||
4. Run specific tests in isolation
|
||||
5. Check factory definitions
|
||||
6. Verify test data setup
|
||||
|
||||
## Test Coverage Suggestions
|
||||
|
||||
Suggest tests for:
|
||||
- **Happy path**: Normal expected usage
|
||||
- **Edge cases**: Boundary conditions
|
||||
- **Error cases**: Invalid inputs
|
||||
- **Null cases**: Nil/empty values
|
||||
- **Integration**: Cross-object interactions
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Focus on actionable next steps
|
||||
- Provide specific line numbers and file paths
|
||||
- Show concrete code examples
|
||||
- Explain the "why" behind failures
|
||||
- Suggest preventive measures
|
||||
- Balance speed of fix with quality of solution
|
||||
131
skills/ruby-test-analyzer/scripts/README.md
Normal file
131
skills/ruby-test-analyzer/scripts/README.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# Test Analyzer Scripts
|
||||
|
||||
Executable shell scripts for running and analyzing RSpec tests.
|
||||
|
||||
## Scripts
|
||||
|
||||
### run_tests.sh
|
||||
Run RSpec tests with intelligent filtering.
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
./run_tests.sh all
|
||||
|
||||
# Run only fast tests (exclude slow)
|
||||
./run_tests.sh fast
|
||||
|
||||
# Run only previously failed tests
|
||||
./run_tests.sh failures
|
||||
|
||||
# Run tests for changed files
|
||||
./run_tests.sh changed
|
||||
|
||||
# Profile slowest tests
|
||||
./run_tests.sh profile
|
||||
|
||||
# Run specific file
|
||||
./run_tests.sh spec/models/user_spec.rb
|
||||
```
|
||||
|
||||
**Modes:**
|
||||
- `all` - All tests with documentation format
|
||||
- `fast` - Exclude tests tagged with :slow
|
||||
- `failures` - Only re-run failed tests (uses .rspec_status)
|
||||
- `changed` - Auto-detect changed files via git diff
|
||||
- `profile` - Show 10 slowest examples
|
||||
- `<file_path>` - Run specific test file
|
||||
|
||||
**Features:**
|
||||
- Auto-detects RSpec availability
|
||||
- Helpful error messages
|
||||
- Smart test file detection from lib/ files
|
||||
- Documentation format for readability
|
||||
|
||||
### analyze_failures.sh
|
||||
Analyze test failures and provide debugging hints.
|
||||
|
||||
```bash
|
||||
./analyze_failures.sh
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
Results: 3 failures out of 47 tests
|
||||
|
||||
Failed examples:
|
||||
|
||||
User#full_name returns first and last name
|
||||
File: spec/user_spec.rb:23
|
||||
Error: RSpec::Expectations::ExpectationNotMetError
|
||||
Message: Expected "John Doe" but got "John"
|
||||
|
||||
...
|
||||
|
||||
Common fixes:
|
||||
- Check for typos in method names
|
||||
- Verify test data setup (factories, fixtures)
|
||||
- Check for nil objects
|
||||
- Review recent code changes
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Parses JSON test results
|
||||
- Groups failures by type
|
||||
- Provides file:line references
|
||||
- Suggests common fixes
|
||||
- Shows debugging commands
|
||||
|
||||
**Requires:**
|
||||
- jq (JSON processor)
|
||||
- RSpec with JSON formatter
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Quick Test Run
|
||||
```bash
|
||||
# Run changed tests
|
||||
./run_tests.sh changed
|
||||
|
||||
# If failures, analyze
|
||||
./analyze_failures.sh
|
||||
```
|
||||
|
||||
### TDD Workflow
|
||||
```bash
|
||||
# Run all tests
|
||||
./run_tests.sh all
|
||||
|
||||
# Profile to find slow tests
|
||||
./run_tests.sh profile
|
||||
|
||||
# Tag slow tests, then run fast
|
||||
./run_tests.sh fast
|
||||
```
|
||||
|
||||
### CI/CD
|
||||
```bash
|
||||
# In CI pipeline
|
||||
./run_tests.sh all || ./analyze_failures.sh
|
||||
```
|
||||
|
||||
## RSpec Configuration
|
||||
|
||||
For best results, add to `.rspec`:
|
||||
```
|
||||
--format progress
|
||||
--require spec_helper
|
||||
--example-status-persistence-file-path .rspec_status
|
||||
```
|
||||
|
||||
And in `spec/spec_helper.rb`:
|
||||
```ruby
|
||||
RSpec.configure do |config|
|
||||
config.example_status_persistence_file_path = ".rspec_status"
|
||||
end
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- RSpec gem
|
||||
- jq (for analyze_failures.sh)
|
||||
- git (for changed mode)
|
||||
42
skills/ruby-test-analyzer/scripts/analyze_failures.sh
Executable file
42
skills/ruby-test-analyzer/scripts/analyze_failures.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
# Analyze RSpec failures and provide debugging hints
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔍 Analyzing test failures..."
|
||||
|
||||
# Run tests and capture output
|
||||
OUTPUT=$(bundle exec rspec --format json --out tmp/rspec_results.json --format progress 2>&1) || true
|
||||
|
||||
if [ ! -f "tmp/rspec_results.json" ]; then
|
||||
echo "No test results found. Run tests first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse JSON results
|
||||
FAILURES=$(jq '.summary.failure_count' tmp/rspec_results.json)
|
||||
TOTAL=$(jq '.summary.example_count' tmp/rspec_results.json)
|
||||
|
||||
echo ""
|
||||
echo "Results: $FAILURES failures out of $TOTAL tests"
|
||||
echo ""
|
||||
|
||||
if [ "$FAILURES" -eq 0 ]; then
|
||||
echo "✅ All tests passing!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Failed examples:"
|
||||
echo ""
|
||||
|
||||
jq -r '.examples[] | select(.status == "failed") | "\(.full_description)\n File: \(.file_path):\(.line_number)\n Error: \(.exception.class)\n Message: \(.exception.message)\n"' tmp/rspec_results.json
|
||||
|
||||
echo ""
|
||||
echo "Common fixes:"
|
||||
echo " - Check for typos in method names"
|
||||
echo " - Verify test data setup (factories, fixtures)"
|
||||
echo " - Check for nil objects"
|
||||
echo " - Review recent code changes"
|
||||
echo ""
|
||||
echo "To debug specific test:"
|
||||
echo " bundle exec rspec <file_path>:<line_number> --format documentation"
|
||||
65
skills/ruby-test-analyzer/scripts/run_tests.sh
Executable file
65
skills/ruby-test-analyzer/scripts/run_tests.sh
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run RSpec tests with intelligent filtering
|
||||
|
||||
set -e
|
||||
|
||||
MODE=${1:-all}
|
||||
|
||||
echo "🧪 Running RSpec tests (mode: $MODE)..."
|
||||
|
||||
# Check if RSpec is available
|
||||
if ! bundle exec rspec --version &> /dev/null; then
|
||||
echo "❌ RSpec not found. Add to Gemfile:"
|
||||
echo " gem 'rspec', '~> 3.12'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$MODE" in
|
||||
all)
|
||||
echo "Running all tests..."
|
||||
bundle exec rspec --format documentation
|
||||
;;
|
||||
fast)
|
||||
echo "Running tests without slow examples..."
|
||||
bundle exec rspec --tag ~slow --format progress
|
||||
;;
|
||||
failures)
|
||||
echo "Running only previously failed tests..."
|
||||
bundle exec rspec --only-failures --format documentation
|
||||
;;
|
||||
changed)
|
||||
echo "Running tests for changed files..."
|
||||
CHANGED_FILES=$(git diff --name-only --diff-filter=AM | grep '\.rb$' | grep -v '_spec\.rb$' || true)
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
echo "No changed Ruby files found"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for file in $CHANGED_FILES; do
|
||||
# Convert lib/foo/bar.rb to spec/foo/bar_spec.rb
|
||||
spec_file=$(echo "$file" | sed 's/^lib/spec/;s/\.rb$/_spec.rb/')
|
||||
if [ -f "$spec_file" ]; then
|
||||
echo "Testing: $spec_file"
|
||||
bundle exec rspec "$spec_file" --format documentation
|
||||
fi
|
||||
done
|
||||
;;
|
||||
profile)
|
||||
echo "Running tests with profiling..."
|
||||
bundle exec rspec --profile 10 --format documentation
|
||||
;;
|
||||
*)
|
||||
# Treat as file path
|
||||
if [ -f "$MODE" ]; then
|
||||
echo "Running test file: $MODE"
|
||||
bundle exec rspec "$MODE" --format documentation
|
||||
else
|
||||
echo "Unknown mode: $MODE"
|
||||
echo "Available modes: all, fast, failures, changed, profile, <file_path>"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo "✅ Test run complete"
|
||||
Reference in New Issue
Block a user