Ruby Routes Gem
A lightweight, flexible routing system for Ruby that provides a Rails-like DSL for defining and matching HTTP routes.
Features
- Rails-like DSL: Familiar syntax for defining routes
- HTTP Method Support: GET, POST, PUT, PATCH, DELETE, and custom methods
- RESTful Resources: Automatic generation of RESTful routes
- Nested Routes: Support for nested resources and namespaces
- Secure Route Constraints: Powerful constraint system with built-in security (see CONSTRAINTS.md)
- Named Routes: Generate URLs from route names
- Path Generation: Build URLs with parameters
- Scope Support: Group routes with common options
- Concerns: Reusable route groups
- Lightweight: Minimal dependencies, fast performance
Installation
Add this line to your application's Gemfile:
gem 'ruby_routes'
And then execute:
bundle install
Or install it yourself as:
gem install ruby_routes
Basic Usage
Simple Routes
require 'ruby_routes'
router = RubyRoutes.draw do
get '/', to: 'home#index'
get '/about', to: 'pages#about'
post '/users', to: 'users#create'
put '/users/:id', to: 'users#update'
delete '/users/:id', to: 'users#destroy'
end
RESTful Resources
router = RubyRoutes.draw do
resources :users
resources :posts
resources :comments
end
This creates the following routes:
GET /users→users#indexGET /users/new→users#newPOST /users→users#createGET /users/:id→users#showGET /users/:id/edit→users#editPUT /users/:id→users#updatePATCH /users/:id→users#updateDELETE /users/:id→users#destroy
Named Routes
router = RubyRoutes.draw do
get '/users', as: :users, to: 'users#index'
get '/users/:id', as: :user, to: 'users#show'
post '/users', as: :create_user, to: 'users#create'
end
# Generate paths
router.route_set.generate_path(:users) # => "/users"
router.route_set.generate_path(:user, id: '123') # => "/users/123"
Namespaces
router = RubyRoutes.draw do
namespace :admin do
resources :users
resources :posts
end
end
# Creates routes like:
# GET /admin/users → admin/users#index
# GET /admin/users/:id → admin/users#show
# etc.
Nested Resources
router = RubyRoutes.draw do
resources :categories do
resources :products
end
end
# Creates routes like:
# GET /categories/:category_id/products → products#index
# GET /categories/:category_id/products/:id → products#show
# etc.
Route Constraints
Ruby Routes provides a powerful and secure constraint system to validate route parameters. For security reasons, Proc constraints are deprecated - use the secure alternatives below.
Built-in Constraint Types
router = RubyRoutes.draw do
# Integer validation
get '/users/:id', to: 'users#show', constraints: { id: :int }
# UUID validation
get '/resources/:uuid', to: 'resources#show', constraints: { uuid: :uuid }
# Email validation
get '/users/:email', to: 'users#show', constraints: { email: :email }
# URL-friendly slug validation
get '/posts/:slug', to: 'posts#show', constraints: { slug: :slug }
# Alphabetic characters only
get '/categories/:name', to: 'categories#show', constraints: { name: :alpha }
# Alphanumeric characters only
get '/codes/:code', to: 'codes#show', constraints: { code: :alphanumeric }
end
Hash-based Constraints (Recommended)
router = RubyRoutes.draw do
# Length constraints
get '/users/:username', to: 'users#show',
constraints: {
username: {
min_length: 3,
max_length: 20,
format: /\A[a-zA-Z0-9_]+\z/
}
}
# Allowed values (whitelist)
get '/posts/:status', to: 'posts#show',
constraints: {
status: { in: %w[draft published archived] }
}
# Numeric ranges
get '/products/:price', to: 'products#show',
constraints: {
price: { range: 1..10000 }
}
end
Regular Expression Constraints
router = RubyRoutes.draw do
# Custom regex pattern (with ReDoS protection)
get '/products/:sku', to: 'products#show',
constraints: { sku: /\A[A-Z]{2}\d{4}\z/ }
end
⚠️ Security Notice: Proc Constraints Deprecated
# ❌ DEPRECATED - Security risk!
get '/users/:id', to: 'users#show',
constraints: { id: ->(value) { value.to_i > 0 } }
# ✅ Use secure alternatives instead:
get '/users/:id', to: 'users#show',
constraints: { id: { range: 1..Float::INFINITY } }
📚 For complete constraint documentation, see CONSTRAINTS.md
🔄 For migration help, see MIGRATION_GUIDE.md
Scopes
router = RubyRoutes.draw do
scope defaults: { format: 'html' } do
get '/posts', to: 'posts#index'
end
end
Concerns
router = RubyRoutes.draw do
concern :commentable do
resources :comments
end
resources :posts do
concerns :commentable
end
resources :articles do
concerns :commentable
end
end
Root Route
router = RubyRoutes.draw do
root to: 'home#index'
end
Route Matching
router = RubyRoutes.draw do
get '/users/:id', to: 'users#show'
post '/users', to: 'users#create'
end
# Match a request
result = router.route_set.match('GET', '/users/123')
if result
puts "Controller: #{result[:controller]}"
puts "Action: #{result[:action]}"
puts "Params: #{result[:params]}"
# => Controller: users
# => Action: show
# => Params: {"id"=>"123"}
end
Path Generation
router = RubyRoutes.draw do
get '/users/:id', as: :user, to: 'users#show'
get '/posts/:id/comments/:comment_id', as: :post_comment, to: 'comments#show'
end
# Generate paths
router.route_set.generate_path(:user, id: '123')
# => "/users/123"
router.route_set.generate_path(:post_comment, id: '456', comment_id: '789')
# => "/posts/456/comments/789"
Integration with Rack
require 'rack'
require 'ruby_routes'
# Define routes
router = RubyRoutes.draw do
get '/', to: 'home#index'
get '/users', to: 'users#index'
get '/users/:id', to: 'users#show'
end
# Create Rack app
class RubyRoutesApp
def initialize(router)
@router = router
end
def call(env)
request_method = env['REQUEST_METHOD']
request_path = env['PATH_INFO']
route_info = @router.route_set.match(request_method, request_path)
if route_info
# Handle the request
controller = route_info[:controller]
action = route_info[:action]
params = route_info[:params]
# Your controller logic here
[200, {'Content-Type' => 'text/html'}, ["Hello from #{controller}##{action}"]]
else
[404, {'Content-Type' => 'text/html'}, ['Not Found']]
end
end
end
# Run the app
app = RubyRoutesApp.new(router)
Rack::Handler::WEBrick.run app, Port: 9292
API Reference
RubyRoutes.draw(&block)
Creates a new router instance and yields to the block for route definition.
HTTP Methods
get(path, options = {})post(path, options = {})put(path, options = {})patch(path, options = {})delete(path, options = {})match(path, options = {})
Resource Methods
resources(name, options = {})- Creates RESTful routes for a collectionresource(name, options = {})- Creates RESTful routes for a singular resource
Options
to: 'controller#action'- Specifies controller and actioncontroller: 'name'- Specifies controller nameaction: 'name'- Specifies action nameas: :name- Names the route for path generationvia: :method- Specifies HTTP method(s)constraints: {}- Adds route constraintsdefaults: {}- Sets default parameters
RouteSet Methods
match(method, path)- Matches a request to a routegenerate_path(name, params = {})- Generates path from named routefind_route(method, path)- Finds a specific routefind_named_route(name)- Finds a named route
Documentation
Core Documentation
- CONSTRAINTS.md - Complete guide to route constraints and security best practices
- MIGRATION_GUIDE.md - Step-by-step guide for migrating from deprecated Proc constraints
Examples
See the examples/ directory for more detailed examples:
examples/basic_usage.rb- Basic routing examplesexamples/rack_integration.rb- Full Rack application example
Security
Ruby Routes prioritizes security and has implemented several protections:
🔒 Security Features
- XSS Protection: All HTML output is properly escaped
- ReDoS Protection: Regular expression constraints have timeout protection
- Secure Constraints: Deprecated dangerous Proc constraints in favor of secure alternatives
- Thread Safety: All caching and shared resources are thread-safe
- Input Validation: Comprehensive parameter validation before reaching application code
⚠️ Important Security Notice
Proc constraints are deprecated due to security risks and will be removed in a future version. They allow arbitrary code execution which can be exploited for:
- Code injection attacks
- Denial of service attacks
- System compromise
Migration Required: If you're using Proc constraints, please migrate to secure alternatives using our Migration Guide.
Testing
Run the test suite:
bundle exec rspec
The test suite includes comprehensive security tests to ensure all protections are working correctly.
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -am 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Create a new Pull Request
License
This gem is available as open source under the terms of the MIT License.
Acknowledgments
This gem was inspired by Rails routing and aims to provide a lightweight alternative for Ruby applications that need flexible routing without the full Rails framework.