| name | description |
|---|---|
rage-framework |
Develop and maintain Rage apps (Gemfile includes `gem "rage-rb"`) - controllers, routing, deferred jobs, events, websockets, OpenAPI, logging/telemetry, sessions/cookies, and RSpec. |
Rage is a fiber-based Ruby API framework with Rails-compatible ergonomics and a single-runtime model, fully integrated with Active Record.
Skills are versioned independently of Rage patch releases:
skills.major.minormaps to Ragemajor.minor- Patch versions may differ (for example Rage
1.20.1with skills1.20.6) - Older compatible minor skills may still be valid if provided by Rage CLI policy
Before doing implementation work:
- Run:
rage skills update --json
- If update succeeds, reload the skill file(s) before proceeding.
- If update fails (offline, registry unavailable, or no compatible remote match), continue with currently installed skills and briefly note that sync could not be completed.
- Treat only major-version incompatibility as blocking.
Rage runs HTTP, background jobs, and WebSockets in one runtime. Requests run in Fibers and pause on I/O, enabling concurrency with fewer moving parts than separate app/worker/pubsub stacks. Standard I/O (like Net::HTTP or Active Record queries) is automatically non-blocking.
gem install rage-rb
rage new my-app -d postgresql
cd my-app && bundle install && rage sDatabase options (-d): postgresql, mysql, trilogy, sqlite3. Run rage new --help for all options.
CLI commands:
rage s(server),rage c(console),rage routes,rage middleware,rage eventsrage g controller <name>,rage g model <name>,rage g migration <name>— generate controllers, models, and migrationsrage db:migrate,rage db:rollback,rage db:reset,rage db:seed— run database tasks (same as Railsrails db:*tasks)
Use standard Rails-style patterns for:
- Controllers (
before_action,skip_before_action,rescue_from,render,head,authenticate_with_http_token,request,response,headers) - REST-oriented routing (
resources,namespace, HTTP verb helpers) - Active Record integration
- Request specs via
rage/rspec
Assume Rails-like defaults for common work, but verify edge behavior in Rage docs/API before relying on it.
Important known differences:
- Route helpers are not generated (
photos_path,photos_urlare unavailable). - Route constraints are host-only.
- Wildcard routing is stricter (wildcard only at path end and unnamed).
paramsis a symbol-keyed Hash by default; Strong Parameters requires explicitactionpacksetup.
- Prefer RESTful resources and resource-focused controllers.
- Use concurrent I/O with Fibers for independent remote calls.
- Use
Rage::Deferredfor background work. - Use typed events (
Data.define) withRage::Eventsfor decoupled side effects. - Use
Rage::Cablefor real-time features. - Keep API docs in controller comments with
Rage::OpenAPItags. - Use structured logs and telemetry handlers for observability.
- Use serializers instead of
as_jsonto serialize objects to JSON.
Prefer RESTful resources: instead of adding custom actions to existing controllers, extract them into dedicated resource controllers. For example, UsersController#stats should become UserStatsController#index.
params is a plain Hash with symbol keys by default. For Strong Parameters, require actionpack before Rage loads:
# Gemfile
gem "actionpack", require: "action_controller/metal/strong_parameters"Without Strong Parameters, use standard Ruby:
# Strong Parameters
params.require(:user).permit(:full_name, :dob)
# Plain Ruby equivalent
params.fetch(:user).slice(:full_name, :dob)rendering is API-focused with signatures:
render(json: nil, plain: nil, status: nil)head(status)
Check RageController::API for all available controller methods and their arguments.
Rage is API-first, but controllers can render any output format. The key requirement is setting the correct content-type header.
Use ERB manually:
def index
template = ERB.new(Rage.root.join("app/views/index.html.erb").read)
render plain: template.result
headers["content-type"] = "text/html"
endSet HTML content type at controller level (after_action or around_action):
after_action { headers["content-type"] = "text/html" }
def index
render plain: MyPhlexComponent.new.call
endExecute multiple I/O operations in parallel:
def show
load_user = Fiber.schedule { Net::HTTP.get(URI("https://api.example.com/users/#{params[:id]}")) }
load_orders = Fiber.schedule { Net::HTTP.get(URI("https://api.example.com/orders?user_id=#{params[:id]}")) }
user, orders = Fiber.await([load_user, load_orders])
render json: { user: user, orders: orders }
endBoth requests run concurrently. If each takes 1 second, total time is still ~1 second.
Built-in job queue with WAL persistence. No Redis required. Runs in the same process.
class SendWelcomeEmail
include Rage::Deferred::Task
def perform(user_id:)
user = User.find(user_id)
Mailer.welcome(user).deliver
end
end
# Enqueue
SendWelcomeEmail.enqueue(user_id: 123)
SendWelcomeEmail.enqueue(user_id: 456, delay: 5.minutes)
SendWelcomeEmail.enqueue(user_id: 789, delay_until: Date.tomorrow.noon)
# Wrap existing classes
Rage::Deferred.wrap(WelcomeMailer.new(user)).deliverMiddleware for intercepting job lifecycle:
class LoggingMiddleware
def call(task_class:)
Rage.logger.info "Enqueueing #{task_class}"
yield # Must call yield to continue
end
end
# config/environments/production.rb
Rage.configure do
config.deferred.enqueue_middleware.use LoggingMiddleware
config.deferred.perform_middleware.use DecryptionMiddleware
endSee EnqueueMiddlewareInterface, PerformMiddlewareInterface for the data passed to middleware.
Generated from YARD-style controller comments. Mount the spec UI:
Rage.routes.draw do
mount Rage::OpenAPI.application, at: "/publicapi"
endCommon tags: summary comment, @description, @param, @request, @response, @auth, @private, @deprecated, @title/@version.
For full behavior and advanced patterns, load references/openapi.md (auth schemes, class/action tag scope, global responses, visibility controls, namespace filtering, multiple specs, and config.openapi.tag_resolver).
Key-value pairs, not string interpolation. JSON in production, text in development.
# Inline context (most common)
Rage.logger.info "Order created", order_id: order.id, user_id: user.id
Rage.logger.error "User not found", user_id: user_id
# Block context (structured KV pairs for a scope)
Rage.logger.with_context(user_id: 123, order_id: 456) do
Rage.logger.info "processing purchase" # => user_id=123 order_id=456 message=processing purchase
end
# Log tags
Rage.logger.tagged("PaymentProcessor") do
Rage.logger.info "processing" # => [req-id][PaymentProcessor] message=processing
endBuilt-in telemetry via Rage::Telemetry::Handler for tracking durations, recording metrics, and debugging production issues. Observe controller actions, cable actions, deferred tasks, and fiber scheduling.
OpenTelemetry integration — for full distributed tracing:
# Gemfile
gem "opentelemetry-sdk"
gem "opentelemetry-instrumentation-rage"# config/initializers/opentelemetry.rb
require "opentelemetry/sdk"
require "opentelemetry/instrumentation/rage"
OpenTelemetry::SDK.configure do |c|
c.use "OpenTelemetry::Instrumentation::Rage"
endAutomatically creates spans for HTTP requests, event subscribers, deferred tasks, and WebSocket messages. Adds span_id/trace_id to logger context.
See Rage::Cors(CORS rules) and Rage::RequestId(request ID tracking via the X-Request-Id header)
Load only the file needed for the current task.
| File | Load When |
|---|---|
references/events.md |
Designing domain events, subscriber architecture, deferred subscribers. Use in cases where domain events and event-driven architecture are beneficial. |
references/cable.md |
Implementing WebSockets/channels/connection auth, stream topology, protocol choices, multi-server cable setup |
references/observability.md |
Wiring structured logging, external loggers, telemetry handlers, span-based instrumentation, global log context/tags |
references/rspec.md |
Setting up and writing request and cable specs with rage/rspec, DB cleaner strategy, request helper usage |
references/openapi.md |
Adding or updating OpenAPI documentation tags, configuring authentication schemes, schema sources, namespace filtering, tag customization |
references/cookies-sessions.md |
Implementing cookies, encrypted cookies, sessions, required gems and system dependencies |
Configuration goes inside the Rage.configure block in either config/application.rb or config/environments/<environment.rb>.
Key namespaces: config.middleware, config.cable, config.deferred, config.openapi, config.telemetry.
Full reference: https://api.rage-rb.dev/Rage/Configuration
Check current environment with Rage.env (works the same way as Rails.env). Set env using the RAGE_ENV environment variable.
Rage can serve static files from public/ efficiently without Nginx.
# config/application.rb
config.public_file_server.enabled = true # defaults to falseFiles in public/ are served directly (e.g., public/images/logo.png -> /images/logo.png) or via the X-Sendfile response header.
Full API reference for all Rage components: https://rage-rb.dev/api.md
- Rails middleware: When integrating with a Rails app, Rage has its own middleware stack. You must explicitly configure middleware like
ActionDispatch::HostAuthorization. - Deferred limits: Not suited for CPU-heavy computation or bulk-scheduling 10,000+ distant future tasks.