Initial commit
This commit is contained in:
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
|
||||
Reference in New Issue
Block a user