Back to list
bastos

gem-development

by bastos

Bastos' Claude Code Ruby Plugin Marketplace

0🍴 0📅 Jan 24, 2026

SKILL.md


name: gem-development description: This skill should be used when the user asks about "gem development", "create a gem", "gemspec", "publish gem", "RubyGems", "bundler", "gem structure", "gem versioning", "semantic versioning", "gem testing", "gem documentation", or needs guidance on creating and publishing Ruby gems. version: 1.0.0

Ruby Gem Development

Guide to creating, testing, and publishing Ruby gems.

Creating a New Gem

Using Bundler

# Create gem scaffold
bundle gem my_gem

# With options
bundle gem my_gem --test=minitest --ci=github --linter=rubocop

Generated structure:

my_gem/
├── lib/
│   ├── my_gem/
│   │   └── version.rb
│   └── my_gem.rb
├── test/ or spec/
├── bin/
│   ├── console
│   └── setup
├── Gemfile
├── Rakefile
├── my_gem.gemspec
├── README.md
├── LICENSE.txt
└── CHANGELOG.md

Manual Structure

my_gem/
├── lib/
│   ├── my_gem/
│   │   ├── version.rb
│   │   ├── configuration.rb
│   │   └── core_class.rb
│   └── my_gem.rb          # Main entry point
├── test/
│   ├── test_helper.rb
│   └── my_gem_test.rb
├── Gemfile
├── Rakefile
├── my_gem.gemspec
├── README.md
├── LICENSE.txt
└── CHANGELOG.md

The Gemspec

Complete Example

# my_gem.gemspec
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         = ["you@example.com"]

  spec.summary       = "Short summary of your gem"
  spec.description   = "Longer description explaining 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,
    "source_code_uri"   => "https://github.com/username/my_gem",
    "changelog_uri"     => "https://github.com/username/my_gem/blob/main/CHANGELOG.md",
    "bug_tracker_uri"   => "https://github.com/username/my_gem/issues",
    "documentation_uri" => "https://rubydoc.info/gems/my_gem",
    "rubygems_mfa_required" => "true"
  }

  # Files to include
  spec.files = Dir.chdir(__dir__) do
    `git ls-files -z`.split("\x0").reject do |f|
      f.match?(%r{\A(?:test|spec|features)/})
    end
  end

  spec.bindir        = "exe"
  spec.executables   = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
  spec.require_paths = ["lib"]

  # Runtime dependencies
  spec.add_dependency "some_gem", "~> 1.0"

  # Development dependencies (prefer Gemfile for these)
  # spec.add_development_dependency "rake", "~> 13.0"
end

Version File

# lib/my_gem/version.rb
module MyGem
  VERSION = "0.1.0"
end

Main Entry Point

# lib/my_gem.rb
require_relative "my_gem/version"
require_relative "my_gem/configuration"
require_relative "my_gem/client"

module MyGem
  class Error < StandardError; end
  class ConfigurationError < Error; end
  class APIError < Error; end

  class << self
    attr_accessor :configuration
  end

  def self.configure
    self.configuration ||= Configuration.new
    yield(configuration)
  end

  def self.reset_configuration!
    self.configuration = Configuration.new
  end
end

Configuration Pattern

# lib/my_gem/configuration.rb
module MyGem
  class Configuration
    attr_accessor :api_key, :timeout, :logger, :debug

    def initialize
      @api_key = nil
      @timeout = 30
      @logger = Logger.new($stdout)
      @debug = false
    end

    def validate!
      raise ConfigurationError, "API key required" if api_key.nil?
    end
  end
end

Usage:

MyGem.configure do |config|
  config.api_key = "your-key"
  config.timeout = 60
  config.debug = true
end

Testing Your Gem

Test Helper

# test/test_helper.rb
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
require "my_gem"
require "minitest/autorun"

# Optional: VCR for API testing
require "vcr"
VCR.configure do |config|
  config.cassette_library_dir = "test/fixtures/vcr_cassettes"
  config.hook_into :webmock
end

Example Tests

# test/my_gem_test.rb
require "test_helper"

class MyGemTest < Minitest::Test
  def setup
    MyGem.reset_configuration!
  end

  def test_version
    refute_nil MyGem::VERSION
  end

  def test_configuration
    MyGem.configure do |config|
      config.api_key = "test-key"
    end

    assert_equal "test-key", MyGem.configuration.api_key
  end

  def test_requires_api_key
    assert_raises(MyGem::ConfigurationError) do
      MyGem.configuration.validate!
    end
  end
end

Rakefile

# Rakefile
require "bundler/gem_tasks"
require "rake/testtask"

Rake::TestTask.new(:test) do |t|
  t.libs << "test"
  t.libs << "lib"
  t.test_files = FileList["test/**/*_test.rb"]
end

require "rubocop/rake_task"
RuboCop::RakeTask.new

task default: %i[test rubocop]

Versioning

Semantic Versioning

MAJOR.MINOR.PATCH

1.0.0 - Initial stable release
1.0.1 - Bug fix (backwards compatible)
1.1.0 - New feature (backwards compatible)
2.0.0 - Breaking change

Pre-release Versions

VERSION = "1.0.0.alpha"
VERSION = "1.0.0.beta1"
VERSION = "1.0.0.rc1"

Updating Version

# Use a rake task
# lib/tasks/version.rake
namespace :version do
  desc "Bump patch version"
  task :patch do
    bump_version(:patch)
  end

  desc "Bump minor version"
  task :minor do
    bump_version(:minor)
  end

  desc "Bump major version"
  task :major do
    bump_version(:major)
  end

  def bump_version(type)
    version_file = "lib/my_gem/version.rb"
    content = File.read(version_file)
    current = content.match(/VERSION = "(.+)"/)[1]
    parts = current.split(".").map(&:to_i)

    case type
    when :patch then parts[2] += 1
    when :minor then parts[1] += 1; parts[2] = 0
    when :major then parts[0] += 1; parts[1] = 0; parts[2] = 0
    end

    new_version = parts.join(".")
    new_content = content.gsub(/VERSION = ".+"/, %(VERSION = "#{new_version}"))
    File.write(version_file, new_content)
    puts "Bumped to #{new_version}"
  end
end

Documentation

YARD Documentation

# Add to Gemfile
# gem 'yard', group: :development

# Example documentation
module MyGem
  # Client for interacting with the API
  #
  # @example Basic usage
  #   client = MyGem::Client.new(api_key: "key")
  #   result = client.fetch("resource")
  #
  # @see https://api.example.com/docs API Documentation
  class Client
    # Create a new client instance
    #
    # @param api_key [String] API authentication key
    # @param timeout [Integer] Request timeout in seconds
    # @return [Client] a new client instance
    # @raise [ConfigurationError] if api_key is nil
    def initialize(api_key:, timeout: 30)
      @api_key = api_key
      @timeout = timeout
    end

    # Fetch a resource from the API
    #
    # @param resource [String] Resource path to fetch
    # @param params [Hash] Optional query parameters
    # @option params [Integer] :limit Maximum results
    # @option params [Integer] :offset Pagination offset
    # @return [Hash] Parsed JSON response
    # @raise [APIError] on API errors
    def fetch(resource, params = {})
      # implementation
    end
  end
end

Generate docs:

yard doc
yard server  # View at http://localhost:8808

README Template

# MyGem

Short description of what your gem does.

## Installation

Add to your Gemfile:

    gem 'my_gem'

Or install directly:

    gem install my_gem

## Usage

    require 'my_gem'

    MyGem.configure do |config|
      config.api_key = ENV['API_KEY']
    end

    client = MyGem::Client.new
    result = client.fetch('users')

## Development

    git clone https://github.com/username/my_gem
    cd my_gem
    bin/setup
    rake test

## Contributing

Bug reports and pull requests welcome on GitHub.

## License

MIT License

Publishing

First-time Setup

# Create account at rubygems.org
gem signin

Release Process

# 1. Update version in lib/my_gem/version.rb
# 2. Update CHANGELOG.md
# 3. Commit changes
git add -A
git commit -m "Release v1.0.0"

# 4. Build and push
bundle exec rake release
# This will:
# - Build the gem
# - Tag the version
# - Push to RubyGems
# - Push to GitHub

Manual Release

# Build
gem build my_gem.gemspec

# Push
gem push my_gem-1.0.0.gem

Yanking a Bad Release

# Remove a version (use carefully!)
gem yank my_gem -v 1.0.0

CI/CD

GitHub Actions

# .github/workflows/ci.yml
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', '3.3']

    steps:
      - uses: actions/checkout@v4
      - 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 test
      - name: Run linter
        run: bundle exec rubocop

Auto-publish on Release

# .github/workflows/publish.yml
name: Publish

on:
  release:
    types: [published]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.2'
      - name: Publish to RubyGems
        run: |
          gem build *.gemspec
          gem push *.gem
        env:
          GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}

Score

Total Score

65/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon