sinatra-patterns

Sinatra Patterns Skill

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "sinatra-patterns" with this command: npx skills add geoffjay/claude-plugins/geoffjay-claude-plugins-sinatra-patterns

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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

documentation-update

No summary provided by upstream source.

Repository SourceNeeds Review
General

git-troubleshooting

No summary provided by upstream source.

Repository SourceNeeds Review
General

git-advanced

No summary provided by upstream source.

Repository SourceNeeds Review
General

tokio-patterns

No summary provided by upstream source.

Repository SourceNeeds Review