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,231 @@
# Code Smell Logger Hook
This hook analyzes Ruby files after they're written and logs detected code smells to help track refactoring opportunities.
## Trigger
- Event: post-write
- Pattern: **/*.rb
## Purpose
Maintain a running log of code smells to:
- Track refactoring debt over time
- Prioritize refactoring efforts
- Learn from recurring patterns
- Monitor code quality trends
## Analysis Process
### 1. Detect Code Smells
Analyze the file for common smells based on Sandi Metz's principles:
**Complexity Smells:**
- Methods longer than 5 lines
- Classes longer than 100 lines
- Cyclomatic complexity > 3
- Nested conditionals (depth > 2)
- Long parameter lists (> 4 parameters)
**Design Smells:**
- Missing dependency injection
- Feature envy (accessing other objects' data)
- Data clumps (same parameters used together)
- Primitive obsession
- Law of Demeter violations (chained method calls)
**OO Design Smells:**
- Missing single responsibility
- Unclear public/private interface
- Deep inheritance (> 1 level)
- Missing abstraction opportunities
- Duplication (similar code patterns)
**Ruby-Specific Smells:**
- Missing frozen_string_literal
- Not using Ruby iterators (using for loops)
- Not using blocks/yield appropriately
- Missing null object pattern where appropriate
- Overly complex conditionals vs polymorphism
### 2. Calculate Severity
**High Priority:**
- Multiple responsibilities in one class
- Deep nesting (> 2 levels)
- Methods > 10 lines
- Clear SOLID violations
**Medium Priority:**
- Methods 6-10 lines
- Missing dependency injection
- Law of Demeter violations
- Primitive obsession
**Low Priority:**
- Methods exactly 5 lines
- Minor style issues
- Potential improvements (not violations)
### 3. Log Format
Append to `.claude/code-smells.log`:
```
[TIMESTAMP] - [FILE_PATH]
Severity: [HIGH/MEDIUM/LOW]
Smell: [SMELL_NAME]
Location: [CLASS_NAME]#[METHOD_NAME] (lines X-Y)
Description: [Specific issue detected]
Suggestion: [Refactoring approach]
Related Pattern: [POODR principle or pattern]
---
```
## Example Output
```
2025-11-01 14:23:15 - lib/user_service.rb
Severity: HIGH
Smell: God Object / Multiple Responsibilities
Location: UserService (lines 1-156)
Description: Class has 8 public methods handling authentication, validation, email sending, and database operations
Suggestion: Extract into separate services: UserAuthenticator, UserValidator, UserMailer, UserRepository
Related Pattern: Single Responsibility Principle (POODR Ch. 2)
---
2025-11-01 14:23:15 - lib/user_service.rb
Severity: MEDIUM
Smell: Long Method
Location: UserService#create_user (lines 23-42)
Description: Method is 19 lines, handles validation, creation, and email sending
Suggestion: Extract methods: validate_user_data, persist_user, send_welcome_email
Related Pattern: Small Methods (Sandi's Rules)
---
2025-11-01 14:23:15 - lib/order_processor.rb
Severity: MEDIUM
Smell: Missing Dependency Injection
Location: OrderProcessor#process (line 15)
Description: Hard-coded instantiation of EmailMailer.new inside method
Suggestion: Inject mailer dependency through constructor
Related Pattern: Dependency Injection (POODR Ch. 3)
---
2025-11-01 14:23:15 - lib/payment_handler.rb
Severity: LOW
Smell: Primitive Obsession
Location: PaymentHandler#validate_card (lines 8-12)
Description: Using string for credit card number validation
Suggestion: Create CreditCard value object with validation
Related Pattern: Value Object pattern
---
```
## Response to User
After logging, provide a brief summary:
**Code Smell Analysis Complete**
Analyzed: [file_path]
**Detected Issues:**
- 1 High Priority: God Object in UserService
- 2 Medium Priority: Long method, missing DI
- 1 Low Priority: Primitive obsession
**Top Recommendation:**
Extract UserService into separate concerns following Single Responsibility Principle.
Full details logged to `.claude/code-smells.log`
**Quick Actions:**
- Run `/refactor` to get detailed refactoring plan
- Run `/review-ruby` for comprehensive code review
- View log: `cat .claude/code-smells.log | tail -20`
## Log Management
### View Recent Smells
```bash
# Last 20 entries
tail -20 .claude/code-smells.log
# Smells from specific file
grep "lib/user.rb" .claude/code-smells.log
# High priority only
grep "Severity: HIGH" .claude/code-smells.log
# By smell type
grep "Smell: Long Method" .claude/code-smells.log
```
### Generate Summary Report
```bash
# Count by severity
grep -c "Severity: HIGH" .claude/code-smells.log
grep -c "Severity: MEDIUM" .claude/code-smells.log
grep -c "Severity: LOW" .claude/code-smells.log
# Most common smells
grep "Smell:" .claude/code-smells.log | sort | uniq -c | sort -rn
```
## Configuration
Customize in `.claude/settings.json`:
```json
{
"plugins": {
"rubyist": {
"codeSmellLogging": {
"enabled": true,
"logPath": ".claude/code-smells.log",
"severityThreshold": "MEDIUM",
"excludePatterns": [
"**/*_spec.rb",
"db/migrate/**",
"lib/generated/**"
],
"autoRefactorSuggestions": true
}
}
}
}
```
## Integration with Refactoring Workflow
The log helps prioritize refactoring:
1. **Review accumulated smells** weekly/sprint
2. **Prioritize by frequency** (same smell in multiple files)
3. **Track improvements** (smell resolution over time)
4. **Learn patterns** (recurring issues suggest training needs)
## Educational Benefit
Over time, the log becomes a learning tool:
- See which smells occur most frequently
- Understand which POODR principles need focus
- Track improvement in code quality
- Share patterns with team
## Privacy Note
The log is local to the repository and should be added to `.gitignore` unless team wants to track collectively:
```
# .gitignore
.claude/code-smells.log
```
Or commit for team awareness:
```
# Track team code quality
!.claude/code-smells.log
```

35
hooks/hooks.json Normal file
View File

@@ -0,0 +1,35 @@
{
"hooks": [
{
"name": "ruby-pre-commit",
"description": "Run RuboCop on staged Ruby files before commit",
"event": "pre-commit",
"command": "git diff --cached --name-only --diff-filter=ACM | grep '\\.rb$' | xargs -r bundle exec rubocop --force-exclusion",
"continueOnError": false,
"enabled": true
},
{
"name": "ruby-post-write",
"description": "Suggest tests for newly created Ruby files",
"event": "post-write",
"pattern": "**/*.rb",
"excludePattern": "**/*_spec.rb",
"action": "suggest-tests"
},
{
"name": "code-smell-logger",
"description": "Log detected code smells after file writes to aid refactoring tracking",
"event": "post-write",
"pattern": "**/*.rb",
"action": "analyze-and-log-smells"
},
{
"name": "ruby-test-on-save",
"description": "Run related tests when Ruby files are saved",
"event": "post-write",
"pattern": "lib/**/*.rb",
"action": "run-related-tests",
"enabled": false
}
]
}

136
hooks/suggest-tests.md Normal file
View File

@@ -0,0 +1,136 @@
# Suggest Tests Hook
This hook triggers after writing a new Ruby file and suggests relevant tests.
## Trigger
- Event: post-write
- Pattern: **/*.rb (excluding *_spec.rb files)
## Action
When a new Ruby file is created or significantly modified, analyze the file and suggest appropriate tests.
## Analysis Process
1. **Read the File**
- Determine the class/module name
- Identify public methods
- Note any dependencies or collaborators
- Identify edge cases from code structure
2. **Check for Existing Tests**
- Look for corresponding spec file (e.g., `lib/user.rb``spec/user_spec.rb`)
- If exists, check coverage of new methods
- If missing, offer to create spec file
3. **Generate Test Suggestions**
Based on the code structure, suggest tests for:
### Public Methods
- Happy path (normal operation)
- Edge cases (boundary conditions)
- Error cases (invalid inputs)
- Null cases (nil/empty values)
### Class Responsibilities
- Single Responsibility compliance
- Interface clarity
- Dependency injection points
### Common Patterns
- Data object serialization (to_h, to_json)
- Factory methods (build, from_json, from_h)
- Validation logic
- Error handling
- State management
## Response Format
**New File Detected: [file_path]**
I noticed you created/modified a Ruby file with:
- Class: [ClassName]
- Public methods: [method1, method2, method3]
- Dependencies: [list]
**Test File Status:**
- [ ] Spec file exists at: [spec_path]
- [ ] No spec file found
**Suggested Tests:**
```ruby
# spec/path/to/class_spec.rb
require 'spec_helper'
RSpec.describe ClassName do
describe '.build' do
it 'creates instance from hash attributes' do
# Test factory method
end
context 'with missing attributes' do
it 'handles gracefully' do
# Test edge case
end
end
end
describe '#public_method' do
let(:instance) { described_class.new(dependencies) }
it 'performs expected behavior' do
# Happy path test
end
context 'when error condition occurs' do
it 'raises appropriate exception' do
# Error handling test
end
end
end
describe '#to_h' do
it 'serializes to hash with expected keys' do
# Serialization test
end
end
end
```
**Additional Considerations:**
- [ ] Test dependency injection
- [ ] Test error handling for [specific method]
- [ ] Test edge case: [description]
- [ ] Add integration test for [interaction]
Would you like me to:
1. Create the spec file with these tests?
2. Add tests for specific methods?
3. Skip test suggestions for this file?
## Configuration
Users can disable this hook in `.claude/settings.json`:
```json
{
"hooks": {
"ruby-post-write": {
"enabled": false
}
}
}
```
Or configure per-project patterns to exclude:
```json
{
"hooks": {
"ruby-post-write": {
"excludePattern": ["lib/generated/**", "lib/legacy/**"]
}
}
}
```