Files
2025-11-30 08:30:07 +08:00

635 lines
12 KiB
Markdown

# 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