funapi 0.1.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.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.claude/25-09-01-OPENAPI_IMPLEMENTATION.md +233 -0
  3. data/.claude/25-09-05-RESPONSE_SCHEMA.md +383 -0
  4. data/.claude/25-09-10-OPENAPI_PLAN.md +219 -0
  5. data/.claude/25-10-26-MIDDLEWARE_IMPLEMENTATION.md +230 -0
  6. data/.claude/25-10-26-MIDDLEWARE_PLAN.md +353 -0
  7. data/.claude/25-10-27-BACKGROUND_TASKS_ANALYSIS.md +325 -0
  8. data/.claude/25-10-27-DEPENDENCY_IMPLEMENTATION_SUMMARY.md +325 -0
  9. data/.claude/25-10-27-DEPENDENCY_INJECTION_PLAN.md +753 -0
  10. data/.claude/25-12-24-LIFECYCLE_HOOKS_PLAN.md +421 -0
  11. data/.claude/25-12-24-PUBLISHING_AND_DOGFOODING_PLAN.md +327 -0
  12. data/.claude/25-12-24-TEMPLATE_RENDERING_PLAN.md +704 -0
  13. data/.claude/DECISIONS.md +397 -0
  14. data/.claude/PROJECT_PLAN.md +80 -0
  15. data/.claude/TESTING_PLAN.md +285 -0
  16. data/.claude/TESTING_STATUS.md +157 -0
  17. data/.tool-versions +1 -0
  18. data/AGENTS.md +416 -0
  19. data/CHANGELOG.md +5 -0
  20. data/CODE_OF_CONDUCT.md +132 -0
  21. data/LICENSE.txt +21 -0
  22. data/README.md +660 -0
  23. data/Rakefile +10 -0
  24. data/docs +8 -0
  25. data/docs-site/.gitignore +3 -0
  26. data/docs-site/Gemfile +9 -0
  27. data/docs-site/app.rb +138 -0
  28. data/docs-site/content/essential/handler.md +156 -0
  29. data/docs-site/content/essential/lifecycle.md +161 -0
  30. data/docs-site/content/essential/middleware.md +201 -0
  31. data/docs-site/content/essential/openapi.md +155 -0
  32. data/docs-site/content/essential/routing.md +123 -0
  33. data/docs-site/content/essential/validation.md +166 -0
  34. data/docs-site/content/getting-started/at-glance.md +82 -0
  35. data/docs-site/content/getting-started/key-concepts.md +150 -0
  36. data/docs-site/content/getting-started/quick-start.md +127 -0
  37. data/docs-site/content/index.md +81 -0
  38. data/docs-site/content/patterns/async-operations.md +137 -0
  39. data/docs-site/content/patterns/background-tasks.md +143 -0
  40. data/docs-site/content/patterns/database.md +175 -0
  41. data/docs-site/content/patterns/dependencies.md +141 -0
  42. data/docs-site/content/patterns/deployment.md +212 -0
  43. data/docs-site/content/patterns/error-handling.md +184 -0
  44. data/docs-site/content/patterns/response-schema.md +159 -0
  45. data/docs-site/content/patterns/templates.md +193 -0
  46. data/docs-site/content/patterns/testing.md +218 -0
  47. data/docs-site/mise.toml +2 -0
  48. data/docs-site/public/css/style.css +234 -0
  49. data/docs-site/templates/layouts/docs.html.erb +28 -0
  50. data/docs-site/templates/page.html.erb +3 -0
  51. data/docs-site/templates/partials/_nav.html.erb +19 -0
  52. data/examples/background_tasks_demo.rb +159 -0
  53. data/examples/demo_middleware.rb +55 -0
  54. data/examples/demo_openapi.rb +63 -0
  55. data/examples/dependency_block_demo.rb +150 -0
  56. data/examples/dependency_cleanup_demo.rb +146 -0
  57. data/examples/dependency_injection_demo.rb +200 -0
  58. data/examples/lifecycle_demo.rb +57 -0
  59. data/examples/middleware_demo.rb +74 -0
  60. data/examples/templates/layouts/application.html.erb +66 -0
  61. data/examples/templates/todos/_todo.html.erb +15 -0
  62. data/examples/templates/todos/index.html.erb +12 -0
  63. data/examples/templates_demo.rb +87 -0
  64. data/lib/funapi/application.rb +521 -0
  65. data/lib/funapi/async.rb +57 -0
  66. data/lib/funapi/background_tasks.rb +52 -0
  67. data/lib/funapi/config.rb +23 -0
  68. data/lib/funapi/database/sequel/fibered_connection_pool.rb +87 -0
  69. data/lib/funapi/dependency_wrapper.rb +66 -0
  70. data/lib/funapi/depends.rb +138 -0
  71. data/lib/funapi/exceptions.rb +72 -0
  72. data/lib/funapi/middleware/base.rb +13 -0
  73. data/lib/funapi/middleware/cors.rb +23 -0
  74. data/lib/funapi/middleware/request_logger.rb +32 -0
  75. data/lib/funapi/middleware/trusted_host.rb +34 -0
  76. data/lib/funapi/middleware.rb +4 -0
  77. data/lib/funapi/openapi/schema_converter.rb +85 -0
  78. data/lib/funapi/openapi/spec_generator.rb +179 -0
  79. data/lib/funapi/router.rb +43 -0
  80. data/lib/funapi/schema.rb +65 -0
  81. data/lib/funapi/server/falcon.rb +38 -0
  82. data/lib/funapi/template_response.rb +17 -0
  83. data/lib/funapi/templates.rb +111 -0
  84. data/lib/funapi/version.rb +5 -0
  85. data/lib/funapi.rb +14 -0
  86. data/sig/fun_api.rbs +499 -0
  87. metadata +220 -0
@@ -0,0 +1,219 @@
1
+ ## OpenAPI/Swagger Documentation Generation - Implementation Plan
2
+
3
+ ### Goal
4
+
5
+ Automatically generate OpenAPI 3.0 specification and serve Swagger UI for FunApi applications, similar
6
+ to FastAPI's /docs endpoint.
7
+
8
+ ### Key Design Decisions
9
+
10
+ 1. Architecture Overview
11
+
12
+ • Store route metadata during app initialization
13
+ • Generate OpenAPI spec on-demand (or cache at startup)
14
+ • Serve Swagger UI at /docs and /redoc endpoints
15
+ • Provide JSON spec at /openapi.json
16
+
17
+ 2. Information to Extract from Routes
18
+
19
+ • Path - Route template (e.g., /users/:id)
20
+ • Method - HTTP verb (GET, POST, etc.)
21
+ • Parameters:
22
+ • Path params (from template: /users/:id → id parameter)
23
+ • Query params (from query: schema)
24
+ • Body params (from body: schema)
25
+ • Responses:
26
+ • Response schema (from response_schema:)
27
+ • Status code (from handler return value)
28
+ • Tags - Group endpoints logically (future)
29
+ • Description/Summary - Optional metadata (future)
30
+
31
+ 3. Dry-Schema Introspection Strategy From my research:
32
+
33
+ schema = Dry::Schema.Params { required(:name).filled(:string); required(:age).filled(:integer) }
34
+ # Available introspection:
35
+ # - schema.key_map → list of keys
36
+ # - schema.rules → validation rules (complex nested structure)
37
+ # - schema.types → type information per key
38
+
39
+ Need to build a schema-to-JSON-schema converter that traverses the dry-schema structure.
40
+
41
+ 4. Route Metadata Storage
42
+
43
+ # Update Router to store rich route information
44
+ Route = Struct.new(:verb, :pattern, :keys, :handler, :metadata)
45
+
46
+ # metadata contains:
47
+ {
48
+ path_template: '/users/:id',
49
+ query_schema: QuerySchema,
50
+ body_schema: UserInput,
51
+ response_schema: UserOutput,
52
+ summary: "Create user", # future
53
+ description: "...", # future
54
+ tags: ["users"] # future
55
+ }
56
+
57
+ 5. Implementation Components
58
+
59
+ #### Component 1: Route Metadata Collection
60
+
61
+ # lib/fun_api/openapi/route_metadata.rb
62
+ class RouteMetadata
63
+ attr_reader :verb, :path, :query_schema, :body_schema, :response_schema
64
+
65
+ def initialize(verb:, path:, **schemas)
66
+ @verb = verb
67
+ @path = path
68
+ @query_schema = schemas[:query]
69
+ @body_schema = schemas[:body]
70
+ @response_schema = schemas[:response_schema]
71
+ end
72
+
73
+ def to_openapi_operation
74
+ # Generate OpenAPI Operation Object
75
+ end
76
+ end
77
+
78
+ #### Component 2: Schema to JSON Schema Converter
79
+
80
+ # lib/fun_api/openapi/schema_converter.rb
81
+ class SchemaConverter
82
+ def self.to_json_schema(dry_schema)
83
+ # Convert dry-schema to OpenAPI Schema Object
84
+ # Traverse schema.key_map and schema.types
85
+ # Build JSON Schema representation
86
+ end
87
+
88
+ def self.extract_field_info(key, types)
89
+ # Determine: type, required, format, etc.
90
+ end
91
+ end
92
+
93
+ #### Component 3: OpenAPI Spec Generator
94
+
95
+ # lib/fun_api/openapi/spec_generator.rb
96
+ class SpecGenerator
97
+ def initialize(routes, info: {})
98
+ @routes = routes
99
+ @info = info
100
+ end
101
+
102
+ def generate
103
+ {
104
+ openapi: "3.0.3",
105
+ info: build_info,
106
+ paths: build_paths,
107
+ components: build_components
108
+ }
109
+ end
110
+ end
111
+
112
+ #### Component 4: Documentation Endpoints
113
+
114
+ # Auto-register these routes:
115
+ # GET /docs → Swagger UI HTML
116
+ # GET /redoc → ReDoc HTML
117
+ # GET /openapi.json → OpenAPI spec
118
+
119
+ ### Implementation Sequence
120
+
121
+ 1. Phase 1: Route Metadata Storage ✓ Plan
122
+ • Modify Router.add to store full metadata
123
+ • Update App.add_route to pass all schema info
124
+ • Create RouteMetadata class
125
+ 2. Phase 2: Schema Introspection ✓ Plan
126
+ • Create SchemaConverter class
127
+ • Implement dry-schema → JSON Schema conversion
128
+ • Handle: string, integer, float, boolean, arrays, nested objects
129
+ • Handle: required vs optional fields
130
+ 3. Phase 3: OpenAPI Spec Generation ✓ Plan
131
+ • Create SpecGenerator class
132
+ • Generate paths object from routes
133
+ • Generate components/schemas from all schemas
134
+ • Generate parameter objects for path/query params
135
+ 4. Phase 4: Swagger UI Integration ✓ Plan
136
+ • Add /openapi.json endpoint
137
+ • Add /docs endpoint (Swagger UI)
138
+ • Use CDN-hosted Swagger UI HTML
139
+ 5. Phase 5: Testing & Refinement ✓ Plan
140
+ • Test with complex schemas
141
+ • Test with nested objects
142
+ • Test with arrays
143
+ • Ensure Swagger UI renders correctly
144
+
145
+
146
+ ### Example Output
147
+
148
+ For this route:
149
+
150
+ api.post '/users', body: UserInput, response_schema: UserOutput do |input, req, task|
151
+ # ...
152
+ end
153
+
154
+ Generate:
155
+
156
+ {
157
+ "paths": {
158
+ "/users": {
159
+ "post": {
160
+ "requestBody": {
161
+ "content": {
162
+ "application/json": {
163
+ "schema": { "$ref": "#/components/schemas/UserInput" }
164
+ }
165
+ }
166
+ },
167
+ "responses": {
168
+ "200": {
169
+ "description": "Successful response",
170
+ "content": {
171
+ "application/json": {
172
+ "schema": { "$ref": "#/components/schemas/UserOutput" }
173
+ }
174
+ }
175
+ }
176
+ }
177
+ }
178
+ }
179
+ },
180
+ "components": {
181
+ "schemas": {
182
+ "UserInput": {
183
+ "type": "object",
184
+ "properties": {
185
+ "username": { "type": "string" },
186
+ "email": { "type": "string" },
187
+ "password": { "type": "string" }
188
+ },
189
+ "required": ["username", "email", "password"]
190
+ },
191
+ "UserOutput": {
192
+ "type": "object",
193
+ "properties": {
194
+ "id": { "type": "integer" },
195
+ "username": { "type": "string" },
196
+ "email": { "type": "string" }
197
+ },
198
+ "required": ["id", "username", "email"]
199
+ }
200
+ }
201
+ }
202
+ }
203
+
204
+ ### Questions for You
205
+
206
+ 1. Schema Names: Should we require users to name their schemas explicitly, or auto-generate names from
207
+ constants?
208
+ # Option A: Use constant name
209
+ UserInput = FunApi::Schema.define { ... } # Name: "UserInput"
210
+
211
+ # Option B: Explicit naming
212
+ api.post '/users', body: { schema: UserInput, name: "UserCreateRequest" }
213
+
214
+ 2. Default Info: What should the default info object contain? (title, version, description)
215
+ 3. Docs Path: Should /docs be configurable or fixed?
216
+ 4. Schema Deduplication: If the same schema is used multiple times, should we dedupe in components?
217
+
218
+ Ready to implement when you give the go-ahead! Should I proceed with the implementation in the next
219
+ session (since we're in READ-ONLY mode now)?
@@ -0,0 +1,230 @@
1
+ # Middleware Implementation - Complete ✅
2
+
3
+ ## Summary
4
+
5
+ FunApi now has a comprehensive middleware system that supports both standard Rack middleware and FastAPI-style convenience methods. The implementation leverages Ruby's battle-tested Rack ecosystem while providing an excellent developer experience.
6
+
7
+ ## What Was Implemented
8
+
9
+ ### 1. Core Middleware System
10
+ **Files Modified:**
11
+ - `lib/fun_api/application.rb` - Added `use` method, `build_middleware_chain`, updated `call`
12
+ - `lib/fun_api/router.rb` - Fixed root route (`/`) matching bug
13
+
14
+ **Key Features:**
15
+ - Standard Rack middleware support (LIFO execution order)
16
+ - Automatic keyword argument handling for middleware
17
+ - Middleware stack building with proper wrapping
18
+ - Compatible with entire Rack ecosystem
19
+
20
+ ### 2. Built-in Middleware
21
+
22
+ #### A. CORS Middleware (`lib/fun_api/middleware/cors.rb`)
23
+ - Wraps `rack-cors` gem (battle-tested, 15+ years)
24
+ - FastAPI-style configuration
25
+ - Supports origins, methods, headers, credentials, max_age
26
+
27
+ ```ruby
28
+ api.add_cors(
29
+ allow_origins: ['http://localhost:3000'],
30
+ allow_methods: ['GET', 'POST'],
31
+ allow_headers: ['Content-Type']
32
+ )
33
+ ```
34
+
35
+ #### B. Trusted Host Middleware (`lib/fun_api/middleware/trusted_host.rb`)
36
+ - Validates `Host` header to prevent host header attacks
37
+ - Supports string matching and regex patterns
38
+ - Returns 400 for invalid hosts
39
+
40
+ ```ruby
41
+ api.add_trusted_host(
42
+ allowed_hosts: ['localhost', '127.0.0.1', /\.example\.com$/]
43
+ )
44
+ ```
45
+
46
+ #### C. Request Logger Middleware (`lib/fun_api/middleware/request_logger.rb`)
47
+ - Logs all requests with timing information
48
+ - Configurable logger and log level
49
+ - Format: `METHOD PATH STATUS DURATIONms`
50
+
51
+ ```ruby
52
+ api.add_request_logger(logger: my_logger, level: :debug)
53
+ ```
54
+
55
+ #### D. Gzip Compression (Convenience Method)
56
+ - Delegates to `Rack::Deflater` (built into Rack)
57
+ - Automatically compresses JSON responses
58
+
59
+ ```ruby
60
+ api.add_gzip
61
+ ```
62
+
63
+ ### 3. Dependencies Added
64
+ **File:** `fun_api.gemspec`
65
+ - Added `rack-cors >= 2.0` dependency
66
+
67
+ ### 4. Documentation
68
+ **File:** `README.md`
69
+ - Added comprehensive middleware section
70
+ - Examples for built-in middleware
71
+ - Examples for using standard Rack middleware
72
+ - Examples for custom middleware
73
+ - Updated "Current Status" section
74
+
75
+ ### 5. Examples
76
+ **File:** `examples/middleware_demo.rb`
77
+ - Complete working demo of all middleware features
78
+ - Shows CORS, logging, trusted host in action
79
+ - Demonstrates async operations with middleware
80
+ - Includes validation and OpenAPI integration
81
+
82
+ ### 6. Bug Fixes
83
+ **Router Root Route Fix:**
84
+ - Fixed regex generation for `/` route
85
+ - Previously `/` would generate empty regex `/\A\z/` which never matched
86
+ - Now explicitly handles root route case
87
+
88
+ ## Technical Details
89
+
90
+ ### Middleware Execution Order
91
+
92
+ FunApi uses standard Rack ordering (LIFO - Last In, First Out for wrapping, FIFO for execution):
93
+
94
+ ```ruby
95
+ app.use Middleware1 # Executes FIRST
96
+ app.use Middleware2 # Executes SECOND
97
+ app.use Middleware3 # Executes THIRD
98
+ # Router executes LAST
99
+ ```
100
+
101
+ Request flow: Middleware1 → Middleware2 → Middleware3 → Router → Middleware3 → Middleware2 → Middleware1
102
+
103
+ ### Keyword Argument Handling
104
+
105
+ The middleware system automatically detects and converts keyword arguments:
106
+
107
+ ```ruby
108
+ # User writes:
109
+ api.add_cors(allow_origins: ['*'])
110
+
111
+ # Internally stored as:
112
+ @middleware_stack << [Cors, [{allow_origins: ['*']}], nil]
113
+
114
+ # build_middleware_chain detects hash and converts back:
115
+ Cors.new(app, **{allow_origins: ['*']})
116
+ ```
117
+
118
+ This allows both positional and keyword arguments to work seamlessly.
119
+
120
+ ## Usage Examples
121
+
122
+ ### Basic Middleware Usage
123
+
124
+ ```ruby
125
+ app = FunApi::App.new do |api|
126
+ api.add_cors
127
+ api.add_request_logger
128
+
129
+ api.get '/api/users' do |input, req, task|
130
+ [{ users: [] }, 200]
131
+ end
132
+ end
133
+ ```
134
+
135
+ ### Using Rack Middleware
136
+
137
+ ```ruby
138
+ app = FunApi::App.new do |api|
139
+ # Any Rack middleware works!
140
+ api.use Rack::Attack
141
+ api.use Rack::ETag
142
+ api.use Rack::Session::Cookie, secret: ENV['SECRET']
143
+
144
+ # Custom middleware
145
+ api.use MyCustomMiddleware, option1: 'value'
146
+ end
147
+ ```
148
+
149
+ ### Custom Middleware
150
+
151
+ ```ruby
152
+ class TimingMiddleware
153
+ def initialize(app)
154
+ @app = app
155
+ end
156
+
157
+ def call(env)
158
+ start = Time.now
159
+ status, headers, body = @app.call(env)
160
+ duration = Time.now - start
161
+ headers['X-Response-Time'] = "#{(duration * 1000).round(2)}ms"
162
+ [status, headers, body]
163
+ end
164
+ end
165
+
166
+ app.use TimingMiddleware
167
+ ```
168
+
169
+ ## Testing
170
+
171
+ All middleware has been tested:
172
+ - ✅ CORS headers correctly added
173
+ - ✅ Trusted host blocks invalid hosts
174
+ - ✅ Request logger outputs request info
175
+ - ✅ Middleware chain executes in correct order
176
+ - ✅ Works with async operations
177
+ - ✅ Compatible with validation and response schemas
178
+ - ✅ Integrates with OpenAPI documentation
179
+
180
+ ## Benefits
181
+
182
+ 1. **Rack Ecosystem Access** - All existing Rack middleware (100s of gems) work immediately
183
+ 2. **FastAPI-style DX** - Convenience methods for common use cases
184
+ 3. **Battle-tested** - Delegates to proven libraries (rack-cors, Rack::Deflater)
185
+ 4. **Async Compatible** - Works seamlessly with FunApi's async foundation
186
+ 5. **Zero Magic** - Clear, explicit middleware stack
187
+ 6. **Production Ready** - CORS, logging, host validation out of the box
188
+
189
+ ## Files Created/Modified
190
+
191
+ ### Created:
192
+ 1. `lib/fun_api/middleware.rb` - Main middleware loader
193
+ 2. `lib/fun_api/middleware/base.rb` - Base middleware class
194
+ 3. `lib/fun_api/middleware/cors.rb` - CORS wrapper
195
+ 4. `lib/fun_api/middleware/trusted_host.rb` - Host validation
196
+ 5. `lib/fun_api/middleware/request_logger.rb` - Request logging
197
+ 6. `examples/middleware_demo.rb` - Complete demo application
198
+ 7. `.claude/25-10-26-MIDDLEWARE_IMPLEMENTATION.md` - This document
199
+
200
+ ### Modified:
201
+ 1. `lib/fun_api/application.rb` - Added middleware system + convenience methods
202
+ 2. `lib/fun_api/router.rb` - Fixed root route bug
203
+ 3. `fun_api.gemspec` - Added rack-cors dependency
204
+ 4. `README.md` - Added middleware documentation
205
+
206
+ ## What's Next
207
+
208
+ The middleware system is now production-ready. Future enhancements could include:
209
+
210
+ 1. **More Built-in Middleware:**
211
+ - Rate limiting wrapper (rack-attack)
212
+ - Authentication helpers
213
+ - Request ID tracking
214
+ - Error reporting hooks
215
+
216
+ 2. **Middleware Configuration:**
217
+ - Global middleware vs per-route middleware
218
+ - Middleware groups
219
+ - Conditional middleware
220
+
221
+ 3. **Documentation:**
222
+ - Middleware guide (separate doc)
223
+ - More examples
224
+ - Best practices
225
+
226
+ ## Conclusion
227
+
228
+ FunApi now has a robust, Rack-compatible middleware system that matches FastAPI's developer experience while leveraging Ruby's mature ecosystem. The implementation is clean, well-tested, and production-ready.
229
+
230
+ **Status: ✅ COMPLETE**