otto 1.5.0 → 2.0.0.pre1
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 +44 -5
- data/.github/workflows/claude-code-review.yml +53 -0
- data/.github/workflows/claude.yml +49 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +24 -345
- data/CHANGELOG.rst +83 -0
- data/CLAUDE.md +56 -0
- data/Gemfile +21 -5
- data/Gemfile.lock +69 -31
- data/README.md +2 -0
- data/bin/rspec +16 -0
- data/changelog.d/20250911_235619_delano_next.rst +28 -0
- data/changelog.d/20250912_123055_delano_remove_ostruct.rst +21 -0
- data/changelog.d/20250912_175625_claude_delano_remove_ostruct.rst +21 -0
- data/changelog.d/README.md +120 -0
- data/changelog.d/scriv.ini +5 -0
- data/docs/.gitignore +1 -0
- data/docs/migrating/v2.0.0-pre1.md +276 -0
- data/examples/.gitignore +1 -0
- data/examples/advanced_routes/README.md +33 -0
- data/examples/advanced_routes/app/controllers/handlers/async.rb +9 -0
- data/examples/advanced_routes/app/controllers/handlers/dynamic.rb +9 -0
- data/examples/advanced_routes/app/controllers/handlers/static.rb +9 -0
- data/examples/advanced_routes/app/controllers/modules/auth.rb +9 -0
- data/examples/advanced_routes/app/controllers/modules/transformer.rb +9 -0
- data/examples/advanced_routes/app/controllers/modules/validator.rb +9 -0
- data/examples/advanced_routes/app/controllers/routes_app.rb +232 -0
- data/examples/advanced_routes/app/controllers/v2/admin.rb +9 -0
- data/examples/advanced_routes/app/controllers/v2/config.rb +9 -0
- data/examples/advanced_routes/app/controllers/v2/settings.rb +9 -0
- data/examples/advanced_routes/app/logic/admin/logic/manager.rb +27 -0
- data/examples/advanced_routes/app/logic/admin/panel.rb +27 -0
- data/examples/advanced_routes/app/logic/analytics_processor.rb +25 -0
- data/examples/advanced_routes/app/logic/complex/business/handler.rb +27 -0
- data/examples/advanced_routes/app/logic/data_logic.rb +23 -0
- data/examples/advanced_routes/app/logic/data_processor.rb +25 -0
- data/examples/advanced_routes/app/logic/input_validator.rb +24 -0
- data/examples/advanced_routes/app/logic/nested/feature/logic.rb +27 -0
- data/examples/advanced_routes/app/logic/reports_generator.rb +27 -0
- data/examples/advanced_routes/app/logic/simple_logic.rb +25 -0
- data/examples/advanced_routes/app/logic/system/config/manager.rb +27 -0
- data/examples/advanced_routes/app/logic/test_logic.rb +23 -0
- data/examples/advanced_routes/app/logic/transform_logic.rb +23 -0
- data/examples/advanced_routes/app/logic/upload_logic.rb +23 -0
- data/examples/advanced_routes/app/logic/v2/logic/dashboard.rb +27 -0
- data/examples/advanced_routes/app/logic/v2/logic/processor.rb +27 -0
- data/examples/advanced_routes/app.rb +33 -0
- data/examples/advanced_routes/config.rb +23 -0
- data/examples/advanced_routes/config.ru +7 -0
- data/examples/advanced_routes/puma.rb +20 -0
- data/examples/advanced_routes/routes +167 -0
- data/examples/advanced_routes/run.rb +39 -0
- data/examples/advanced_routes/test.rb +58 -0
- data/examples/authentication_strategies/README.md +32 -0
- data/examples/authentication_strategies/app/auth.rb +68 -0
- data/examples/authentication_strategies/app/controllers/auth_controller.rb +29 -0
- data/examples/authentication_strategies/app/controllers/main_controller.rb +28 -0
- data/examples/authentication_strategies/config.ru +24 -0
- data/examples/authentication_strategies/routes +37 -0
- data/examples/basic/README.md +29 -0
- data/examples/basic/app.rb +7 -35
- data/examples/basic/routes +0 -9
- data/examples/mcp_demo/README.md +87 -0
- data/examples/mcp_demo/app.rb +51 -0
- data/examples/mcp_demo/config.ru +17 -0
- data/examples/mcp_demo/routes +9 -0
- data/examples/security_features/README.md +46 -0
- data/examples/security_features/app.rb +23 -24
- data/examples/security_features/config.ru +8 -10
- data/lib/otto/core/configuration.rb +167 -0
- data/lib/otto/core/error_handler.rb +86 -0
- data/lib/otto/core/file_safety.rb +61 -0
- data/lib/otto/core/middleware_stack.rb +157 -0
- data/lib/otto/core/router.rb +183 -0
- data/lib/otto/core/uri_generator.rb +44 -0
- data/lib/otto/design_system.rb +7 -5
- data/lib/otto/helpers/base.rb +3 -0
- data/lib/otto/helpers/request.rb +10 -8
- data/lib/otto/helpers/response.rb +5 -4
- data/lib/otto/helpers/validation.rb +85 -0
- data/lib/otto/mcp/auth/token.rb +77 -0
- data/lib/otto/mcp/protocol.rb +164 -0
- data/lib/otto/mcp/rate_limiting.rb +155 -0
- data/lib/otto/mcp/registry.rb +100 -0
- data/lib/otto/mcp/route_parser.rb +77 -0
- data/lib/otto/mcp/server.rb +206 -0
- data/lib/otto/mcp/validation.rb +123 -0
- data/lib/otto/response_handlers/auto.rb +39 -0
- data/lib/otto/response_handlers/base.rb +16 -0
- data/lib/otto/response_handlers/default.rb +16 -0
- data/lib/otto/response_handlers/factory.rb +39 -0
- data/lib/otto/response_handlers/json.rb +28 -0
- data/lib/otto/response_handlers/redirect.rb +25 -0
- data/lib/otto/response_handlers/view.rb +24 -0
- data/lib/otto/response_handlers.rb +9 -135
- data/lib/otto/route.rb +9 -9
- data/lib/otto/route_definition.rb +30 -33
- data/lib/otto/route_handlers/base.rb +121 -0
- data/lib/otto/route_handlers/class_method.rb +89 -0
- data/lib/otto/route_handlers/factory.rb +29 -0
- data/lib/otto/route_handlers/instance_method.rb +69 -0
- data/lib/otto/route_handlers/lambda.rb +59 -0
- data/lib/otto/route_handlers/logic_class.rb +93 -0
- data/lib/otto/route_handlers.rb +10 -376
- data/lib/otto/security/authentication/auth_strategy.rb +44 -0
- data/lib/otto/security/authentication/authentication_middleware.rb +123 -0
- data/lib/otto/security/authentication/failure_result.rb +36 -0
- data/lib/otto/security/authentication/strategies/api_key_strategy.rb +40 -0
- data/lib/otto/security/authentication/strategies/permission_strategy.rb +47 -0
- data/lib/otto/security/authentication/strategies/public_strategy.rb +19 -0
- data/lib/otto/security/authentication/strategies/role_strategy.rb +57 -0
- data/lib/otto/security/authentication/strategies/session_strategy.rb +41 -0
- data/lib/otto/security/authentication/strategy_result.rb +223 -0
- data/lib/otto/security/authentication.rb +28 -282
- data/lib/otto/security/config.rb +15 -11
- data/lib/otto/security/configurator.rb +219 -0
- data/lib/otto/security/csrf.rb +8 -143
- data/lib/otto/security/middleware/csrf_middleware.rb +151 -0
- data/lib/otto/security/middleware/rate_limit_middleware.rb +38 -0
- data/lib/otto/security/middleware/validation_middleware.rb +252 -0
- data/lib/otto/security/rate_limiter.rb +86 -0
- data/lib/otto/security/rate_limiting.rb +16 -0
- data/lib/otto/security/validator.rb +8 -292
- data/lib/otto/static.rb +3 -0
- data/lib/otto/utils.rb +14 -0
- data/lib/otto/version.rb +3 -1
- data/lib/otto.rb +184 -414
- data/otto.gemspec +11 -6
- metadata +134 -25
- data/examples/dynamic_pages/app.rb +0 -115
- data/examples/dynamic_pages/config.ru +0 -30
- data/examples/dynamic_pages/routes +0 -21
- data/examples/helpers_demo/app.rb +0 -244
- data/examples/helpers_demo/config.ru +0 -26
- data/examples/helpers_demo/routes +0 -7
@@ -0,0 +1,276 @@
|
|
1
|
+
# Migrating to Otto v2.0.0-pre1
|
2
|
+
|
3
|
+
## What's New in v2.0.0-pre1
|
4
|
+
|
5
|
+
This pre-release includes extensive test coverage improvements (76 new test cases), core module refactoring, middleware stack unification, and a major update to Logic class authentication patterns. See the [full changelog](../../CHANGELOG.rst#changelog-2.0.0-pre1) for complete details.
|
6
|
+
|
7
|
+
The main breaking changes affect:
|
8
|
+
1. Applications that directly manipulate the middleware stack
|
9
|
+
2. Logic classes using the old authentication signature
|
10
|
+
|
11
|
+
## Middleware Stack Unified API
|
12
|
+
|
13
|
+
Otto v2.0.0-pre1 introduces a significant refactoring of the middleware stack management, providing a more consistent and efficient approach to middleware configuration.
|
14
|
+
|
15
|
+
### Key Changes
|
16
|
+
|
17
|
+
#### Unified Middleware Registration
|
18
|
+
|
19
|
+
Previous versions had separate legacy and new middleware stacks. In v2.0.0-pre1, we've consolidated these into a single, more powerful middleware stack.
|
20
|
+
|
21
|
+
**Before:**
|
22
|
+
```ruby
|
23
|
+
# Old approach with separate stacks
|
24
|
+
otto.middleware_stack << SomeMiddleware
|
25
|
+
otto.middleware.add(AnotherMiddleware)
|
26
|
+
```
|
27
|
+
|
28
|
+
**After:**
|
29
|
+
```ruby
|
30
|
+
# Unified middleware registration
|
31
|
+
otto.use(SomeMiddleware)
|
32
|
+
otto.use(AnotherMiddleware)
|
33
|
+
```
|
34
|
+
|
35
|
+
#### Performance Improvements
|
36
|
+
|
37
|
+
- Middleware lookup is now O(1) using a Set
|
38
|
+
- Memoized middleware list reduces repeated array creation
|
39
|
+
- Prevent duplicate middleware registrations with identical configurations
|
40
|
+
|
41
|
+
### Migration Steps
|
42
|
+
|
43
|
+
1. Replace all `otto.middleware_stack <<` calls with `otto.use()`
|
44
|
+
2. Remove any direct references to `otto.middleware_stack`
|
45
|
+
3. Use `otto.middleware.includes?()` instead of manual stack checks
|
46
|
+
|
47
|
+
### Authentication and Security Methods
|
48
|
+
|
49
|
+
Authentication and security methods now consistently use the new unified middleware stack:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
# Before
|
53
|
+
otto.middleware_stack << Otto::Security::CSRFMiddleware
|
54
|
+
|
55
|
+
# After (no change needed)
|
56
|
+
otto.enable_csrf_protection!
|
57
|
+
```
|
58
|
+
|
59
|
+
### Performance Considerations
|
60
|
+
|
61
|
+
The new implementation is more memory-efficient and provides faster middleware lookups, especially for applications with many middleware components.
|
62
|
+
|
63
|
+
### Potential Breaking Changes
|
64
|
+
|
65
|
+
- Code relying on direct manipulation of `middleware_stack` will need updates
|
66
|
+
- Method signatures for middleware configuration remain the same
|
67
|
+
|
68
|
+
### Example Migration
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
# Old code
|
72
|
+
class MyApp
|
73
|
+
def initialize
|
74
|
+
@otto = Otto.new
|
75
|
+
@otto.middleware_stack << CustomMiddleware
|
76
|
+
@otto.middleware.add(AnotherMiddleware)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# New code
|
81
|
+
class MyApp
|
82
|
+
def initialize
|
83
|
+
@otto = Otto.new
|
84
|
+
@otto.use(CustomMiddleware)
|
85
|
+
@otto.use(AnotherMiddleware)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
### Troubleshooting
|
91
|
+
|
92
|
+
If you encounter any issues with middleware registration or configuration, please file an issue at [Otto GitHub Repository](https://github.com/delano/otto/issues).
|
93
|
+
|
94
|
+
## Logic Class RequestContext Pattern
|
95
|
+
|
96
|
+
Otto v2.0.0-pre1 introduces a major improvement to Logic class authentication with the new RequestContext pattern.
|
97
|
+
|
98
|
+
### Key Changes
|
99
|
+
|
100
|
+
#### New Constructor Signature
|
101
|
+
|
102
|
+
Logic classes now use a cleaner, more powerful constructor signature that provides immutable context.
|
103
|
+
|
104
|
+
**Before:**
|
105
|
+
```ruby
|
106
|
+
class MyLogic
|
107
|
+
attr_reader :session, :user, :params, :locale
|
108
|
+
|
109
|
+
def initialize(session, user, params, locale)
|
110
|
+
@session = session
|
111
|
+
@user = user
|
112
|
+
@params = params
|
113
|
+
@locale = locale
|
114
|
+
end
|
115
|
+
|
116
|
+
def process
|
117
|
+
return { error: 'not authenticated' } unless @user
|
118
|
+
{ result: 'success', user_name: @user['name'] }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
```
|
122
|
+
|
123
|
+
**After:**
|
124
|
+
```ruby
|
125
|
+
class MyLogic
|
126
|
+
attr_reader :context, :params, :locale
|
127
|
+
|
128
|
+
def initialize(context, params, locale)
|
129
|
+
@context = context
|
130
|
+
@params = params
|
131
|
+
@locale = locale
|
132
|
+
end
|
133
|
+
|
134
|
+
def process
|
135
|
+
return { error: 'not authenticated' } unless @context.authenticated?
|
136
|
+
{
|
137
|
+
result: 'success',
|
138
|
+
user_name: @context.user_name,
|
139
|
+
roles: @context.roles,
|
140
|
+
permissions: @context.permissions
|
141
|
+
}
|
142
|
+
end
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
#### RequestContext Benefits
|
147
|
+
|
148
|
+
1. **Immutable Structure** - RequestContext is a Ruby Data class that can't be accidentally modified
|
149
|
+
2. **Helper Methods** - Built-in methods like `authenticated?`, `has_role?`, `has_permission?`
|
150
|
+
3. **Cleaner Interface** - Single context parameter instead of separate session/user parameters
|
151
|
+
4. **Type Safety** - Better IDE support and documentation
|
152
|
+
5. **Future-proof** - New authentication data automatically available
|
153
|
+
|
154
|
+
#### RequestContext API
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
# Authentication status
|
158
|
+
context.authenticated? # Boolean: has non-empty user data
|
159
|
+
context.anonymous? # Boolean: not authenticated
|
160
|
+
|
161
|
+
# User information
|
162
|
+
context.user_id # User ID from various possible locations
|
163
|
+
context.user_name # User name/username
|
164
|
+
context.session_id # Session identifier
|
165
|
+
|
166
|
+
# Role and permission checks
|
167
|
+
context.has_role?('admin') # Single role check
|
168
|
+
context.has_permission?('write') # Single permission check
|
169
|
+
context.has_any_role?('admin', 'mod') # Multiple role check
|
170
|
+
context.roles # Array of all user roles
|
171
|
+
context.permissions # Array of all user permissions
|
172
|
+
|
173
|
+
# Raw data access
|
174
|
+
context.session # Session hash
|
175
|
+
context.user # User hash
|
176
|
+
context.auth_method # Authentication method used
|
177
|
+
context.metadata # Additional context data
|
178
|
+
```
|
179
|
+
|
180
|
+
### Migration Steps
|
181
|
+
|
182
|
+
1. **Update Logic class constructor signatures** from 4 parameters to 3:
|
183
|
+
```ruby
|
184
|
+
# Change this:
|
185
|
+
def initialize(session, user, params, locale)
|
186
|
+
|
187
|
+
# To this:
|
188
|
+
def initialize(context, params, locale)
|
189
|
+
```
|
190
|
+
|
191
|
+
2. **Update instance variables**:
|
192
|
+
```ruby
|
193
|
+
# Change this:
|
194
|
+
@session = session
|
195
|
+
@user = user
|
196
|
+
|
197
|
+
# To this:
|
198
|
+
@context = context
|
199
|
+
```
|
200
|
+
|
201
|
+
3. **Update authentication checks**:
|
202
|
+
```ruby
|
203
|
+
# Change this:
|
204
|
+
return error unless @user
|
205
|
+
|
206
|
+
# To this:
|
207
|
+
return error unless @context.authenticated?
|
208
|
+
```
|
209
|
+
|
210
|
+
4. **Update data access**:
|
211
|
+
```ruby
|
212
|
+
# Change this:
|
213
|
+
user_name = @user&.dig('name')
|
214
|
+
user_role = @user&.dig('role')
|
215
|
+
|
216
|
+
# To this:
|
217
|
+
user_name = @context.user_name
|
218
|
+
user_role = @context.roles.first
|
219
|
+
```
|
220
|
+
|
221
|
+
### Example Migration
|
222
|
+
|
223
|
+
**Before (v1.x):**
|
224
|
+
```ruby
|
225
|
+
class AdminPanel
|
226
|
+
attr_reader :session, :user, :params, :locale
|
227
|
+
|
228
|
+
def initialize(session, user, params, locale)
|
229
|
+
@session = session
|
230
|
+
@user = user
|
231
|
+
@params = params
|
232
|
+
@locale = locale
|
233
|
+
end
|
234
|
+
|
235
|
+
def raise_concerns
|
236
|
+
raise 'Access denied' unless @user&.dig('role') == 'admin'
|
237
|
+
end
|
238
|
+
|
239
|
+
def process
|
240
|
+
{
|
241
|
+
panel: 'admin',
|
242
|
+
user: @user&.dig('name') || 'unknown',
|
243
|
+
session_id: @session&.dig('id')
|
244
|
+
}
|
245
|
+
end
|
246
|
+
end
|
247
|
+
```
|
248
|
+
|
249
|
+
**After (v2.0):**
|
250
|
+
```ruby
|
251
|
+
class AdminPanel
|
252
|
+
attr_reader :context, :params, :locale
|
253
|
+
|
254
|
+
def initialize(context, params, locale)
|
255
|
+
@context = context
|
256
|
+
@params = params
|
257
|
+
@locale = locale
|
258
|
+
end
|
259
|
+
|
260
|
+
def raise_concerns
|
261
|
+
raise 'Access denied' unless @context.has_role?('admin')
|
262
|
+
end
|
263
|
+
|
264
|
+
def process
|
265
|
+
{
|
266
|
+
panel: 'admin',
|
267
|
+
user: @context.user_name || 'unknown',
|
268
|
+
session_id: @context.session_id,
|
269
|
+
authenticated: @context.authenticated?,
|
270
|
+
permissions: @context.permissions
|
271
|
+
}
|
272
|
+
end
|
273
|
+
end
|
274
|
+
```
|
275
|
+
|
276
|
+
This provides a cleaner, more maintainable interface while giving Logic classes access to rich authentication context.
|
data/examples/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
!**/README.md
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Otto - Advanced Routes Example
|
2
|
+
|
3
|
+
This example demonstrates the advanced routing syntax features available in Otto, such as response type negotiation, CSRF exemptions, and routing to logic classes.
|
4
|
+
|
5
|
+
## Project Structure
|
6
|
+
|
7
|
+
The example is structured to separate concerns, making it easier to navigate:
|
8
|
+
|
9
|
+
- `config.ru`: The main Rackup file that loads and runs the Otto application.
|
10
|
+
- `routes`: A comprehensive file demonstrating various advanced routing syntaxes. This file serves as a good reference.
|
11
|
+
- `app.rb`: A simple loader that requires all controller and logic files.
|
12
|
+
- `app/controllers/`: Contains the `RoutesApp` class and other namespaced controller modules that handle incoming requests.
|
13
|
+
- `app/logic/`: Contains various "Logic Classes". These are special classes that can be routed to directly, encapsulating business logic for a specific route. They are organized into namespaces to show how Otto handles complex class names.
|
14
|
+
- `run.rb`, `puma.rb`, `test.rb`: Alternative ways to run or test the application.
|
15
|
+
|
16
|
+
## Key Features Demonstrated
|
17
|
+
|
18
|
+
- **Response Types:** Defining `response=json`, `response=view`, etc., directly in the `routes` file.
|
19
|
+
- **CSRF Exemption:** Using `csrf=exempt` for APIs or webhooks.
|
20
|
+
- **Logic Classes:** Routing directly to a class (e.g., `GET /logic/simple SimpleLogic`). Otto automatically instantiates it and calls its `process` method.
|
21
|
+
- **Namespaced Targets:** Routing to deeply namespaced classes and modules (e.g., `GET /logic/v2/dashboard V2::Logic::Dashboard`).
|
22
|
+
- **Custom Parameters:** Adding arbitrary key-value parameters to a route for custom logic.
|
23
|
+
|
24
|
+
## Running the Example
|
25
|
+
|
26
|
+
1. Make sure you have the necessary gems installed (`bundle install`).
|
27
|
+
2. Run the application from the root of the `otto` project:
|
28
|
+
|
29
|
+
```sh
|
30
|
+
rackup examples/advanced_routes/config.ru
|
31
|
+
```
|
32
|
+
|
33
|
+
3. The application will be running at `http://localhost:9292`. You can use `curl` or your browser to test the various routes defined in the `routes` file.
|
@@ -0,0 +1,232 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'time'
|
5
|
+
|
6
|
+
# Main application class demonstrating advanced routes syntax
|
7
|
+
class RoutesApp
|
8
|
+
# ========================================
|
9
|
+
# BASIC ROUTES
|
10
|
+
# ========================================
|
11
|
+
|
12
|
+
def self.index
|
13
|
+
[200, { 'content-type' => 'text/html' }, ['<h1>Advanced Routes Syntax Example</h1><p>Otto v1.5.0+ Features</p>']]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.receive_feedback
|
17
|
+
[200, { 'content-type' => 'text/plain' }, ['Feedback received']]
|
18
|
+
end
|
19
|
+
|
20
|
+
# ========================================
|
21
|
+
# RESPONSE TYPE ROUTES
|
22
|
+
# ========================================
|
23
|
+
|
24
|
+
def self.list_users
|
25
|
+
users = [
|
26
|
+
{ id: 1, name: 'Alice', role: 'admin' },
|
27
|
+
{ id: 2, name: 'Bob', role: 'user' },
|
28
|
+
]
|
29
|
+
[200, { 'content-type' => 'application/json' }, [users.to_json]]
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.create_user
|
33
|
+
[201, { 'content-type' => 'application/json' }, ['{"message": "User created", "id": 3}']]
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.health_check
|
37
|
+
[200, { 'content-type' => 'application/json' }, ['{"status": "healthy", "timestamp": "' + Time.now.iso8601 + '"}']]
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.update_user
|
41
|
+
[200, { 'content-type' => 'application/json' }, ['{"message": "User updated"}']]
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.delete_user
|
45
|
+
[204, {}, ['']]
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.dashboard
|
49
|
+
[200, { 'content-type' => 'text/html' }, ['<h1>Dashboard</h1><p>HTML view response</p>']]
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.reports
|
53
|
+
[200, { 'content-type' => 'text/html' }, ['<h1>Reports</h1><p>View response type demonstration</p>']]
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.admin_panel
|
57
|
+
[200, { 'content-type' => 'text/html' }, ['<h1>Admin Panel</h1><p>Administrative interface</p>']]
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.login_redirect
|
61
|
+
[302, { 'location' => '/dashboard' }, ['']]
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.logout_redirect
|
65
|
+
[302, { 'location' => '/' }, ['']]
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.home_redirect
|
69
|
+
[302, { 'location' => '/dashboard' }, ['']]
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.flexible_data
|
73
|
+
data = { message: 'This is flexible data', timestamp: Time.now.iso8601 }
|
74
|
+
[200, { 'content-type' => 'application/json' }, [data.to_json]]
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.flexible_content
|
78
|
+
[200, { 'content-type' => 'application/json' }, ['{"content": "Auto response type", "format": "negotiated"}']]
|
79
|
+
end
|
80
|
+
|
81
|
+
# ========================================
|
82
|
+
# CSRF ROUTES
|
83
|
+
# ========================================
|
84
|
+
|
85
|
+
def self.webhook_handler
|
86
|
+
[200, { 'content-type' => 'application/json' }, ['{"message": "Webhook processed (CSRF exempt)"}']]
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.external_update
|
90
|
+
[200, { 'content-type' => 'application/json' }, ['{"message": "External update processed"}']]
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.cleanup_data
|
94
|
+
[200, { 'content-type' => 'application/json' }, ['{"message": "Data cleanup completed"}']]
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.sync_data
|
98
|
+
[200, { 'content-type' => 'application/json' }, ['{"message": "Data sync completed"}']]
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.update_settings
|
102
|
+
[200, { 'content-type' => 'text/plain' }, ['Settings updated (CSRF protected)']]
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.change_password
|
106
|
+
[200, { 'content-type' => 'text/plain' }, ['Password changed (CSRF protected)']]
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.delete_profile
|
110
|
+
[204, {}, ['']]
|
111
|
+
end
|
112
|
+
|
113
|
+
# ========================================
|
114
|
+
# MULTIPLE PARAMETER COMBINATIONS
|
115
|
+
# ========================================
|
116
|
+
|
117
|
+
def self.api_data
|
118
|
+
[200, { 'content-type' => 'application/json' }, ['{"api": "v1", "data": "response"}']]
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.api_submit
|
122
|
+
[201, { 'content-type' => 'application/json' }, ['{"message": "API submission processed"}']]
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.api_update
|
126
|
+
[200, { 'content-type' => 'application/json' }, ['{"message": "API update completed"}']]
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.admin_dashboard
|
130
|
+
[200, { 'content-type' => 'text/html' }, ['<h1>Admin Dashboard</h1><p>Administrative view</p>']]
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.admin_settings
|
134
|
+
[200, { 'content-type' => 'text/html' }, ['<h1>Admin Settings</h1><p>Settings updated</p>']]
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.mixed_content
|
138
|
+
[200, { 'content-type' => 'application/json' }, ['{"type": "mixed", "csrf": "exempt", "response": "auto"}']]
|
139
|
+
end
|
140
|
+
|
141
|
+
# ========================================
|
142
|
+
# CUSTOM PARAMETERS
|
143
|
+
# ========================================
|
144
|
+
|
145
|
+
def self.show_config
|
146
|
+
[200, { 'content-type' => 'application/json' }, ['{"environment": "production", "config": "displayed"}']]
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.debug_info
|
150
|
+
[200, { 'content-type' => 'application/json' }, ['{"environment": "development", "debug": true}']]
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.update_config
|
154
|
+
[200, { 'content-type' => 'application/json' }, ['{"message": "Config updated", "environment": "production"}']]
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.feature_flags
|
158
|
+
[200, { 'content-type' => 'application/json' }, ['{"feature": "advanced", "mode": "enabled", "flags": []}']]
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.toggle_feature
|
162
|
+
[200, { 'content-type' => 'application/json' }, ['{"feature": "beta", "mode": "test", "toggled": true}']]
|
163
|
+
end
|
164
|
+
|
165
|
+
# ========================================
|
166
|
+
# PARAMETER VALUE VARIATIONS
|
167
|
+
# ========================================
|
168
|
+
|
169
|
+
def self.api_v1
|
170
|
+
[200, { 'content-type' => 'application/json' }, ['{"version": "1.0", "api": "v1"}']]
|
171
|
+
end
|
172
|
+
|
173
|
+
def self.api_v2
|
174
|
+
[200, { 'content-type' => 'application/json' }, ['{"version": "2.0", "api": "v2"}']]
|
175
|
+
end
|
176
|
+
|
177
|
+
def self.api_legacy
|
178
|
+
[200, { 'content-type' => 'application/json' }, ['{"version": "legacy", "deprecated": true}']]
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.complex_query
|
182
|
+
[200, { 'content-type' => 'application/json' }, ['{"query": "complex", "filter": "key=value"}']]
|
183
|
+
end
|
184
|
+
|
185
|
+
def self.config_db
|
186
|
+
[200, { 'content-type' => 'application/json' }, ['{"connection": "host=localhost", "configured": true}']]
|
187
|
+
end
|
188
|
+
|
189
|
+
# ========================================
|
190
|
+
# ERROR HANDLERS
|
191
|
+
# ========================================
|
192
|
+
|
193
|
+
def self.not_found
|
194
|
+
[404, { 'content-type' => 'text/html' }, ['<h1>404 - Page Not Found</h1><p>Advanced routes syntax example</p>']]
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.server_error
|
198
|
+
[500, { 'content-type' => 'text/html' }, ['<h1>500 - Server Error</h1><p>Something went wrong</p>']]
|
199
|
+
end
|
200
|
+
|
201
|
+
# ========================================
|
202
|
+
# TESTING ROUTES
|
203
|
+
# ========================================
|
204
|
+
|
205
|
+
def self.test_json
|
206
|
+
[200, { 'content-type' => 'application/json' }, ['{"test": "json", "response_type": "json"}']]
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.test_view
|
210
|
+
[200, { 'content-type' => 'text/html' }, ['<h1>Test View</h1><p>HTML view response</p>']]
|
211
|
+
end
|
212
|
+
|
213
|
+
def self.test_redirect
|
214
|
+
[302, { 'location' => '/' }, ['']]
|
215
|
+
end
|
216
|
+
|
217
|
+
def self.test_auto
|
218
|
+
[200, { 'content-type' => 'application/json' }, ['{"test": "auto", "response_type": "auto"}']]
|
219
|
+
end
|
220
|
+
|
221
|
+
def self.test_csrf
|
222
|
+
[200, { 'content-type' => 'text/html' }, ['<h1>CSRF Test</h1><p>POST request (CSRF protected)</p>']]
|
223
|
+
end
|
224
|
+
|
225
|
+
def self.test_no_csrf
|
226
|
+
[200, { 'content-type' => 'text/html' }, ['<h1>No CSRF Test</h1><p>POST request (CSRF exempt)</p>']]
|
227
|
+
end
|
228
|
+
|
229
|
+
def self.test_everything
|
230
|
+
[200, { 'content-type' => 'application/json' }, ['{"message": "All parameters tested", "csrf": "exempt", "custom": "value"}']]
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Admin
|
4
|
+
module Logic
|
5
|
+
class Manager
|
6
|
+
attr_reader :context, :params, :locale
|
7
|
+
|
8
|
+
def initialize(context, params, locale)
|
9
|
+
@context = context
|
10
|
+
@params = params
|
11
|
+
@locale = locale
|
12
|
+
end
|
13
|
+
|
14
|
+
def process
|
15
|
+
{
|
16
|
+
admin_manager: 'Active',
|
17
|
+
namespace: 'Admin::Logic',
|
18
|
+
management_tools: ['users', 'settings', 'logs'],
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def response_data
|
23
|
+
{ admin_logic_manager: process }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|