ruby_routes 2.2.0 โ 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +240 -163
- data/lib/ruby_routes/constant.rb +137 -18
- data/lib/ruby_routes/lru_strategies/hit_strategy.rb +31 -4
- data/lib/ruby_routes/lru_strategies/miss_strategy.rb +21 -0
- data/lib/ruby_routes/node.rb +86 -36
- data/lib/ruby_routes/radix_tree/finder.rb +213 -0
- data/lib/ruby_routes/radix_tree/inserter.rb +96 -0
- data/lib/ruby_routes/radix_tree.rb +65 -230
- data/lib/ruby_routes/route/check_helpers.rb +115 -0
- data/lib/ruby_routes/route/constraint_validator.rb +173 -0
- data/lib/ruby_routes/route/param_support.rb +200 -0
- data/lib/ruby_routes/route/path_builder.rb +84 -0
- data/lib/ruby_routes/route/path_generation.rb +87 -0
- data/lib/ruby_routes/route/query_helpers.rb +56 -0
- data/lib/ruby_routes/route/segment_compiler.rb +166 -0
- data/lib/ruby_routes/route/small_lru.rb +93 -18
- data/lib/ruby_routes/route/validation_helpers.rb +174 -0
- data/lib/ruby_routes/route/warning_helpers.rb +57 -0
- data/lib/ruby_routes/route.rb +127 -501
- data/lib/ruby_routes/route_set/cache_helpers.rb +76 -0
- data/lib/ruby_routes/route_set/collection_helpers.rb +125 -0
- data/lib/ruby_routes/route_set.rb +140 -132
- data/lib/ruby_routes/router/build_helpers.rb +99 -0
- data/lib/ruby_routes/router/builder.rb +97 -0
- data/lib/ruby_routes/router/http_helpers.rb +135 -0
- data/lib/ruby_routes/router/resource_helpers.rb +137 -0
- data/lib/ruby_routes/router/scope_helpers.rb +127 -0
- data/lib/ruby_routes/router.rb +196 -182
- data/lib/ruby_routes/segment.rb +28 -8
- data/lib/ruby_routes/segments/base_segment.rb +40 -4
- data/lib/ruby_routes/segments/dynamic_segment.rb +48 -12
- data/lib/ruby_routes/segments/static_segment.rb +43 -7
- data/lib/ruby_routes/segments/wildcard_segment.rb +58 -12
- data/lib/ruby_routes/string_extensions.rb +52 -15
- data/lib/ruby_routes/url_helpers.rb +106 -24
- data/lib/ruby_routes/utility/inflector_utility.rb +35 -0
- data/lib/ruby_routes/utility/key_builder_utility.rb +171 -77
- data/lib/ruby_routes/utility/method_utility.rb +137 -0
- data/lib/ruby_routes/utility/path_utility.rb +75 -28
- data/lib/ruby_routes/utility/route_utility.rb +30 -2
- data/lib/ruby_routes/version.rb +3 -1
- data/lib/ruby_routes.rb +68 -11
- metadata +27 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57b9470b49019746492c10fd0e202c6c544c78459f36e0d2c1f8d400103def93
|
4
|
+
data.tar.gz: 8e59a14f7854cadb955d381fcf7947b7769d0041de5067f3e3dc5827d13cbb26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c746c95407c222fdd209e464c3f1b89971987d8b41a557b7f173e1acb55fa5d6626b030bab26aed81c7ff766bd9da8c4ba4c2895ce167558a61c089923e14f12
|
7
|
+
data.tar.gz: 6b512f5e810f19fa4031b33f29d3efa72feabb841e8ac3558af67ce6ec43556cacc85577993015cde7f3f400c10e9a36cbc8e007831808a798fa80204fd57046
|
data/README.md
CHANGED
@@ -1,26 +1,42 @@
|
|
1
|
-
# Ruby Routes
|
1
|
+
# Ruby Routes
|
2
2
|
|
3
|
-
A
|
3
|
+
A high-performance, lightweight routing system for Ruby applications providing a Rails-like DSL for defining and matching HTTP routes.
|
4
4
|
|
5
5
|
## Features
|
6
6
|
|
7
|
-
-
|
8
|
-
-
|
9
|
-
-
|
10
|
-
-
|
11
|
-
-
|
12
|
-
-
|
13
|
-
-
|
14
|
-
-
|
15
|
-
-
|
16
|
-
|
7
|
+
- **๐ High Performance**: Fast routing with 99.99% cache hit rate
|
8
|
+
- **๐ Rails-like DSL**: Familiar syntax for defining routes
|
9
|
+
- **๐ฃ๏ธ RESTful Resources**: Automatic generation of RESTful routes
|
10
|
+
- **๐ Secure Constraints**: Built-in security with comprehensive constraint system
|
11
|
+
- **๐งฉ Modular Design**: Nested resources, scopes, concerns, and namespaces
|
12
|
+
- **๐ Named Routes**: Generate paths from route names with automatic parameter handling
|
13
|
+
- **๐งต Thread Safety**: All caching and shared resources are thread-safe
|
14
|
+
- **๐ Optimized Caching**: Smart caching strategies for route recognition and path generation
|
15
|
+
- **๐ชถ Lightweight**: Minimal dependencies for easy integration
|
16
|
+
|
17
|
+
## Table of Contents
|
18
|
+
|
19
|
+
- [Installation](#installation)
|
20
|
+
- [Quick Start](#quick-start)
|
21
|
+
- [Basic Usage](#basic-usage)
|
22
|
+
- [Route Constraints](#route-constraints)
|
23
|
+
- [Route Matching](#route-matching)
|
24
|
+
- [Path Generation](#path-generation)
|
25
|
+
- [Integration with Rack](#integration-with-rack)
|
26
|
+
- [API Reference](#api-reference)
|
27
|
+
- [Performance](#performance)
|
28
|
+
- [Security](#security)
|
29
|
+
- [Documentation](#documentation)
|
30
|
+
- [Testing](#testing)
|
31
|
+
- [Contributing](#contributing)
|
32
|
+
- [License](#license)
|
17
33
|
|
18
34
|
## Installation
|
19
35
|
|
20
36
|
Add this line to your application's Gemfile:
|
21
37
|
|
22
38
|
```ruby
|
23
|
-
gem 'ruby_routes'
|
39
|
+
gem 'ruby_routes', '~> 2.3.0'
|
24
40
|
```
|
25
41
|
|
26
42
|
And then execute:
|
@@ -35,13 +51,35 @@ Or install it yourself as:
|
|
35
51
|
gem install ruby_routes
|
36
52
|
```
|
37
53
|
|
54
|
+
## Quick Start
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
require 'ruby_routes'
|
58
|
+
|
59
|
+
# Define routes
|
60
|
+
router = RubyRoutes.draw do
|
61
|
+
root to: 'home#index'
|
62
|
+
resources :users
|
63
|
+
|
64
|
+
namespace :api do
|
65
|
+
resources :products
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Match a request
|
70
|
+
result = router.route_set.match('GET', '/users/123')
|
71
|
+
# => {controller: 'users', action: 'show', params: {'id' => '123'}, route: #<RubyRoutes::Route...>}
|
72
|
+
|
73
|
+
# Generate a path
|
74
|
+
path = router.route_set.generate_path(:user, id: 456)
|
75
|
+
# => "/users/456"
|
76
|
+
```
|
77
|
+
|
38
78
|
## Basic Usage
|
39
79
|
|
40
80
|
### Simple Routes
|
41
81
|
|
42
82
|
```ruby
|
43
|
-
require 'ruby_routes'
|
44
|
-
|
45
83
|
router = RubyRoutes.draw do
|
46
84
|
get '/', to: 'home#index'
|
47
85
|
get '/about', to: 'pages#about'
|
@@ -57,19 +95,19 @@ end
|
|
57
95
|
router = RubyRoutes.draw do
|
58
96
|
resources :users
|
59
97
|
resources :posts
|
60
|
-
resources :comments
|
61
98
|
end
|
62
99
|
```
|
63
100
|
|
64
101
|
This creates the following routes:
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
102
|
+
| HTTP Method | Path | Controller#Action | Named Route |
|
103
|
+
|-------------|--------------------|--------------------|-------------------|
|
104
|
+
| GET | /users | users#index | users_path |
|
105
|
+
| GET | /users/new | users#new | new_user_path |
|
106
|
+
| POST | /users | users#create | users_path |
|
107
|
+
| GET | /users/:id | users#show | user_path |
|
108
|
+
| GET | /users/:id/edit | users#edit | edit_user_path |
|
109
|
+
| PUT/PATCH | /users/:id | users#update | user_path |
|
110
|
+
| DELETE | /users/:id | users#destroy | user_path |
|
73
111
|
|
74
112
|
### Named Routes
|
75
113
|
|
@@ -77,7 +115,6 @@ This creates the following routes:
|
|
77
115
|
router = RubyRoutes.draw do
|
78
116
|
get '/users', as: :users, to: 'users#index'
|
79
117
|
get '/users/:id', as: :user, to: 'users#show'
|
80
|
-
post '/users', as: :create_user, to: 'users#create'
|
81
118
|
end
|
82
119
|
|
83
120
|
# Generate paths
|
@@ -94,13 +131,12 @@ router = RubyRoutes.draw do
|
|
94
131
|
resources :posts
|
95
132
|
end
|
96
133
|
end
|
97
|
-
|
98
|
-
# Creates routes like:
|
99
|
-
# GET /admin/users โ admin/users#index
|
100
|
-
# GET /admin/users/:id โ admin/users#show
|
101
|
-
# etc.
|
102
134
|
```
|
103
135
|
|
136
|
+
Creates routes like:
|
137
|
+
- `GET /admin/users` โ `admin/users#index`
|
138
|
+
- `GET /admin/users/:id` โ `admin/users#show`
|
139
|
+
|
104
140
|
### Nested Resources
|
105
141
|
|
106
142
|
```ruby
|
@@ -109,18 +145,60 @@ router = RubyRoutes.draw do
|
|
109
145
|
resources :products
|
110
146
|
end
|
111
147
|
end
|
148
|
+
```
|
149
|
+
|
150
|
+
Creates routes like:
|
151
|
+
- `GET /categories/:category_id/products` โ `products#index`
|
152
|
+
- `GET /categories/:category_id/products/:id` โ `products#show`
|
153
|
+
|
154
|
+
### Scopes & Concerns
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
router = RubyRoutes.draw do
|
158
|
+
# Scopes
|
159
|
+
scope defaults: { format: 'json' } do
|
160
|
+
get '/api/users', to: 'api/users#index'
|
161
|
+
end
|
162
|
+
|
163
|
+
# Concerns (reusable route groups)
|
164
|
+
concern :commentable do
|
165
|
+
resources :comments
|
166
|
+
end
|
167
|
+
|
168
|
+
resources :posts, concerns: :commentable
|
169
|
+
resources :articles, concerns: :commentable
|
170
|
+
end
|
171
|
+
```
|
172
|
+
|
173
|
+
### Method Chaining
|
112
174
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
#
|
175
|
+
```ruby
|
176
|
+
router = RubyRoutes.draw do
|
177
|
+
get('/users', to: 'users#index')
|
178
|
+
.post('/users', to: 'users#create')
|
179
|
+
.put('/users/:id', to: 'users#update')
|
180
|
+
.delete('/users/:id', to: 'users#destroy')
|
181
|
+
end
|
182
|
+
```
|
183
|
+
|
184
|
+
### Thread-safe Builder
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
# Accumulate routes without mutating a live router
|
188
|
+
router = RubyRoutes::Router.build do
|
189
|
+
resources :users
|
190
|
+
namespace :admin do
|
191
|
+
resources :posts
|
192
|
+
end
|
193
|
+
end
|
194
|
+
# router is now finalized and thread-safe
|
117
195
|
```
|
118
196
|
|
119
|
-
|
197
|
+
## Route Constraints
|
120
198
|
|
121
|
-
Ruby Routes provides a powerful
|
199
|
+
Ruby Routes provides a powerful constraint system to validate route parameters before they reach your controllers.
|
122
200
|
|
123
|
-
|
201
|
+
### Built-in Constraint Types
|
124
202
|
|
125
203
|
```ruby
|
126
204
|
router = RubyRoutes.draw do
|
@@ -133,18 +211,12 @@ router = RubyRoutes.draw do
|
|
133
211
|
# Email validation
|
134
212
|
get '/users/:email', to: 'users#show', constraints: { email: :email }
|
135
213
|
|
136
|
-
#
|
214
|
+
# Slug validation
|
137
215
|
get '/posts/:slug', to: 'posts#show', constraints: { slug: :slug }
|
138
|
-
|
139
|
-
# Alphabetic characters only
|
140
|
-
get '/categories/:name', to: 'categories#show', constraints: { name: :alpha }
|
141
|
-
|
142
|
-
# Alphanumeric characters only
|
143
|
-
get '/codes/:code', to: 'codes#show', constraints: { code: :alphanumeric }
|
144
216
|
end
|
145
217
|
```
|
146
218
|
|
147
|
-
|
219
|
+
### Hash-based Constraints (Recommended)
|
148
220
|
|
149
221
|
```ruby
|
150
222
|
router = RubyRoutes.draw do
|
@@ -160,19 +232,15 @@ router = RubyRoutes.draw do
|
|
160
232
|
|
161
233
|
# Allowed values (whitelist)
|
162
234
|
get '/posts/:status', to: 'posts#show',
|
163
|
-
constraints: {
|
164
|
-
status: { in: %w[draft published archived] }
|
165
|
-
}
|
235
|
+
constraints: { status: { in: %w[draft published archived] } }
|
166
236
|
|
167
237
|
# Numeric ranges
|
168
238
|
get '/products/:price', to: 'products#show',
|
169
|
-
constraints: {
|
170
|
-
price: { range: 1..10000 }
|
171
|
-
}
|
239
|
+
constraints: { price: { range: 1..10000 } }
|
172
240
|
end
|
173
241
|
```
|
174
242
|
|
175
|
-
|
243
|
+
### Regular Expression Constraints
|
176
244
|
|
177
245
|
```ruby
|
178
246
|
router = RubyRoutes.draw do
|
@@ -182,7 +250,7 @@ router = RubyRoutes.draw do
|
|
182
250
|
end
|
183
251
|
```
|
184
252
|
|
185
|
-
|
253
|
+
โ ๏ธ **Security Notice**: Proc constraints are deprecated due to security risks:
|
186
254
|
|
187
255
|
```ruby
|
188
256
|
# โ DEPRECATED - Security risk!
|
@@ -194,44 +262,8 @@ get '/users/:id', to: 'users#show',
|
|
194
262
|
constraints: { id: { range: 1..Float::INFINITY } }
|
195
263
|
```
|
196
264
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
### Scopes
|
201
|
-
|
202
|
-
```ruby
|
203
|
-
router = RubyRoutes.draw do
|
204
|
-
scope defaults: { format: 'html' } do
|
205
|
-
get '/posts', to: 'posts#index'
|
206
|
-
end
|
207
|
-
end
|
208
|
-
```
|
209
|
-
|
210
|
-
### Concerns
|
211
|
-
|
212
|
-
```ruby
|
213
|
-
router = RubyRoutes.draw do
|
214
|
-
concern :commentable do
|
215
|
-
resources :comments
|
216
|
-
end
|
217
|
-
|
218
|
-
resources :posts do
|
219
|
-
concerns :commentable
|
220
|
-
end
|
221
|
-
|
222
|
-
resources :articles do
|
223
|
-
concerns :commentable
|
224
|
-
end
|
225
|
-
end
|
226
|
-
```
|
227
|
-
|
228
|
-
### Root Route
|
229
|
-
|
230
|
-
```ruby
|
231
|
-
router = RubyRoutes.draw do
|
232
|
-
root to: 'home#index'
|
233
|
-
end
|
234
|
-
```
|
265
|
+
๐ For complete constraint documentation, see [CONSTRAINTS.md](CONSTRAINTS.md)
|
266
|
+
๐ For migration help, see [MIGRATION_GUIDE.md](MIGRATION_GUIDE.md)
|
235
267
|
|
236
268
|
## Route Matching
|
237
269
|
|
@@ -244,12 +276,10 @@ end
|
|
244
276
|
# Match a request
|
245
277
|
result = router.route_set.match('GET', '/users/123')
|
246
278
|
if result
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
# =>
|
251
|
-
# => Action: show
|
252
|
-
# => Params: {"id"=>"123"}
|
279
|
+
controller = result[:controller] # => "users"
|
280
|
+
action = result[:action] # => "show"
|
281
|
+
params = result[:params] # => {"id"=>"123"}
|
282
|
+
route = result[:route] # => #<RubyRoutes::Route...>
|
253
283
|
end
|
254
284
|
```
|
255
285
|
|
@@ -262,10 +292,10 @@ router = RubyRoutes.draw do
|
|
262
292
|
end
|
263
293
|
|
264
294
|
# Generate paths
|
265
|
-
router.route_set.generate_path(:user, id: '123')
|
295
|
+
path1 = router.route_set.generate_path(:user, id: '123')
|
266
296
|
# => "/users/123"
|
267
297
|
|
268
|
-
router.route_set.generate_path(:post_comment, id: '456', comment_id: '789')
|
298
|
+
path2 = router.route_set.generate_path(:post_comment, id: '456', comment_id: '789')
|
269
299
|
# => "/posts/456/comments/789"
|
270
300
|
```
|
271
301
|
|
@@ -275,33 +305,28 @@ router.route_set.generate_path(:post_comment, id: '456', comment_id: '789')
|
|
275
305
|
require 'rack'
|
276
306
|
require 'ruby_routes'
|
277
307
|
|
278
|
-
# Define routes
|
279
|
-
router = RubyRoutes.draw do
|
280
|
-
get '/', to: 'home#index'
|
281
|
-
get '/users', to: 'users#index'
|
282
|
-
get '/users/:id', to: 'users#show'
|
283
|
-
end
|
284
|
-
|
285
|
-
# Create Rack app
|
286
308
|
class RubyRoutesApp
|
287
|
-
def initialize
|
288
|
-
@router =
|
309
|
+
def initialize
|
310
|
+
@router = RubyRoutes.draw do
|
311
|
+
root to: 'home#index'
|
312
|
+
resources :users
|
313
|
+
get '/about', to: 'pages#about'
|
314
|
+
end
|
289
315
|
end
|
290
316
|
|
291
317
|
def call(env)
|
292
318
|
request_method = env['REQUEST_METHOD']
|
293
319
|
request_path = env['PATH_INFO']
|
294
320
|
|
295
|
-
|
321
|
+
result = @router.route_set.match(request_method, request_path)
|
296
322
|
|
297
|
-
if
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
params = route_info[:params]
|
323
|
+
if result
|
324
|
+
controller_name = result[:controller]
|
325
|
+
action_name = result[:action]
|
326
|
+
params = result[:params]
|
302
327
|
|
303
328
|
# Your controller logic here
|
304
|
-
[200, {'Content-Type' => 'text/html'}, ["
|
329
|
+
[200, {'Content-Type' => 'text/html'}, ["#{controller_name}##{action_name} with #{params}"]]
|
305
330
|
else
|
306
331
|
[404, {'Content-Type' => 'text/html'}, ['Not Found']]
|
307
332
|
end
|
@@ -309,89 +334,103 @@ class RubyRoutesApp
|
|
309
334
|
end
|
310
335
|
|
311
336
|
# Run the app
|
312
|
-
|
313
|
-
Rack::Handler::WEBrick.run app, Port: 9292
|
337
|
+
Rack::Handler::WEBrick.run RubyRoutesApp.new, Port: 9292
|
314
338
|
```
|
315
339
|
|
316
340
|
## API Reference
|
317
341
|
|
318
|
-
### RubyRoutes
|
319
|
-
|
320
|
-
Creates a new router instance and yields to the block for route definition.
|
321
|
-
|
322
|
-
### HTTP Methods
|
323
|
-
|
324
|
-
- `get(path, options = {})`
|
325
|
-
- `post(path, options = {})`
|
326
|
-
- `put(path, options = {})`
|
327
|
-
- `patch(path, options = {})`
|
328
|
-
- `delete(path, options = {})`
|
329
|
-
- `match(path, options = {})`
|
330
|
-
|
331
|
-
### Resource Methods
|
342
|
+
### RubyRoutes
|
332
343
|
|
333
|
-
- `
|
334
|
-
- `resource(name, options = {})` - Creates RESTful routes for a singular resource
|
344
|
+
- `RubyRoutes.draw(&block)` - Creates a new router and yields the block for route definition
|
335
345
|
|
336
|
-
###
|
346
|
+
### Router Methods
|
337
347
|
|
338
|
-
- `
|
339
|
-
- `
|
340
|
-
- `
|
341
|
-
- `
|
342
|
-
- `
|
343
|
-
- `
|
344
|
-
- `
|
348
|
+
- `get(path, options = {})` - Define a GET route
|
349
|
+
- `post(path, options = {})` - Define a POST route
|
350
|
+
- `put(path, options = {})` - Define a PUT route
|
351
|
+
- `patch(path, options = {})` - Define a PATCH route
|
352
|
+
- `delete(path, options = {})` - Define a DELETE route
|
353
|
+
- `match(path, options = {})` - Define a route for multiple HTTP methods
|
354
|
+
- `root(options = {})` - Define a root route (/)
|
355
|
+
- `resources(name, options = {})` - Define RESTful resource routes
|
356
|
+
- `resource(name, options = {})` - Define singular RESTful resource routes
|
357
|
+
- `namespace(name, options = {})` - Group routes with a namespace
|
358
|
+
- `scope(options = {})` - Group routes with shared options
|
359
|
+
- `concern(name, &block)` - Define a reusable route concern
|
360
|
+
- `concerns(names)` - Use defined concerns in the current context
|
361
|
+
- `build(&block)` - Create a thread-safe, finalized router by accumulating routes in a builder
|
345
362
|
|
346
363
|
### RouteSet Methods
|
347
364
|
|
348
|
-
- `match(method, path)` -
|
349
|
-
- `generate_path(name, params = {})` -
|
350
|
-
- `find_route(method, path)` -
|
351
|
-
- `find_named_route(name)` -
|
365
|
+
- `match(method, path)` - Match a request to a route
|
366
|
+
- `generate_path(name, params = {})` - Generate a path from a named route
|
367
|
+
- `find_route(method, path)` - Find a specific route
|
368
|
+
- `find_named_route(name)` - Find a named route by name
|
352
369
|
|
353
|
-
##
|
370
|
+
## Performance
|
354
371
|
|
355
|
-
|
356
|
-
- **[CONSTRAINTS.md](CONSTRAINTS.md)** - Complete guide to route constraints and security best practices
|
357
|
-
- **[MIGRATION_GUIDE.md](MIGRATION_GUIDE.md)** - Step-by-step guide for migrating from deprecated Proc constraints
|
372
|
+
Ruby Routes is optimized for high-performance applications:
|
358
373
|
|
359
|
-
|
374
|
+
- **Fast** routing
|
375
|
+
- **99.99% cache hit rate** for common access patterns
|
376
|
+
- **Low memory footprint** with bounded caches and object reuse
|
377
|
+
- **Zero memory leaks** in long-running applications
|
360
378
|
|
361
|
-
|
379
|
+
Performance metrics (from `benchmark/` directory):
|
362
380
|
|
363
|
-
|
364
|
-
|
381
|
+
| Operation | Operations/sec | Memory Usage |
|
382
|
+
|-----------|----------------|-------------|
|
383
|
+
| Route Matching | ~250,000/sec | Low |
|
384
|
+
| Path Generation | ~400,000/sec | Low |
|
385
|
+
| Static Routes | ~500,000/sec | Minimal |
|
365
386
|
|
366
387
|
## Security
|
367
388
|
|
368
|
-
Ruby Routes prioritizes security
|
389
|
+
Ruby Routes prioritizes security with these protections:
|
369
390
|
|
370
391
|
### ๐ Security Features
|
371
|
-
|
392
|
+
|
372
393
|
- **ReDoS Protection**: Regular expression constraints have timeout protection
|
373
|
-
- **Secure Constraints**:
|
374
|
-
- **Thread Safety**: All
|
375
|
-
- **Input Validation**: Comprehensive parameter validation
|
394
|
+
- **Secure Constraints**: Type-safe constraint system without code execution
|
395
|
+
- **Thread Safety**: All shared resources are thread-safe
|
396
|
+
- **Input Validation**: Comprehensive parameter validation
|
397
|
+
|
398
|
+
### โ ๏ธ Security Notice
|
376
399
|
|
377
|
-
### โ ๏ธ Important Security Notice
|
378
400
|
**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:
|
401
|
+
|
379
402
|
- Code injection attacks
|
380
403
|
- Denial of service attacks
|
381
404
|
- System compromise
|
382
405
|
|
383
406
|
**Migration Required**: If you're using Proc constraints, please migrate to secure alternatives using our [Migration Guide](MIGRATION_GUIDE.md).
|
384
407
|
|
408
|
+
**Note**: RubyRoutes is a routing library and does not provide application-level security features such as XSS protection, CSRF protection, or authentication. These should be handled by your web framework or additional security middleware.
|
409
|
+
|
410
|
+
## Documentation
|
411
|
+
|
412
|
+
### Core Documentation
|
413
|
+
|
414
|
+
- **[CONSTRAINTS.md](CONSTRAINTS.md)** - Complete guide to route constraints and security
|
415
|
+
- **[MIGRATION_GUIDE.md](MIGRATION_GUIDE.md)** - Guide for migrating from deprecated Proc constraints
|
416
|
+
- **[USAGE.md](USAGE.md)** - Extended usage scenarios
|
417
|
+
|
418
|
+
### Examples
|
419
|
+
|
420
|
+
See the `examples/` directory for more detailed examples:
|
421
|
+
|
422
|
+
- `examples/basic_usage.rb` - Basic routing examples
|
423
|
+
- `examples/rack_integration.rb` - Full Rack application example
|
424
|
+
- `examples/constraints.rb` - Route constraint examples
|
425
|
+
|
385
426
|
## Testing
|
386
427
|
|
387
|
-
|
428
|
+
Ruby Routes has comprehensive test coverage:
|
388
429
|
|
389
430
|
```bash
|
390
431
|
bundle exec rspec
|
391
432
|
```
|
392
433
|
|
393
|
-
The test suite includes comprehensive security tests to ensure all protections are working correctly.
|
394
|
-
|
395
434
|
## Contributing
|
396
435
|
|
397
436
|
1. Fork the repository
|
@@ -407,3 +446,41 @@ This gem is available as open source under the terms of the [MIT License](LICENS
|
|
407
446
|
## Acknowledgments
|
408
447
|
|
409
448
|
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.
|
449
|
+
|
450
|
+
## Thread-safe Build (Isolated Builder)
|
451
|
+
|
452
|
+
Use the builder to accumulate routes without mutating a live router:
|
453
|
+
|
454
|
+
```ruby
|
455
|
+
router = RubyRoutes::Router.build do
|
456
|
+
resources :users
|
457
|
+
namespace :admin do
|
458
|
+
resources :posts
|
459
|
+
end
|
460
|
+
end
|
461
|
+
# router is now finalized (immutable)
|
462
|
+
```
|
463
|
+
|
464
|
+
If you need manual steps:
|
465
|
+
|
466
|
+
```ruby
|
467
|
+
builder = RubyRoutes::Router::Builder.new do
|
468
|
+
get '/health', to: 'system#health'
|
469
|
+
end
|
470
|
+
router = builder.build # finalized
|
471
|
+
```
|
472
|
+
|
473
|
+
## Fluent Method Chaining
|
474
|
+
|
475
|
+
For a more concise style, the routing DSL supports method chaining:
|
476
|
+
|
477
|
+
```ruby
|
478
|
+
router = RubyRoutes.draw do
|
479
|
+
get('/users', to: 'users#index')
|
480
|
+
.post('/users', to: 'users#create')
|
481
|
+
.put('/users/:id', to: 'users#update')
|
482
|
+
.delete('/users/:id', to: 'users#destroy')
|
483
|
+
.resources(:posts)
|
484
|
+
end
|
485
|
+
```
|
486
|
+
|