Claude Code Plugins

Community-maintained marketplace

Feedback

sinatra-patterns

@geoffjay/claude-plugins
0
0

Common Sinatra patterns, routing strategies, error handling, and application organization. Use when building Sinatra applications or designing routes.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name sinatra-patterns
description Common Sinatra patterns, routing strategies, error handling, and application organization. Use when building Sinatra applications or designing routes.

Sinatra Patterns Skill

Tier 1: Quick Reference

Common Routing Patterns

Basic Routes:

get '/' do
  'Hello World'
end

post '/users' do
  # Create user
end

put '/users/:id' do
  # Update user
end

delete '/users/:id' do
  # Delete user
end

Route Parameters:

# Named parameters
get '/users/:id' do
  User.find(params[:id])
end

# Parameter constraints
get '/users/:id', :id => /\d+/ do
  # Only matches numeric IDs
end

# Wildcard
get '/files/*.*' do
  # params['splat'] contains matched segments
end

Query Parameters:

get '/search' do
  query = params[:q]
  page = params[:page] || 1
  results = search(query, page: page)
end

Basic Middleware

# Session middleware
use Rack::Session::Cookie, secret: ENV['SESSION_SECRET']

# Security middleware
use Rack::Protection

# Logging
use Rack::CommonLogger

# Compression
use Rack::Deflater

Simple Error Handling

not_found do
  'Page not found'
end

error do
  'Internal server error'
end

error 401 do
  'Unauthorized'
end

Helpers

helpers do
  def logged_in?
    !session[:user_id].nil?
  end

  def current_user
    @current_user ||= User.find_by(id: session[:user_id])
  end
end

Tier 2: Detailed Instructions

Advanced Routing

Modular Applications:

# app/controllers/base_controller.rb
class BaseController < Sinatra::Base
  configure do
    set :views, Proc.new { File.join(root, '../views') }
    set :public_folder, Proc.new { File.join(root, '../public') }
  end

  helpers do
    def json_response(data, status = 200)
      content_type :json
      halt status, data.to_json
    end
  end
end

# app/controllers/users_controller.rb
class UsersController < BaseController
  get '/' do
    users = User.all
    json_response(users.map(&:to_hash))
  end

  get '/:id' do
    user = User.find(params[:id]) || halt(404)
    json_response(user.to_hash)
  end

  post '/' do
    user = User.create(params[:user])
    if user.persisted?
      json_response(user.to_hash, 201)
    else
      json_response({ errors: user.errors }, 422)
    end
  end
end

# config.ru
map '/users' do
  run UsersController
end

Namespaces:

require 'sinatra/namespace'

class App < Sinatra::Base
  register Sinatra::Namespace

  namespace '/api' do
    namespace '/v1' do
      get '/users' do
        # GET /api/v1/users
      end

      namespace '/admin' do
        before do
          authenticate_admin!
        end

        get '/stats' do
          # GET /api/v1/admin/stats
        end
      end
    end
  end
end

Route Conditions:

# User agent condition
get '/', :agent => /iPhone/ do
  # Mobile version
end

# Custom conditions
set(:auth) do |role|
  condition do
    unless current_user && current_user.has_role?(role)
      halt 403
    end
  end
end

get '/admin', :auth => :admin do
  # Only accessible to admins
end

# Host-based routing
get '/', :host => 'admin.example.com' do
  # Admin subdomain
end

Content Negotiation:

get '/users/:id', :provides => [:json, :xml, :html] do
  user = User.find(params[:id])

  case request.accept.first.to_s
  when 'application/json'
    json user.to_json
  when 'application/xml'
    xml user.to_xml
  else
    erb :user, locals: { user: user }
  end
end

# Or using provides helper
get '/users/:id' do
  user = User.find(params[:id])

  respond_to do |format|
    format.json { json user.to_json }
    format.xml { xml user.to_xml }
    format.html { erb :user, locals: { user: user } }
  end
end

Middleware Composition

Custom Middleware:

class RequestLogger
  def initialize(app)
    @app = app
  end

  def call(env)
    start_time = Time.now
    status, headers, body = @app.call(env)
    duration = Time.now - start_time

    puts "#{env['REQUEST_METHOD']} #{env['PATH_INFO']} - #{status} (#{duration}s)"

    [status, headers, body]
  end
end

use RequestLogger

Middleware Ordering:

# config.ru
use Rack::Deflater                    # Compression first
use Rack::Static                       # Static files
use Rack::CommonLogger                 # Logging
use Rack::Session::Cookie             # Sessions
use Rack::Protection                   # Security
use CustomAuthentication              # Auth
run Application

Template Integration

ERB Templates:

# views/layout.erb
<!DOCTYPE html>
<html>
<head>
  <title><%= @title || 'My App' %></title>
</head>
<body>
  <%= yield %>
</body>
</html>

# views/users/index.erb
<h1>Users</h1>
<ul>
  <% @users.each do |user| %>
    <li><%= user.name %></li>
  <% end %>
</ul>

# Controller
get '/users' do
  @users = User.all
  @title = 'User List'
  erb :'users/index'
end

Inline Templates:

get '/' do
  erb :index
end

__END__

@@layout
<!DOCTYPE html>
<html>
  <body><%= yield %></body>
</html>

@@index
<h1>Welcome</h1>

Template Engines:

# Haml
get '/' do
  haml :index
end

# Slim
get '/' do
  slim :index
end

# Liquid (safe for user content)
get '/' do
  liquid :index, locals: { user: current_user }
end

Error Handling Patterns

Comprehensive Error Handling:

class Application < Sinatra::Base
  # Development configuration
  configure :development do
    set :show_exceptions, :after_handler
    set :dump_errors, true
  end

  # Production configuration
  configure :production do
    set :show_exceptions, false
    set :dump_errors, false
  end

  # Specific exception handlers
  error ActiveRecord::RecordNotFound do
    status 404
    json({ error: 'Resource not found' })
  end

  error ActiveRecord::RecordInvalid do
    status 422
    json({ error: 'Validation failed', details: env['sinatra.error'].message })
  end

  error Sequel::NoMatchingRow do
    status 404
    json({ error: 'Not found' })
  end

  # HTTP status handlers
  not_found do
    json({ error: 'Endpoint not found' })
  end

  error 401 do
    json({ error: 'Unauthorized' })
  end

  error 403 do
    json({ error: 'Forbidden' })
  end

  error 422 do
    json({ error: 'Unprocessable entity' })
  end

  # Catch-all error handler
  error do
    error = env['sinatra.error']
    logger.error("Error: #{error.message}")
    logger.error(error.backtrace.join("\n"))

    status 500
    json({ error: 'Internal server error' })
  end
end

Before/After Filters

Request Filters:

# Global before filter
before do
  content_type :json
end

# Path-specific filters
before '/admin/*' do
  authenticate_admin!
end

# Conditional filters
before do
  pass unless request.path.start_with?('/api')
  authenticate_api_user!
end

# After filters
after do
  # Add CORS headers
  headers 'Access-Control-Allow-Origin' => '*'
end

# Modify response
after do
  response.body = response.body.map(&:upcase) if params[:uppercase]
end

Session Management

Cookie Sessions:

use Rack::Session::Cookie,
  key: 'app.session',
  secret: ENV['SESSION_SECRET'],
  expire_after: 86400,  # 1 day
  secure: production?,
  httponly: true,
  same_site: :strict

helpers do
  def login(user)
    session[:user_id] = user.id
    session[:logged_in_at] = Time.now.to_i
  end

  def logout
    session.clear
  end

  def current_user
    return nil unless session[:user_id]
    @current_user ||= User.find_by(id: session[:user_id])
  end
end

Tier 3: Resources & Examples

Full Application Example

See assets/modular-app-template/ for complete modular application structure.

Performance Patterns

Caching:

# HTTP caching
get '/public/data' do
  cache_control :public, max_age: 3600
  etag calculate_etag
  last_modified last_update_time

  json PublicData.all.map(&:to_hash)
end

# Fragment caching with Redis
require 'redis'

helpers do
  def cache_fetch(key, expires_in: 300, &block)
    cached = REDIS.get(key)
    return JSON.parse(cached) if cached

    data = block.call
    REDIS.setex(key, expires_in, data.to_json)
    data
  end
end

get '/expensive-data' do
  data = cache_fetch('expensive-data', expires_in: 600) do
    perform_expensive_query
  end

  json data
end

Streaming Responses:

# Stream large responses
get '/large-export' do
  stream do |out|
    User.find_each do |user|
      out << user.to_csv_row
    end
  end
end

# Server-Sent Events
get '/events', provides: 'text/event-stream' do
  stream :keep_open do |out|
    EventSource.subscribe do |event|
      out << "data: #{event.to_json}\n\n"
    end
  end
end

Production Configuration

Complete config.ru:

# config.ru
require_relative 'config/environment'

# Production middleware
if ENV['RACK_ENV'] == 'production'
  use Rack::SSL
  use Rack::Deflater
end

# Static files
use Rack::Static,
  urls: ['/css', '/js', '/images'],
  root: 'public',
  header_rules: [
    [:all, {'Cache-Control' => 'public, max-age=31536000'}]
  ]

# Logging
use Rack::CommonLogger

# Sessions
use Rack::Session::Cookie,
  secret: ENV['SESSION_SECRET'],
  same_site: :strict,
  httponly: true,
  secure: ENV['RACK_ENV'] == 'production'

# Security
use Rack::Protection,
  except: [:session_hijacking],
  use: :all

# Rate limiting (production)
if ENV['RACK_ENV'] == 'production'
  require 'rack/attack'
  use Rack::Attack
end

# Mount applications
map '/api/v1' do
  run ApiV1::Application
end

map '/' do
  run WebApplication
end

Rack::Attack Configuration:

# config/rack_attack.rb
class Rack::Attack
  # Throttle login attempts
  throttle('login/ip', limit: 5, period: 60) do |req|
    req.ip if req.path == '/login' && req.post?
  end

  # Throttle API requests
  throttle('api/ip', limit: 100, period: 60) do |req|
    req.ip if req.path.start_with?('/api')
  end

  # Block suspicious requests
  blocklist('block bad user agents') do |req|
    req.user_agent =~ /bad_bot/i
  end
end

Testing Patterns

See references/testing-examples.rb for comprehensive test patterns.

Project Structure

Recommended modular structure:

app/
  controllers/
    base_controller.rb
    api_controller.rb
    users_controller.rb
    posts_controller.rb
  models/
    user.rb
    post.rb
  services/
    user_service.rb
    authentication_service.rb
  helpers/
    application_helpers.rb
    view_helpers.rb
config/
  environment.rb
  database.yml
  puma.rb
db/
  migrations/
lib/
  middleware/
    custom_auth.rb
  tasks/
public/
  css/
  js/
  images/
views/
  layout.erb
  users/
    index.erb
    show.erb
spec/
  controllers/
  models/
  spec_helper.rb
config.ru
Gemfile
Rakefile
README.md

Additional Resources

  • Routing Examples: assets/routing-examples.rb
  • Middleware Patterns: assets/middleware-patterns.rb
  • Modular App Template: assets/modular-app-template/
  • Production Config: references/production-config.rb
  • Testing Guide: references/testing-examples.rb