Contributing to PromptManager¶
Thank you for your interest in contributing to PromptManager! This guide will help you get started with contributing to the project.
Getting Started¶
Prerequisites¶
- Ruby 3.0 or higher
- Git
- A GitHub account
Development Setup¶
- Fork the repository on GitHub
-
Clone your fork locally:
-
Install dependencies:
-
Run the tests to ensure everything is working:
-
Create a feature branch:
Development Workflow¶
Code Style¶
We follow standard Ruby conventions and use RuboCop for code style enforcement:
Testing¶
We use RSpec for testing. Please ensure all tests pass and add tests for new features:
# Run all tests
bundle exec rspec
# Run specific test file
bundle exec rspec spec/prompt_spec.rb
# Run tests with coverage
COVERAGE=true bundle exec rspec
Test Coverage¶
We aim for high test coverage. Check coverage after running tests:
Making Changes¶
Adding New Features¶
- Create an issue first to discuss the feature
- Write tests for your feature (TDD approach preferred)
- Implement the feature with clear, readable code
- Update documentation as needed
- Ensure all tests pass
Bug Fixes¶
- Create a failing test that reproduces the bug
- Fix the bug with minimal changes
- Ensure the test now passes
- Check for any regressions
Example: Adding a New Storage Adapter¶
# 1. Create the adapter class
class MyCustomAdapter < PromptManager::Storage::Base
def read(prompt_id)
# Implementation
end
def write(prompt_id, content)
# Implementation
end
# ... other required methods
end
# 2. Add comprehensive tests
RSpec.describe MyCustomAdapter do
let(:adapter) { described_class.new(config_options) }
include_examples 'a storage adapter'
describe 'custom functionality' do
it 'handles specific use case' do
# Test implementation
end
end
end
# 3. Update documentation
# Add to docs/storage/custom-adapters.md
Code Guidelines¶
Ruby Style Guide¶
- Use 2 spaces for indentation
- Follow Ruby naming conventions (snake_case for methods and variables)
- Keep line length under 100 characters
- Use descriptive method and variable names
- Add comments for complex logic
Architecture Principles¶
- Single Responsibility: Each class should have one clear purpose
- Open/Closed: Open for extension, closed for modification
- Dependency Injection: Avoid hard dependencies, use dependency injection
- Error Handling: Handle errors gracefully with meaningful messages
Example Code Structure¶
module PromptManager
module Storage
class CustomAdapter < Base
# Clear initialization with validation
def initialize(connection_string:, **options)
validate_connection_string(connection_string)
@connection = establish_connection(connection_string)
super(**options)
end
# Clear method responsibilities
def read(prompt_id)
validate_prompt_id(prompt_id)
result = @connection.get(key_for(prompt_id))
raise PromptNotFoundError.new("Prompt '#{prompt_id}' not found") unless result
result
rescue ConnectionError => e
raise StorageError.new("Connection failed: #{e.message}")
end
private
# Helper methods are private and focused
def validate_prompt_id(prompt_id)
raise ArgumentError, 'prompt_id cannot be nil' if prompt_id.nil?
raise ArgumentError, 'prompt_id cannot be empty' if prompt_id.empty?
end
def key_for(prompt_id)
"prompts:#{prompt_id}"
end
end
end
end
Testing Guidelines¶
Test Structure¶
RSpec.describe PromptManager::Prompt do
# Use let blocks for test setup
let(:prompt_id) { 'test_prompt' }
let(:storage) { instance_double(PromptManager::Storage::Base) }
let(:prompt) { described_class.new(id: prompt_id, storage: storage) }
describe '#render' do
context 'when prompt exists' do
before do
allow(storage).to receive(:read)
.with(prompt_id)
.and_return('Hello [NAME]!')
end
it 'renders with parameters' do
result = prompt.render(name: 'World')
expect(result).to eq 'Hello World!'
end
it 'handles missing parameters gracefully' do
expect {
prompt.render
}.to raise_error(PromptManager::MissingParametersError)
end
end
context 'when prompt does not exist' do
before do
allow(storage).to receive(:read)
.with(prompt_id)
.and_raise(PromptManager::PromptNotFoundError)
end
it 'raises PromptNotFoundError' do
expect {
prompt.render
}.to raise_error(PromptManager::PromptNotFoundError)
end
end
end
end
Shared Examples¶
Use shared examples for common behavior:
# spec/support/shared_examples/storage_adapter.rb
RSpec.shared_examples 'a storage adapter' do
describe 'required interface' do
it 'implements read method' do
expect(adapter).to respond_to(:read)
end
it 'implements write method' do
expect(adapter).to respond_to(:write)
end
it 'implements exist? method' do
expect(adapter).to respond_to(:exist?)
end
end
describe 'basic functionality' do
let(:prompt_id) { 'test_prompt' }
let(:content) { 'Hello [NAME]!' }
it 'stores and retrieves content' do
adapter.write(prompt_id, content)
expect(adapter.read(prompt_id)).to eq content
end
end
end
Documentation¶
Code Documentation¶
Use YARD for inline documentation:
# Renders a prompt with the given parameters
#
# @param parameters [Hash] Key-value pairs for parameter substitution
# @return [String] The rendered prompt content
# @raise [PromptNotFoundError] If the prompt cannot be found
# @raise [MissingParametersError] If required parameters are missing
#
# @example Basic usage
# prompt = PromptManager::Prompt.new(id: 'welcome')
# result = prompt.render(name: 'John', company: 'Acme Corp')
#
# @example With nested parameters
# prompt.render(user: { name: 'Jane', email: 'jane@example.com' })
def render(parameters = {})
# Implementation
end
README Updates¶
Update the main README.md if your changes affect: - Installation instructions - Basic usage examples - Configuration options - Major features
Changelog¶
Add entries to CHANGELOG.md for: - New features - Bug fixes - Breaking changes - Deprecations
Format:
## [Unreleased]
### Added
- New feature description
### Changed
- Changed behavior description
### Fixed
- Bug fix description
### Deprecated
- Deprecated feature description
Submitting Changes¶
Before Submitting¶
- Ensure all tests pass:
bundle exec rspec
- Check code style:
bundle exec rubocop
- Update documentation as needed
- Add changelog entry if applicable
- Rebase your branch on the latest main branch
Pull Request Guidelines¶
-
Create a clear title: "Add Redis storage adapter" or "Fix parameter parsing bug"
-
Write a detailed description:
-
Link related issues: "Closes #123" or "Fixes #456"
-
Request appropriate reviewers
Pull Request Checklist¶
- Tests added/updated and passing
- Code follows style guidelines
- Documentation updated
- Changelog updated (if applicable)
- No merge conflicts with main branch
- PR description is clear and complete
Development Resources¶
Project Structure¶
prompt_manager/
├── lib/
│ └── prompt_manager/
│ ├── prompt.rb # Core Prompt class
│ ├── storage/ # Storage adapters
│ ├── directive_processor.rb # Directive processing
│ └── configuration.rb # Configuration management
├── spec/ # Test files
│ ├── prompt_manager/
│ ├── support/ # Test helpers
│ └── fixtures/ # Test data
├── docs/ # Documentation
└── examples/ # Usage examples
Key Classes and Modules¶
PromptManager::Prompt
- Main interface for prompt operationsPromptManager::Storage::Base
- Abstract storage adapterPromptManager::DirectiveProcessor
- Handles//include
and custom directivesPromptManager::Configuration
- Configuration management
Common Development Tasks¶
# Run tests for specific component
bundle exec rspec spec/prompt_manager/storage/
# Generate test coverage report
COVERAGE=true bundle exec rspec
# Check for security vulnerabilities
bundle audit
# Update dependencies
bundle update
# Generate documentation
yard doc
Getting Help¶
- GitHub Issues: For bug reports and feature requests
- Discussions: For questions and general discussion
- Email: For security-related issues
Recognition¶
Contributors are recognized in:
- CONTRIBUTORS.md
file
- Release notes for major contributions
- GitHub contributor statistics
Thank you for contributing to PromptManager! 🎉