internal/skills/content/sinatra/SKILL.md
Sinatra framework guardrails, patterns, and best practices for AI-assisted development. Use when working with Sinatra projects, or when the user mentions Sinatra. Provides lightweight Ruby web patterns, routing DSL, middleware, and REST API guidelines.
npx skillsauth add ar4mirez/samuel sinatraInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Applies to: Sinatra 3.x, Ruby 3.0+, REST APIs, Microservices, Prototypes Language Guide: @.claude/skills/ruby-guide/SKILL.md
Sinatra is a lightweight DSL for building web applications and APIs in Ruby. It maps HTTP verbs directly to Ruby blocks, providing minimal ceremony and maximum flexibility.
Use Sinatra when:
Consider alternatives when:
Sinatra::Base) for production applicationssinatra-contrib for JSON, namespaces, and reloader supportregister and Sinatra::Namespaceapp/helpers/dotenvsession_secret from ENV (not random fallback in production)before filters* in production)rack-test for HTTP integration testsdatabase_cleaner to reset state between testsmyapp/
├── app.rb # All routes and config
├── config.ru # Rack configuration
├── Gemfile
├── public/ # Static assets
├── views/ # ERB/Haml templates
│ ├── layout.erb
│ └── index.erb
└── spec/
└── app_spec.rb
myapp/
├── config.ru # Rack entry point
├── Gemfile
├── Rakefile # Database tasks
├── app/
│ ├── main.rb # Sinatra::Base application class
│ ├── routes/ # Route modules (one per resource)
│ │ ├── auth.rb
│ │ ├── users.rb
│ │ └── posts.rb
│ ├── models/ # ActiveRecord/Sequel models
│ │ ├── user.rb
│ │ └── post.rb
│ ├── services/ # Business logic (no Sinatra imports)
│ │ └── user_service.rb
│ └── helpers/ # Reusable helper modules
│ ├── auth_helper.rb
│ └── response_helper.rb
├── config/
│ ├── database.yml
│ └── environment.rb # Boot: dotenv, bundler, DB setup
├── db/
│ └── migrations/
├── public/
├── views/
└── spec/
├── spec_helper.rb
├── routes/
└── models/
Key conventions:
app/main.rb contains the Sinatra::Base subclass and wires everything togetherapp/routes/ holds one module per resource, registered via registerapp/services/ holds business logic with no framework couplingapp/helpers/ holds modules included via helpers DSLconfig/environment.rb boots dotenv, bundler, and database connectionSinatra maps HTTP verbs directly to Ruby blocks. Parameters are captured via :name symbols.
# Basic CRUD routes
get '/users' { json users: User.all.map(&:to_h) }
get '/users/:id' { json user: find_user!.to_h }
post '/users' { create_user }
put '/users/:id' { update_user }
delete '/users/:id' { delete_user }
# Named parameters (available in params hash)
get '/users/:id' do
user = User.find(params[:id])
json user: user.to_h
end
# Splat (wildcard) parameters
get '/files/*.*' do
# params['splat'] => ['path/to/file', 'ext']
end
# Query parameters
get '/search' do
# params[:q], params[:page]
end
# Regular expression routes
get %r{/posts/(\d+)} do |id|
Post.find(id)
end
Use sinatra/namespace to group routes with shared prefixes.
require 'sinatra/namespace'
class App < Sinatra::Base
register Sinatra::Namespace
namespace '/api/v1' do
namespace '/users' do
get { json users: User.all.map(&:to_h) }
post { create_user }
get '/:id' do
json user: User.find(params[:id]).to_h
end
end
end
end
# JSON body (use a helper for safety)
def json_params
@json_params ||= begin
body = request.body.read
body.empty? ? {} : JSON.parse(body, symbolize_names: true)
end
rescue JSON::ParserError
halt 400, json(error: 'Invalid JSON')
end
# Form data
params[:field_name]
# Headers
request.env['HTTP_AUTHORIZATION']
request.content_type
request.accept?('application/json')
# JSON response (via sinatra/json)
get '/health' do
json status: 'ok', timestamp: Time.now.iso8601
end
# Status codes
post '/users' do
user = User.create!(json_params)
status 201
json user: user.to_h
end
# Halt with error (stops execution immediately)
halt 404, json(error: 'Not found')
halt 401, json(error: 'Unauthorized')
halt 422, json(errors: user.errors.full_messages)
# Redirect
redirect '/login'
redirect '/users', 303 # See Other
# Headers
headers 'X-Custom-Header' => 'value'
content_type :json
Define reusable methods available in routes and views.
# In modular style, define helper modules
module AuthHelper
def current_user
@current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
end
def require_login!
halt 401, json(error: 'Unauthorized') unless current_user
end
def require_admin!
require_login!
halt 403, json(error: 'Forbidden') unless current_user.admin?
end
end
module ResponseHelper
def paginate(collection, per_page: 20)
page = (params[:page] || 1).to_i
per = (params[:per_page] || per_page).to_i
total = collection.count
items = collection.offset((page - 1) * per).limit(per)
{ items: items, meta: { page: page, per_page: per, total: total } }
end
end
# Register in the app
class App < Sinatra::Base
helpers AuthHelper
helpers ResponseHelper
end
Sinatra supports ERB, Haml, Slim, and other Tilt-compatible template engines. For JSON APIs, skip views entirely and use the json helper.
# Render a template (looks in views/ directory)
get '/' do
@title = 'Home'
erb :index # renders views/index.erb
end
# Custom layout
get '/admin' do
erb :dashboard, layout: :admin_layout
end
Use template inheritance via <%= yield %> in views/layout.erb. Keep logic out of templates; use helpers and instance variables set in routes.
class App < Sinatra::Base
# Named error handlers
not_found do
json error: 'Not found'
end
error do
json error: 'Internal server error'
end
# Catch specific exceptions
error ActiveRecord::RecordNotFound do
status 404
json error: 'Resource not found'
end
error ActiveRecord::RecordInvalid do |e|
status 422
json errors: e.record.errors.full_messages
end
error JSON::ParserError do
status 400
json error: 'Invalid JSON'
end
end
configure :development do
enable :show_exceptions
enable :logging
end
configure :production do
disable :show_exceptions
enable :raise_errors
end
Use before and after filters for cross-cutting concerns.
class App < Sinatra::Base
# Run before every request
before do
content_type :json if request.accept?('application/json')
end
# Scoped to path prefix
before '/api/*' do
content_type :json
authenticate_token!
end
# Run after every request
after do
response.headers['X-Request-Id'] = SecureRandom.uuid
end
end
require 'sinatra/base'
require 'sinatra/json'
require 'sinatra/namespace'
class App < Sinatra::Base
register Sinatra::Namespace
configure do
set :server, :puma
set :root, File.dirname(__FILE__)
set :views, proc { File.join(root, '..', 'views') }
enable :sessions
set :session_secret, ENV.fetch('SESSION_SECRET')
end
configure :development do
require 'sinatra/reloader'
register Sinatra::Reloader
enable :logging
end
Dir[File.join(__dir__, 'helpers', '*.rb')].each { |f| require f }
helpers AuthHelper, ResponseHelper
Dir[File.join(__dir__, 'routes', '*.rb')].each { |f| require f }
register Routes::Users
register Routes::Auth
get('/health') { json status: 'ok' }
not_found { json error: 'Not found' }
error { json error: 'Internal server error' }
end
module Routes
module Users
def self.registered(app)
app.namespace '/api/v1/users' do
get { json users: User.all.map(&:to_h) }
get('/:id') { json user: User.find(params[:id]).to_h }
post do
user = User.create!(json_params)
status 201
json user: user.to_h
rescue ActiveRecord::RecordInvalid => e
status 422
json errors: e.record.errors.full_messages
end
end
end
end
end
require 'bundler/setup'
Bundler.require(:default, ENV.fetch('RACK_ENV', 'development'))
require_relative 'config/environment'
require_relative 'app/main'
run App
# Install dependencies
bundle install
# Run development server (with auto-reload)
bundle exec ruby app.rb
# Or with rackup
bundle exec rackup -p 4567
# Run tests
bundle exec rspec
bundle exec rspec --format documentation
# Run with coverage
bundle exec rspec --format progress --require simplecov
# Database tasks
bundle exec rake db:create
bundle exec rake db:migrate
bundle exec rake db:rollback
# Production server
bundle exec puma -C config/puma.rb
# Console (interactive)
bundle exec pry -r ./config/environment
# Docker
docker build -t myapp .
docker run -p 4567:4567 myapp
| Gem | Purpose |
|-----|---------|
| sinatra | Web framework DSL |
| sinatra-contrib | Extensions: JSON, namespace, reloader |
| puma | Production application server |
| rake | Task runner for migrations |
| dotenv | Environment variable loading |
| activerecord / sequel | ORM for database access |
| pg | PostgreSQL adapter |
| bcrypt | Password hashing (has_secure_password) |
| jwt | JSON Web Token authentication |
| rspec | Test framework |
| rack-test | HTTP request helpers for Rack apps |
| database_cleaner-active_record | DB cleanup between tests |
| factory_bot | Test data factories |
For detailed code examples and advanced patterns, see:
development
Zig language guardrails, patterns, and best practices for AI-assisted development. Use when working with Zig files (.zig), build.zig, or when the user mentions Zig. Provides comptime patterns, allocator conventions, C interop guidelines, and testing standards specific to this project's coding standards.
tools
WordPress framework guardrails, patterns, and best practices for AI-assisted development. Use when working with WordPress projects, or when the user mentions WordPress. Provides theme development, plugin architecture, REST API, blocks, and security guidelines.
tools
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs. Use when testing web apps, automating browser interactions, or debugging frontend issues.
tools
Suite of tools for creating elaborate, multi-component web applications using modern frontend technologies (React, Tailwind CSS, shadcn/ui). Use for complex projects requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX pages.