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,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Admin
|
4
|
+
class Panel
|
5
|
+
attr_reader :context, :params, :locale
|
6
|
+
|
7
|
+
def initialize(context, params, locale)
|
8
|
+
@context = context
|
9
|
+
@params = params
|
10
|
+
@locale = locale
|
11
|
+
end
|
12
|
+
|
13
|
+
def process
|
14
|
+
{
|
15
|
+
admin_panel: 'Logic processed',
|
16
|
+
namespace: 'Admin',
|
17
|
+
access_level: 'admin',
|
18
|
+
authenticated: @context.authenticated?,
|
19
|
+
has_admin_role: @context.has_role?('admin'),
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def response_data
|
24
|
+
{ admin_panel_logic: process }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Analytics
|
4
|
+
class Processor
|
5
|
+
attr_reader :context, :params, :locale
|
6
|
+
|
7
|
+
def initialize(context, params, locale)
|
8
|
+
@context = context
|
9
|
+
@params = params
|
10
|
+
@locale = locale
|
11
|
+
end
|
12
|
+
|
13
|
+
def process
|
14
|
+
{
|
15
|
+
analytics: 'Processed',
|
16
|
+
metrics: { users: 123, events: 456 },
|
17
|
+
period: @params['period'] || 'today',
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def response_data
|
22
|
+
{ analytics_processor: process }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Complex
|
4
|
+
module Business
|
5
|
+
class Handler
|
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
|
+
complex_business: 'Handled',
|
17
|
+
namespace: 'Complex::Business',
|
18
|
+
business_logic: 'executed',
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def response_data
|
23
|
+
{ complex_business_handler: process }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class DataLogic
|
4
|
+
attr_reader :context, :params, :locale
|
5
|
+
|
6
|
+
def initialize(context, params, locale)
|
7
|
+
@context = context
|
8
|
+
@params = params
|
9
|
+
@locale = locale
|
10
|
+
end
|
11
|
+
|
12
|
+
def process
|
13
|
+
{
|
14
|
+
data_logic: 'Processed',
|
15
|
+
response_format: 'json',
|
16
|
+
input_data: @params,
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def response_data
|
21
|
+
{ data_logic_result: process }
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class DataProcessor
|
4
|
+
attr_reader :context, :params, :locale
|
5
|
+
|
6
|
+
def initialize(context, params, locale)
|
7
|
+
@context = context
|
8
|
+
@params = params
|
9
|
+
@locale = locale
|
10
|
+
end
|
11
|
+
|
12
|
+
def process
|
13
|
+
{
|
14
|
+
processed: 'Data processing complete',
|
15
|
+
input_params: @params.keys,
|
16
|
+
timestamp: Time.now.iso8601,
|
17
|
+
authenticated: @context.authenticated?,
|
18
|
+
user_id: @context.user_id,
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def response_data
|
23
|
+
{ data_processor: process }
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class InputValidator
|
4
|
+
attr_reader :context, :params, :locale
|
5
|
+
|
6
|
+
def initialize(context, params, locale)
|
7
|
+
@context = context
|
8
|
+
@params = params
|
9
|
+
@locale = locale
|
10
|
+
end
|
11
|
+
|
12
|
+
def process
|
13
|
+
{
|
14
|
+
validation: 'Input validated successfully',
|
15
|
+
validated_fields: @params.keys,
|
16
|
+
locale: @locale,
|
17
|
+
authenticated: @context.authenticated?,
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def response_data
|
22
|
+
{ validator: process }
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nested
|
4
|
+
module Feature
|
5
|
+
class Logic
|
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
|
+
nested_feature: 'Processed',
|
17
|
+
depth: 3,
|
18
|
+
namespace: 'Nested::Feature',
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def response_data
|
23
|
+
{ nested_feature_logic: process }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Reports
|
4
|
+
class Generator
|
5
|
+
attr_reader :context, :params, :locale
|
6
|
+
|
7
|
+
def initialize(context, params, locale)
|
8
|
+
@context = context
|
9
|
+
@params = params
|
10
|
+
@locale = locale
|
11
|
+
end
|
12
|
+
|
13
|
+
def process
|
14
|
+
{
|
15
|
+
report_generation: 'Complete',
|
16
|
+
namespace: 'Reports',
|
17
|
+
data_points: 100,
|
18
|
+
authenticated: @context.authenticated?,
|
19
|
+
user_permissions: @context.permissions,
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def response_data
|
24
|
+
{ report_generator: process }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class SimpleLogic
|
4
|
+
attr_reader :context, :params, :locale
|
5
|
+
|
6
|
+
def initialize(context, params, locale)
|
7
|
+
@context = context
|
8
|
+
@params = params
|
9
|
+
@locale = locale
|
10
|
+
end
|
11
|
+
|
12
|
+
def process
|
13
|
+
{
|
14
|
+
message: 'Simple logic processed',
|
15
|
+
params: @params,
|
16
|
+
locale: @locale,
|
17
|
+
authenticated: @context.authenticated?,
|
18
|
+
user: @context.user_name,
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def response_data
|
23
|
+
{ simple_logic: process }
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module System
|
4
|
+
module Config
|
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
|
+
system_config: 'Updated',
|
17
|
+
namespace: 'System::Config',
|
18
|
+
csrf_exempt: true,
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def response_data
|
23
|
+
{ system_config_manager: process }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class TestLogic
|
4
|
+
attr_reader :context, :params, :locale
|
5
|
+
|
6
|
+
def initialize(context, params, locale)
|
7
|
+
@context = context
|
8
|
+
@params = params
|
9
|
+
@locale = locale
|
10
|
+
end
|
11
|
+
|
12
|
+
def process
|
13
|
+
{
|
14
|
+
test: 'Logic class test',
|
15
|
+
params_received: @params,
|
16
|
+
processing_complete: true,
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def response_data
|
21
|
+
{ test_logic: process }
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class TransformLogic
|
4
|
+
attr_reader :context, :params, :locale
|
5
|
+
|
6
|
+
def initialize(context, params, locale)
|
7
|
+
@context = context
|
8
|
+
@params = params
|
9
|
+
@locale = locale
|
10
|
+
end
|
11
|
+
|
12
|
+
def process
|
13
|
+
{
|
14
|
+
transformation: 'Complete',
|
15
|
+
input_size: @params.to_s.length,
|
16
|
+
output_format: 'json',
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def response_data
|
21
|
+
{ transform_result: process }
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class UploadLogic
|
4
|
+
attr_reader :context, :params, :locale
|
5
|
+
|
6
|
+
def initialize(context, params, locale)
|
7
|
+
@context = context
|
8
|
+
@params = params
|
9
|
+
@locale = locale
|
10
|
+
end
|
11
|
+
|
12
|
+
def process
|
13
|
+
{
|
14
|
+
upload: 'Processing complete',
|
15
|
+
csrf_exempt: true,
|
16
|
+
files_processed: @params.keys.count,
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def response_data
|
21
|
+
{ upload_result: process }
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module V2
|
4
|
+
module Logic
|
5
|
+
class Dashboard
|
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
|
+
v2_dashboard: 'Rendered',
|
17
|
+
version: '2.0',
|
18
|
+
features: ['metrics', 'charts', 'reports'],
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def response_data
|
23
|
+
{ v2_dashboard: process }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module V2
|
4
|
+
module Logic
|
5
|
+
class Processor
|
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
|
+
v2_processor: 'Complete',
|
17
|
+
csrf_exempt: true,
|
18
|
+
processing_time: 0.05,
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def response_data
|
23
|
+
{ v2_processor: process }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Application Loader
|
4
|
+
|
5
|
+
# Controllers
|
6
|
+
require_relative 'app/controllers/routes_app'
|
7
|
+
require_relative 'app/controllers/handlers/async'
|
8
|
+
require_relative 'app/controllers/handlers/dynamic'
|
9
|
+
require_relative 'app/controllers/handlers/static'
|
10
|
+
require_relative 'app/controllers/modules/auth'
|
11
|
+
require_relative 'app/controllers/modules/transformer'
|
12
|
+
require_relative 'app/controllers/modules/validator'
|
13
|
+
require_relative 'app/controllers/v2/admin'
|
14
|
+
require_relative 'app/controllers/v2/config'
|
15
|
+
require_relative 'app/controllers/v2/settings'
|
16
|
+
|
17
|
+
# Logic Classes
|
18
|
+
require_relative 'app/logic/admin/logic/manager'
|
19
|
+
require_relative 'app/logic/admin/panel'
|
20
|
+
require_relative 'app/logic/analytics_processor'
|
21
|
+
require_relative 'app/logic/complex/business/handler'
|
22
|
+
require_relative 'app/logic/data_logic'
|
23
|
+
require_relative 'app/logic/data_processor'
|
24
|
+
require_relative 'app/logic/input_validator'
|
25
|
+
require_relative 'app/logic/nested/feature/logic'
|
26
|
+
require_relative 'app/logic/reports_generator'
|
27
|
+
require_relative 'app/logic/simple_logic'
|
28
|
+
require_relative 'app/logic/system/config/manager'
|
29
|
+
require_relative 'app/logic/test_logic'
|
30
|
+
require_relative 'app/logic/transform_logic'
|
31
|
+
require_relative 'app/logic/upload_logic'
|
32
|
+
require_relative 'app/logic/v2/logic/dashboard'
|
33
|
+
require_relative 'app/logic/v2/logic/processor'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rack'
|
4
|
+
require_relative '../../lib/otto'
|
5
|
+
require_relative 'app'
|
6
|
+
|
7
|
+
# Simple Otto configuration demonstrating advanced routes syntax
|
8
|
+
otto = Otto.new('routes')
|
9
|
+
|
10
|
+
# Enable basic security features to demonstrate CSRF functionality
|
11
|
+
otto.enable_csrf_protection!
|
12
|
+
|
13
|
+
# Set error handlers
|
14
|
+
otto.not_found = lambda do |_env|
|
15
|
+
RoutesApp.not_found
|
16
|
+
end
|
17
|
+
|
18
|
+
otto.server_error = lambda do |_env, _error|
|
19
|
+
RoutesApp.server_error
|
20
|
+
end
|
21
|
+
|
22
|
+
# Return the configured app. This allows runner scripts to use the same instance.
|
23
|
+
otto
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'puma'
|
5
|
+
|
6
|
+
# The shared config file returns the configured Otto app
|
7
|
+
otto_app = require_relative 'config'
|
8
|
+
|
9
|
+
# Configure Puma server
|
10
|
+
Puma::Server.new(otto_app).tap do |server|
|
11
|
+
server.add_tcp_listener '127.0.0.1', 9292
|
12
|
+
|
13
|
+
puts "Otto Advanced Routes Example running on http://localhost:9292"
|
14
|
+
puts "Press Ctrl+C to stop"
|
15
|
+
|
16
|
+
# Handle Ctrl+C gracefully
|
17
|
+
trap('INT') { server.stop }
|
18
|
+
|
19
|
+
server.run
|
20
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# Advanced Routes Syntax - Otto v1.5.0+ Features
|
2
|
+
# This file demonstrates the advanced routing syntax without complex authentication
|
3
|
+
|
4
|
+
# ========================================
|
5
|
+
# BASIC ROUTES (Original Otto Syntax)
|
6
|
+
# ========================================
|
7
|
+
|
8
|
+
GET / RoutesApp#index
|
9
|
+
POST /feedback RoutesApp#receive_feedback
|
10
|
+
|
11
|
+
# ========================================
|
12
|
+
# RESPONSE TYPE ROUTES
|
13
|
+
# ========================================
|
14
|
+
|
15
|
+
# JSON response routes
|
16
|
+
GET /api/users RoutesApp#list_users response=json
|
17
|
+
POST /api/users RoutesApp#create_user response=json
|
18
|
+
GET /api/health RoutesApp#health_check response=json
|
19
|
+
PUT /api/users/:id RoutesApp#update_user response=json
|
20
|
+
DELETE /api/users/:id RoutesApp#delete_user response=json
|
21
|
+
|
22
|
+
# View/HTML response routes
|
23
|
+
GET /dashboard RoutesApp#dashboard response=view
|
24
|
+
GET /reports RoutesApp#reports response=view
|
25
|
+
GET /admin RoutesApp#admin_panel response=view
|
26
|
+
|
27
|
+
# Redirect response routes
|
28
|
+
GET /login RoutesApp#login_redirect response=redirect
|
29
|
+
GET /logout RoutesApp#logout_redirect response=redirect
|
30
|
+
GET /home RoutesApp#home_redirect response=redirect
|
31
|
+
|
32
|
+
# Auto response type (content negotiation)
|
33
|
+
GET /data RoutesApp#flexible_data response=auto
|
34
|
+
GET /content RoutesApp#flexible_content response=auto
|
35
|
+
|
36
|
+
# ========================================
|
37
|
+
# CSRF PROTECTION ROUTES
|
38
|
+
# ========================================
|
39
|
+
|
40
|
+
# CSRF exempt routes (useful for APIs and webhooks)
|
41
|
+
POST /api/webhook RoutesApp#webhook_handler csrf=exempt
|
42
|
+
PUT /api/external RoutesApp#external_update csrf=exempt
|
43
|
+
DELETE /api/cleanup RoutesApp#cleanup_data csrf=exempt
|
44
|
+
PATCH /api/sync RoutesApp#sync_data csrf=exempt
|
45
|
+
|
46
|
+
# Standard CSRF protected routes (default behavior)
|
47
|
+
POST /settings RoutesApp#update_settings
|
48
|
+
PUT /password RoutesApp#change_password
|
49
|
+
DELETE /profile RoutesApp#delete_profile
|
50
|
+
|
51
|
+
# ========================================
|
52
|
+
# MULTIPLE PARAMETER COMBINATIONS
|
53
|
+
# ========================================
|
54
|
+
|
55
|
+
# API routes with response type and CSRF exemption
|
56
|
+
GET /api/v1/data RoutesApp#api_data response=json
|
57
|
+
POST /api/v1/submit RoutesApp#api_submit response=json csrf=exempt
|
58
|
+
PUT /api/v1/update RoutesApp#api_update response=json csrf=exempt
|
59
|
+
|
60
|
+
# View routes with multiple parameters
|
61
|
+
GET /admin/dashboard RoutesApp#admin_dashboard response=view
|
62
|
+
POST /admin/settings RoutesApp#admin_settings response=view
|
63
|
+
|
64
|
+
# Mixed content routes
|
65
|
+
GET /mixed/endpoint RoutesApp#mixed_content response=auto csrf=exempt
|
66
|
+
|
67
|
+
# ========================================
|
68
|
+
# LOGIC CLASS ROUTES (New in v1.5.0+)
|
69
|
+
# ========================================
|
70
|
+
|
71
|
+
# Simple Logic class (no . or # in target)
|
72
|
+
GET /logic/simple SimpleLogic
|
73
|
+
POST /logic/process DataProcessor
|
74
|
+
PUT /logic/validate InputValidator
|
75
|
+
|
76
|
+
# Namespaced Logic classes
|
77
|
+
GET /logic/admin Admin::Panel
|
78
|
+
GET /logic/reports Reports::Generator
|
79
|
+
POST /logic/analytics Analytics::Processor
|
80
|
+
|
81
|
+
# Logic classes with parameters
|
82
|
+
GET /logic/data DataLogic response=json
|
83
|
+
POST /logic/upload UploadLogic response=json csrf=exempt
|
84
|
+
PUT /logic/transform TransformLogic response=json
|
85
|
+
|
86
|
+
# Complex namespaced Logic routes
|
87
|
+
GET /logic/v2/dashboard V2::Logic::Dashboard response=view
|
88
|
+
POST /logic/v2/process V2::Logic::Processor response=json csrf=exempt
|
89
|
+
GET /logic/admin/manager Admin::Logic::Manager response=json
|
90
|
+
|
91
|
+
# Deeply nested Logic classes
|
92
|
+
GET /logic/nested/feature Nested::Feature::Logic
|
93
|
+
POST /logic/complex/handler Complex::Business::Handler response=json
|
94
|
+
PUT /logic/system/config System::Config::Manager response=json csrf=exempt
|
95
|
+
|
96
|
+
# ========================================
|
97
|
+
# NAMESPACED CLASS ROUTES
|
98
|
+
# ========================================
|
99
|
+
|
100
|
+
# Class method routes with namespaces
|
101
|
+
GET /v2/admin V2::Admin.show response=view
|
102
|
+
POST /v2/config V2::Config.update response=json
|
103
|
+
PUT /v2/settings V2::Settings.modify response=json csrf=exempt
|
104
|
+
|
105
|
+
# Instance method routes with namespaces
|
106
|
+
GET /modules/auth Modules::Auth#process
|
107
|
+
POST /modules/validator Modules::Validator#validate response=json
|
108
|
+
PUT /modules/transformer Modules::Transformer#transform response=json csrf=exempt
|
109
|
+
|
110
|
+
# Mixed class and instance methods
|
111
|
+
GET /handlers/static Handlers::Static.serve
|
112
|
+
POST /handlers/dynamic Handlers::Dynamic#process response=json
|
113
|
+
PUT /handlers/async Handlers::Async#execute response=json csrf=exempt
|
114
|
+
|
115
|
+
# ========================================
|
116
|
+
# CUSTOM PARAMETERS
|
117
|
+
# ========================================
|
118
|
+
|
119
|
+
# Routes with custom configuration parameters
|
120
|
+
GET /config/env RoutesApp#show_config env=production
|
121
|
+
GET /config/debug RoutesApp#debug_info env=development debug=true
|
122
|
+
POST /config/update RoutesApp#update_config env=production response=json
|
123
|
+
|
124
|
+
# Routes with multiple custom parameters
|
125
|
+
GET /feature/flags RoutesApp#feature_flags feature=advanced mode=enabled
|
126
|
+
POST /feature/toggle RoutesApp#toggle_feature feature=beta mode=test response=json csrf=exempt
|
127
|
+
|
128
|
+
# ========================================
|
129
|
+
# PARAMETER VALUE VARIATIONS
|
130
|
+
# ========================================
|
131
|
+
|
132
|
+
# Parameters with special values
|
133
|
+
GET /api/v1 RoutesApp#api_v1 version=1.0 response=json
|
134
|
+
GET /api/v2 RoutesApp#api_v2 version=2.0 response=json
|
135
|
+
POST /api/legacy RoutesApp#api_legacy version=legacy response=json csrf=exempt
|
136
|
+
|
137
|
+
# Parameters with equals in values (edge case)
|
138
|
+
GET /query/complex RoutesApp#complex_query filter=key=value response=json
|
139
|
+
POST /config/connection RoutesApp#config_db connection=host=localhost csrf=exempt
|
140
|
+
|
141
|
+
# ========================================
|
142
|
+
# ERROR HANDLERS
|
143
|
+
# ========================================
|
144
|
+
|
145
|
+
GET /404 RoutesApp#not_found response=view
|
146
|
+
GET /500 RoutesApp#server_error response=view
|
147
|
+
|
148
|
+
# ========================================
|
149
|
+
# TESTING ROUTES
|
150
|
+
# ========================================
|
151
|
+
|
152
|
+
# Routes for testing different parameter combinations
|
153
|
+
GET /test/json RoutesApp#test_json response=json
|
154
|
+
GET /test/view RoutesApp#test_view response=view
|
155
|
+
GET /test/redirect RoutesApp#test_redirect response=redirect
|
156
|
+
GET /test/auto RoutesApp#test_auto response=auto
|
157
|
+
|
158
|
+
POST /test/csrf RoutesApp#test_csrf
|
159
|
+
POST /test/no-csrf RoutesApp#test_no_csrf csrf=exempt
|
160
|
+
|
161
|
+
GET /test/logic TestLogic
|
162
|
+
POST /test/logic-json TestLogic response=json
|
163
|
+
PUT /test/logic-exempt TestLogic response=json csrf=exempt
|
164
|
+
|
165
|
+
# Complex test routes
|
166
|
+
GET /test/complex TestLogic response=auto
|
167
|
+
POST /test/everything RoutesApp#test_everything response=json csrf=exempt custom=value
|