ruby_routes 2.1.0 โ 2.3.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 +232 -162
- 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 +82 -41
- data/lib/ruby_routes/radix_tree/finder.rb +164 -0
- data/lib/ruby_routes/radix_tree/inserter.rb +98 -0
- data/lib/ruby_routes/radix_tree.rb +83 -142
- data/lib/ruby_routes/route/check_helpers.rb +109 -0
- data/lib/ruby_routes/route/constraint_validator.rb +159 -0
- data/lib/ruby_routes/route/param_support.rb +202 -0
- data/lib/ruby_routes/route/path_builder.rb +86 -0
- data/lib/ruby_routes/route/path_generation.rb +102 -0
- data/lib/ruby_routes/route/query_helpers.rb +56 -0
- data/lib/ruby_routes/route/segment_compiler.rb +163 -0
- data/lib/ruby_routes/route/small_lru.rb +96 -17
- data/lib/ruby_routes/route/validation_helpers.rb +151 -0
- data/lib/ruby_routes/route/warning_helpers.rb +54 -0
- data/lib/ruby_routes/route.rb +121 -451
- data/lib/ruby_routes/route_set/cache_helpers.rb +174 -0
- data/lib/ruby_routes/route_set/collection_helpers.rb +127 -0
- data/lib/ruby_routes/route_set.rb +126 -148
- data/lib/ruby_routes/router/build_helpers.rb +100 -0
- data/lib/ruby_routes/router/builder.rb +96 -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 +109 -0
- data/lib/ruby_routes/router.rb +196 -179
- 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 +56 -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 +179 -0
- data/lib/ruby_routes/utility/method_utility.rb +137 -0
- data/lib/ruby_routes/utility/path_utility.rb +89 -0
- data/lib/ruby_routes/utility/route_utility.rb +49 -0
- data/lib/ruby_routes/version.rb +3 -1
- data/lib/ruby_routes.rb +68 -11
- metadata +30 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 59f7b87f3f7b94ce54fcc9a8a3d917833c93312412b160cf82268552fd0454ba
|
4
|
+
data.tar.gz: 40417f1672ac4e849f6a85a9170fd01f1619ef931b8bd58e1f52b8896d23605c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c099d775cf22a7327e8f8b1cfdcb1d51495c08717f20eb879b89e6dbfe2e4ae56cb028ae494e13a72c311c668454d6a6ab8f4e4c7a26a76d0d02ec99ca3c342
|
7
|
+
data.tar.gz: 754386d02e0387500ae507f5a2e0ad7c11b46a04c7a461aafbee34fd7ad1d19ef7d7ff1ad207375e2e3be205d8af4afa11c06be8d4e2dbedcb8f4e571055994c
|
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**: 40x faster 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.1.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
|
+
```
|
112
172
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
173
|
+
### Method Chaining
|
174
|
+
|
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
|
117
182
|
```
|
118
183
|
|
119
|
-
###
|
184
|
+
### Thread-safe Builder
|
120
185
|
|
121
|
-
|
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
|
195
|
+
```
|
196
|
+
|
197
|
+
## Route Constraints
|
122
198
|
|
123
|
-
|
199
|
+
Ruby Routes provides a powerful constraint system to validate route parameters before they reach your controllers.
|
200
|
+
|
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,72 +334,67 @@ 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
|
342
|
+
### RubyRoutes
|
323
343
|
|
324
|
-
- `
|
325
|
-
- `post(path, options = {})`
|
326
|
-
- `put(path, options = {})`
|
327
|
-
- `patch(path, options = {})`
|
328
|
-
- `delete(path, options = {})`
|
329
|
-
- `match(path, options = {})`
|
344
|
+
- `RubyRoutes.draw(&block)` - Creates a new router and yields the block for route definition
|
330
345
|
|
331
|
-
###
|
346
|
+
### Router Methods
|
332
347
|
|
333
|
-
- `
|
334
|
-
- `
|
335
|
-
|
336
|
-
|
337
|
-
|
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
|
345
361
|
|
346
362
|
### RouteSet Methods
|
347
363
|
|
348
|
-
- `match(method, path)` -
|
349
|
-
- `generate_path(name, params = {})` -
|
350
|
-
- `find_route(method, path)` -
|
351
|
-
- `find_named_route(name)` -
|
364
|
+
- `match(method, path)` - Match a request to a route
|
365
|
+
- `generate_path(name, params = {})` - Generate a path from a named route
|
366
|
+
- `find_route(method, path)` - Find a specific route
|
367
|
+
- `find_named_route(name)` - Find a named route by name
|
352
368
|
|
353
|
-
##
|
369
|
+
## Performance
|
354
370
|
|
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
|
371
|
+
Ruby Routes is optimized for high-performance applications:
|
358
372
|
|
359
|
-
|
373
|
+
- **40x faster** routing compared to many alternatives
|
374
|
+
- **99.99% cache hit rate** for common access patterns
|
375
|
+
- **Low memory footprint** with bounded caches and object reuse
|
376
|
+
- **Zero memory leaks** in long-running applications
|
360
377
|
|
361
|
-
|
378
|
+
Performance metrics (from `benchmark/` directory):
|
362
379
|
|
363
|
-
|
364
|
-
|
380
|
+
| Operation | Operations/sec | Memory Usage |
|
381
|
+
|-----------|----------------|-------------|
|
382
|
+
| Route Matching | ~250,000/sec | Low |
|
383
|
+
| Path Generation | ~400,000/sec | Low |
|
384
|
+
| Static Routes | ~500,000/sec | Minimal |
|
365
385
|
|
366
386
|
## Security
|
367
387
|
|
368
|
-
Ruby Routes prioritizes security
|
388
|
+
Ruby Routes prioritizes security with these protections:
|
369
389
|
|
370
390
|
### ๐ Security Features
|
371
391
|
- **XSS Protection**: All HTML output is properly escaped
|
372
392
|
- **ReDoS Protection**: Regular expression constraints have timeout protection
|
373
|
-
- **Secure Constraints**:
|
374
|
-
- **Thread Safety**: All
|
375
|
-
- **Input Validation**: Comprehensive parameter validation
|
393
|
+
- **Secure Constraints**: Type-safe constraint system without code execution
|
394
|
+
- **Thread Safety**: All shared resources are thread-safe
|
395
|
+
- **Input Validation**: Comprehensive parameter validation
|
376
396
|
|
377
|
-
### โ ๏ธ
|
397
|
+
### โ ๏ธ Security Notice
|
378
398
|
**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:
|
379
399
|
- Code injection attacks
|
380
400
|
- Denial of service attacks
|
@@ -382,16 +402,28 @@ Ruby Routes prioritizes security and has implemented several protections:
|
|
382
402
|
|
383
403
|
**Migration Required**: If you're using Proc constraints, please migrate to secure alternatives using our [Migration Guide](MIGRATION_GUIDE.md).
|
384
404
|
|
405
|
+
## Documentation
|
406
|
+
|
407
|
+
### Core Documentation
|
408
|
+
- **[CONSTRAINTS.md](CONSTRAINTS.md)** - Complete guide to route constraints and security
|
409
|
+
- **[MIGRATION_GUIDE.md](MIGRATION_GUIDE.md)** - Guide for migrating from deprecated Proc constraints
|
410
|
+
- **[SECURITY_FIXES.md](SECURITY_FIXES.md)** - Details on security improvements
|
411
|
+
- **[USAGE.md](USAGE.md)** - Extended usage scenarios
|
412
|
+
|
413
|
+
### Examples
|
414
|
+
See the `examples/` directory for more detailed examples:
|
415
|
+
- `examples/basic_usage.rb` - Basic routing examples
|
416
|
+
- `examples/rack_integration.rb` - Full Rack application example
|
417
|
+
- `examples/constraints.rb` - Route constraint examples
|
418
|
+
|
385
419
|
## Testing
|
386
420
|
|
387
|
-
|
421
|
+
Ruby Routes has comprehensive test coverage:
|
388
422
|
|
389
423
|
```bash
|
390
424
|
bundle exec rspec
|
391
425
|
```
|
392
426
|
|
393
|
-
The test suite includes comprehensive security tests to ensure all protections are working correctly.
|
394
|
-
|
395
427
|
## Contributing
|
396
428
|
|
397
429
|
1. Fork the repository
|
@@ -407,3 +439,41 @@ This gem is available as open source under the terms of the [MIT License](LICENS
|
|
407
439
|
## Acknowledgments
|
408
440
|
|
409
441
|
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.
|
442
|
+
|
443
|
+
## Thread-safe Build (Isolated Builder)
|
444
|
+
|
445
|
+
Use the builder to accumulate routes without mutating a live router:
|
446
|
+
|
447
|
+
```ruby
|
448
|
+
router = RubyRoutes::Router.build do
|
449
|
+
resources :users
|
450
|
+
namespace :admin do
|
451
|
+
resources :posts
|
452
|
+
end
|
453
|
+
end
|
454
|
+
# router is now finalized (immutable)
|
455
|
+
```
|
456
|
+
|
457
|
+
If you need manual steps:
|
458
|
+
|
459
|
+
```ruby
|
460
|
+
builder = RubyRoutes::Router::Builder.new do
|
461
|
+
get '/health', to: 'system#health'
|
462
|
+
end
|
463
|
+
router = builder.build # finalized
|
464
|
+
```
|
465
|
+
|
466
|
+
## Fluent Method Chaining
|
467
|
+
|
468
|
+
For a more concise style, the routing DSL supports method chaining:
|
469
|
+
|
470
|
+
```ruby
|
471
|
+
router = RubyRoutes.draw do
|
472
|
+
get('/users', to: 'users#index')
|
473
|
+
.post('/users', to: 'users#create')
|
474
|
+
.put('/users/:id', to: 'users#update')
|
475
|
+
.delete('/users/:id', to: 'users#destroy')
|
476
|
+
.resources(:posts)
|
477
|
+
end
|
478
|
+
```
|
479
|
+
|