sinatra-patterns
Common Sinatra patterns, routing strategies, error handling, and application organization. Use when building Sinatra applications or designing routes.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Common Sinatra patterns, routing strategies, error handling, and application organization. Use when building Sinatra applications or designing routes.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Common Clap patterns and idioms for argument parsing, validation, and CLI design. Use when implementing CLI arguments with Clap v4+.
Configuration management patterns including file formats, precedence, environment variables, and XDG directories. Use when implementing configuration systems for CLI applications.
Distribution and packaging patterns including shell completions, man pages, cross-compilation, and release automation. Use when preparing CLI tools for distribution.
CLI user experience best practices for error messages, colors, progress indicators, and output formatting. Use when improving CLI usability and user experience.
Advanced concurrency patterns for Tokio including fan-out/fan-in, pipeline processing, rate limiting, and coordinated shutdown. Use when building high-concurrency async systems.
Network programming patterns with Hyper, Tonic, and Tower. Use when building HTTP services, gRPC applications, implementing middleware, connection pooling, or health checks.
| name | sinatra-patterns |
| description | Common Sinatra patterns, routing strategies, error handling, and application organization. Use when building Sinatra applications or designing routes. |
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
# Session middleware
use Rack::Session::Cookie, secret: ENV['SESSION_SECRET']
# Security middleware
use Rack::Protection
# Logging
use Rack::CommonLogger
# Compression
use Rack::Deflater
not_found do
'Page not found'
end
error do
'Internal server error'
end
error 401 do
'Unauthorized'
end
helpers do
def logged_in?
!session[:user_id].nil?
end
def current_user
@current_user ||= User.find_by(id: session[:user_id])
end
end
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
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
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
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
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
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
See assets/modular-app-template/ for complete modular application structure.
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
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
See references/testing-examples.rb for comprehensive test patterns.
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
assets/routing-examples.rbassets/middleware-patterns.rbassets/modular-app-template/references/production-config.rbreferences/testing-examples.rb