Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:30:07 +08:00
commit d6f6fcbaad
33 changed files with 4697 additions and 0 deletions

View 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

View 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)

View 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"

View 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

View 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

View 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"

View 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

View 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)

View 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}\""

View 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)"

View 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

View 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)

View 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"

View 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"

View 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

View 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)

View 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"

View 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"