otto 2.0.0.pre3 → 2.0.0.pre8
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/.github/workflows/ci.yml +1 -1
- data/.github/workflows/claude-code-review.yml +1 -1
- data/.github/workflows/code-smells.yml +143 -0
- data/.gitignore +4 -0
- data/.pre-commit-config.yaml +2 -2
- data/.reek.yml +99 -0
- data/CHANGELOG.rst +156 -0
- data/CLAUDE.md +74 -540
- data/Gemfile +4 -2
- data/Gemfile.lock +58 -19
- data/README.md +49 -1
- data/examples/advanced_routes/README.md +137 -20
- data/examples/authentication_strategies/README.md +212 -19
- data/examples/backtrace_sanitization_demo.rb +86 -0
- data/examples/basic/README.md +61 -10
- data/examples/error_handler_registration.rb +136 -0
- data/examples/logging_improvements.rb +76 -0
- data/examples/mcp_demo/README.md +187 -27
- data/examples/security_features/README.md +249 -30
- data/examples/simple_geo_resolver.rb +107 -0
- data/lib/otto/core/configuration.rb +15 -20
- data/lib/otto/core/error_handler.rb +138 -8
- data/lib/otto/core/file_safety.rb +2 -2
- data/lib/otto/core/freezable.rb +2 -2
- data/lib/otto/core/middleware_stack.rb +2 -2
- data/lib/otto/core/router.rb +61 -8
- data/lib/otto/core/uri_generator.rb +2 -2
- data/lib/otto/core.rb +2 -0
- data/lib/otto/design_system.rb +2 -2
- data/lib/otto/env_keys.rb +61 -12
- data/lib/otto/helpers/base.rb +2 -2
- data/lib/otto/helpers/request.rb +8 -3
- data/lib/otto/helpers/response.rb +2 -2
- data/lib/otto/helpers/validation.rb +2 -2
- data/lib/otto/helpers.rb +2 -0
- data/lib/otto/locale/config.rb +2 -2
- data/lib/otto/locale/middleware.rb +160 -0
- data/lib/otto/locale.rb +10 -0
- data/lib/otto/logging_helpers.rb +273 -0
- data/lib/otto/mcp/auth/token.rb +2 -2
- data/lib/otto/mcp/protocol.rb +2 -2
- data/lib/otto/mcp/rate_limiting.rb +2 -2
- data/lib/otto/mcp/registry.rb +2 -2
- data/lib/otto/mcp/route_parser.rb +2 -2
- data/lib/otto/mcp/schema_validation.rb +2 -2
- data/lib/otto/mcp/server.rb +2 -2
- data/lib/otto/mcp.rb +2 -0
- data/lib/otto/privacy/config.rb +2 -0
- data/lib/otto/privacy/geo_resolver.rb +199 -29
- data/lib/otto/privacy/ip_privacy.rb +2 -0
- data/lib/otto/privacy/redacted_fingerprint.rb +18 -8
- data/lib/otto/privacy.rb +2 -0
- data/lib/otto/response_handlers/auto.rb +2 -0
- data/lib/otto/response_handlers/base.rb +2 -0
- data/lib/otto/response_handlers/default.rb +2 -0
- data/lib/otto/response_handlers/factory.rb +2 -0
- data/lib/otto/response_handlers/json.rb +2 -0
- data/lib/otto/response_handlers/redirect.rb +2 -0
- data/lib/otto/response_handlers/view.rb +2 -0
- data/lib/otto/response_handlers.rb +2 -2
- data/lib/otto/route.rb +4 -4
- data/lib/otto/route_definition.rb +42 -15
- data/lib/otto/route_handlers/base.rb +2 -0
- data/lib/otto/route_handlers/class_method.rb +26 -26
- data/lib/otto/route_handlers/factory.rb +2 -2
- data/lib/otto/route_handlers/instance_method.rb +16 -6
- data/lib/otto/route_handlers/lambda.rb +8 -20
- data/lib/otto/route_handlers/logic_class.rb +33 -8
- data/lib/otto/route_handlers.rb +2 -2
- data/lib/otto/security/authentication/auth_failure.rb +2 -2
- data/lib/otto/security/authentication/auth_strategy.rb +11 -4
- data/lib/otto/security/authentication/route_auth_wrapper/response_builder.rb +123 -0
- data/lib/otto/security/authentication/route_auth_wrapper/role_authorization.rb +120 -0
- data/lib/otto/security/authentication/route_auth_wrapper/strategy_resolver.rb +69 -0
- data/lib/otto/security/authentication/route_auth_wrapper.rb +185 -195
- data/lib/otto/security/authentication/strategies/api_key_strategy.rb +2 -0
- data/lib/otto/security/authentication/strategies/noauth_strategy.rb +2 -0
- data/lib/otto/security/authentication/strategies/permission_strategy.rb +2 -0
- data/lib/otto/security/authentication/strategies/role_strategy.rb +2 -0
- data/lib/otto/security/authentication/strategies/session_strategy.rb +2 -0
- data/lib/otto/security/authentication/strategy_result.rb +6 -5
- data/lib/otto/security/authentication.rb +2 -2
- data/lib/otto/security/authorization_error.rb +73 -0
- data/lib/otto/security/config.rb +2 -2
- data/lib/otto/security/configurator.rb +17 -2
- data/lib/otto/security/csrf.rb +2 -2
- data/lib/otto/security/middleware/csrf_middleware.rb +11 -1
- data/lib/otto/security/middleware/ip_privacy_middleware.rb +31 -11
- data/lib/otto/security/middleware/rate_limit_middleware.rb +2 -0
- data/lib/otto/security/middleware/validation_middleware.rb +15 -0
- data/lib/otto/security/rate_limiter.rb +2 -2
- data/lib/otto/security/rate_limiting.rb +2 -2
- data/lib/otto/security/validator.rb +2 -2
- data/lib/otto/security.rb +3 -0
- data/lib/otto/static.rb +2 -2
- data/lib/otto/utils.rb +27 -2
- data/lib/otto/version.rb +3 -3
- data/lib/otto.rb +174 -14
- data/otto.gemspec +7 -3
- metadata +25 -15
- data/benchmark_middleware_wrap.rb +0 -163
- data/changelog.d/20251014_144317_delano_54_thats_a_wrapper.rst +0 -36
- data/changelog.d/20251014_161526_delano_54_thats_a_wrapper.rst +0 -5
data/Gemfile
CHANGED
|
@@ -14,6 +14,7 @@ gem 'rackup'
|
|
|
14
14
|
group :test do
|
|
15
15
|
gem 'rack-test'
|
|
16
16
|
gem 'rspec', '~> 3.13'
|
|
17
|
+
gem 'user_agent_parser', '~> 2.18' # Validate anonymized UAs preserve semantic info
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
# bundle config set with 'optional'
|
|
@@ -21,17 +22,18 @@ group :development, :test, optional: true do
|
|
|
21
22
|
# Keep gems that need to be in both environments
|
|
22
23
|
gem 'json_schemer'
|
|
23
24
|
gem 'rack-attack'
|
|
25
|
+
gem 'reek', '~> 6.5'
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
group :development do
|
|
27
29
|
gem 'benchmark'
|
|
28
30
|
gem 'debug'
|
|
29
|
-
gem 'rubocop', '~> 1.81.
|
|
31
|
+
gem 'rubocop', '~> 1.81.7', require: false
|
|
30
32
|
gem 'rubocop-performance', require: false
|
|
31
33
|
gem 'rubocop-rspec', require: false
|
|
32
34
|
gem 'rubocop-thread_safety', require: false
|
|
33
35
|
gem 'ruby-lsp', require: false
|
|
34
36
|
gem 'stackprof', require: false
|
|
35
37
|
gem 'syntax_tree', require: false
|
|
36
|
-
gem 'tryouts', '~> 3.
|
|
38
|
+
gem 'tryouts', '~> 3.7.1', require: false
|
|
37
39
|
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
otto (2.0.0.
|
|
4
|
+
otto (2.0.0.pre8)
|
|
5
5
|
concurrent-ruby (~> 1.3, < 2.0)
|
|
6
6
|
facets (~> 3.1)
|
|
7
7
|
ipaddr (~> 1, < 2.0)
|
|
@@ -9,13 +9,13 @@ PATH
|
|
|
9
9
|
loofah (~> 2.20)
|
|
10
10
|
rack (~> 3.1, < 4.0)
|
|
11
11
|
rack-parser (~> 0.7)
|
|
12
|
-
rexml (
|
|
12
|
+
rexml (~> 3.4)
|
|
13
13
|
|
|
14
14
|
GEM
|
|
15
15
|
remote: https://rubygems.org/
|
|
16
16
|
specs:
|
|
17
17
|
ast (2.4.3)
|
|
18
|
-
benchmark (0.
|
|
18
|
+
benchmark (0.5.0)
|
|
19
19
|
bigdecimal (3.2.3)
|
|
20
20
|
concurrent-ruby (1.3.5)
|
|
21
21
|
crass (1.0.6)
|
|
@@ -24,6 +24,35 @@ GEM
|
|
|
24
24
|
irb (~> 1.10)
|
|
25
25
|
reline (>= 0.3.8)
|
|
26
26
|
diff-lcs (1.6.2)
|
|
27
|
+
dry-configurable (1.3.0)
|
|
28
|
+
dry-core (~> 1.1)
|
|
29
|
+
zeitwerk (~> 2.6)
|
|
30
|
+
dry-core (1.1.0)
|
|
31
|
+
concurrent-ruby (~> 1.0)
|
|
32
|
+
logger
|
|
33
|
+
zeitwerk (~> 2.6)
|
|
34
|
+
dry-inflector (1.2.0)
|
|
35
|
+
dry-initializer (3.2.0)
|
|
36
|
+
dry-logic (1.6.0)
|
|
37
|
+
bigdecimal
|
|
38
|
+
concurrent-ruby (~> 1.0)
|
|
39
|
+
dry-core (~> 1.1)
|
|
40
|
+
zeitwerk (~> 2.6)
|
|
41
|
+
dry-schema (1.14.1)
|
|
42
|
+
concurrent-ruby (~> 1.0)
|
|
43
|
+
dry-configurable (~> 1.0, >= 1.0.1)
|
|
44
|
+
dry-core (~> 1.1)
|
|
45
|
+
dry-initializer (~> 3.2)
|
|
46
|
+
dry-logic (~> 1.5)
|
|
47
|
+
dry-types (~> 1.8)
|
|
48
|
+
zeitwerk (~> 2.6)
|
|
49
|
+
dry-types (1.8.3)
|
|
50
|
+
bigdecimal (~> 3.0)
|
|
51
|
+
concurrent-ruby (~> 1.0)
|
|
52
|
+
dry-core (~> 1.0)
|
|
53
|
+
dry-inflector (~> 1.0)
|
|
54
|
+
dry-logic (~> 1.4)
|
|
55
|
+
zeitwerk (~> 2.6)
|
|
27
56
|
erb (5.1.1)
|
|
28
57
|
facets (3.1.0)
|
|
29
58
|
hana (1.3.7)
|
|
@@ -33,7 +62,7 @@ GEM
|
|
|
33
62
|
pp (>= 0.6.0)
|
|
34
63
|
rdoc (>= 4.0.0)
|
|
35
64
|
reline (>= 0.4.2)
|
|
36
|
-
json (2.15.
|
|
65
|
+
json (2.15.2)
|
|
37
66
|
json_schemer (2.4.0)
|
|
38
67
|
bigdecimal
|
|
39
68
|
hana (~> 1.3)
|
|
@@ -63,7 +92,7 @@ GEM
|
|
|
63
92
|
nokogiri (1.18.10-x86_64-linux-musl)
|
|
64
93
|
racc (~> 1.4)
|
|
65
94
|
parallel (1.27.0)
|
|
66
|
-
parser (3.3.
|
|
95
|
+
parser (3.3.10.0)
|
|
67
96
|
ast (~> 2.4.1)
|
|
68
97
|
racc
|
|
69
98
|
pastel (0.8.0)
|
|
@@ -72,13 +101,13 @@ GEM
|
|
|
72
101
|
prettyprint
|
|
73
102
|
prettier_print (1.2.1)
|
|
74
103
|
prettyprint (0.2.0)
|
|
75
|
-
prism (1.
|
|
104
|
+
prism (1.6.0)
|
|
76
105
|
psych (5.2.6)
|
|
77
106
|
date
|
|
78
107
|
stringio
|
|
79
108
|
racc (1.8.1)
|
|
80
|
-
rack (3.2.
|
|
81
|
-
rack-attack (6.
|
|
109
|
+
rack (3.2.4)
|
|
110
|
+
rack-attack (6.8.0)
|
|
82
111
|
rack (>= 1.0, < 4)
|
|
83
112
|
rack-parser (0.7.0)
|
|
84
113
|
rack
|
|
@@ -93,24 +122,30 @@ GEM
|
|
|
93
122
|
erb
|
|
94
123
|
psych (>= 4.0.0)
|
|
95
124
|
tsort
|
|
125
|
+
reek (6.5.0)
|
|
126
|
+
dry-schema (~> 1.13)
|
|
127
|
+
logger (~> 1.6)
|
|
128
|
+
parser (~> 3.3.0)
|
|
129
|
+
rainbow (>= 2.0, < 4.0)
|
|
130
|
+
rexml (~> 3.1)
|
|
96
131
|
regexp_parser (2.11.3)
|
|
97
132
|
reline (0.6.2)
|
|
98
133
|
io-console (~> 0.5)
|
|
99
134
|
rexml (3.4.4)
|
|
100
|
-
rspec (3.13.
|
|
135
|
+
rspec (3.13.2)
|
|
101
136
|
rspec-core (~> 3.13.0)
|
|
102
137
|
rspec-expectations (~> 3.13.0)
|
|
103
138
|
rspec-mocks (~> 3.13.0)
|
|
104
|
-
rspec-core (3.13.
|
|
139
|
+
rspec-core (3.13.6)
|
|
105
140
|
rspec-support (~> 3.13.0)
|
|
106
141
|
rspec-expectations (3.13.5)
|
|
107
142
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
108
143
|
rspec-support (~> 3.13.0)
|
|
109
|
-
rspec-mocks (3.13.
|
|
144
|
+
rspec-mocks (3.13.6)
|
|
110
145
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
111
146
|
rspec-support (~> 3.13.0)
|
|
112
147
|
rspec-support (3.13.6)
|
|
113
|
-
rubocop (1.81.
|
|
148
|
+
rubocop (1.81.7)
|
|
114
149
|
json (~> 2.3)
|
|
115
150
|
language_server-protocol (~> 3.17.0.2)
|
|
116
151
|
lint_roller (~> 1.1.0)
|
|
@@ -124,10 +159,10 @@ GEM
|
|
|
124
159
|
rubocop-ast (1.47.1)
|
|
125
160
|
parser (>= 3.3.7.2)
|
|
126
161
|
prism (~> 1.4)
|
|
127
|
-
rubocop-performance (1.26.
|
|
162
|
+
rubocop-performance (1.26.1)
|
|
128
163
|
lint_roller (~> 1.1)
|
|
129
164
|
rubocop (>= 1.75.0, < 2.0)
|
|
130
|
-
rubocop-ast (>= 1.
|
|
165
|
+
rubocop-ast (>= 1.47.1, < 2.0)
|
|
131
166
|
rubocop-rspec (3.7.0)
|
|
132
167
|
lint_roller (~> 1.1)
|
|
133
168
|
rubocop (~> 1.72, >= 1.72.1)
|
|
@@ -135,7 +170,7 @@ GEM
|
|
|
135
170
|
lint_roller (~> 1.1)
|
|
136
171
|
rubocop (~> 1.72, >= 1.72.1)
|
|
137
172
|
rubocop-ast (>= 1.44.0, < 2.0)
|
|
138
|
-
ruby-lsp (0.26.
|
|
173
|
+
ruby-lsp (0.26.2)
|
|
139
174
|
language_server-protocol (~> 3.17.0)
|
|
140
175
|
prism (>= 1.2, < 2.0)
|
|
141
176
|
rbs (>= 3, < 5)
|
|
@@ -145,8 +180,8 @@ GEM
|
|
|
145
180
|
stringio (3.1.7)
|
|
146
181
|
syntax_tree (6.3.0)
|
|
147
182
|
prettier_print (>= 1.2.0)
|
|
148
|
-
tryouts (3.
|
|
149
|
-
concurrent-ruby (~> 1.0)
|
|
183
|
+
tryouts (3.7.1)
|
|
184
|
+
concurrent-ruby (~> 1.0, < 2)
|
|
150
185
|
irb
|
|
151
186
|
minitest (~> 5.0)
|
|
152
187
|
pastel (~> 0.8)
|
|
@@ -161,6 +196,8 @@ GEM
|
|
|
161
196
|
unicode-display_width (3.2.0)
|
|
162
197
|
unicode-emoji (~> 4.1)
|
|
163
198
|
unicode-emoji (4.1.0)
|
|
199
|
+
user_agent_parser (2.20.0)
|
|
200
|
+
zeitwerk (2.7.3)
|
|
164
201
|
|
|
165
202
|
PLATFORMS
|
|
166
203
|
aarch64-linux-gnu
|
|
@@ -180,15 +217,17 @@ DEPENDENCIES
|
|
|
180
217
|
rack-attack
|
|
181
218
|
rack-test
|
|
182
219
|
rackup
|
|
220
|
+
reek (~> 6.5)
|
|
183
221
|
rspec (~> 3.13)
|
|
184
|
-
rubocop (~> 1.81.
|
|
222
|
+
rubocop (~> 1.81.7)
|
|
185
223
|
rubocop-performance
|
|
186
224
|
rubocop-rspec
|
|
187
225
|
rubocop-thread_safety
|
|
188
226
|
ruby-lsp
|
|
189
227
|
stackprof
|
|
190
228
|
syntax_tree
|
|
191
|
-
tryouts (~> 3.
|
|
229
|
+
tryouts (~> 3.7.1)
|
|
230
|
+
user_agent_parser (~> 2.18)
|
|
192
231
|
|
|
193
232
|
BUNDLED WITH
|
|
194
233
|
2.7.1
|
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Define your rack-apps in plain-text with built-in security.**
|
|
4
4
|
|
|
5
|
-
> **v2.0.0-
|
|
5
|
+
> **v2.0.0-pre6 Available**: This pre-release includes major improvements to middleware management, logging, and request callback handling. See [changelog](CHANGELOG.rst) for details and upgrade notes.
|
|
6
6
|
|
|
7
7
|

|
|
8
8
|
|
|
@@ -13,6 +13,14 @@ $ cd myapp && ls
|
|
|
13
13
|
config.ru app.rb routes
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
## Why Otto?
|
|
17
|
+
|
|
18
|
+
- **Security by Default**: Automatic IP masking for public addresses, user agent anonymization, CSRF protection, and input validation
|
|
19
|
+
- **Privacy First**: Masks public IPs, strips user agent versions, provides country-level geo-location only—no external APIs needed
|
|
20
|
+
- **Simple Routing**: Define routes in plain-text files with zero configuration overhead
|
|
21
|
+
- **Built-in Authentication**: Multiple strategies including API keys, tokens, role-based access, and custom implementations
|
|
22
|
+
- **Developer Friendly**: Works with any Rack server, minimal dependencies, easy testing and debugging
|
|
23
|
+
|
|
16
24
|
## Routes File
|
|
17
25
|
```
|
|
18
26
|
# routes
|
|
@@ -76,6 +84,22 @@ app = Otto.new("./routes", {
|
|
|
76
84
|
|
|
77
85
|
Security features include CSRF protection, input validation, security headers, and trusted proxy configuration.
|
|
78
86
|
|
|
87
|
+
## Privacy by Default
|
|
88
|
+
|
|
89
|
+
Otto automatically masks public IP addresses and anonymizes user agents to comply with GDPR, CCPA, and other privacy regulations:
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
# Public IPs are automatically masked (203.0.113.9 → 203.0.113.0)
|
|
93
|
+
# Private IPs are NOT masked by default (127.0.0.1, 192.168.x.x, 10.x.x.x)
|
|
94
|
+
app = Otto.new("./routes")
|
|
95
|
+
|
|
96
|
+
# User agents: versions stripped for privacy
|
|
97
|
+
# Geo-location: country-level only, no external APIs or databases
|
|
98
|
+
# IP hashing: daily-rotating hashes enable analytics without tracking
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Private and localhost IPs are exempted by default for development convenience, but this behavior can be customized via `configure_ip_privacy()` method. Geolocation uses CDN headers (Cloudflare, AWS, etc.) with fallback to IP ranges—no external services required. See [CLAUDE.md](CLAUDE.md) for detailed configuration options.
|
|
102
|
+
|
|
79
103
|
## Internationalization Support
|
|
80
104
|
|
|
81
105
|
Otto provides built-in locale detection and management:
|
|
@@ -132,6 +156,24 @@ end
|
|
|
132
156
|
|
|
133
157
|
The locale helper checks multiple sources in order of precedence and validates against your configured locales.
|
|
134
158
|
|
|
159
|
+
## Examples
|
|
160
|
+
|
|
161
|
+
Otto includes comprehensive examples demonstrating different features:
|
|
162
|
+
|
|
163
|
+
- **[Basic Example](examples/basic/)** - Get your first Otto app running in minutes
|
|
164
|
+
- **[Advanced Routes](examples/advanced_routes/)** - Response types, CSRF exemption, logic classes, and namespaced routing
|
|
165
|
+
- **[Authentication Strategies](examples/authentication_strategies/)** - Token, API key, and role-based authentication
|
|
166
|
+
- **[Security Features](examples/security_features/)** - CSRF protection, input validation, file uploads, and security headers
|
|
167
|
+
- **[MCP Demo](examples/mcp_demo/)** - JSON-RPC 2.0 endpoints for CLI automation and integrations
|
|
168
|
+
|
|
169
|
+
### Standalone Tutorials
|
|
170
|
+
|
|
171
|
+
- **[Error Handler Registration](examples/error_handler_registration.rb)** - Prevent 500 errors for expected business exceptions
|
|
172
|
+
- **[Logging Improvements](examples/logging_improvements.rb)** - Structured logging with automatic timing
|
|
173
|
+
- **[Geo-location Extension](examples/simple_geo_resolver.rb)** - Extending geo-location with custom resolvers
|
|
174
|
+
|
|
175
|
+
See the [examples/](examples/) directory for more.
|
|
176
|
+
|
|
135
177
|
## Requirements
|
|
136
178
|
|
|
137
179
|
- Ruby 3.2+
|
|
@@ -143,6 +185,12 @@ The locale helper checks multiple sources in order of precedence and validates a
|
|
|
143
185
|
gem install otto
|
|
144
186
|
```
|
|
145
187
|
|
|
188
|
+
## Documentation
|
|
189
|
+
|
|
190
|
+
- **[CLAUDE.md](CLAUDE.md)** - Comprehensive developer guidance covering authentication architecture, configuration freezing, IP privacy, structured logging, and multi-app patterns
|
|
191
|
+
- **[docs/](docs/)** - Technical guides and migration guides
|
|
192
|
+
- **[CHANGELOG.rst](CHANGELOG.rst)** - Version history, breaking changes, and upgrade notes
|
|
193
|
+
|
|
146
194
|
## AI Development Assistance
|
|
147
195
|
|
|
148
196
|
Version 1.2.0's security features were developed with AI assistance:
|
|
@@ -1,33 +1,150 @@
|
|
|
1
1
|
# Otto - Advanced Routes Example
|
|
2
2
|
|
|
3
|
-
This example demonstrates
|
|
3
|
+
This example demonstrates advanced routing features in Otto, including response type negotiation, CSRF exemptions, logic classes, and namespaced routing.
|
|
4
|
+
|
|
5
|
+
## What You'll Learn
|
|
6
|
+
|
|
7
|
+
- How to define response types (JSON, view, redirect) in routes
|
|
8
|
+
- Using logic classes to encapsulate business logic
|
|
9
|
+
- CSRF exemption for APIs and webhooks
|
|
10
|
+
- Routing to namespaced classes with complex hierarchies
|
|
11
|
+
- Custom route parameters for flexible routing
|
|
12
|
+
- How Otto handles multiple controllers and modules
|
|
4
13
|
|
|
5
14
|
## Project Structure
|
|
6
15
|
|
|
7
|
-
The example is
|
|
16
|
+
The example is organized to separate concerns:
|
|
8
17
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
18
|
+
- `config.ru`: Rack configuration that loads and runs the Otto application
|
|
19
|
+
- `routes`: Comprehensive reference for advanced routing syntax
|
|
20
|
+
- `app.rb`: Loader that requires all controller and logic files
|
|
21
|
+
- `app/controllers/`: Handler classes (`RoutesApp`, namespaced controllers)
|
|
22
|
+
- `app/logic/`: Business logic classes (simple, nested, namespaced)
|
|
23
|
+
- `run.rb`, `puma.rb`, `test.rb`: Alternative server/test runners
|
|
15
24
|
|
|
16
25
|
## Key Features Demonstrated
|
|
17
26
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
### Response Types
|
|
28
|
+
Define how responses are formatted directly in routes:
|
|
29
|
+
```
|
|
30
|
+
GET /api/users UserController#list response=json
|
|
31
|
+
GET /page PageController#show response=view
|
|
32
|
+
GET /old-url PageController#new-url response=redirect
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Logic Classes
|
|
36
|
+
Route to specialized classes that encapsulate business logic:
|
|
37
|
+
```
|
|
38
|
+
GET /calculate DataProcessor # Otto auto-instantiates and calls #process
|
|
39
|
+
GET /report ReportGenerator # Same pattern
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### CSRF Exemption
|
|
43
|
+
Mark routes that don't need CSRF tokens (APIs, webhooks):
|
|
44
|
+
```
|
|
45
|
+
POST /api/webhook WebhookHandler#receive csrf=exempt
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Namespaced Routing
|
|
49
|
+
Handle complex class hierarchies naturally:
|
|
50
|
+
```
|
|
51
|
+
GET /v2/dashboard V2::Logic::Dashboard
|
|
52
|
+
GET /admin/panel Admin::Panel#dashboard
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Custom Parameters
|
|
56
|
+
Add arbitrary key-value pairs for flexible routing:
|
|
57
|
+
```
|
|
58
|
+
GET /admin AdminPanel#dashboard role=admin
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## How to Run
|
|
62
|
+
|
|
63
|
+
### Using rackup (recommended)
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
cd examples/advanced_routes
|
|
67
|
+
rackup config.ru
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Using alternative runners
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
ruby run.rb # Basic rackup
|
|
74
|
+
ruby puma.rb # Using puma server
|
|
75
|
+
ruby test.rb # For testing
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The application will be running at `http://localhost:9292`.
|
|
79
|
+
|
|
80
|
+
## Testing Routes
|
|
81
|
+
|
|
82
|
+
Use curl to test the different routes:
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
# JSON response
|
|
86
|
+
curl http://localhost:9292/json/test
|
|
87
|
+
|
|
88
|
+
# View response
|
|
89
|
+
curl http://localhost:9292/view/test
|
|
90
|
+
|
|
91
|
+
# Logic class routing
|
|
92
|
+
curl http://localhost:9292/logic/simple
|
|
93
|
+
|
|
94
|
+
# Namespaced routing
|
|
95
|
+
curl http://localhost:9292/logic/v2/dashboard
|
|
96
|
+
|
|
97
|
+
# Custom parameters
|
|
98
|
+
curl "http://localhost:9292/custom?role=admin"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Expected Output
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
Listening on 127.0.0.1:9292, CTRL+C to stop
|
|
105
|
+
|
|
106
|
+
[JSON response]
|
|
107
|
+
GET /json/test 200 OK
|
|
108
|
+
Content-Type: application/json
|
|
109
|
+
{"status": "success", "data": {...}}
|
|
110
|
+
|
|
111
|
+
[Logic class routing]
|
|
112
|
+
GET /logic/simple 200 OK
|
|
113
|
+
{"processed": true, "input": "test"}
|
|
114
|
+
|
|
115
|
+
[Namespaced routing]
|
|
116
|
+
GET /logic/v2/dashboard 200 OK
|
|
117
|
+
{"version": "2.0", "dashboard": {...}}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## File Structure Details
|
|
121
|
+
|
|
122
|
+
### Routes File
|
|
123
|
+
The `routes` file is extensively commented to explain each feature:
|
|
124
|
+
- Response type specification
|
|
125
|
+
- CSRF exemption for APIs
|
|
126
|
+
- Logic class routing syntax
|
|
127
|
+
- Namespaced class resolution
|
|
128
|
+
- Custom parameter examples
|
|
129
|
+
|
|
130
|
+
### Controllers (`app/controllers/`)
|
|
131
|
+
- `RoutesApp`: Main controller with basic handlers
|
|
132
|
+
- Namespaced modules: Demonstrate complex class hierarchies
|
|
133
|
+
- Handlers return appropriate responses (JSON, HTML, redirects)
|
|
134
|
+
|
|
135
|
+
### Logic Classes (`app/logic/`)
|
|
136
|
+
- Simple classes: Basic business logic
|
|
137
|
+
- Nested classes: Show how Otto handles namespace resolution
|
|
138
|
+
- Parameterized logic: Demonstrate custom route parameters
|
|
23
139
|
|
|
24
|
-
##
|
|
140
|
+
## Next Steps
|
|
25
141
|
|
|
26
|
-
|
|
27
|
-
|
|
142
|
+
- Review the `routes` file for syntax reference
|
|
143
|
+
- Examine handler methods to see request/response patterns
|
|
144
|
+
- Check logic classes for business logic encapsulation patterns
|
|
145
|
+
- Explore [Authentication](../authentication_strategies/) for protecting routes
|
|
146
|
+
- See [Security Features](../security_features/) for CSRF, validation, file uploads
|
|
28
147
|
|
|
29
|
-
|
|
30
|
-
rackup examples/advanced_routes/config.ru
|
|
31
|
-
```
|
|
148
|
+
## Further Reading
|
|
32
149
|
|
|
33
|
-
|
|
150
|
+
- [CLAUDE.md](../../CLAUDE.md) - Comprehensive developer guidance
|